Mercurial > sdl-ios-xcode
diff src/audio/macosx/SDL_coreaudio.c @ 2049:5f6550e5184f
Merged SDL-ryan-multiple-audio-device branch r2803:2871 into the trunk.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Tue, 17 Oct 2006 09:15:21 +0000 |
parents | adf732f1f016 |
children | 72cc2226d6a3 |
line wrap: on
line diff
--- a/src/audio/macosx/SDL_coreaudio.c Sun Oct 01 16:10:41 2006 +0000 +++ b/src/audio/macosx/SDL_coreaudio.c Tue Oct 17 09:15:21 2006 +0000 @@ -21,6 +21,7 @@ */ #include "SDL_config.h" +#include <CoreAudio/CoreAudio.h> #include <AudioUnit/AudioUnit.h> #include "SDL_audio.h" @@ -28,79 +29,233 @@ #include "../SDL_sysaudio.h" #include "SDL_coreaudio.h" - -/* Audio driver functions */ +#define DEBUG_COREAUDIO 0 -static int Core_OpenAudio(_THIS, SDL_AudioSpec * spec); -static void Core_WaitAudio(_THIS); -static void Core_PlayAudio(_THIS); -static Uint8 *Core_GetAudioBuf(_THIS); -static void Core_CloseAudio(_THIS); +typedef struct COREAUDIO_DeviceList +{ + AudioDeviceID id; + const char *name; +} COREAUDIO_DeviceList; -/* Audio driver bootstrap functions */ - -static int -Audio_Available(void) -{ - return (1); -} +static COREAUDIO_DeviceList *inputDevices = NULL; +static int inputDeviceCount = 0; +static COREAUDIO_DeviceList *outputDevices = NULL; +static int outputDeviceCount = 0; static void -Audio_DeleteDevice(SDL_AudioDevice * device) +free_device_list(COREAUDIO_DeviceList **devices, int *devCount) { - SDL_free(device->hidden); - SDL_free(device); + if (*devices) { + int i = *devCount; + while (i--) + SDL_free((void *) (*devices)[i].name); + SDL_free(*devices); + *devices = NULL; + } + *devCount = 0; } -static SDL_AudioDevice * -Audio_CreateDevice(int devindex) + +static void +build_device_list(int iscapture, COREAUDIO_DeviceList **devices, int *devCount) { - SDL_AudioDevice *this; + Boolean outWritable = 0; + OSStatus result = noErr; + UInt32 size = 0; + AudioDeviceID *devs = NULL; + UInt32 i = 0; + UInt32 max = 0; + + free_device_list(devices, devCount); + + result = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, + &size, &outWritable); + + if (result != kAudioHardwareNoError) + return; + + devs = (AudioDeviceID *) alloca(size); + if (devs == NULL) + return; + + max = size / sizeof (AudioDeviceID); + *devices = (COREAUDIO_DeviceList *) SDL_malloc(max * sizeof (**devices)); + if (*devices == NULL) + return; + + result = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, + &size, devs); + if (result != kAudioHardwareNoError) + return; + + for (i = 0; i < max; i++) { + CFStringRef cfstr = NULL; + char *ptr = NULL; + AudioDeviceID dev = devs[i]; + AudioBufferList *buflist = NULL; + int usable = 0; + CFIndex len = 0; + + result = AudioDeviceGetPropertyInfo(dev, 0, iscapture, + kAudioDevicePropertyStreamConfiguration, + &size, &outWritable); + if (result != noErr) + continue; + + buflist = (AudioBufferList *) SDL_malloc(size); + if (buflist == NULL) + continue; + + result = AudioDeviceGetProperty(dev, 0, iscapture, + kAudioDevicePropertyStreamConfiguration, + &size, buflist); - /* Initialize all variables that we clean on shutdown */ - this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice)); - if (this) { - SDL_memset(this, 0, (sizeof *this)); - this->hidden = (struct SDL_PrivateAudioData *) - SDL_malloc((sizeof *this->hidden)); + if (result == noErr) { + UInt32 j; + for (j = 0; j < buflist->mNumberBuffers; j++) { + if (buflist->mBuffers[j].mNumberChannels > 0) { + usable = 1; + break; + } + } + } + + SDL_free(buflist); + + if (!usable) + continue; + + size = sizeof (CFStringRef); + result = AudioDeviceGetProperty(dev, 0, iscapture, + kAudioObjectPropertyName, + &size, &cfstr); + + if (result != kAudioHardwareNoError) + continue; + + len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), + kCFStringEncodingUTF8); + + ptr = (char *) SDL_malloc(len + 1); + usable = ( (ptr != NULL) && + (CFStringGetCString(cfstr,ptr,len+1,kCFStringEncodingUTF8)) ); + + CFRelease(cfstr); + + if (usable) { + len = strlen(ptr); + /* Some devices have whitespace at the end...trim it. */ + while ((len > 0) && (ptr[len-1] == ' ')) { + len--; + } + usable = (len > 0); + } + + if (!usable) { + SDL_free(ptr); + } else { + ptr[len] = '\0'; + + #if DEBUG_COREAUDIO + printf("COREAUDIO: Found %s device #%d: '%s' (devid %d)\n", + ((iscapture) ? "capture" : "output"), + (int) *devCount, ptr, (int) dev); + #endif + + (*devices)[*devCount].id = dev; + (*devices)[*devCount].name = ptr; + (*devCount)++; + } } - if ((this == NULL) || (this->hidden == NULL)) { - SDL_OutOfMemory(); - if (this) { - SDL_free(this); - } - return (0); - } - SDL_memset(this->hidden, 0, (sizeof *this->hidden)); - - /* Set the function pointers */ - this->OpenAudio = Core_OpenAudio; - this->WaitAudio = Core_WaitAudio; - this->PlayAudio = Core_PlayAudio; - this->GetAudioBuf = Core_GetAudioBuf; - this->CloseAudio = Core_CloseAudio; - - this->free = Audio_DeleteDevice; - - return this; } -AudioBootStrap COREAUDIO_bootstrap = { - "coreaudio", "Mac OS X CoreAudio", - Audio_Available, Audio_CreateDevice -}; +static inline void +build_device_lists(void) +{ + build_device_list(0, &outputDevices, &outputDeviceCount); + build_device_list(1, &inputDevices, &inputDeviceCount); +} + + +static inline void +free_device_lists(void) +{ + free_device_list(&outputDevices, &outputDeviceCount); + free_device_list(&inputDevices, &inputDeviceCount); +} + + +static int +find_device_id(const char *devname, int iscapture, AudioDeviceID *id) +{ + int i = ((iscapture) ? inputDeviceCount : outputDeviceCount); + COREAUDIO_DeviceList *devs = ((iscapture) ? inputDevices : outputDevices); + while (i--) { + if (SDL_strcmp(devname, devs->name) == 0) { + *id = devs->id; + return 1; + } + devs++; + } + + return 0; +} + + +static int +COREAUDIO_DetectDevices(int iscapture) +{ + if (iscapture) { + build_device_list(1, &inputDevices, &inputDeviceCount); + return inputDeviceCount; + } else { + build_device_list(0, &outputDevices, &outputDeviceCount); + return outputDeviceCount; + } + + return 0; /* shouldn't ever hit this. */ +} + + +static const char * +COREAUDIO_GetDeviceName(int index, int iscapture) +{ + if ((iscapture) && (index < inputDeviceCount)) { + return inputDevices[index].name; + } else if ((!iscapture) && (index < outputDeviceCount)) { + return outputDevices[index].name; + } + + SDL_SetError("No such device"); + return NULL; +} + + +static void +COREAUDIO_Deinitialize(void) +{ + free_device_lists(); +} + /* The CoreAudio callback */ static OSStatus -audioCallback(void *inRefCon, - AudioUnitRenderActionFlags inActionFlags, +outputCallback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp * inTimeStamp, - UInt32 inBusNumber, AudioBuffer * ioData) + 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); @@ -118,109 +273,251 @@ remaining = ioData->mDataByteSize; ptr = ioData->mData; while (remaining > 0) { - if (bufferOffset >= bufferSize) { + if (this->hidden->bufferOffset >= this->hidden->bufferSize) { /* Generate the data */ - SDL_memset(buffer, this->spec.silence, bufferSize); + SDL_memset(this->hidden->buffer, this->spec.silence, + this->hidden->bufferSize); SDL_mutexP(this->mixer_lock); - (*this->spec.callback) (this->spec.userdata, buffer, bufferSize); + (*this->spec.callback) (this->spec.userdata, this->hidden->buffer, + this->hidden->bufferSize); SDL_mutexV(this->mixer_lock); - bufferOffset = 0; + this->hidden->bufferOffset = 0; } - len = bufferSize - bufferOffset; + len = this->hidden->bufferSize - this->hidden->bufferOffset; if (len > remaining) len = remaining; - SDL_memcpy(ptr, (char *) buffer + bufferOffset, len); + SDL_memcpy(ptr, + (char *) this->hidden->buffer + this->hidden->bufferOffset, + len); ptr = (char *) ptr + len; remaining -= len; - bufferOffset += len; + this->hidden->bufferOffset += len; } return 0; } -/* Dummy functions -- we don't use thread-based audio */ -void -Core_WaitAudio(_THIS) +static OSStatus +inputCallback(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp * inTimeStamp, + UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData) { - return; -} - -void -Core_PlayAudio(_THIS) -{ - return; -} - -Uint8 * -Core_GetAudioBuf(_THIS) -{ - return (NULL); + //err = AudioUnitRender(afr->fAudioUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, afr->fAudioBuffer); + // !!! FIXME: write me! + return noErr; } -void -Core_CloseAudio(_THIS) + +static void +COREAUDIO_CloseDevice(_THIS) { - OSStatus result; - struct AudioUnitInputCallback callback; - - /* stop processing the audio unit */ - result = AudioOutputUnitStop(outputAudioUnit); - if (result != noErr) { - SDL_SetError("Core_CloseAudio: AudioOutputUnitStop"); - return; - } + if (this->hidden != NULL) { + 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); - /* Remove the input callback */ - callback.inputProc = 0; - callback.inputProcRefCon = 0; - result = AudioUnitSetProperty(outputAudioUnit, - kAudioUnitProperty_SetInputCallback, - kAudioUnitScope_Input, - 0, &callback, sizeof(callback)); - if (result != noErr) { - SDL_SetError - ("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)"); - return; + /* 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); + + SDL_free(this->hidden->buffer); + SDL_free(this->hidden); + this->hidden = NULL; } +} - result = CloseComponent(outputAudioUnit); - if (result != noErr) { - SDL_SetError("Core_CloseAudio: CloseComponent"); - return; - } - - SDL_free(buffer); -} #define CHECK_RESULT(msg) \ if (result != noErr) { \ + COREAUDIO_CloseDevice(this); \ SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \ - return -1; \ + return 0; \ + } + +static int +find_device_by_name(_THIS, const char *devname, int iscapture) +{ + AudioDeviceID devid = 0; + OSStatus result = noErr; + UInt32 size = 0; + UInt32 alive = 0; + pid_t pid = 0; + + if (devname == NULL) { + size = sizeof (AudioDeviceID); + const AudioHardwarePropertyID propid = + ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice : + kAudioHardwarePropertyDefaultOutputDevice); + + result = AudioHardwareGetProperty(propid, &size, &devid); + CHECK_RESULT("AudioHardwareGetProperty (default device)"); + } else { + if (!find_device_id(devname, iscapture, &devid)) { + SDL_SetError("CoreAudio: No such audio device."); + return 0; + } } + size = sizeof (alive); + result = AudioDeviceGetProperty(devid, 0, iscapture, + kAudioDevicePropertyDeviceIsAlive, + &size, &alive); + CHECK_RESULT("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)"); -int -Core_OpenAudio(_THIS, SDL_AudioSpec * spec) + if (!alive) { + SDL_SetError("CoreAudio: requested device exists, but isn't alive."); + return 0; + } + + size = sizeof (pid); + result = AudioDeviceGetProperty(devid, 0, iscapture, + kAudioDevicePropertyHogMode, &size, &pid); + + /* some devices don't support this property, so errors are fine here. */ + if ((result == noErr) && (pid != -1)) { + SDL_SetError("CoreAudio: requested device is being hogged."); + return 0; + } + + this->hidden->deviceID = devid; + return 1; +} + + +static int +prepare_audiounit(_THIS, const char *devname, int iscapture, + const AudioStreamBasicDescription *strdesc) { OSStatus result = noErr; - Component comp; + AURenderCallbackStruct callback; ComponentDescription desc; - struct AudioUnitInputCallback callback; + Component comp = NULL; + int use_system_device = 0; + 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); + + if (!find_device_by_name(this, devname, iscapture)) { + SDL_SetError("Couldn't find requested CoreAudio device"); + return 0; + } + + SDL_memset(&desc, '\0', sizeof(ComponentDescription)); + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_HALOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + comp = FindNextComponent(NULL, &desc); + if (comp == NULL) { + SDL_SetError("Couldn't find requested CoreAudio component"); + return 0; + } + + /* Open & initialize the audio unit */ + result = OpenAComponent(comp, &this->hidden->audioUnit); + CHECK_RESULT("OpenAComponent"); + + // !!! 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(spec->format); + 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 */ - memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription)); + SDL_memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription)); strdesc.mFormatID = kAudioFormatLinearPCM; strdesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; - strdesc.mChannelsPerFrame = spec->channels; - strdesc.mSampleRate = spec->freq; + strdesc.mChannelsPerFrame = this->spec.channels; + strdesc.mSampleRate = this->spec.freq; strdesc.mFramesPerPacket = 1; while ((!valid_datatype) && (test_format)) { - spec->format = 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: @@ -234,13 +531,13 @@ case AUDIO_F32LSB: case AUDIO_F32MSB: valid_datatype = 1; - strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(spec->format); - if (SDL_AUDIO_ISBIGENDIAN(spec->format)) + strdesc.mBitsPerChannel = SDL_AUDIO_BITSIZE(this->spec.format); + if (SDL_AUDIO_ISBIGENDIAN(this->spec.format)) strdesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; - if (SDL_AUDIO_ISFLOAT(spec->format)) + if (SDL_AUDIO_ISFLOAT(this->spec.format)) strdesc.mFormatFlags |= kLinearPCMFormatFlagIsFloat; - else if (SDL_AUDIO_ISSIGNED(spec->format)) + else if (SDL_AUDIO_ISSIGNED(this->spec.format)) strdesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; break; } @@ -248,7 +545,7 @@ if (!valid_datatype) { /* shouldn't happen, but just in case... */ SDL_SetError("Unsupported audio format"); - return (-1); + return 0; } strdesc.mBytesPerFrame = @@ -256,52 +553,31 @@ strdesc.mBytesPerPacket = strdesc.mBytesPerFrame * strdesc.mFramesPerPacket; - /* Locate the default output audio unit */ - memset(&desc, '\0', sizeof(ComponentDescription)); - desc.componentType = kAudioUnitComponentType; - desc.componentSubType = kAudioUnitSubType_Output; - desc.componentManufacturer = kAudioUnitID_DefaultOutput; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - - comp = FindNextComponent(NULL, &desc); - if (comp == NULL) { - SDL_SetError - ("Failed to start CoreAudio: FindNextComponent returned NULL"); - return -1; + if (!prepare_audiounit(this, devname, iscapture, &strdesc)) { + return 0; /* prepare_audiounit() will call SDL_SetError()... */ } - /* Open & initialize the default output audio unit */ - result = OpenAComponent(comp, &outputAudioUnit); - CHECK_RESULT("OpenAComponent") - result = AudioUnitInitialize(outputAudioUnit); - CHECK_RESULT("AudioUnitInitialize") - /* Set the input format of the audio unit. */ - result = AudioUnitSetProperty(outputAudioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, &strdesc, sizeof(strdesc)); - CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)") - /* Set the audio callback */ - callback.inputProc = audioCallback; - callback.inputProcRefCon = this; - result = AudioUnitSetProperty(outputAudioUnit, - kAudioUnitProperty_SetInputCallback, - kAudioUnitScope_Input, - 0, &callback, sizeof(callback)); - CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)") - /* Calculate the final parameters for this audio specification */ - SDL_CalculateAudioSpec(spec); - - /* Allocate a sample buffer */ - bufferOffset = bufferSize = this->spec.size; - buffer = SDL_malloc(bufferSize); - - /* Finally, start processing of the audio unit */ - result = AudioOutputUnitStart(outputAudioUnit); - CHECK_RESULT("AudioOutputUnitStart") - /* We're running! */ - return (1); + return 1; /* good to go. */ } +static int +COREAUDIO_Init(SDL_AudioDriverImpl *impl) +{ + /* Set the function pointers */ + impl->DetectDevices = COREAUDIO_DetectDevices; + impl->GetDeviceName = COREAUDIO_GetDeviceName; + impl->OpenDevice = COREAUDIO_OpenDevice; + impl->CloseDevice = COREAUDIO_CloseDevice; + impl->Deinitialize = COREAUDIO_Deinitialize; + impl->ProvidesOwnCallbackThread = 1; + + build_device_lists(); /* do an initial check for devices... */ + + return 1; +} + +AudioBootStrap COREAUDIO_bootstrap = { + "coreaudio", "Mac OS X CoreAudio", COREAUDIO_Init, 0 +}; + /* vi: set ts=4 sw=4 expandtab: */