summaryrefslogtreecommitdiffstats
path: root/audio/coreaudio.m
diff options
context:
space:
mode:
authorPhilippe Mathieu-Daudé2022-02-15 11:05:44 +0100
committerPhilippe Mathieu-Daudé2022-03-15 13:36:33 +0100
commit8b46d7e2dc8ec4b3515e43bc28ee77d9afab8b23 (patch)
treef83c979025bbe826044811a6f66a5ad4ec283b46 /audio/coreaudio.m
parentcoreaudio: Always return 0 in handle_voice_change (diff)
downloadqemu-8b46d7e2dc8ec4b3515e43bc28ee77d9afab8b23.tar.gz
qemu-8b46d7e2dc8ec4b3515e43bc28ee77d9afab8b23.tar.xz
qemu-8b46d7e2dc8ec4b3515e43bc28ee77d9afab8b23.zip
audio: Rename coreaudio extension to use Objective-C compiler
The coreaudio library includes Objective-C declarations (using the caret '^' symbol to declare block references [*]). When building with a C compiler we get: [175/839] Compiling C object libcommon.fa.p/audio_coreaudio.c.o In file included from /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h:18, from ../../audio/coreaudio.c:26: /Library/Developer/CommandLineTools/SDKs/MacOSX12.sdk/System/Library/Frameworks/CoreAudio.framework/Headers/AudioHardware.h:162:2: error: expected identifier or '(' before '^' token 162 | (^AudioObjectPropertyListenerBlock)( UInt32 inNumberAddresses, | ^ FAILED: libcommon.fa.p/audio_coreaudio.c.o Rename the file to use the Objective-C default extension (.m) so meson calls the correct compiler. [*] https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html Reviewed-by: Christian Schoenebeck <qemu_oss@crudebyte.com> Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Diffstat (limited to 'audio/coreaudio.m')
-rw-r--r--audio/coreaudio.m687
1 files changed, 687 insertions, 0 deletions
diff --git a/audio/coreaudio.m b/audio/coreaudio.m
new file mode 100644
index 0000000000..3186b68474
--- /dev/null
+++ b/audio/coreaudio.m
@@ -0,0 +1,687 @@
+/*
+ * QEMU OS X CoreAudio audio driver
+ *
+ * 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 "qemu/osdep.h"
+#include <CoreAudio/CoreAudio.h>
+#include <pthread.h> /* pthread_X */
+
+#include "qemu/main-loop.h"
+#include "qemu/module.h"
+#include "audio.h"
+
+#define AUDIO_CAP "coreaudio"
+#include "audio_int.h"
+
+typedef struct coreaudioVoiceOut {
+ HWVoiceOut hw;
+ pthread_mutex_t buf_mutex;
+ AudioDeviceID outputDeviceID;
+ int frameSizeSetting;
+ uint32_t bufferCount;
+ UInt32 audioDevicePropertyBufferFrameSize;
+ AudioDeviceIOProcID ioprocid;
+ bool enabled;
+} coreaudioVoiceOut;
+
+#if !defined(MAC_OS_VERSION_12_0) \
+ || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0)
+#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
+#endif
+
+static const AudioObjectPropertyAddress voice_addr = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMain
+};
+
+static OSStatus coreaudio_get_voice(AudioDeviceID *id)
+{
+ UInt32 size = sizeof(*id);
+
+ return AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &voice_addr,
+ 0,
+ NULL,
+ &size,
+ id);
+}
+
+static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
+ AudioValueRange *framerange)
+{
+ UInt32 size = sizeof(*framerange);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSizeRange,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMain
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ framerange);
+}
+
+static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
+{
+ UInt32 size = sizeof(*framesize);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMain
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ framesize);
+}
+
+static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
+{
+ UInt32 size = sizeof(*framesize);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMain
+ };
+
+ return AudioObjectSetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ size,
+ framesize);
+}
+
+static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
+ AudioStreamBasicDescription *d)
+{
+ UInt32 size = sizeof(*d);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyStreamFormat,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMain
+ };
+
+ return AudioObjectSetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ size,
+ d);
+}
+
+static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
+{
+ UInt32 size = sizeof(*result);
+ AudioObjectPropertyAddress addr = {
+ kAudioDevicePropertyDeviceIsRunning,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMain
+ };
+
+ return AudioObjectGetPropertyData(id,
+ &addr,
+ 0,
+ NULL,
+ &size,
+ result);
+}
+
+static void coreaudio_logstatus (OSStatus status)
+{
+ const 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 %" PRId32 "\n", (int32_t)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);
+}
+
+#define coreaudio_playback_logerr(status, ...) \
+ coreaudio_logerr2(status, "playback", __VA_ARGS__)
+
+static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_lock (&core->buf_mutex);
+ if (err) {
+ dolog ("Could not lock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&core->buf_mutex);
+ if (err) {
+ dolog ("Could not unlock voice for %s\nReason: %s\n",
+ fn_name, strerror (err));
+ return -1;
+ }
+ return 0;
+}
+
+#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
+ static ret_type glue(coreaudio_, name)args_decl \
+ { \
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
+ ret_type ret; \
+ \
+ if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
+ return 0; \
+ } \
+ \
+ ret = glue(audio_generic_, name)args; \
+ \
+ coreaudio_buf_unlock(core, "coreaudio_" #name); \
+ return ret; \
+ }
+COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
+COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
+ (hw, size))
+COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
+ (HWVoiceOut *hw, void *buf, size_t size),
+ (hw, buf, size))
+COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
+ (hw, buf, size))
+#undef COREAUDIO_WRAPPER_FUNC
+
+/*
+ * callback to feed audiooutput buffer. called without iothread lock.
+ * allowed to lock "buf_mutex", but disallowed to have any other locks.
+ */
+static OSStatus audioDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp *inNow,
+ const AudioBufferList *inInputData,
+ const AudioTimeStamp *inInputTime,
+ AudioBufferList *outOutputData,
+ const AudioTimeStamp *inOutputTime,
+ void *hwptr)
+{
+ UInt32 frameCount, pending_frames;
+ void *out = outOutputData->mBuffers[0].mData;
+ HWVoiceOut *hw = hwptr;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
+ size_t len;
+
+ if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
+ }
+
+ if (inDevice != core->outputDeviceID) {
+ coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
+ return 0;
+ }
+
+ frameCount = core->audioDevicePropertyBufferFrameSize;
+ pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
+
+ /* if there are not enough samples, set signal and return */
+ if (pending_frames < frameCount) {
+ inInputTime = 0;
+ coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
+ }
+
+ len = frameCount * hw->info.bytes_per_frame;
+ while (len) {
+ size_t write_len, start;
+
+ start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
+ assert(start < hw->size_emul);
+
+ write_len = MIN(MIN(hw->pending_emul, len),
+ hw->size_emul - start);
+
+ memcpy(out, hw->buf_emul + start, write_len);
+ hw->pending_emul -= write_len;
+ len -= write_len;
+ out += write_len;
+ }
+
+ coreaudio_buf_unlock (core, "audioDeviceIOProc");
+ return 0;
+}
+
+static OSStatus init_out_device(coreaudioVoiceOut *core)
+{
+ OSStatus status;
+ AudioValueRange frameRange;
+
+ AudioStreamBasicDescription streamBasicDescription = {
+ .mBitsPerChannel = core->hw.info.bits,
+ .mBytesPerFrame = core->hw.info.bytes_per_frame,
+ .mBytesPerPacket = core->hw.info.bytes_per_frame,
+ .mChannelsPerFrame = core->hw.info.nchannels,
+ .mFormatFlags = kLinearPCMFormatFlagIsFloat,
+ .mFormatID = kAudioFormatLinearPCM,
+ .mFramesPerPacket = 1,
+ .mSampleRate = core->hw.info.freq
+ };
+
+ status = coreaudio_get_voice(&core->outputDeviceID);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr (status,
+ "Could not get default output Device\n");
+ return status;
+ }
+ if (core->outputDeviceID == kAudioDeviceUnknown) {
+ dolog ("Could not initialize playback - Unknown Audiodevice\n");
+ return status;
+ }
+
+ /* get minimum and maximum buffer frame sizes */
+ status = coreaudio_get_framesizerange(core->outputDeviceID,
+ &frameRange);
+ if (status == kAudioHardwareBadObjectError) {
+ return 0;
+ }
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr (status,
+ "Could not get device buffer frame range\n");
+ return status;
+ }
+
+ if (frameRange.mMinimum > core->frameSizeSetting) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
+ dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
+ } else if (frameRange.mMaximum < core->frameSizeSetting) {
+ core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
+ dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
+ } else {
+ core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
+ }
+
+ /* set Buffer Frame Size */
+ status = coreaudio_set_framesize(core->outputDeviceID,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status == kAudioHardwareBadObjectError) {
+ return 0;
+ }
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr (status,
+ "Could not set device buffer frame size %" PRIu32 "\n",
+ (uint32_t)core->audioDevicePropertyBufferFrameSize);
+ return status;
+ }
+
+ /* get Buffer Frame Size */
+ status = coreaudio_get_framesize(core->outputDeviceID,
+ &core->audioDevicePropertyBufferFrameSize);
+ if (status == kAudioHardwareBadObjectError) {
+ return 0;
+ }
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr (status,
+ "Could not get device buffer frame size\n");
+ return status;
+ }
+ core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
+
+ /* set Samplerate */
+ status = coreaudio_set_streamformat(core->outputDeviceID,
+ &streamBasicDescription);
+ if (status == kAudioHardwareBadObjectError) {
+ return 0;
+ }
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr (status,
+ "Could not set samplerate %lf\n",
+ streamBasicDescription.mSampleRate);
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return status;
+ }
+
+ /*
+ * set Callback.
+ *
+ * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
+ * internal function named HALB_Mutex::Lock(), which locks a mutex in
+ * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
+ * AudioObjectGetPropertyData, which is called by coreaudio driver.
+ * Therefore, the specified callback must be designed to avoid a deadlock
+ * with the callers of AudioObjectGetPropertyData.
+ */
+ core->ioprocid = NULL;
+ status = AudioDeviceCreateIOProcID(core->outputDeviceID,
+ audioDeviceIOProc,
+ &core->hw,
+ &core->ioprocid);
+ if (status == kAudioHardwareBadDeviceError) {
+ return 0;
+ }
+ if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
+ coreaudio_playback_logerr (status, "Could not set IOProc\n");
+ core->outputDeviceID = kAudioDeviceUnknown;
+ return status;
+ }
+
+ return 0;
+}
+
+static void fini_out_device(coreaudioVoiceOut *core)
+{
+ OSStatus status;
+ UInt32 isrunning;
+
+ /* stop playback */
+ status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
+ if (status != kAudioHardwareBadObjectError) {
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr(status,
+ "Could not determine whether Device is playing\n");
+ }
+
+ if (isrunning) {
+ status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
+ if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
+ coreaudio_logerr(status, "Could not stop playback\n");
+ }
+ }
+ }
+
+ /* remove callback */
+ status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
+ core->ioprocid);
+ if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
+ coreaudio_logerr(status, "Could not remove IOProc\n");
+ }
+ core->outputDeviceID = kAudioDeviceUnknown;
+}
+
+static void update_device_playback_state(coreaudioVoiceOut *core)
+{
+ OSStatus status;
+ UInt32 isrunning;
+
+ status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
+ if (status != kAudioHardwareNoError) {
+ if (status != kAudioHardwareBadObjectError) {
+ coreaudio_logerr(status,
+ "Could not determine whether Device is playing\n");
+ }
+
+ return;
+ }
+
+ if (core->enabled) {
+ /* start playback */
+ if (!isrunning) {
+ status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
+ if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not resume playback\n");
+ }
+ }
+ } else {
+ /* stop playback */
+ if (isrunning) {
+ status = AudioDeviceStop(core->outputDeviceID,
+ core->ioprocid);
+ if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
+ coreaudio_logerr(status, "Could not pause playback\n");
+ }
+ }
+ }
+}
+
+/* called without iothread lock. */
+static OSStatus handle_voice_change(
+ AudioObjectID in_object_id,
+ UInt32 in_number_addresses,
+ const AudioObjectPropertyAddress *in_addresses,
+ void *in_client_data)
+{
+ coreaudioVoiceOut *core = in_client_data;
+
+ qemu_mutex_lock_iothread();
+
+ if (core->outputDeviceID) {
+ fini_out_device(core);
+ }
+
+ if (!init_out_device(core)) {
+ update_device_playback_state(core);
+ }
+
+ qemu_mutex_unlock_iothread();
+ return 0;
+}
+
+static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
+{
+ OSStatus status;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ int err;
+ Audiodev *dev = drv_opaque;
+ AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
+ struct audsettings obt_as;
+
+ /* create mutex */
+ err = pthread_mutex_init(&core->buf_mutex, NULL);
+ if (err) {
+ dolog("Could not create mutex\nReason: %s\n", strerror (err));
+ return -1;
+ }
+
+ obt_as = *as;
+ as = &obt_as;
+ as->fmt = AUDIO_FORMAT_F32;
+ audio_pcm_init_info (&hw->info, as);
+
+ core->frameSizeSetting = audio_buffer_frames(
+ qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
+
+ core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
+
+ status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
+ &voice_addr, handle_voice_change,
+ core);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr (status,
+ "Could not listen to voice property change\n");
+ return -1;
+ }
+
+ if (init_out_device(core)) {
+ status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
+ &voice_addr,
+ handle_voice_change,
+ core);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_playback_logerr(status,
+ "Could not remove voice property change listener\n");
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static void coreaudio_fini_out (HWVoiceOut *hw)
+{
+ OSStatus status;
+ int err;
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
+ &voice_addr,
+ handle_voice_change,
+ core);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr(status, "Could not remove voice property change listener\n");
+ }
+
+ fini_out_device(core);
+
+ /* destroy mutex */
+ err = pthread_mutex_destroy(&core->buf_mutex);
+ if (err) {
+ dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+ }
+}
+
+static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
+{
+ coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+
+ core->enabled = enable;
+ update_device_playback_state(core);
+}
+
+static void *coreaudio_audio_init(Audiodev *dev)
+{
+ return dev;
+}
+
+static void coreaudio_audio_fini (void *opaque)
+{
+}
+
+static struct audio_pcm_ops coreaudio_pcm_ops = {
+ .init_out = coreaudio_init_out,
+ .fini_out = coreaudio_fini_out,
+ /* wrapper for audio_generic_write */
+ .write = coreaudio_write,
+ /* wrapper for audio_generic_buffer_get_free */
+ .buffer_get_free = coreaudio_buffer_get_free,
+ /* wrapper for audio_generic_get_buffer_out */
+ .get_buffer_out = coreaudio_get_buffer_out,
+ /* wrapper for audio_generic_put_buffer_out */
+ .put_buffer_out = coreaudio_put_buffer_out,
+ .enable_out = coreaudio_enable_out
+};
+
+static struct audio_driver coreaudio_audio_driver = {
+ .name = "coreaudio",
+ .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
+ .init = coreaudio_audio_init,
+ .fini = coreaudio_audio_fini,
+ .pcm_ops = &coreaudio_pcm_ops,
+ .can_be_default = 1,
+ .max_voices_out = 1,
+ .max_voices_in = 0,
+ .voice_size_out = sizeof (coreaudioVoiceOut),
+ .voice_size_in = 0
+};
+
+static void register_audio_coreaudio(void)
+{
+ audio_driver_register(&coreaudio_audio_driver);
+}
+type_init(register_audio_coreaudio);