Mercurial > sdl-ios-xcode
diff src/audio/iphoneos/SDL_coreaudio_iphone.c @ 2765:f55c87ae336b
Final merge of Google Summer of Code 2008 work...
Bring SDL to iPhone and iPod Touch
by Holmes Futrell, mentored by Sam Lantinga
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sat, 04 Oct 2008 06:46:59 +0000 |
parents | |
children | 99210400e8b9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audio/iphoneos/SDL_coreaudio_iphone.c Sat Oct 04 06:46:59 2008 +0000 @@ -0,0 +1,340 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include <AudioUnit/AudioUnit.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +#include "SDL_coreaudio_iphone.h" + +#define DEBUG_COREAUDIO 0 + +static void +COREAUDIO_Deinitialize(void) +{ +} + +/* The CoreAudio callback */ +static OSStatus +outputCallback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioDataList) +{ + SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon; + AudioBuffer *ioData = &ioDataList->mBuffers[0]; + UInt32 remaining, len; + void *ptr; + + /* Is there ever more than one buffer, and what do you do with it? */ + if (ioDataList->mNumberBuffers != 1) { + return noErr; + } + + /* Only do anything if audio is enabled and not paused */ + if (!this->enabled || this->paused) { + SDL_memset(ioData->mData, this->spec.silence, ioData->mDataByteSize); + return 0; + } + + /* No SDL conversion should be needed here, ever, since we accept + any input format in OpenAudio, and leave the conversion to CoreAudio. + */ + /* + assert(!this->convert.needed); + assert(this->spec.channels == ioData->mNumberChannels); + */ + + remaining = ioData->mDataByteSize; + ptr = ioData->mData; + while (remaining > 0) { + if (this->hidden->bufferOffset >= this->hidden->bufferSize) { + /* Generate the data */ + SDL_memset(this->hidden->buffer, this->spec.silence, + this->hidden->bufferSize); + SDL_mutexP(this->mixer_lock); + (*this->spec.callback) (this->spec.userdata, this->hidden->buffer, + this->hidden->bufferSize); + SDL_mutexV(this->mixer_lock); + this->hidden->bufferOffset = 0; + } + + len = this->hidden->bufferSize - this->hidden->bufferOffset; + if (len > remaining) + len = remaining; + SDL_memcpy(ptr, + (char *) this->hidden->buffer + this->hidden->bufferOffset, + len); + ptr = (char *) ptr + len; + remaining -= len; + this->hidden->bufferOffset += len; + } + + return 0; +} + +static OSStatus +inputCallback(void *inRefCon, + AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList * ioData) +{ + //err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer); + // !!! FIXME: write me! + return noErr; +} + + +static void +COREAUDIO_CloseDevice(_THIS) +{ + if (this->hidden != NULL) { + if (this->hidden->audioUnitOpened) { + OSStatus result = noErr; + AURenderCallbackStruct callback; + const AudioUnitElement output_bus = 0; + const AudioUnitElement input_bus = 1; + const int iscapture = this->iscapture; + const AudioUnitElement bus = + ((iscapture) ? input_bus : output_bus); + const AudioUnitScope scope = + ((iscapture) ? kAudioUnitScope_Output : + kAudioUnitScope_Input); + + /* stop processing the audio unit */ + result = AudioOutputUnitStop(this->hidden->audioUnit); + + /* Remove the input callback */ + SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct)); + result = AudioUnitSetProperty(this->hidden->audioUnit, + kAudioUnitProperty_SetRenderCallback, + scope, bus, &callback, + sizeof(callback)); + + //CloseComponent(this->hidden->audioUnit); + this->hidden->audioUnitOpened = 0; + } + SDL_free(this->hidden->buffer); + SDL_free(this->hidden); + this->hidden = NULL; + } +} + + +#define CHECK_RESULT(msg) \ + if (result != noErr) { \ + COREAUDIO_CloseDevice(this); \ + SDL_SetError("CoreAudio error (%s): %d", msg, result); \ + return 0; \ + } + +static int +prepare_audiounit(_THIS, const char *devname, int iscapture, + const AudioStreamBasicDescription * strdesc) +{ + OSStatus result = noErr; + AURenderCallbackStruct callback; + AudioComponentDescription desc; + AudioComponent comp = NULL; + + UInt32 enableIO = 0; + const AudioUnitElement output_bus = 0; + const AudioUnitElement input_bus = 1; + const AudioUnitElement bus = ((iscapture) ? input_bus : output_bus); + const AudioUnitScope scope = ((iscapture) ? kAudioUnitScope_Output : + kAudioUnitScope_Input); + + SDL_memset(&desc, '\0', sizeof(AudioComponentDescription)); + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + comp = AudioComponentFindNext(NULL, &desc); + if (comp == NULL) { + SDL_SetError("Couldn't find requested CoreAudio component"); + return 0; + } + + /* Open & initialize the audio unit */ + /* + AudioComponentInstanceNew only available on iPhone OS 2.0 and Mac OS X 10.6 + We can't use OpenAComponent on iPhone because it is not present + */ + result = AudioComponentInstanceNew(comp, &this->hidden->audioUnit); + CHECK_RESULT("AudioComponentInstanceNew"); + + this->hidden->audioUnitOpened = 1; + + // !!! FIXME: this is wrong? + enableIO = ((iscapture) ? 1 : 0); + result = AudioUnitSetProperty(this->hidden->audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, input_bus, + &enableIO, sizeof(enableIO)); + CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO input)"); + + // !!! FIXME: this is wrong? + enableIO = ((iscapture) ? 0 : 1); + result = AudioUnitSetProperty(this->hidden->audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, output_bus, + &enableIO, sizeof(enableIO)); + CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_EnableIO output)"); + + /*result = AudioUnitSetProperty(this->hidden->audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, 0, + &this->hidden->deviceID, + sizeof(AudioDeviceID)); + + CHECK_RESULT("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)"); */ + + /* Set the data format of the audio unit. */ + result = AudioUnitSetProperty(this->hidden->audioUnit, + kAudioUnitProperty_StreamFormat, + scope, bus, strdesc, sizeof(*strdesc)); + CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)"); + + /* Set the audio callback */ + SDL_memset(&callback, '\0', sizeof(AURenderCallbackStruct)); + callback.inputProc = ((iscapture) ? inputCallback : outputCallback); + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(this->hidden->audioUnit, + kAudioUnitProperty_SetRenderCallback, + scope, bus, &callback, sizeof(callback)); + CHECK_RESULT + ("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)"); + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(&this->spec); + + /* Allocate a sample buffer */ + this->hidden->bufferOffset = this->hidden->bufferSize = this->spec.size; + this->hidden->buffer = SDL_malloc(this->hidden->bufferSize); + + result = AudioUnitInitialize(this->hidden->audioUnit); + CHECK_RESULT("AudioUnitInitialize"); + + /* Finally, start processing of the audio unit */ + result = AudioOutputUnitStart(this->hidden->audioUnit); + CHECK_RESULT("AudioOutputUnitStart"); + /* We're running! */ + return 1; +} + +static int +COREAUDIO_OpenDevice(_THIS, const char *devname, int iscapture) +{ + AudioStreamBasicDescription strdesc; + SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format); + int valid_datatype = 0; + + /* Initialize all variables that we clean on shutdown */ + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + if (this->hidden == NULL) { + SDL_OutOfMemory(); + return (0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Setup a AudioStreamBasicDescription with the requested format */ + SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription)); + strdesc.mFormatID = kAudioFormatLinearPCM; + strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; + strdesc.mChannelsPerFrame = this->spec.channels; + strdesc.mSampleRate = this->spec.freq; + strdesc.mFramesPerPacket = 1; + + while ((!valid_datatype) && (test_format)) { + this->spec.format = test_format; + /* Just a list of valid SDL formats, so people don't pass junk here. */ + switch (test_format) { + case AUDIO_U8: + case AUDIO_S8: + case AUDIO_U16LSB: + case AUDIO_S16LSB: + case AUDIO_U16MSB: + case AUDIO_S16MSB: + case AUDIO_S32LSB: + case AUDIO_S32MSB: + case AUDIO_F32LSB: + case AUDIO_F32MSB: + valid_datatype = 1; + strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format); + if (SDL_AUDIO_ISBIGENDIAN(this->spec.format)) + strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; + + if (SDL_AUDIO_ISFLOAT(this->spec.format)) + strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat; + else if (SDL_AUDIO_ISSIGNED(this->spec.format)) + strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; + break; + } + } + + if (!valid_datatype) { /* shouldn't happen, but just in case... */ + COREAUDIO_CloseDevice(this); + SDL_SetError("Unsupported audio format"); + return 0; + } + + strdesc.mBytesPerFrame = + strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8; + strdesc.mBytesPerPacket = + strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; + + if (!prepare_audiounit(this, devname, iscapture, &strdesc)) { + COREAUDIO_CloseDevice(this); + return 0; /* prepare_audiounit() will call SDL_SetError()... */ + } + + return 1; /* good to go. */ +} + +static int +COREAUDIO_Init(SDL_AudioDriverImpl * impl) +{ + /* Set the function pointers */ + impl->OpenDevice = COREAUDIO_OpenDevice; + impl->CloseDevice = COREAUDIO_CloseDevice; + impl->Deinitialize = COREAUDIO_Deinitialize; + impl->ProvidesOwnCallbackThread = 1; + + /* added for iPhone */ + impl->OnlyHasDefaultInputDevice = 1; + impl->OnlyHasDefaultOutputDevice = 1; + impl->HasCaptureSupport = 0; /* still needs to be written */ + + return 1; +} + +AudioBootStrap COREAUDIOIPHONE_bootstrap = { + "coreaudio-iphoneos", "SDL CoreAudio (iPhone OS) audio driver", + COREAUDIO_Init, 0 +}; + +/* vi: set ts=4 sw=4 expandtab: */