diff options
Diffstat (limited to 'src/modules/coreaudio')
-rw-r--r-- | src/modules/coreaudio/module-coreaudio-detect.c | 268 | ||||
-rw-r--r-- | src/modules/coreaudio/module-coreaudio-device.c | 866 |
2 files changed, 0 insertions, 1134 deletions
diff --git a/src/modules/coreaudio/module-coreaudio-detect.c b/src/modules/coreaudio/module-coreaudio-detect.c deleted file mode 100644 index 263fc72d..00000000 --- a/src/modules/coreaudio/module-coreaudio-detect.c +++ /dev/null @@ -1,268 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009,2010 Daniel Mack <daniel@caiaq.de> - - PulseAudio 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. - - PulseAudio 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 - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <pulse/xmalloc.h> - -#include <pulsecore/module.h> -#include <pulsecore/core-util.h> -#include <pulsecore/modargs.h> -#include <pulsecore/log.h> -#include <pulsecore/llist.h> - -#include <CoreAudio/CoreAudio.h> - -#include "module-coreaudio-detect-symdef.h" - -#define DEVICE_MODULE_NAME "module-coreaudio-device" - -PA_MODULE_AUTHOR("Daniel Mack"); -PA_MODULE_DESCRIPTION("CoreAudio device detection"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(TRUE); -PA_MODULE_USAGE(""); - -typedef struct ca_device ca_device; - -struct ca_device { - AudioObjectID id; - unsigned int module_index; - PA_LLIST_FIELDS(ca_device); -}; - -struct userdata { - int detect_fds[2]; - pa_io_event *detect_io; - - PA_LLIST_HEAD(ca_device, devices); -}; - -static int ca_device_added(struct pa_module *m, AudioObjectID id) { - AudioObjectPropertyAddress property_address; - OSStatus err; - pa_module *mod; - struct userdata *u; - struct ca_device *dev; - char *args, tmp[64]; - UInt32 size; - - pa_assert(m); - pa_assert_se(u = m->userdata); - - /* To prevent generating a black hole that will suck us in, - don't create sources/sinks for PulseAudio virtual devices */ - - property_address.mSelector = kAudioDevicePropertyDeviceManufacturer; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - size = sizeof(tmp); - err = AudioObjectGetPropertyData(id, &property_address, 0, NULL, &size, tmp); - - if (!err && strcmp(tmp, "pulseaudio.org") == 0) - return 0; - - args = pa_sprintf_malloc("object_id=%d", (int) id); - pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME, args); - mod = pa_module_load(m->core, DEVICE_MODULE_NAME, args); - pa_xfree(args); - - if (!mod) { - pa_log_info("Failed to load module %s with arguments '%s'", DEVICE_MODULE_NAME, args); - return -1; - } - - dev = pa_xnew0(ca_device, 1); - dev->module_index = mod->index; - dev->id = id; - - PA_LLIST_INIT(ca_device, dev); - PA_LLIST_PREPEND(ca_device, u->devices, dev); - - return 0; -} - -static int ca_update_device_list(struct pa_module *m) { - AudioObjectPropertyAddress property_address; - OSStatus err; - UInt32 i, size, num_devices; - AudioDeviceID *device_id; - struct ca_device *dev; - struct userdata *u; - - pa_assert(m); - pa_assert_se(u = m->userdata); - - property_address.mSelector = kAudioHardwarePropertyDevices; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - /* get the number of currently available audio devices */ - err = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &property_address, 0, NULL, &size); - if (err) { - pa_log("Unable to get data size for kAudioHardwarePropertyDevices."); - return -1; - } - - num_devices = size / sizeof(AudioDeviceID); - device_id = pa_xnew(AudioDeviceID, num_devices); - - err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &property_address, 0, NULL, &size, device_id); - if (err) { - pa_log("Unable to get kAudioHardwarePropertyDevices."); - pa_xfree(device_id); - return -1; - } - - /* scan for devices which are reported but not in our cached list */ - for (i = 0; i < num_devices; i++) { - bool found = FALSE; - - PA_LLIST_FOREACH(dev, u->devices) - if (dev->id == device_id[i]) { - found = TRUE; - break; - } - - if (!found) - ca_device_added(m, device_id[i]); - } - - /* scan for devices which are in our cached list but are not reported */ -scan_removed: - - PA_LLIST_FOREACH(dev, u->devices) { - bool found = FALSE; - - for (i = 0; i < num_devices; i++) - if (dev->id == device_id[i]) { - found = TRUE; - break; - } - - if (!found) { - pa_log_debug("object id %d has been removed (module index %d) %p", (unsigned int) dev->id, dev->module_index, dev); - pa_module_unload_request_by_index(m->core, dev->module_index, TRUE); - PA_LLIST_REMOVE(ca_device, u->devices, dev); - pa_xfree(dev); - /* the current list item pointer is not valid anymore, so start over. */ - goto scan_removed; - } - } - - pa_xfree(device_id); - return 0; -} - -static OSStatus property_listener_proc(AudioObjectID objectID, UInt32 numberAddresses, - const AudioObjectPropertyAddress inAddresses[], - void *clientData) -{ - struct userdata *u = clientData; - char dummy = 1; - - pa_assert(u); - - /* dispatch module load/unload operations in main thread */ - write(u->detect_fds[1], &dummy, 1); - - return 0; -} - -static void detect_handle(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { - pa_module *m = userdata; - char dummy; - - pa_assert(m); - - read(fd, &dummy, 1); - ca_update_device_list(m); -} - -int pa__init(pa_module *m) { - struct userdata *u = pa_xnew0(struct userdata, 1); - AudioObjectPropertyAddress property_address; - - pa_assert(m); - - m->userdata = u; - - property_address.mSelector = kAudioHardwarePropertyDevices; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - if (AudioObjectAddPropertyListener(kAudioObjectSystemObject, &property_address, property_listener_proc, u)) { - pa_log("AudioObjectAddPropertyListener() failed."); - goto fail; - } - - if (ca_update_device_list(m)) - goto fail; - - pa_assert_se(pipe(u->detect_fds) == 0); - pa_assert_se(u->detect_io = m->core->mainloop->io_new(m->core->mainloop, u->detect_fds[0], PA_IO_EVENT_INPUT, detect_handle, m)); - - return 0; - -fail: - pa_xfree(u); - return -1; -} - -void pa__done(pa_module *m) { - struct userdata *u; - struct ca_device *dev; - AudioObjectPropertyAddress property_address; - - pa_assert(m); - pa_assert_se(u = m->userdata); - - dev = u->devices; - - property_address.mSelector = kAudioHardwarePropertyDevices; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &property_address, property_listener_proc, u); - - while (dev) { - struct ca_device *next = dev->next; - - pa_module_unload_request_by_index(m->core, dev->module_index, TRUE); - pa_xfree(dev); - - dev = next; - } - - if (u->detect_fds[0] >= 0) - close(u->detect_fds[0]); - - if (u->detect_fds[1] >= 0) - close(u->detect_fds[1]); - - if (u->detect_io) - m->core->mainloop->io_free(u->detect_io); - - pa_xfree(u); -} diff --git a/src/modules/coreaudio/module-coreaudio-device.c b/src/modules/coreaudio/module-coreaudio-device.c deleted file mode 100644 index cc4600b5..00000000 --- a/src/modules/coreaudio/module-coreaudio-device.c +++ /dev/null @@ -1,866 +0,0 @@ -/*** - This file is part of PulseAudio. - - Copyright 2009,2010 Daniel Mack <daniel@caiaq.de> - - PulseAudio 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. - - PulseAudio 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 - General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PulseAudio; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -/* TODO: - - implement hardware volume controls - - handle audio device stream format changes (will require changes to the core) -*/ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <pulse/xmalloc.h> -#include <pulse/util.h> - -#include <pulsecore/core-error.h> -#include <pulsecore/sink.h> -#include <pulsecore/source.h> -#include <pulsecore/module.h> -#include <pulsecore/sample-util.h> -#include <pulsecore/core-util.h> -#include <pulsecore/modargs.h> -#include <pulsecore/log.h> -#include <pulsecore/macro.h> -#include <pulsecore/llist.h> -#include <pulsecore/card.h> -#include <pulsecore/strbuf.h> -#include <pulsecore/thread.h> -#include <pulsecore/thread-mq.h> - -#include <CoreAudio/CoreAudio.h> -#include <CoreAudio/CoreAudioTypes.h> -#include <CoreAudio/AudioHardware.h> - -#include "module-coreaudio-device-symdef.h" - -#define DEFAULT_FRAMES_PER_IOPROC 512 - -PA_MODULE_AUTHOR("Daniel Mack"); -PA_MODULE_DESCRIPTION("CoreAudio device"); -PA_MODULE_VERSION(PACKAGE_VERSION); -PA_MODULE_LOAD_ONCE(FALSE); -PA_MODULE_USAGE("object_id=<the CoreAudio device id> " - "ioproc_frames=<audio frames per IOProc call> "); - -static const char* const valid_modargs[] = { - "object_id", - "ioproc_frames", - NULL -}; - -enum { - CA_MESSAGE_RENDER = PA_SINK_MESSAGE_MAX, -}; - -typedef struct coreaudio_sink coreaudio_sink; -typedef struct coreaudio_source coreaudio_source; - -struct userdata { - AudioObjectID object_id; - AudioDeviceIOProcID proc_id; - - pa_thread_mq thread_mq; - pa_asyncmsgq *async_msgq; - - pa_rtpoll *rtpoll; - pa_thread *thread; - - pa_module *module; - pa_card *card; - pa_bool_t running; - - char *device_name, *vendor_name; - - const AudioBufferList *render_input_data; - AudioBufferList *render_output_data; - - AudioStreamBasicDescription stream_description; - - PA_LLIST_HEAD(coreaudio_sink, sinks); - PA_LLIST_HEAD(coreaudio_source, sources); -}; - -struct coreaudio_sink { - pa_sink *pa_sink; - struct userdata *userdata; - - char *name; - unsigned int channel_idx; - pa_bool_t active; - - pa_channel_map map; - pa_sample_spec ss; - - PA_LLIST_FIELDS(coreaudio_sink); -}; - -struct coreaudio_source { - pa_source *pa_source; - struct userdata *userdata; - - char *name; - unsigned int channel_idx; - pa_bool_t active; - - pa_channel_map map; - pa_sample_spec ss; - - PA_LLIST_FIELDS(coreaudio_source); -}; - -static OSStatus io_render_proc (AudioDeviceID device, - const AudioTimeStamp *now, - const AudioBufferList *inputData, - const AudioTimeStamp *inputTime, - AudioBufferList *outputData, - const AudioTimeStamp *outputTime, - void *clientData) -{ - struct userdata *u = clientData; - - pa_assert(u); - pa_assert(device == u->object_id); - - u->render_input_data = inputData; - u->render_output_data = outputData; - - if (u->sinks) - pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sinks->pa_sink), - CA_MESSAGE_RENDER, NULL, 0, NULL) == 0); - - if (u->sources) - pa_assert_se(pa_asyncmsgq_send(u->async_msgq, PA_MSGOBJECT(u->sources->pa_source), - CA_MESSAGE_RENDER, NULL, 0, NULL) == 0); - - return 0; -} - -static OSStatus ca_stream_format_changed(AudioObjectID objectID, - UInt32 numberAddresses, - const AudioObjectPropertyAddress addresses[], - void *clientData) -{ - struct userdata *u = clientData; - UInt32 i; - - pa_assert(u); - - /* REVISIT: PA can't currently handle external format change requests. - * Hence, we set the original format back in this callback to avoid horrible audio artefacts. - * The device settings will appear to be 'locked' for any application as long as the PA daemon is running. - * Once we're able to propagate such events up in the core, this needs to be changed. */ - - for (i = 0; i < numberAddresses; i++) - AudioObjectSetPropertyData(objectID, addresses + i, 0, NULL, sizeof(u->stream_description), &u->stream_description); - - return 0; -} - -static pa_usec_t get_latency_us(pa_object *o) { - struct userdata *u; - pa_sample_spec *ss; - bool is_source; - UInt32 v, total = 0; - UInt32 err, size = sizeof(v); - AudioObjectPropertyAddress property_address; - AudioObjectID stream_id; - - if (pa_sink_isinstance(o)) { - coreaudio_sink *sink = PA_SINK(o)->userdata; - - u = sink->userdata; - ss = &sink->ss; - is_source = FALSE; - } else if (pa_source_isinstance(o)) { - coreaudio_source *source = PA_SOURCE(o)->userdata; - - u = source->userdata; - ss = &source->ss; - is_source = TRUE; - } else - pa_assert_not_reached(); - - pa_assert(u); - - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - /* get the device latency */ - property_address.mSelector = kAudioDevicePropertyLatency; - size = sizeof(total); - AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &total); - total += v; - - /* the the IOProc buffer size */ - property_address.mSelector = kAudioDevicePropertyBufferFrameSize; - size = sizeof(v); - AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v); - total += v; - - /* IOProc safety offset - this value is the same for both directions, hence we divide it by 2 */ - property_address.mSelector = kAudioDevicePropertySafetyOffset; - size = sizeof(v); - AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v); - total += v / 2; - - /* get the stream latency. - * FIXME: this assumes the stream latency is the same for all streams */ - property_address.mSelector = kAudioDevicePropertyStreams; - size = sizeof(stream_id); - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &stream_id); - if (!err) { - property_address.mSelector = kAudioStreamPropertyLatency; - size = sizeof(v); - err = AudioObjectGetPropertyData(stream_id, &property_address, 0, NULL, &size, &v); - if (!err) - total += v; - } - - return pa_bytes_to_usec(total * pa_frame_size(ss), ss); -} - -static void ca_device_check_device_state(struct userdata *u) { - coreaudio_sink *ca_sink; - coreaudio_source *ca_source; - pa_bool_t active = FALSE; - - pa_assert(u); - - for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next) - if (ca_sink->active) - active = TRUE; - - for (ca_source = u->sources; ca_source; ca_source = ca_source->next) - if (ca_source->active) - active = TRUE; - - if (active && !u->running) - AudioDeviceStart(u->object_id, u->proc_id); - else if (!active && u->running) - AudioDeviceStop(u->object_id, u->proc_id); - - u->running = active; -} - -static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - coreaudio_sink *sink = PA_SINK(o)->userdata; - struct userdata *u = sink->userdata; - unsigned int i; - pa_memchunk audio_chunk; - - switch (code) { - case CA_MESSAGE_RENDER: { - /* audio out */ - for (i = 0; i < u->render_output_data->mNumberBuffers; i++) { - AudioBuffer *buf = u->render_output_data->mBuffers + i; - - pa_assert(sink); - - if (PA_SINK_IS_OPENED(sink->pa_sink->thread_info.state)) { - if (sink->pa_sink->thread_info.rewind_requested) - pa_sink_process_rewind(sink->pa_sink, 0); - - audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, FALSE); - audio_chunk.length = buf->mDataByteSize; - audio_chunk.index = 0; - - pa_sink_render_into_full(sink->pa_sink, &audio_chunk); - pa_memblock_unref_fixed(audio_chunk.memblock); - } - - sink = sink->next; - } - - return 0; - } - - case PA_SINK_MESSAGE_GET_LATENCY: { - *((pa_usec_t *) data) = get_latency_us(PA_OBJECT(o)); - return 0; - } - - case PA_SINK_MESSAGE_SET_STATE: - switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) { - case PA_SINK_SUSPENDED: - case PA_SINK_IDLE: - sink->active = FALSE; - break; - - case PA_SINK_RUNNING: - sink->active = TRUE; - break; - - case PA_SINK_UNLINKED: - case PA_SINK_INIT: - case PA_SINK_INVALID_STATE: - ; - } - - ca_device_check_device_state(sink->userdata); - break; - } - - return pa_sink_process_msg(o, code, data, offset, chunk); -} - -static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { - coreaudio_source *source = PA_SOURCE(o)->userdata; - struct userdata *u = source->userdata; - unsigned int i; - pa_memchunk audio_chunk; - - switch (code) { - case CA_MESSAGE_RENDER: { - /* audio in */ - for (i = 0; i < u->render_input_data->mNumberBuffers; i++) { - const AudioBuffer *buf = u->render_input_data->mBuffers + i; - - pa_assert(source); - - if (PA_SOURCE_IS_OPENED(source->pa_source->thread_info.state)) { - audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, TRUE); - audio_chunk.length = buf->mDataByteSize; - audio_chunk.index = 0; - - pa_source_post(source->pa_source, &audio_chunk); - pa_memblock_unref_fixed(audio_chunk.memblock); - } - - source = source->next; - } - - return 0; - } - - case PA_SOURCE_MESSAGE_GET_LATENCY: { - *((pa_usec_t *) data) = get_latency_us(PA_OBJECT(o)); - return 0; - } - - case PA_SOURCE_MESSAGE_SET_STATE: - switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { - case PA_SOURCE_SUSPENDED: - case PA_SOURCE_IDLE: - source->active = FALSE; - break; - - case PA_SOURCE_RUNNING: - source->active = TRUE; - break; - - case PA_SOURCE_UNLINKED: - case PA_SOURCE_INIT: - case PA_SOURCE_INVALID_STATE: - ; - } - - ca_device_check_device_state(source->userdata); - break; - } - - return pa_source_process_msg(o, code, data, offset, chunk); -} - -static int ca_device_create_sink(pa_module *m, AudioBuffer *buf, int channel_idx) { - OSStatus err; - UInt32 size; - struct userdata *u = m->userdata; - pa_sink_new_data new_data; - pa_sink_flags_t flags = PA_SINK_LATENCY | PA_SINK_HARDWARE; - coreaudio_sink *ca_sink; - pa_sink *sink; - unsigned int i; - char tmp[255]; - pa_strbuf *strbuf; - AudioObjectPropertyAddress property_address; - - ca_sink = pa_xnew0(coreaudio_sink, 1); - ca_sink->map.channels = buf->mNumberChannels; - ca_sink->ss.channels = buf->mNumberChannels; - ca_sink->channel_idx = channel_idx; - - /* build a name for this stream */ - strbuf = pa_strbuf_new(); - - for (i = 0; i < buf->mNumberChannels; i++) { - property_address.mSelector = kAudioObjectPropertyElementName; - property_address.mScope = kAudioDevicePropertyScopeOutput; - property_address.mElement = channel_idx + i + 1; - size = sizeof(tmp); - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, tmp); - if (err || !strlen(tmp)) - snprintf(tmp, sizeof(tmp), "Channel %d", property_address.mElement); - - if (i > 0) - pa_strbuf_puts(strbuf, ", "); - - pa_strbuf_puts(strbuf, tmp); - } - - ca_sink->name = pa_strbuf_tostring_free(strbuf); - - pa_log_debug("Stream name is >%s<", ca_sink->name); - - /* default to mono streams */ - for (i = 0; i < ca_sink->map.channels; i++) - ca_sink->map.map[i] = PA_CHANNEL_POSITION_MONO; - - if (buf->mNumberChannels == 2) { - ca_sink->map.map[0] = PA_CHANNEL_POSITION_LEFT; - ca_sink->map.map[1] = PA_CHANNEL_POSITION_RIGHT; - } - - ca_sink->ss.rate = u->stream_description.mSampleRate; - ca_sink->ss.format = PA_SAMPLE_FLOAT32LE; - - pa_sink_new_data_init(&new_data); - new_data.card = u->card; - new_data.driver = __FILE__; - new_data.module = u->module; - new_data.namereg_fail = FALSE; - pa_sink_new_data_set_name(&new_data, ca_sink->name); - pa_sink_new_data_set_channel_map(&new_data, &ca_sink->map); - pa_sink_new_data_set_sample_spec(&new_data, &ca_sink->ss); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap"); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio"); - pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize); - - if (u->vendor_name) - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name); - - sink = pa_sink_new(m->core, &new_data, flags); - pa_sink_new_data_done(&new_data); - - if (!sink) { - pa_log("unable to create sink."); - return -1; - } - - sink->parent.process_msg = sink_process_msg; - sink->userdata = ca_sink; - - pa_sink_set_asyncmsgq(sink, u->thread_mq.inq); - pa_sink_set_rtpoll(sink, u->rtpoll); - - ca_sink->pa_sink = sink; - ca_sink->userdata = u; - - PA_LLIST_PREPEND(coreaudio_sink, u->sinks, ca_sink); - - return 0; -} - -static int ca_device_create_source(pa_module *m, AudioBuffer *buf, int channel_idx) { - OSStatus err; - UInt32 size; - struct userdata *u = m->userdata; - pa_source_new_data new_data; - pa_source_flags_t flags = PA_SOURCE_LATENCY | PA_SOURCE_HARDWARE; - coreaudio_source *ca_source; - pa_source *source; - unsigned int i; - char tmp[255]; - pa_strbuf *strbuf; - AudioObjectPropertyAddress property_address; - - ca_source = pa_xnew0(coreaudio_source, 1); - ca_source->map.channels = buf->mNumberChannels; - ca_source->ss.channels = buf->mNumberChannels; - ca_source->channel_idx = channel_idx; - - /* build a name for this stream */ - strbuf = pa_strbuf_new(); - - for (i = 0; i < buf->mNumberChannels; i++) { - property_address.mSelector = kAudioObjectPropertyElementName; - property_address.mScope = kAudioDevicePropertyScopeInput; - property_address.mElement = channel_idx + i + 1; - size = sizeof(tmp); - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, tmp); - if (err || !strlen(tmp)) - snprintf(tmp, sizeof(tmp), "Channel %d", property_address.mElement); - - if (i > 0) - pa_strbuf_puts(strbuf, ", "); - - pa_strbuf_puts(strbuf, tmp); - } - - ca_source->name = pa_strbuf_tostring_free(strbuf); - - pa_log_debug("Stream name is >%s<", ca_source->name); - - /* default to mono streams */ - for (i = 0; i < ca_source->map.channels; i++) - ca_source->map.map[i] = PA_CHANNEL_POSITION_MONO; - - if (buf->mNumberChannels == 2) { - ca_source->map.map[0] = PA_CHANNEL_POSITION_LEFT; - ca_source->map.map[1] = PA_CHANNEL_POSITION_RIGHT; - } - - ca_source->ss.rate = u->stream_description.mSampleRate; - ca_source->ss.format = PA_SAMPLE_FLOAT32LE; - - pa_source_new_data_init(&new_data); - new_data.card = u->card; - new_data.driver = __FILE__; - new_data.module = u->module; - new_data.namereg_fail = FALSE; - pa_source_new_data_set_name(&new_data, ca_source->name); - pa_source_new_data_set_channel_map(&new_data, &ca_source->map); - pa_source_new_data_set_sample_spec(&new_data, &ca_source->ss); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, u->device_name); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_DESCRIPTION, u->device_name); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_ACCESS_MODE, "mmap"); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_CLASS, "sound"); - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_API, "CoreAudio"); - pa_proplist_setf(new_data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) buf->mDataByteSize); - - if (u->vendor_name) - pa_proplist_sets(new_data.proplist, PA_PROP_DEVICE_VENDOR_NAME, u->vendor_name); - - source = pa_source_new(m->core, &new_data, flags); - pa_source_new_data_done(&new_data); - - if (!source) { - pa_log("unable to create source."); - return -1; - } - - source->parent.process_msg = source_process_msg; - source->userdata = ca_source; - - pa_source_set_asyncmsgq(source, u->thread_mq.inq); - pa_source_set_rtpoll(source, u->rtpoll); - - ca_source->pa_source = source; - ca_source->userdata = u; - - PA_LLIST_PREPEND(coreaudio_source, u->sources, ca_source); - - return 0; -} - -static int ca_device_create_streams(pa_module *m, bool direction_in) { - OSStatus err; - UInt32 size, i, channel_idx; - struct userdata *u = m->userdata; - AudioBufferList *buffer_list; - AudioObjectPropertyAddress property_address; - - property_address.mScope = direction_in ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; - property_address.mElement = kAudioObjectPropertyElementMaster; - - /* get current stream format */ - size = sizeof(AudioStreamBasicDescription); - property_address.mSelector = kAudioDevicePropertyStreamFormat; - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &u->stream_description); - if (err) { - /* no appropriate streams found - silently bail. */ - return -1; - } - - if (u->stream_description.mFormatID != kAudioFormatLinearPCM) { - pa_log("Unsupported audio format '%c%c%c%c'", - (char) (u->stream_description.mFormatID >> 24), - (char) (u->stream_description.mFormatID >> 16) & 0xff, - (char) (u->stream_description.mFormatID >> 8) & 0xff, - (char) (u->stream_description.mFormatID & 0xff)); - return -1; - } - - /* get stream configuration */ - size = 0; - property_address.mSelector = kAudioDevicePropertyStreamConfiguration; - err = AudioObjectGetPropertyDataSize(u->object_id, &property_address, 0, NULL, &size); - if (err) { - pa_log("Failed to get kAudioDevicePropertyStreamConfiguration (%s).", direction_in ? "input" : "output"); - return -1; - } - - if (!size) - return 0; - - buffer_list = (AudioBufferList *) pa_xmalloc(size); - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, buffer_list); - - if (!err) { - pa_log_debug("Sample rate: %f", u->stream_description.mSampleRate); - pa_log_debug("%d bytes per packet", (unsigned int) u->stream_description.mBytesPerPacket); - pa_log_debug("%d frames per packet", (unsigned int) u->stream_description.mFramesPerPacket); - pa_log_debug("%d bytes per frame", (unsigned int) u->stream_description.mBytesPerFrame); - pa_log_debug("%d channels per frame", (unsigned int) u->stream_description.mChannelsPerFrame); - pa_log_debug("%d bits per channel", (unsigned int) u->stream_description.mBitsPerChannel); - - for (channel_idx = 0, i = 0; i < buffer_list->mNumberBuffers; i++) { - AudioBuffer *buf = buffer_list->mBuffers + i; - - if (direction_in) - ca_device_create_source(m, buf, channel_idx); - else - ca_device_create_sink(m, buf, channel_idx); - - channel_idx += buf->mNumberChannels; - } - } - - pa_xfree(buffer_list); - return 0; -} - -static void thread_func(void *userdata) { - struct userdata *u = userdata; - - pa_assert(u); - pa_assert(u->module); - pa_assert(u->module->core); - - pa_log_debug("Thread starting up"); - - if (u->module->core->realtime_scheduling) - pa_make_realtime(u->module->core->realtime_priority); - - pa_thread_mq_install(&u->thread_mq); - - for (;;) { - int ret; - - ret = pa_rtpoll_run(u->rtpoll, TRUE); - - if (ret < 0) - goto fail; - - if (ret == 0) - goto finish; - } - -fail: - /* If this was no regular exit from the loop we have to continue - * processing messages until we received PA_MESSAGE_SHUTDOWN */ - pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); - pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); - -finish: - pa_log_debug("Thread shutting down"); -} - -int pa__init(pa_module *m) { - OSStatus err; - UInt32 size, frames; - struct userdata *u = NULL; - pa_modargs *ma = NULL; - char tmp[64]; - pa_card_new_data card_new_data; - coreaudio_sink *ca_sink; - coreaudio_source *ca_source; - AudioObjectPropertyAddress property_address; - - pa_assert(m); - - if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { - pa_log("Failed to parse module arguments."); - goto fail; - } - - u = pa_xnew0(struct userdata, 1); - u->module = m; - m->userdata = u; - - if (pa_modargs_get_value_u32(ma, "object_id", (unsigned int *) &u->object_id) != 0) { - pa_log("Failed to parse object_id argument."); - goto fail; - } - - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - /* get device product name */ - property_address.mSelector = kAudioDevicePropertyDeviceName; - size = sizeof(tmp); - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, tmp); - if (err) { - pa_log("Failed to get kAudioDevicePropertyDeviceName (err = %08x).", (int) err); - goto fail; - } - - u->device_name = pa_xstrdup(tmp); - - pa_card_new_data_init(&card_new_data); - pa_proplist_sets(card_new_data.proplist, PA_PROP_DEVICE_STRING, tmp); - card_new_data.driver = __FILE__; - pa_card_new_data_set_name(&card_new_data, tmp); - pa_log_info("Initializing module for CoreAudio device '%s' (id %d)", tmp, (unsigned int) u->object_id); - - /* get device vendor name (may fail) */ - property_address.mSelector = kAudioDevicePropertyDeviceManufacturer; - size = sizeof(tmp); - err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, tmp); - if (!err) - u->vendor_name = pa_xstrdup(tmp); - - /* create the card object */ - u->card = pa_card_new(m->core, &card_new_data); - if (!u->card) { - pa_log("Unable to create card.\n"); - goto fail; - } - - pa_card_new_data_done(&card_new_data); - u->card->userdata = u; - - u->rtpoll = pa_rtpoll_new(); - pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); - u->async_msgq = pa_asyncmsgq_new(0); - pa_rtpoll_item_new_asyncmsgq_read(u->rtpoll, PA_RTPOLL_EARLY-1, u->async_msgq); - - PA_LLIST_HEAD_INIT(coreaudio_sink, u->sinks); - - /* create sinks */ - ca_device_create_streams(m, FALSE); - - /* create sources */ - ca_device_create_streams(m, TRUE); - - /* create the message thread */ - if (!(u->thread = pa_thread_new("coreaudio", thread_func, u))) { - pa_log("Failed to create thread."); - goto fail; - } - - /* register notification callback for stream format changes */ - property_address.mSelector = kAudioDevicePropertyStreamFormat; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - AudioObjectAddPropertyListener(u->object_id, &property_address, ca_stream_format_changed, u); - - /* set number of frames in IOProc */ - frames = DEFAULT_FRAMES_PER_IOPROC; - pa_modargs_get_value_u32(ma, "ioproc_frames", (unsigned int *) &frames); - - property_address.mSelector = kAudioDevicePropertyBufferFrameSize; - AudioObjectSetPropertyData(u->object_id, &property_address, 0, NULL, sizeof(frames), &frames); - pa_log_debug("%u frames per IOProc\n", (unsigned int) frames); - - /* create one ioproc for both directions */ - err = AudioDeviceCreateIOProcID(u->object_id, io_render_proc, u, &u->proc_id); - if (err) { - pa_log("AudioDeviceCreateIOProcID() failed (err = %08x\n).", (int) err); - goto fail; - } - - for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next) - pa_sink_put(ca_sink->pa_sink); - - for (ca_source = u->sources; ca_source; ca_source = ca_source->next) - pa_source_put(ca_source->pa_source); - - pa_modargs_free(ma); - - return 0; - -fail: - if (u) - pa__done(m); - - if (ma) - pa_modargs_free(ma); - - return -1; -} - -void pa__done(pa_module *m) { - struct userdata *u; - coreaudio_sink *ca_sink; - coreaudio_source *ca_source; - AudioObjectPropertyAddress property_address; - - pa_assert(m); - - u = m->userdata; - pa_assert(u); - - /* unlink sinks */ - for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next) - if (ca_sink->pa_sink) - pa_sink_unlink(ca_sink->pa_sink); - - /* unlink sources */ - for (ca_source = u->sources; ca_source; ca_source = ca_source->next) - if (ca_source->pa_source) - pa_source_unlink(ca_source->pa_source); - - if (u->thread) { - pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); - pa_thread_free(u->thread); - pa_thread_mq_done(&u->thread_mq); - pa_asyncmsgq_unref(u->async_msgq); - } - - /* free sinks */ - for (ca_sink = u->sinks; ca_sink;) { - coreaudio_sink *next = ca_sink->next; - - if (ca_sink->pa_sink) - pa_sink_unref(ca_sink->pa_sink); - - pa_xfree(ca_sink->name); - pa_xfree(ca_sink); - ca_sink = next; - } - - /* free sources */ - for (ca_source = u->sources; ca_source;) { - coreaudio_source *next = ca_source->next; - - if (ca_source->pa_source) - pa_source_unref(ca_source->pa_source); - - pa_xfree(ca_source->name); - pa_xfree(ca_source); - ca_source = next; - } - - if (u->proc_id) { - AudioDeviceStop(u->object_id, u->proc_id); - AudioDeviceDestroyIOProcID(u->object_id, u->proc_id); - } - - property_address.mSelector = kAudioDevicePropertyStreamFormat; - property_address.mScope = kAudioObjectPropertyScopeGlobal; - property_address.mElement = kAudioObjectPropertyElementMaster; - - AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &property_address, ca_stream_format_changed, u); - - pa_xfree(u->device_name); - pa_xfree(u->vendor_name); - pa_rtpoll_free(u->rtpoll); - pa_card_free(u->card); - - pa_xfree(u); -} |