| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- /*
- Simple DirectMedia Layer
- Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- */
- #include "../../SDL_internal.h"
- #include "SDL_audio.h"
- #include "../SDL_audio_c.h"
- #include "../SDL_sysaudio.h"
- #include "SDL_coreaudio.h"
- #include "SDL_assert.h"
- #define DEBUG_COREAUDIO 0
- static void COREAUDIO_CloseDevice(_THIS);
- #define CHECK_RESULT(msg) \
- if (result != noErr) { \
- COREAUDIO_CloseDevice(this); \
- SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
- return 0; \
- }
- #if MACOSX_COREAUDIO
- typedef void (*addDevFn)(const char *name, AudioDeviceID devId, void *data);
- static void
- addToDevList(const char *name, AudioDeviceID devId, void *data)
- {
- SDL_AddAudioDevice addfn = (SDL_AddAudioDevice) data;
- addfn(name);
- }
- typedef struct
- {
- const char *findname;
- AudioDeviceID devId;
- int found;
- } FindDevIdData;
- static void
- findDevId(const char *name, AudioDeviceID devId, void *_data)
- {
- FindDevIdData *data = (FindDevIdData *) _data;
- if (!data->found) {
- if (SDL_strcmp(name, data->findname) == 0) {
- data->found = 1;
- data->devId = devId;
- }
- }
- }
- static void
- build_device_list(int iscapture, addDevFn addfn, void *addfndata)
- {
- OSStatus result = noErr;
- UInt32 size = 0;
- AudioDeviceID *devs = NULL;
- UInt32 i = 0;
- UInt32 max = 0;
- AudioObjectPropertyAddress addr = {
- kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
- result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr,
- 0, NULL, &size);
- if (result != kAudioHardwareNoError)
- return;
- devs = (AudioDeviceID *) alloca(size);
- if (devs == NULL)
- return;
- result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
- 0, NULL, &size, devs);
- if (result != kAudioHardwareNoError)
- return;
- max = size / sizeof (AudioDeviceID);
- 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;
- addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
- kAudioDevicePropertyScopeOutput;
- addr.mSelector = kAudioDevicePropertyStreamConfiguration;
- result = AudioObjectGetPropertyDataSize(dev, &addr, 0, NULL, &size);
- if (result != noErr)
- continue;
- buflist = (AudioBufferList *) SDL_malloc(size);
- if (buflist == NULL)
- continue;
- result = AudioObjectGetPropertyData(dev, &addr, 0, NULL,
- &size, buflist);
- 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;
- addr.mSelector = kAudioObjectPropertyName;
- size = sizeof (CFStringRef);
- result = AudioObjectGetPropertyData(dev, &addr, 0, NULL, &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) {
- 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
- addfn(ptr, dev, addfndata);
- }
- SDL_free(ptr); /* addfn() would have copied the string. */
- }
- }
- static void
- COREAUDIO_DetectDevices(int iscapture, SDL_AddAudioDevice addfn)
- {
- build_device_list(iscapture, addToDevList, addfn);
- }
- 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;
- AudioObjectPropertyAddress addr = {
- 0,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster
- };
- if (devname == NULL) {
- size = sizeof (AudioDeviceID);
- addr.mSelector =
- ((iscapture) ? kAudioHardwarePropertyDefaultInputDevice :
- kAudioHardwarePropertyDefaultOutputDevice);
- result = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr,
- 0, NULL, &size, &devid);
- CHECK_RESULT("AudioHardwareGetProperty (default device)");
- } else {
- FindDevIdData data;
- SDL_zero(data);
- data.findname = devname;
- build_device_list(iscapture, findDevId, &data);
- if (!data.found) {
- SDL_SetError("CoreAudio: No such audio device.");
- return 0;
- }
- devid = data.devId;
- }
- addr.mSelector = kAudioDevicePropertyDeviceIsAlive;
- addr.mScope = iscapture ? kAudioDevicePropertyScopeInput :
- kAudioDevicePropertyScopeOutput;
- size = sizeof (alive);
- result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &size, &alive);
- CHECK_RESULT
- ("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
- if (!alive) {
- SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
- return 0;
- }
- addr.mSelector = kAudioDevicePropertyHogMode;
- size = sizeof (pid);
- result = AudioObjectGetPropertyData(devid, &addr, 0, NULL, &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;
- }
- #endif
- /* The CoreAudio callback */
- static OSStatus
- outputCallback(void *inRefCon,
- AudioUnitRenderActionFlags * ioActionFlags,
- const AudioTimeStamp * inTimeStamp,
- UInt32 inBusNumber, UInt32 inNumberFrames,
- AudioBufferList * ioData)
- {
- SDL_AudioDevice *this = (SDL_AudioDevice *) inRefCon;
- AudioBuffer *abuf;
- UInt32 remaining, len;
- void *ptr;
- UInt32 i;
- /* Only do anything if audio is enabled and not paused */
- if (!this->enabled || this->paused) {
- for (i = 0; i < ioData->mNumberBuffers; i++) {
- abuf = &ioData->mBuffers[i];
- SDL_memset(abuf->mData, this->spec.silence, abuf->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.
- */
- /*
- SDL_assert(!this->convert.needed);
- SDL_assert(this->spec.channels == ioData->mNumberChannels);
- */
- for (i = 0; i < ioData->mNumberBuffers; i++) {
- abuf = &ioData->mBuffers[i];
- remaining = abuf->mDataByteSize;
- ptr = abuf->mData;
- while (remaining > 0) {
- if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
- /* Generate the data */
- SDL_LockMutex(this->mixer_lock);
- (*this->spec.callback)(this->spec.userdata,
- this->hidden->buffer, this->hidden->bufferSize);
- SDL_UnlockMutex(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));
- #if MACOSX_COREAUDIO
- CloseComponent(this->hidden->audioUnit);
- #else
- AudioComponentInstanceDispose(this->hidden->audioUnit);
- #endif
- this->hidden->audioUnitOpened = 0;
- }
- SDL_free(this->hidden->buffer);
- SDL_free(this->hidden);
- this->hidden = NULL;
- }
- }
- static int
- prepare_audiounit(_THIS, const char *devname, int iscapture,
- const AudioStreamBasicDescription * strdesc)
- {
- OSStatus result = noErr;
- AURenderCallbackStruct callback;
- #if MACOSX_COREAUDIO
- ComponentDescription desc;
- Component comp = NULL;
- #else
- AudioComponentDescription desc;
- AudioComponent comp = NULL;
- #endif
- 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 MACOSX_COREAUDIO
- if (!find_device_by_name(this, devname, iscapture)) {
- SDL_SetError("Couldn't find requested CoreAudio device");
- return 0;
- }
- #endif
- SDL_zero(desc);
- desc.componentType = kAudioUnitType_Output;
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- #if MACOSX_COREAUDIO
- desc.componentSubType = kAudioUnitSubType_DefaultOutput;
- comp = FindNextComponent(NULL, &desc);
- #else
- desc.componentSubType = kAudioUnitSubType_RemoteIO;
- comp = AudioComponentFindNext(NULL, &desc);
- #endif
- if (comp == NULL) {
- SDL_SetError("Couldn't find requested CoreAudio component");
- return 0;
- }
- /* Open & initialize the audio unit */
- #if MACOSX_COREAUDIO
- result = OpenAComponent(comp, &this->hidden->audioUnit);
- CHECK_RESULT("OpenAComponent");
- #else
- /*
- 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");
- #endif
- this->hidden->audioUnitOpened = 1;
- #if MACOSX_COREAUDIO
- result = AudioUnitSetProperty(this->hidden->audioUnit,
- kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, 0,
- &this->hidden->deviceID,
- sizeof(AudioDeviceID));
- CHECK_RESULT
- ("AudioUnitSetProperty (kAudioOutputUnitProperty_CurrentDevice)");
- #endif
- /* 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_SetRenderCallback)");
- /* 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) {
- return SDL_OutOfMemory();
- }
- 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);
- return SDL_SetError("Unsupported audio format");
- }
- strdesc.mBytesPerFrame =
- strdesc.mBitsPerChannel * strdesc.mChannelsPerFrame / 8;
- strdesc.mBytesPerPacket =
- strdesc.mBytesPerFrame * strdesc.mFramesPerPacket;
- if (!prepare_audiounit(this, devname, iscapture, &strdesc)) {
- COREAUDIO_CloseDevice(this);
- return -1; /* prepare_audiounit() will call SDL_SetError()... */
- }
- return 0; /* good to go. */
- }
- static int
- COREAUDIO_Init(SDL_AudioDriverImpl * impl)
- {
- /* Set the function pointers */
- impl->OpenDevice = COREAUDIO_OpenDevice;
- impl->CloseDevice = COREAUDIO_CloseDevice;
- #if MACOSX_COREAUDIO
- impl->DetectDevices = COREAUDIO_DetectDevices;
- #else
- impl->OnlyHasDefaultOutputDevice = 1;
- /* Set category to ambient sound so that other music continues playing.
- You can change this at runtime in your own code if you need different
- behavior. If this is common, we can add an SDL hint for this.
- */
- AudioSessionInitialize(NULL, NULL, NULL, nil);
- UInt32 category = kAudioSessionCategory_AmbientSound;
- AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(UInt32), &category);
- #endif
- impl->ProvidesOwnCallbackThread = 1;
- return 1; /* this audio target is available. */
- }
- AudioBootStrap COREAUDIO_bootstrap = {
- "coreaudio", "CoreAudio", COREAUDIO_Init, 0
- };
- /* vi: set ts=4 sw=4 expandtab: */
|