/* Copyright (C) 2010 Grame This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "TiPhoneCoreAudioRenderer.h" static void PrintStreamDesc(AudioStreamBasicDescription *inDesc) { printf("- - - - - - - - - - - - - - - - - - - -\n"); printf(" Sample Rate:%f\n", inDesc->mSampleRate); printf(" Format ID:%.*s\n", (int) sizeof(inDesc->mFormatID), (char*)&inDesc->mFormatID); printf(" Format Flags:%lX\n", inDesc->mFormatFlags); printf(" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); printf(" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); printf(" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); printf(" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); printf(" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); printf("- - - - - - - - - - - - - - - - - - - -\n"); } static void printError(OSStatus err) { switch (err) { case kAudioConverterErr_FormatNotSupported: printf("error code : kAudioConverterErr_FormatNotSupported\n"); break; case kAudioConverterErr_OperationNotSupported: printf("error code : kAudioConverterErr_OperationNotSupported\n"); break; case kAudioConverterErr_PropertyNotSupported: printf("error code : kAudioConverterErr_PropertyNotSupported\n"); break; case kAudioConverterErr_InvalidInputSize: printf("error code : kAudioConverterErr_InvalidInputSize\n"); break; case kAudioConverterErr_InvalidOutputSize: printf("error code : kAudioConverterErr_InvalidOutputSize\n"); break; case kAudioConverterErr_UnspecifiedError: printf("error code : kAudioConverterErr_UnspecifiedError\n"); break; case kAudioConverterErr_BadPropertySizeError: printf("error code : kAudioConverterErr_BadPropertySizeError\n"); break; case kAudioConverterErr_RequiresPacketDescriptionsError: printf("error code : kAudioConverterErr_RequiresPacketDescriptionsError\n"); break; case kAudioConverterErr_InputSampleRateOutOfRange: printf("error code : kAudioConverterErr_InputSampleRateOutOfRange\n"); break; case kAudioConverterErr_OutputSampleRateOutOfRange: printf("error code : kAudioConverterErr_OutputSampleRateOutOfRange\n"); break; default: printf("error code : unknown\n"); break; } } OSStatus TiPhoneCoreAudioRenderer::Render(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32, UInt32 inNumberFrames, AudioBufferList *ioData) { TiPhoneCoreAudioRendererPtr renderer = (TiPhoneCoreAudioRendererPtr)inRefCon; AudioUnitRender(renderer->fAUHAL, ioActionFlags, inTimeStamp, 1, inNumberFrames, renderer->fCAInputData); float coef = float(LONG_MAX); float inv_coef = 1.f/float(LONG_MAX); for (int chan = 0; chan < renderer->fDevNumInChans; chan++) { for (int frame = 0; frame < inNumberFrames; frame++) { renderer->fInChannel[chan][frame] = float(((int*)renderer->fCAInputData->mBuffers[chan].mData)[frame]) * inv_coef; } } renderer->PerformAudioCallback((int)inNumberFrames); for (int chan = 0; chan < renderer->fDevNumOutChans; chan++) { for (int frame = 0; frame < inNumberFrames; frame++) { ((int*)ioData->mBuffers[chan].mData)[frame] = int(renderer->fOutChannel[chan][frame] * coef); } } return 0; } void TiPhoneCoreAudioRenderer::InterruptionListener(void *inClientData, UInt32 inInterruption) { TiPhoneCoreAudioRenderer *obj = (TiPhoneCoreAudioRenderer*)inClientData; printf("Session interrupted! --- %s ---", inInterruption == kAudioSessionBeginInterruption ? "Begin Interruption" : "End Interruption"); if (inInterruption == kAudioSessionEndInterruption) { // make sure we are again the active session AudioSessionSetActive(true); AudioOutputUnitStart(obj->fAUHAL); } if (inInterruption == kAudioSessionBeginInterruption) { AudioOutputUnitStop(obj->fAUHAL); } } int TiPhoneCoreAudioRenderer::Open(int bufferSize, int samplerate) { OSStatus err1; UInt32 outSize; UInt32 enableIO; AudioStreamBasicDescription srcFormat, dstFormat; printf("Open fDevNumInChans = %ld fDevNumOutChans = %ld bufferSize = %ld samplerate = %ld\n", fDevNumInChans, fDevNumOutChans, bufferSize, samplerate); // Initialize and configure the audio session err1 = AudioSessionInitialize(NULL, NULL, InterruptionListener, this); if (err1 != noErr) { printf("Couldn't initialize audio session\n"); printError(err1); return OPEN_ERR; } err1 = AudioSessionSetActive(true); if (err1 != noErr) { printf("Couldn't set audio session active\n"); printError(err1); return OPEN_ERR; } UInt32 audioCategory = kAudioSessionCategory_PlayAndRecord; err1 = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(audioCategory), &audioCategory); if (err1 != noErr) { printf("Couldn't set audio category\n"); printError(err1); return OPEN_ERR; } //err1 = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, self), "couldn't set property listener"); Float64 hwSampleRate; outSize = sizeof(hwSampleRate); err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &outSize, &hwSampleRate); if (err1 != noErr) { printf("Couldn't get hw sample rate\n"); printError(err1); return OPEN_ERR; } else { printf("Get hw sample rate %f\n", hwSampleRate); } Float32 hwBufferSize; outSize = sizeof(hwBufferSize); err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration, &outSize, &hwBufferSize); if (err1 != noErr) { printf("Couldn't get hw buffer duration\n"); printError(err1); return OPEN_ERR; } else { printf("Get hw buffer duration %f\n", hwBufferSize); } UInt32 hwInput; outSize = sizeof(hwInput); err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareInputNumberChannels, &outSize, &hwInput); if (err1 != noErr) { printf("Couldn't get hw input channels\n"); printError(err1); return OPEN_ERR; } else { printf("Get hw input channels %d\n", hwInput); } UInt32 hwOutput; outSize = sizeof(hwOutput); err1 = AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareOutputNumberChannels, &outSize, &hwOutput); if (err1 != noErr) { printf("Couldn't get hw output channels\n"); printError(err1); return OPEN_ERR; } else { printf("Get hw output channels %d\n", hwOutput); } Float32 preferredBufferSize = float(bufferSize) / float(samplerate); printf("preferredBufferSize %f \n", preferredBufferSize); err1 = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(preferredBufferSize), &preferredBufferSize); if (err1 != noErr) { printf("Couldn't set i/o buffer duration\n"); printError(err1); return OPEN_ERR; } Float64 preferredSamplerate = float(samplerate); err1 = AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareSampleRate, sizeof(preferredSamplerate), &preferredSamplerate); if (err1 != noErr) { printf("Couldn't set i/o sample rate\n"); printError(err1); return OPEN_ERR; } // AUHAL AudioComponentDescription cd = {kAudioUnitType_Output, kAudioUnitSubType_RemoteIO, kAudioUnitManufacturer_Apple, 0, 0}; AudioComponent HALOutput = AudioComponentFindNext(NULL, &cd); err1 = AudioComponentInstanceNew(HALOutput, &fAUHAL); if (err1 != noErr) { printf("Error calling OpenAComponent\n"); printError(err1); goto error; } enableIO = 1; err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, 0, &enableIO, sizeof(enableIO)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output\n"); printError(err1); goto error; } enableIO = 1; err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enableIO, sizeof(enableIO)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input\n"); printError(err1); goto error; } UInt32 maxFPS; outSize = sizeof(maxFPS); err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFPS, &outSize); if (err1 != noErr) { printf("Couldn't get kAudioUnitProperty_MaximumFramesPerSlice\n"); printError(err1); goto error; } else { printf("Get kAudioUnitProperty_MaximumFramesPerSlice %d\n", maxFPS); } err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, (UInt32*)&bufferSize, sizeof(UInt32)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); printError(err1); goto error; } err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, (UInt32*)&bufferSize, sizeof(UInt32)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_MaximumFramesPerSlice\n"); printError(err1); goto error; } err1 = AudioUnitInitialize(fAUHAL); if (err1 != noErr) { printf("Cannot initialize AUHAL unit\n"); printError(err1); goto error; } // Setting format if (fDevNumInChans > 0) { outSize = sizeof(AudioStreamBasicDescription); err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, &outSize); if (err1 != noErr) { printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); printError(err1); } PrintStreamDesc(&srcFormat); srcFormat.mFormatID = kAudioFormatLinearPCM; srcFormat.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved; srcFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); srcFormat.mFramesPerPacket = 1; srcFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); srcFormat.mChannelsPerFrame = fDevNumInChans; srcFormat.mBitsPerChannel = 32; PrintStreamDesc(&srcFormat); err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &srcFormat, sizeof(AudioStreamBasicDescription)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Output\n"); printError(err1); } } if (fDevNumOutChans > 0) { outSize = sizeof(AudioStreamBasicDescription); err1 = AudioUnitGetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, &outSize); if (err1 != noErr) { printf("Error calling AudioUnitGetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); printError(err1); } PrintStreamDesc(&dstFormat); dstFormat.mFormatID = kAudioFormatLinearPCM; dstFormat.mFormatFlags = kAudioFormatFlagsCanonical | kLinearPCMFormatFlagIsNonInterleaved; dstFormat.mBytesPerPacket = sizeof(AudioUnitSampleType); dstFormat.mFramesPerPacket = 1; dstFormat.mBytesPerFrame = sizeof(AudioUnitSampleType); dstFormat.mChannelsPerFrame = fDevNumOutChans; dstFormat.mBitsPerChannel = 32; PrintStreamDesc(&dstFormat); err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &dstFormat, sizeof(AudioStreamBasicDescription)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_StreamFormat kAudioUnitScope_Input\n"); printError(err1); } } if (fDevNumInChans > 0 && fDevNumOutChans == 0) { AURenderCallbackStruct output; output.inputProc = Render; output.inputProcRefCon = this; err1 = AudioUnitSetProperty(fAUHAL, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &output, sizeof(output)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 1\n"); printError(err1); goto error; } } else { AURenderCallbackStruct output; output.inputProc = Render; output.inputProcRefCon = this; err1 = AudioUnitSetProperty(fAUHAL, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &output, sizeof(output)); if (err1 != noErr) { printf("Error calling AudioUnitSetProperty - kAudioUnitProperty_SetRenderCallback 0\n"); printError(err1); goto error; } } // Prepare buffers fCAInputData = (AudioBufferList*)malloc(sizeof(UInt32) + fDevNumInChans * sizeof(AudioBuffer)); fCAInputData->mNumberBuffers = fDevNumInChans; for (int i = 0; i < fDevNumInChans; i++) { fCAInputData->mBuffers[i].mNumberChannels = 1; fCAInputData->mBuffers[i].mDataByteSize = bufferSize * sizeof(int); fCAInputData->mBuffers[i].mData = malloc(bufferSize * sizeof(int)); } /* // Add listeners err1 = AudioDeviceAddPropertyListener(fDeviceID, 0, true, kAudioDeviceProcessorOverload, DeviceNotificationCallback, this); if (err != noErr) { jack_error("Error calling AudioDeviceAddPropertyListener with kAudioDeviceProcessorOverload"); printError(err); return -1; } */ return NO_ERR; error: AudioUnitUninitialize(fAUHAL); AudioComponentInstanceDispose(fAUHAL); return OPEN_ERR; } int TiPhoneCoreAudioRenderer::Close() { AudioUnitUninitialize(fAUHAL); AudioComponentInstanceDispose(fAUHAL); return NO_ERR; } int TiPhoneCoreAudioRenderer::Start() { AudioSessionSetActive(true); OSStatus err = AudioOutputUnitStart(fAUHAL); if (err != noErr) { printf("Error while opening device : device open error \n"); return OPEN_ERR; } else { return NO_ERR; } } int TiPhoneCoreAudioRenderer::Stop() { OSStatus err = AudioOutputUnitStop(fAUHAL); if (err != noErr) { printf("Error while closing device : device close error \n"); return OPEN_ERR; } else { return NO_ERR; } }