aboutsummaryrefslogtreecommitdiffstats
path: root/audio/coreaudio.c
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:30:32 -0800
commit8b23a6c7e1aee255004dd19098d4c2462b61b849 (patch)
tree7a4d682ba51f0ff0364c5ca2509f515bdaf96de9 /audio/coreaudio.c
parentf721e3ac031f892af46f255a47d7f54a91317b30 (diff)
downloadexternal_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.zip
external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.gz
external_qemu-8b23a6c7e1aee255004dd19098d4c2462b61b849.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'audio/coreaudio.c')
-rw-r--r--audio/coreaudio.c821
1 files changed, 821 insertions, 0 deletions
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
new file mode 100644
index 0000000..f23ebee
--- /dev/null
+++ b/audio/coreaudio.c
@@ -0,0 +1,821 @@
+/*
+ * QEMU OS X CoreAudio audio driver
+ *
+ * Copyright (c) 2008 The Android Open Source Project
+ * Copyright (c) 2005 Mike Kronenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#include <string.h> /* strerror */
+#include <pthread.h> /* pthread_X */
+
+#include "audio.h"
+
+#define AUDIO_CAP "coreaudio"
+#include "audio_int.h"
+
+#define ENABLE_IN 1
+
+#if 0
+# define D(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+struct {
+ int out_buffer_frames;
+ int out_nbuffers;
+ int in_buffer_frames;
+ int in_nbuffers;
+ int isAtexit;
+} conf = {
+ .out_buffer_frames = 512,
+ .out_nbuffers = 4,
+ .in_buffer_frames = 512,
+ .in_nbuffers = 4,
+ .isAtexit = 0
+};
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** U T I L I T Y R O U T I N E S ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+static void coreaudio_logstatus (OSStatus status)
+{
+ char *str = "BUG";
+
+ switch(status) {
+ case kAudioHardwareNoError:
+ str = "kAudioHardwareNoError";
+ break;
+
+ case kAudioHardwareNotRunningError:
+ str = "kAudioHardwareNotRunningError";
+ break;
+
+ case kAudioHardwareUnspecifiedError:
+ str = "kAudioHardwareUnspecifiedError";
+ break;
+
+ case kAudioHardwareUnknownPropertyError:
+ str = "kAudioHardwareUnknownPropertyError";
+ break;
+
+ case kAudioHardwareBadPropertySizeError:
+ str = "kAudioHardwareBadPropertySizeError";
+ break;
+
+ case kAudioHardwareIllegalOperationError:
+ str = "kAudioHardwareIllegalOperationError";
+ break;
+
+ case kAudioHardwareBadDeviceError:
+ str = "kAudioHardwareBadDeviceError";
+ break;
+
+ case kAudioHardwareBadStreamError:
+ str = "kAudioHardwareBadStreamError";
+ break;
+
+ case kAudioHardwareUnsupportedOperationError:
+ str = "kAudioHardwareUnsupportedOperationError";
+ break;
+
+ case kAudioDeviceUnsupportedFormatError:
+ str = "kAudioDeviceUnsupportedFormatError";
+ break;
+
+ case kAudioDevicePermissionsError:
+ str = "kAudioDevicePermissionsError";
+ break;
+
+ default:
+ AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
+ return;
+ }
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
+ OSStatus status,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_log (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ coreaudio_logstatus (status);
+}
+
+static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
+ OSStatus status,
+ const char *typ,
+ const char *fmt,
+ ...
+ )
+{
+ va_list ap;
+
+ AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ coreaudio_logstatus (status);
+}
+
+static void coreaudio_atexit (void)
+{
+ conf.isAtexit = 1;
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** S H A R E D I N / O U T V O I C E ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+typedef struct coreAudioVoice {
+ pthread_mutex_t mutex;
+ AudioDeviceID deviceID;
+ Boolean isInput;
+ UInt32 bufferFrameSize;
+ AudioStreamBasicDescription streamBasicDescription;
+ AudioDeviceIOProc ioproc;
+ int live;
+ int decr;
+ int pos;
+} coreaudioVoice;
+
+
+static inline UInt32
+coreaudio_voice_isPlaying (coreaudioVoice* core)
+{
+ OSStatus status;
+ UInt32 result = 0;
+ UInt32 propertySize = sizeof(core->deviceID);
+ status = AudioDeviceGetProperty(
+ core->deviceID, 0, core->isInput,
+ kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr(status,
+ "Could not determine whether Device is playing\n");
+ }
+ return result;
+}
+
+static int
+coreaudio_voice_lock (coreaudioVoice* core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_lock (&core->mutex);
+ if (err) {
+ dolog ("Could not lock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+coreaudio_voice_unlock (coreaudioVoice* core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&core->mutex);
+ if (err) {
+ dolog ("Could not unlock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+coreaudio_voice_ctl (coreaudioVoice* core, int cmd)
+{
+ OSStatus status;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ /* start playback */
+ D("%s: %s started\n", __FUNCTION__, core->isInput ? "input" : "output");
+ if (!coreaudio_voice_isPlaying(core)) {
+ status = AudioDeviceStart(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not resume playback\n");
+ }
+ }
+ break;
+
+ case VOICE_DISABLE:
+ /* stop playback */
+ D("%s: %s stopped\n", __FUNCTION__, core->isInput ? "input" : "output");
+ if (!conf.isAtexit) {
+ if (coreaudio_voice_isPlaying(core)) {
+ status = AudioDeviceStop(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not pause playback\n");
+ }
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static void
+coreaudio_voice_fini (coreaudioVoice* core)
+{
+ OSStatus status;
+ int err;
+
+ if (!conf.isAtexit) {
+ /* stop playback */
+ coreaudio_voice_ctl(core, VOICE_DISABLE);
+
+ /* remove callback */
+ status = AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not remove IOProc\n");
+ }
+ }
+ core->deviceID = kAudioDeviceUnknown;
+
+ /* destroy mutex */
+ err = pthread_mutex_destroy(&core->mutex);
+ if (err) {
+ dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+ }
+}
+
+
+static int
+coreaudio_voice_init (coreaudioVoice* core,
+ audsettings_t* as,
+ int frameSize,
+ AudioDeviceIOProc ioproc,
+ void* hw,
+ int input)
+{
+ OSStatus status;
+ UInt32 propertySize;
+ int err;
+ int bits = 8;
+ AudioValueRange frameRange;
+ const char* typ = input ? "input" : "playback";
+
+ core->isInput = input ? true : false;
+
+ /* create mutex */
+ err = pthread_mutex_init(&core->mutex, NULL);
+ if (err) {
+ dolog("Could not create mutex\nReason: %s\n", strerror (err));
+ return -1;
+ }
+
+ if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
+ bits = 16;
+ }
+
+ // TODO: audio_pcm_init_info (&hw->info, as);
+ /* open default output device */
+ /* note: we use DefaultSystemOutputDevice because DefaultOutputDevice seems to
+ * always link to the internal speakers, and not the ones selected through system properties
+ * go figure...
+ */
+ propertySize = sizeof(core->deviceID);
+ status = AudioHardwareGetProperty(
+ input ? kAudioHardwarePropertyDefaultInputDevice :
+ kAudioHardwarePropertyDefaultSystemOutputDevice,
+ &propertySize,
+ &core->deviceID);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get default %s device\n", typ);
+ return -1;
+ }
+ if (core->deviceID == kAudioDeviceUnknown) {
+ dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
+ return -1;
+ }
+
+ /* get minimum and maximum buffer frame sizes */
+ propertySize = sizeof(frameRange);
+ status = AudioDeviceGetProperty(
+ core->deviceID,
+ 0,
+ core->isInput,
+ kAudioDevicePropertyBufferFrameSizeRange,
+ &propertySize,
+ &frameRange);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame range\n");
+ return -1;
+ }
+
+ if (frameRange.mMinimum > frameSize) {
+ core->bufferFrameSize = (UInt32) frameRange.mMinimum;
+ dolog ("warning: Upsizing Output Buffer Frames to %f\n", frameRange.mMinimum);
+ }
+ else if (frameRange.mMaximum < frameSize) {
+ core->bufferFrameSize = (UInt32) frameRange.mMaximum;
+ dolog ("warning: Downsizing Output Buffer Frames to %f\n", frameRange.mMaximum);
+ }
+ else {
+ core->bufferFrameSize = frameSize;
+ }
+
+ /* set Buffer Frame Size */
+ propertySize = sizeof(core->bufferFrameSize);
+ status = AudioDeviceSetProperty(
+ core->deviceID,
+ NULL,
+ 0,
+ core->isInput,
+ kAudioDevicePropertyBufferFrameSize,
+ propertySize,
+ &core->bufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not set device buffer frame size %ld\n",
+ core->bufferFrameSize);
+ return -1;
+ }
+
+ /* get Buffer Frame Size */
+ propertySize = sizeof(core->bufferFrameSize);
+ status = AudioDeviceGetProperty(
+ core->deviceID,
+ 0,
+ core->isInput,
+ kAudioDevicePropertyBufferFrameSize,
+ &propertySize,
+ &core->bufferFrameSize);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get device buffer frame size\n");
+ return -1;
+ }
+ // TODO: hw->samples = *pNBuffers * core->bufferFrameSize;
+
+ /* get StreamFormat */
+ propertySize = sizeof(core->streamBasicDescription);
+ status = AudioDeviceGetProperty(
+ core->deviceID,
+ 0,
+ core->isInput,
+ kAudioDevicePropertyStreamFormat,
+ &propertySize,
+ &core->streamBasicDescription);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ,
+ "Could not get Device Stream properties\n");
+ core->deviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* set Samplerate */
+ core->streamBasicDescription.mSampleRate = (Float64) as->freq;
+ propertySize = sizeof(core->streamBasicDescription);
+ status = AudioDeviceSetProperty(
+ core->deviceID,
+ 0,
+ 0,
+ core->isInput,
+ kAudioDevicePropertyStreamFormat,
+ propertySize,
+ &core->streamBasicDescription);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
+ as->freq);
+ core->deviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* set Callback */
+ core->ioproc = ioproc;
+ status = AudioDeviceAddIOProc(core->deviceID, ioproc, hw);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
+ core->deviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+
+ /* start Playback */
+ if (!input && !coreaudio_voice_isPlaying(core)) {
+ status = AudioDeviceStart(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr2 (status, typ, "Could not start playback\n");
+ AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+ core->deviceID = kAudioDeviceUnknown;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** O U T P U T V O I C E ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+typedef struct coreaudioVoiceOut {
+ HWVoiceOut hw;
+ coreaudioVoice core[1];
+} coreaudioVoiceOut;
+
+#define CORE_OUT(hw) ((coreaudioVoiceOut*)(hw))->core
+
+
+static int
+coreaudio_run_out (HWVoiceOut *hw)
+{
+ int live, decr;
+ coreaudioVoice *core = CORE_OUT(hw);
+
+ if (coreaudio_voice_lock (core, "coreaudio_run_out")) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_out (hw);
+
+ if (core->decr > live) {
+ ldebug ("core->decr %d live %d core->live %d\n",
+ core->decr,
+ live,
+ core->live);
+ }
+
+ decr = audio_MIN (core->decr, live);
+ core->decr -= decr;
+ core->live = live - decr;
+ hw->rpos = core->pos;
+
+ coreaudio_voice_unlock (core, "coreaudio_run_out");
+ return decr;
+}
+
+
+/* callback to feed audiooutput buffer */
+static OSStatus
+audioOutDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* hwptr)
+{
+ UInt32 frame, frameCount;
+ float *out = outOutputData->mBuffers[0].mData;
+ HWVoiceOut *hw = hwptr;
+ coreaudioVoice *core = CORE_OUT(hw);
+ int rpos, live;
+ st_sample_t *src;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ const float scale = 1.f / UINT_MAX;
+#else
+ const float scale = UINT_MAX;
+#endif
+#endif
+
+ if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
+ }
+
+ frameCount = core->bufferFrameSize;
+ live = core->live;
+
+ /* if there are not enough samples, set signal and return */
+ if (live < frameCount) {
+ inInputTime = 0;
+ coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
+ }
+
+ rpos = core->pos;
+ src = hw->mix_buf + rpos;
+
+ /* fill buffer */
+ for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+ *out++ = src[frame].l; /* left channel */
+ *out++ = src[frame].r; /* right channel */
+#else
+#ifdef RECIPROCAL
+ *out++ = src[frame].l * scale; /* left channel */
+ *out++ = src[frame].r * scale; /* right channel */
+#else
+ *out++ = src[frame].l / scale; /* left channel */
+ *out++ = src[frame].r / scale; /* right channel */
+#endif
+#endif
+ }
+
+ rpos = (rpos + frameCount) % hw->samples;
+ core->decr += frameCount;
+ core->pos = rpos;
+
+ coreaudio_voice_unlock (core, "audioDeviceIOProc");
+ return 0;
+}
+
+static int
+coreaudio_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int
+coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ coreaudioVoice* core = CORE_OUT(hw);
+ int err;
+
+ audio_pcm_init_info (&hw->info, as);
+
+ err = coreaudio_voice_init( core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0 );
+ if (err < 0)
+ return err;
+
+ hw->samples = core->bufferFrameSize * conf.out_nbuffers;
+ return 0;
+}
+
+static void
+coreaudio_fini_out (HWVoiceOut *hw)
+{
+
+ coreaudioVoice* core = CORE_OUT(hw);
+
+ coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ coreaudioVoice* core = CORE_OUT(hw);
+
+ return coreaudio_voice_ctl(core, cmd);
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** I N P U T V O I C E ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+
+
+typedef struct coreaudioVoiceIn {
+ HWVoiceIn hw;
+ coreaudioVoice core[1];
+} coreaudioVoiceIn;
+
+#define CORE_IN(hw) ((coreaudioVoiceIn*)(hw))->core
+
+
+static int
+coreaudio_run_in (HWVoiceIn *hw)
+{
+ int decr;
+
+ coreaudioVoice *core = CORE_IN(hw);
+
+ if (coreaudio_voice_lock (core, "coreaudio_run_in")) {
+ return 0;
+ }
+ D("%s: core.decr=%d core.pos=%d\n", __FUNCTION__, core->decr, core->pos);
+ decr = core->decr;
+ core->decr -= decr;
+ hw->wpos = core->pos;
+
+ coreaudio_voice_unlock (core, "coreaudio_run_in");
+ return decr;
+}
+
+
+/* callback to feed audiooutput buffer */
+static OSStatus
+audioInDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* hwptr)
+{
+ UInt32 frame, frameCount;
+ float *in = inInputData->mBuffers[0].mData;
+ HWVoiceIn *hw = hwptr;
+ coreaudioVoice *core = CORE_IN(hw);
+ int wpos, avail;
+ st_sample_t *dst;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ const float scale = 1.f / UINT_MAX;
+#else
+ const float scale = UINT_MAX;
+#endif
+#endif
+
+ if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
+ }
+
+ frameCount = core->bufferFrameSize;
+ avail = hw->samples - hw->total_samples_captured - core->decr;
+
+ D("%s: enter avail=%d core.decr=%d core.pos=%d hw.samples=%d hw.total_samples_captured=%d frameCount=%d\n",
+ __FUNCTION__, avail, core->decr, core->pos, hw->samples, hw->total_samples_captured, (int)frameCount);
+
+ /* if there are not enough samples, set signal and return */
+ if (avail < frameCount) {
+ inInputTime = 0;
+ coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
+ }
+
+ wpos = core->pos;
+ dst = hw->conv_buf + wpos;
+
+ /* fill buffer */
+ for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+ dst[frame].l = *in++; /* left channel */
+ dst[frame].r = *in++; /* right channel */
+#else
+#ifdef RECIPROCAL
+ dst[frame].l = *in++ * scale; /* left channel */
+ dst[frame].r = *in++ * scale; /* right channel */
+#else
+ dst[frame].l = *in++ / scale; /* left channel */
+ dst[frame].r = *in++ / scale; /* right channel */
+#endif
+#endif
+ }
+
+ wpos = (wpos + frameCount) % hw->samples;
+ core->decr += frameCount;
+ core->pos = wpos;
+
+ D("exit: core.decr=%d core.pos=%d\n", core->decr, core->pos);
+ coreaudio_voice_unlock (core, "audioDeviceIOProc");
+ return 0;
+}
+
+static int
+coreaudio_read (SWVoiceIn *sw, void *buf, int len)
+{
+ int result = audio_pcm_sw_read(sw, buf, len);
+ D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result);
+ return result;
+}
+
+static int
+coreaudio_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ coreaudioVoice* core = CORE_IN(hw);
+ int err;
+
+ audio_pcm_init_info (&hw->info, as);
+
+ err = coreaudio_voice_init( core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1 );
+ if (err < 0) {
+ return err;
+ }
+
+ hw->samples = core->bufferFrameSize * conf.in_nbuffers;
+ return 0;
+}
+
+static void
+coreaudio_fini_in (HWVoiceIn *hw)
+{
+
+ coreaudioVoice* core = CORE_IN(hw);
+
+ coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ coreaudioVoice* core = CORE_IN(hw);
+
+ return coreaudio_voice_ctl(core, cmd);
+}
+
+static void*
+coreaudio_audio_init (void)
+{
+ atexit(coreaudio_atexit);
+ return &coreaudio_audio_init;
+}
+
+static void
+coreaudio_audio_fini (void *opaque)
+{
+ (void) opaque;
+}
+
+static struct audio_option coreaudio_options[] = {
+ {"OUT_BUFFER_SIZE", AUD_OPT_INT, &conf.out_buffer_frames,
+ "Size of the output buffer in frames", NULL, 0},
+ {"OUT_BUFFER_COUNT", AUD_OPT_INT, &conf.out_nbuffers,
+ "Number of output buffers", NULL, 0},
+ {"IN_BUFFER_SIZE", AUD_OPT_INT, &conf.in_buffer_frames,
+ "Size of the input buffer in frames", NULL, 0},
+ {"IN_BUFFER_COUNT", AUD_OPT_INT, &conf.in_nbuffers,
+ "Number of input buffers", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops coreaudio_pcm_ops = {
+ coreaudio_init_out,
+ coreaudio_fini_out,
+ coreaudio_run_out,
+ coreaudio_write,
+ coreaudio_ctl_out,
+
+#if ENABLE_IN
+ coreaudio_init_in,
+ coreaudio_fini_in,
+ coreaudio_run_in,
+ coreaudio_read,
+ coreaudio_ctl_in
+#else
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+#endif
+};
+
+struct audio_driver coreaudio_audio_driver = {
+ INIT_FIELD (name = ) "coreaudio",
+ INIT_FIELD (descr = )
+ "CoreAudio (developer.apple.com/audio/coreaudio.html)",
+ INIT_FIELD (options = ) coreaudio_options,
+ INIT_FIELD (init = ) coreaudio_audio_init,
+ INIT_FIELD (fini = ) coreaudio_audio_fini,
+ INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+#if ENABLE_IN
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (coreaudioVoiceIn),
+#else
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 0,
+ INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
+ INIT_FIELD (voice_size_in = ) 0,
+#endif
+};