/* * RageSoundDriver_CA.cpp * stepmania * * Created by Steve Checkoway on Tue Aug 26 2003. * Copyright (c) 2003 Steve Checkoway. All rights reserved. * */ #include "global.h" #include "RageSoundDriver_CA.h" #include "RageSoundManager.h" #include "RageSound.h" #include "RageLog.h" #include "RageThreads.h" #include #include #include #if CURRENT_DRIVER == BROKEN_DRIVER_2 #include #endif const Float64 kSampleRate = 44100.000; const UInt32 kChannelsPerFrame = 2; const UInt32 kBitsPerChannel = 16; const UInt32 kSampleSize = kChannelsPerFrame * kBitsPerChannel / 8; const UInt32 kPacketSize = kSampleSize; #if defined(DEBUG) static char *__errorMessage; static char __fourcc[5]; #define TEST_ERR(result) \ CHECKPOINT; \ memcpy(__fourcc, &result, 4); \ __fourcc[4] = '\000'; \ asprintf(&__errorMessage, "err = %d, %s", result, __fourcc); \ RAGE_ASSERT_M(result == noErr, __errorMessage); \ free(__errorMessage) #else #define TEST_ERR(result) #endif RageSound_CA::RageSound_CA() { #if CURRENT_DRIVER != BROKEN_DRIVER_1 && CURRENT_DRIVER != BROKEN_DRIVER_2 RageException::ThrowNonfatal("Class not finished"); #endif #if CURRENT_DRIVER == BROKEN_DRIVER_1 UInt32 thePropertySize = sizeof(AudioDeviceID); OSStatus err; err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &thePropertySize, &outputDevice); TEST_ERR(err); AudioStreamBasicDescription streamFormat, idealFormat; thePropertySize = sizeof(streamFormat); idealFormat.mSampleRate = kSampleRate; idealFormat.mFormatID = kAudioFormatLinearPCM; idealFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; idealFormat.mBytesPerPacket = kPacketSize; idealFormat.mFramesPerPacket = 1; idealFormat.mBytesPerFrame = kPacketSize; idealFormat.mChannelsPerFrame = kChannelsPerFrame; idealFormat.mBitsPerChannel = kBitsPerChannel; idealFormat.mReserved = 0; memcpy(&streamFormat, &idealFormat, sizeof(streamFormat)); err = AudioDeviceGetProperty(outputDevice, 0, 0, kAudioDevicePropertyStreamFormatMatch, &thePropertySize, &streamFormat); TEST_ERR(err); /* Create the converter to convert from the ideal to the actual format */ err = AudioConverterNew(&idealFormat, &streamFormat, &converter); TEST_ERR(err); /*thePropertySize = sizeof(UInt32); UInt32 frames = 1024; err = AudioStreamSetProperty(outputDevice, NULL, kAudioPropertyWildcardChannel, kAudioDevicePropertyBufferFrameSize, thePropertySize, &frames); TEST_ERR(err);*/ /* Calculate the input buffersize */ thePropertySize = sizeof(UInt32); err = AudioDeviceGetProperty(outputDevice, 0, 0, kAudioDevicePropertyBufferSize, &thePropertySize, &buffersize); TEST_ERR(err); err = AudioConverterGetProperty(converter, kAudioConverterPropertyCalculateInputBufferSize, &thePropertySize, &buffersize); TEST_ERR(err); char fourcc[5]; memcpy(fourcc, &streamFormat.mFormatID, 4); fourcc[4] = '\000'; LOG->Info("rate: %f\n" "Format ID: %s\n" "Format Flags: 0x%X\n" "Bytes per packet: %u\n" "Frames per packet: %u\n" "Bytes per frame: %u\n" "Channels per frame: %u\n" "Bits per channel: %u\n", streamFormat.mSampleRate, fourcc, streamFormat.mFormatFlags, streamFormat.mBytesPerPacket, streamFormat.mFramesPerPacket, streamFormat.mBytesPerFrame, streamFormat.mChannelsPerFrame, streamFormat.mBitsPerChannel); thePropertySize = sizeof(streamFormat); err = AudioDeviceSetProperty(outputDevice, NULL, 0, 0, kAudioDevicePropertyStreamFormat, thePropertySize, &streamFormat); TEST_ERR(err); err = AudioDeviceAddIOProc(outputDevice, GetData, this); TEST_ERR(err); err = AudioDeviceStart(outputDevice, GetData); TEST_ERR(err); latency = 0; //for now #elif CURRENT_DRIVER == BROKEN_DRIVER_2 /* Open the default output unit */ OSStatus err; ComponentDescription desc; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; Component comp = FindNextComponent(NULL, &desc); RAGE_ASSERT_M(comp != NULL, "Couldn't find the component"); err = OpenAComponent(comp, &outputUnit); TEST_ERR(err); /* Set the callback */ AURenderCallbackStruct callback; callback.inputProc = GetData; callback.inputProcRefCon = this; err = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)); TEST_ERR(err); /* Set the input format */ AudioStreamBasicDescription idealFormat; idealFormat.mSampleRate = kSampleRate; idealFormat.mFormatID = kAudioFormatLinearPCM; idealFormat.mFormatFlags = kLinearPCMFormatFlagIsBigEndian | kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; idealFormat.mBytesPerPacket = kPacketSize; idealFormat.mFramesPerPacket = 1; idealFormat.mBytesPerFrame = kPacketSize; idealFormat.mChannelsPerFrame = kChannelsPerFrame; idealFormat.mBitsPerChannel = kBitsPerChannel; idealFormat.mReserved = 0; err = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &idealFormat, sizeof(idealFormat)); TEST_ERR(err); /* Calculate the latency */ latency = 0; /* Initialize the unit */ err = AudioUnitInitialize(outputUnit); TEST_ERR(err); /* Get the underlying device */ UInt32 propertySize = sizeof(outputDevice); err = AudioUnitGetProperty(outputUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &outputDevice, &propertySize); TEST_ERR(err); RAGE_ASSERT(outputDevice); /* Start the outputUnit */ AudioOutputUnitStart(outputUnit); #endif } RageSound_CA::~RageSound_CA() { #if CURRENT_DRIVER == BROKEN_DRIVER_1 OSStatus err; err = AudioDeviceStop(outputDevice, GetData); TEST_ERR(err); err = AudioDeviceRemoveIOProc(outputDevice, GetData); TEST_ERR(err); err = AudioConverterDispose(converter); TEST_ERR(err); #elif CURRENT_DRIVER == BROKEN_DRIVER_2 AudioOutputUnitStop(outputUnit); CloseComponent(outputUnit); #endif } void RageSound_CA::StartMixing(RageSound *snd) { LockMutex L(SOUNDMAN->lock); soundPtr s = new sound; s->snd = snd; sounds.push_back(s); } void RageSound_CA::StopMixing(RageSound *snd) { LockMutex L(SOUNDMAN->lock); /* Find the sound. */ unsigned i; for(i = 0; i < sounds.size(); ++i) if(sounds[i]->snd == snd) break; if(i == sounds.size()) { LOG->Trace("not stopping a sound because it's not playing"); return; } delete sounds[i]; sounds.erase(sounds.begin()+i); } int RageSound_CA::GetPosition(const RageSound *snd) const { AudioTimeStamp time; OSStatus err = AudioDeviceGetCurrentTime(outputDevice, &time); TEST_ERR(err); return ConvertAudioTimeStampToPosition(&time); } void RageSound_CA::Update(float delta) { #pragma unused(delta) LockMutex L(SOUNDMAN->lock); vectorsnds = sounds; for (unsigned i = 0; i < snds.size(); ++i) { if (!sounds[i]->stopping) continue; if (GetPosition(snds[i]->snd) < sounds[i]->flush_pos) continue; snds[i]->snd->StopPlaying(); } } #if CURRENT_DRIVER == BROKEN_DRIVER_1 OSStatus RageSound_CA::GetData(AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData) { unsigned outputSize = outOutputData->mBuffers[0].mDataByteSize; if (!SOUNDMAN) { bzero(outOutputData->mBuffers[0].mData, outputSize); return noErr; } RageSound_CA *THIS = (RageSound_CA *)inClientData; char buffer[THIS->buffersize]; bzero(buffer, THIS->buffersize); LockMutex L(SOUNDMAN->lock); static SoundMixBuffer mix; int position = ConvertAudioTimeStampToPosition(inNow); bool moreThanOneSound = THIS->sounds.size() > 1; for (unsigned i=0; isounds.size(); ++i) { if (THIS->sounds[i]->stopping) continue; unsigned got = THIS->sounds[i]->snd->GetPCM(buffer, THIS->buffersize, position); if (moreThanOneSound) mix.write((SInt16 *)buffer, got / 2); if (got < THIS->buffersize) { THIS->sounds[i]->stopping = true; THIS->sounds[i]->flush_pos = position + got / kSampleSize; } } if (moreThanOneSound) mix.read((SInt16 *)buffer); UInt32 size; return AudioConverterConvertBuffer(THIS->converter, THIS->buffersize, buffer, &size, outOutputData->mBuffers[0].mData); } #elif CURRENT_DRIVER == BROKEN_DRIVER_2 OSStatus RageSound_CA::GetData(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData) { unsigned outputSize = ioData->mBuffers[0].mDataByteSize; if (!SOUNDMAN) { bzero(ioData->mBuffers[0].mData, outputSize); *inActionFlags = kAudioUnitRenderAction_OutputIsSilence; return noErr; } RageSound_CA *THIS = (RageSound_CA *)inRefCon; char *buffer = (char *)ioData->mBuffers[0].mData; bzero(buffer, THIS->buffersize); LockMutex L(SOUNDMAN->lock); static SoundMixBuffer mix; int position = int(inTimeStamp->mSampleTime); //This should be in frames bool moreThanOneSound = THIS->sounds.size() > 1; for (unsigned i=0; isounds.size(); ++i) { if (THIS->sounds[i]->stopping) continue; unsigned got = THIS->sounds[i]->snd->GetPCM(buffer, outputSize, position); if (moreThanOneSound) mix.write((SInt16 *)buffer, got / 2); if (got < outputSize) { THIS->sounds[i]->stopping = true; THIS->sounds[i]->flush_pos = position + got / kSampleSize; } } if (moreThanOneSound) mix.read((SInt16 *)buffer); *inActionFlags = kAudioUnitRenderAction_PostRender; return noErr; } #endif inline int RageSound_CA::ConvertAudioTimeStampToPosition(const AudioTimeStamp *time) { return (int)(time->mSampleTime / time->mRateScalar); //return int(time->mSampleTime); }