summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan C. Gordon <icculus@icculus.org>2006-10-03 05:58:44 +0000
committerRyan C. Gordon <icculus@icculus.org>2006-10-03 05:58:44 +0000
commit22abe195199c22949208d5c0d6b4a7cdd91394fc (patch)
treea0afc3f955d86e3c544166d28e4701f68e28e8f9
parent453a0b6759b0f8cfb1e6d7681dd3f078a72b7ded (diff)
downloadsdl-22abe195199c22949208d5c0d6b4a7cdd91394fc.tar.gz
Ton of work on CoreAudio driver for new 1.3 features...most of the
multi-device support is wired up, and the starts of capture support, too. All is still subject to change, and what's there is still a little flakey.
-rw-r--r--configure.in2
-rw-r--r--src/audio/macosx/SDL_coreaudio.c427
-rw-r--r--src/audio/macosx/SDL_coreaudio.h4
3 files changed, 346 insertions, 87 deletions
diff --git a/configure.in b/configure.in
index 4fc57dfc8..ef5559f28 100644
--- a/configure.in
+++ b/configure.in
@@ -2323,7 +2323,7 @@ AC_HELP_STRING([--enable-render-d3d], [enable the Direct3D render driver [[defau
EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-framework,Carbon"
# If either the audio or CD driver is used, add the AudioUnit framework
if test x$enable_audio = xyes -o x$enable_cdrom = xyes; then
- EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit"
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -Wl,-framework,CoreAudio -framework,AudioToolbox -Wl,-framework,AudioUnit"
fi
;;
*-*-mint*)
diff --git a/src/audio/macosx/SDL_coreaudio.c b/src/audio/macosx/SDL_coreaudio.c
index ca219db42..7e4179640 100644
--- a/src/audio/macosx/SDL_coreaudio.c
+++ b/src/audio/macosx/SDL_coreaudio.c
@@ -21,6 +21,7 @@
*/
#include "SDL_config.h"
+#include <CoreAudio/CoreAudio.h>
#include <AudioUnit/AudioUnit.h>
#include "SDL_audio.h"
@@ -29,6 +30,148 @@
#include "SDL_coreaudio.h"
+typedef struct COREAUDIO_DeviceList
+{
+ AudioDeviceID id;
+ const char *name;
+} COREAUDIO_DeviceList;
+
+static COREAUDIO_DeviceList *inputDevices = NULL;
+static int inputDeviceCount = 0;
+static COREAUDIO_DeviceList *outputDevices = NULL;
+static int outputDeviceCount = 0;
+
+static void
+free_device_list(COREAUDIO_DeviceList **devices, int *devCount)
+{
+ if (*devices) {
+ int i = *devCount;
+ while (i--)
+ SDL_free((void *) (*devices)[i].name);
+ SDL_free(*devices);
+ *devices = NULL;
+ }
+ *devCount = 0;
+}
+
+
+static void
+build_device_list(int iscapture, COREAUDIO_DeviceList **devices, int *devCount)
+{
+ 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++) {
+ char *ptr = NULL;
+ AudioDeviceID dev = devs[i];
+ AudioBufferList *buflist = NULL;
+ int usable = 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);
+
+ 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;
+
+ /* !!! FIXME: use CFStrings, instead, and convert to UTF-8. */
+ result = AudioDeviceGetPropertyInfo(dev, 0, iscapture,
+ kAudioDevicePropertyDeviceName,
+ &size, &outWritable);
+
+ if (result != kAudioHardwareNoError)
+ continue;
+
+ ptr = (char *) SDL_malloc(size + 1);
+ if (ptr == NULL)
+ continue;
+
+ result = AudioDeviceGetProperty(dev, 0, iscapture,
+ kAudioDevicePropertyDeviceName,
+ &size, ptr);
+
+ if (result != kAudioHardwareNoError)
+ continue;
+
+ while ((size > 0) && (ptr[size-1] == ' '))
+ size--; /* I have a USB device with whitespace at the end... */
+
+ if (size == 0) {
+ SDL_free(ptr);
+ } else {
+ ptr[size] = '\0';
+ (*devices)[*devCount].id = dev;
+ (*devices)[*devCount].name = ptr;
+ (*devCount)++;
+ }
+ }
+}
+
+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;
+}
+
+
/* Audio driver functions */
static int COREAUDIO_OpenAudio(_THIS, const char *devname, int iscapture);
@@ -48,6 +191,10 @@ COREAUDIO_Available(void)
static int
COREAUDIO_Init(SDL_AudioDriverImpl *impl)
{
+ /* !!! FIXME: should these _really_ be static? */
+ build_device_list(0, &outputDevices, &outputDeviceCount);
+ build_device_list(1, &inputDevices, &inputDeviceCount);
+
/* Set the function pointers */
impl->OpenAudio = COREAUDIO_OpenAudio;
impl->WaitAudio = COREAUDIO_WaitAudio;
@@ -65,15 +212,22 @@ AudioBootStrap COREAUDIO_bootstrap = {
/* 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;
+ if (ioDataList->mNumberBuffers != 1) {
+ fprintf(stderr, "!!! FIXME SDL!\n");
+ 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);
@@ -116,6 +270,19 @@ audioCallback(void *inRefCon,
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;
+}
+
+
/* Dummy functions -- we don't use thread-based audio */
void
COREAUDIO_WaitAudio(_THIS)
@@ -138,59 +305,191 @@ COREAUDIO_GetAudioBuf(_THIS)
void
COREAUDIO_CloseAudio(_THIS)
{
- OSStatus result;
- struct AudioUnitInputCallback callback;
+ if (this->hidden != NULL) {
+ OSStatus result = noErr;
+ AURenderCallbackStruct callback;
+ const AudioUnitElement output_bus = 0;
+ const AudioUnitElement input_bus = 1;
+ const int iscapture = this->hidden->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 */
+ 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;
+ }
+}
- if (this->hidden == NULL) {
- return;
+
+#define CHECK_RESULT(msg) \
+ if (result != noErr) { \
+ COREAUDIO_CloseAudio(this); \
+ SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
+ return 0; \
}
- /* stop processing the audio unit */
- result = AudioOutputUnitStop(this->hidden->outputAudioUnit);
- if (result != noErr) {
- SDL_SetError("COREAUDIO_CloseAudio: AudioOutputUnitStop");
- return;
+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;
+ }
}
- /* Remove the input callback */
- callback.inputProc = 0;
- callback.inputProcRefCon = 0;
- result = AudioUnitSetProperty(this->hidden->outputAudioUnit,
- kAudioUnitProperty_SetInputCallback,
- kAudioUnitScope_Input,
- 0, &callback, sizeof(callback));
- if (result != noErr) {
- SDL_SetError
- ("COREAUDIO_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
- return;
+ size = sizeof (alive);
+ result = AudioDeviceGetProperty(devid, 0, iscapture,
+ kAudioDevicePropertyDeviceIsAlive,
+ &size, &alive);
+ CHECK_RESULT("AudioDeviceGetProperty (kAudioDevicePropertyDeviceIsAlive)");
+
+ if (!alive) {
+ SDL_SetError("CoreAudio: requested device exists, but isn't alive.");
+ return 0;
}
- result = CloseComponent(this->hidden->outputAudioUnit);
- if (result != noErr) {
- SDL_SetError("COREAUDIO_CloseAudio: CloseComponent");
- return;
+ 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;
}
- SDL_free(this->hidden->buffer);
- SDL_free(this->hidden);
- this->hidden = NULL;
+ this->hidden->deviceID = devid;
+ return 1;
}
-#define CHECK_RESULT(msg) \
- if (result != noErr) { \
- COREAUDIO_CloseAudio(this); \
- SDL_SetError("CoreAudio error (%s): %d", msg, (int) result); \
- return -1; \
+
+static int
+prepare_audiounit(_THIS, const char *devname, int iscapture,
+ const AudioStreamBasicDescription *strdesc)
+{
+ OSStatus result = noErr;
+ AURenderCallbackStruct callback;
+ ComponentDescription desc;
+ 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);
+
+ /* !!! FIXME: move something like this to higher level. */
+ if ( (devname == NULL) && (SDL_getenv("SDL_AUDIO_DEVNAME")) )
+ devname = SDL_getenv("SDL_AUDIO_DEVNAME");
+
+ if (!find_device_by_name(this, devname, iscapture)) {
+ SDL_SetError("Couldn't find requested CoreAudio device");
+ return 0;
}
+ 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 */
+ 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;
+}
+
int
COREAUDIO_OpenAudio(_THIS, const char *devname, int iscapture)
{
- OSStatus result = noErr;
- Component comp;
- ComponentDescription desc;
- struct AudioUnitInputCallback callback;
AudioStreamBasicDescription strdesc;
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
int valid_datatype = 0;
@@ -204,7 +503,7 @@ COREAUDIO_OpenAudio(_THIS, const char *devname, int iscapture)
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
- /* !!! FIXME: check devname and iscapture... */
+ this->hidden->isCapture = iscapture;
/* Setup a AudioStreamBasicDescription with the requested format */
memset(&strdesc, '\0', sizeof(AudioStreamBasicDescription));
@@ -251,53 +550,11 @@ COREAUDIO_OpenAudio(_THIS, const char *devname, int iscapture)
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) {
- COREAUDIO_CloseAudio(this);
- SDL_SetError
- ("Failed to start CoreAudio: FindNextComponent returned NULL");
- return 0;
+ 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, &this->hidden->outputAudioUnit);
- CHECK_RESULT("OpenAComponent")
- result = AudioUnitInitialize(this->hidden->outputAudioUnit);
- CHECK_RESULT("AudioUnitInitialize")
- /* Set the input format of the audio unit. */
- result = AudioUnitSetProperty(this->hidden->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(this->hidden->outputAudioUnit,
- kAudioUnitProperty_SetInputCallback,
- kAudioUnitScope_Input,
- 0, &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);
-
- /* Finally, start processing of the audio unit */
- result = AudioOutputUnitStart(this->hidden->outputAudioUnit);
- CHECK_RESULT("AudioOutputUnitStart")
- /* We're running! */
- return (1);
+ return 1; /* good to go. */
}
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/audio/macosx/SDL_coreaudio.h b/src/audio/macosx/SDL_coreaudio.h
index 0e5460594..33b70f262 100644
--- a/src/audio/macosx/SDL_coreaudio.h
+++ b/src/audio/macosx/SDL_coreaudio.h
@@ -31,10 +31,12 @@
struct SDL_PrivateAudioData
{
- AudioUnit outputAudioUnit;
+ AudioUnit audioUnit;
void *buffer;
UInt32 bufferOffset;
UInt32 bufferSize;
+ AudioDeviceID deviceID;
+ int isCapture;
};
#endif /* _SDL_coreaudio_h */