diff options
Diffstat (limited to '')
-rw-r--r-- | src/output/osx_output_plugin.c (renamed from src/output/osx_plugin.c) | 265 |
1 files changed, 205 insertions, 60 deletions
diff --git a/src/output/osx_plugin.c b/src/output/osx_output_plugin.c index 5284afc29..5ee2f18aa 100644 --- a/src/output/osx_plugin.c +++ b/src/output/osx_output_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2010 The Music Player Daemon Project + * Copyright (C) 2003-2011 The Music Player Daemon Project * http://www.musicpd.org * * This program is free software; you can redistribute it and/or modify @@ -18,6 +18,7 @@ */ #include "config.h" +#include "osx_output_plugin.h" #include "output_api.h" #include <glib.h> @@ -28,6 +29,13 @@ #define G_LOG_DOMAIN "osx" struct osx_output { + struct audio_output base; + + /* configuration settings */ + OSType component_subtype; + /* only applicable with kAudioUnitSubType_HALOutput */ + const char *device_name; + AudioUnit au; GMutex *mutex; GCond *condition; @@ -54,13 +62,36 @@ osx_output_test_default_device(void) return true; } -static void * -osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - G_GNUC_UNUSED const struct config_param *param, - G_GNUC_UNUSED GError **error) +static void +osx_output_configure(struct osx_output *oo, const struct config_param *param) +{ + const char *device = config_get_block_string(param, "device", NULL); + + if (device == NULL || 0 == strcmp(device, "default")) { + oo->component_subtype = kAudioUnitSubType_DefaultOutput; + oo->device_name = NULL; + } + else if (0 == strcmp(device, "system")) { + oo->component_subtype = kAudioUnitSubType_SystemOutput; + oo->device_name = NULL; + } + else { + oo->component_subtype = kAudioUnitSubType_HALOutput; + /* XXX am I supposed to g_strdup() this? */ + oo->device_name = device; + } +} + +static struct audio_output * +osx_output_init(const struct config_param *param, GError **error_r) { struct osx_output *oo = g_new(struct osx_output, 1); + if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) { + g_free(oo); + return NULL; + } + osx_output_configure(oo, param); oo->mutex = g_mutex_new(); oo->condition = g_cond_new(); @@ -69,12 +100,13 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, oo->buffer = NULL; oo->buffer_size = 0; - return oo; + return &oo->base; } -static void osx_output_finish(void *data) +static void +osx_output_finish(struct audio_output *ao) { - struct osx_output *od = data; + struct osx_output *od = (struct osx_output *)ao; g_free(od->buffer); g_mutex_free(od->mutex); @@ -82,22 +114,93 @@ static void osx_output_finish(void *data) g_free(od); } -static void osx_output_cancel(void *data) +static bool +osx_output_set_device(struct osx_output *oo, GError **error) { - struct osx_output *od = data; + bool ret = true; + OSStatus status; + UInt32 size, numdevices; + AudioDeviceID *deviceids = NULL; + char name[256]; + unsigned int i; + + if (oo->component_subtype != kAudioUnitSubType_HALOutput) + goto done; + + /* how many audio devices are there? */ + status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, + &size, + NULL); + if (status != noErr) { + g_set_error(error, osx_output_quark(), status, + "Unable to determine number of OS X audio devices: %s", + GetMacOSStatusCommentString(status)); + ret = false; + goto done; + } - g_mutex_lock(od->mutex); - od->len = 0; - g_mutex_unlock(od->mutex); -} + /* what are the available audio device IDs? */ + numdevices = size / sizeof(AudioDeviceID); + deviceids = g_malloc(size); + status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, + &size, + deviceids); + if (status != noErr) { + g_set_error(error, osx_output_quark(), status, + "Unable to determine OS X audio device IDs: %s", + GetMacOSStatusCommentString(status)); + ret = false; + goto done; + } -static void osx_output_close(void *data) -{ - struct osx_output *od = data; + /* which audio device matches oo->device_name? */ + for (i = 0; i < numdevices; i++) { + size = sizeof(name); + status = AudioDeviceGetProperty(deviceids[i], 0, false, + kAudioDevicePropertyDeviceName, + &size, name); + if (status != noErr) { + g_set_error(error, osx_output_quark(), status, + "Unable to determine OS X device name " + "(device %u): %s", + (unsigned int) deviceids[i], + GetMacOSStatusCommentString(status)); + ret = false; + goto done; + } + if (strcmp(oo->device_name, name) == 0) { + g_debug("found matching device: ID=%u, name=%s", + (unsigned int) deviceids[i], name); + break; + } + } + if (i == numdevices) { + g_warning("Found no audio device with name '%s' " + "(will use default audio device)", + oo->device_name); + goto done; + } - AudioOutputUnitStop(od->au); - AudioUnitUninitialize(od->au); - CloseComponent(od->au); + status = AudioUnitSetProperty(oo->au, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &(deviceids[i]), + sizeof(AudioDeviceID)); + if (status != noErr) { + g_set_error(error, osx_output_quark(), status, + "Unable to set OS X audio output device: %s", + GetMacOSStatusCommentString(status)); + ret = false; + goto done; + } + g_debug("set OS X audio output device ID=%u, name=%s", + (unsigned int) deviceids[i], name); + +done: + if (deviceids != NULL) + g_free(deviceids); + return ret; } static OSStatus @@ -147,61 +250,89 @@ osx_render(void *vdata, } static bool -osx_output_open(void *data, struct audio_format *audio_format, GError **error) +osx_output_enable(struct audio_output *ao, GError **error_r) { - struct osx_output *od = data; - ComponentDescription desc; - Component comp; - AURenderCallbackStruct callback; - AudioStreamBasicDescription stream_description; - OSStatus status; - ComponentResult result; + struct osx_output *oo = (struct osx_output *)ao; + ComponentDescription desc; desc.componentType = kAudioUnitType_Output; - desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentSubType = oo->component_subtype; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; - comp = FindNextComponent(NULL, &desc); + Component comp = FindNextComponent(NULL, &desc); if (comp == 0) { - g_set_error(error, osx_output_quark(), 0, + g_set_error(error_r, osx_output_quark(), 0, "Error finding OS X component"); return false; } - status = OpenAComponent(comp, &od->au); + OSStatus status = OpenAComponent(comp, &oo->au); if (status != noErr) { - g_set_error(error, osx_output_quark(), 0, + g_set_error(error_r, osx_output_quark(), status, "Unable to open OS X component: %s", GetMacOSStatusCommentString(status)); return false; } - status = AudioUnitInitialize(od->au); - if (status != noErr) { - CloseComponent(od->au); - g_set_error(error, osx_output_quark(), 0, - "Unable to initialize OS X audio unit: %s", - GetMacOSStatusCommentString(status)); + if (!osx_output_set_device(oo, error_r)) { + CloseComponent(oo->au); return false; } + AURenderCallbackStruct callback; callback.inputProc = osx_render; - callback.inputProcRefCon = od; + callback.inputProcRefCon = oo; - result = AudioUnitSetProperty(od->au, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Input, 0, - &callback, sizeof(callback)); + ComponentResult result = + AudioUnitSetProperty(oo->au, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, 0, + &callback, sizeof(callback)); if (result != noErr) { - AudioUnitUninitialize(od->au); - CloseComponent(od->au); - g_set_error(error, osx_output_quark(), 0, + CloseComponent(oo->au); + g_set_error(error_r, osx_output_quark(), result, "unable to set callback for OS X audio unit"); return false; } + return true; +} + +static void +osx_output_disable(struct audio_output *ao) +{ + struct osx_output *oo = (struct osx_output *)ao; + + CloseComponent(oo->au); +} + +static void +osx_output_cancel(struct audio_output *ao) +{ + struct osx_output *od = (struct osx_output *)ao; + + g_mutex_lock(od->mutex); + od->len = 0; + g_mutex_unlock(od->mutex); +} + +static void +osx_output_close(struct audio_output *ao) +{ + struct osx_output *od = (struct osx_output *)ao; + + AudioOutputUnitStop(od->au); + AudioUnitUninitialize(od->au); +} + +static bool +osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) +{ + struct osx_output *od = (struct osx_output *)ao; + + AudioStreamBasicDescription stream_description; stream_description.mSampleRate = audio_format->sample_rate; stream_description.mFormatID = kAudioFormatLinearPCM; stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; @@ -215,9 +346,13 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error) stream_description.mBitsPerChannel = 16; break; + case SAMPLE_FORMAT_S32: + stream_description.mBitsPerChannel = 32; + break; + default: - audio_format->format = SAMPLE_FORMAT_S16; - stream_description.mBitsPerChannel = 16; + audio_format->format = SAMPLE_FORMAT_S32; + stream_description.mBitsPerChannel = 32; break; } @@ -231,18 +366,25 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error) stream_description.mBytesPerFrame = stream_description.mBytesPerPacket; stream_description.mChannelsPerFrame = audio_format->channels; - result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, 0, - &stream_description, - sizeof(stream_description)); + ComponentResult result = + AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, 0, + &stream_description, + sizeof(stream_description)); if (result != noErr) { - AudioUnitUninitialize(od->au); - CloseComponent(od->au); - g_set_error(error, osx_output_quark(), 0, + g_set_error(error, osx_output_quark(), result, "Unable to set format on OS X device"); return false; } + OSStatus status = AudioUnitInitialize(od->au); + if (status != noErr) { + g_set_error(error, osx_output_quark(), status, + "Unable to initialize OS X audio unit: %s", + GetMacOSStatusCommentString(status)); + return false; + } + /* create a buffer of 1s */ od->buffer_size = (audio_format->sample_rate) * audio_format_frame_size(audio_format); @@ -253,7 +395,8 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error) status = AudioOutputUnitStart(od->au); if (status != 0) { - g_set_error(error, osx_output_quark(), 0, + AudioUnitUninitialize(od->au); + g_set_error(error, osx_output_quark(), status, "unable to start audio output: %s", GetMacOSStatusCommentString(status)); return false; @@ -263,10 +406,10 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error) } static size_t -osx_output_play(void *data, const void *chunk, size_t size, +osx_output_play(struct audio_output *ao, const void *chunk, size_t size, G_GNUC_UNUSED GError **error) { - struct osx_output *od = data; + struct osx_output *od = (struct osx_output *)ao; size_t start, nbytes; g_mutex_lock(od->mutex); @@ -296,11 +439,13 @@ osx_output_play(void *data, const void *chunk, size_t size, return nbytes; } -const struct audio_output_plugin osxPlugin = { +const struct audio_output_plugin osx_output_plugin = { .name = "osx", .test_default_device = osx_output_test_default_device, .init = osx_output_init, .finish = osx_output_finish, + .enable = osx_output_enable, + .disable = osx_output_disable, .open = osx_output_open, .close = osx_output_close, .play = osx_output_play, |