audio: Rename coreaudio extension to use Objective-C compiler
authorPhilippe Mathieu-Daudé <f4bug@amsat.org>
Tue, 15 Feb 2022 10:05:44 +0000 (11:05 +0100)
committerPhilippe Mathieu-Daudé <f4bug@amsat.org>
Tue, 15 Mar 2022 12:36:33 +0000 (13:36 +0100)
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>
audio/coreaudio.c [deleted file]
audio/coreaudio.m [new file with mode: 0644]
audio/meson.build

diff --git a/audio/coreaudio.c b/audio/coreaudio.c
deleted file mode 100644 (file)
index 3186b68..0000000
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * 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);
diff --git a/audio/coreaudio.m b/audio/coreaudio.m
new file mode 100644 (file)
index 0000000..3186b68
--- /dev/null
@@ -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);
index d9b295514f5251ccdc9818e6b757c3e59bf0f1d4..94dab16891d5bb89eb440cf1f683fca195b30ca6 100644 (file)
@@ -7,7 +7,7 @@ softmmu_ss.add(files(
   'wavcapture.c',
 ))
 
-softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.c'))
+softmmu_ss.add(when: coreaudio, if_true: files('coreaudio.m'))
 softmmu_ss.add(when: dsound, if_true: files('dsoundaudio.c', 'audio_win_int.c'))
 
 audio_modules = {}