qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

coreaudio.m (22498B)


      1 /*
      2  * QEMU OS X CoreAudio audio driver
      3  *
      4  * Copyright (c) 2005 Mike Kronenberg
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a copy
      7  * of this software and associated documentation files (the "Software"), to deal
      8  * in the Software without restriction, including without limitation the rights
      9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     10  * copies of the Software, and to permit persons to whom the Software is
     11  * furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
     19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     22  * THE SOFTWARE.
     23  */
     24 
     25 #include "qemu/osdep.h"
     26 #include <CoreAudio/CoreAudio.h>
     27 #include <pthread.h>            /* pthread_X */
     28 
     29 #include "qemu/main-loop.h"
     30 #include "qemu/module.h"
     31 #include "audio.h"
     32 
     33 #define AUDIO_CAP "coreaudio"
     34 #include "audio_int.h"
     35 
     36 typedef struct coreaudioVoiceOut {
     37     HWVoiceOut hw;
     38     pthread_mutex_t buf_mutex;
     39     AudioDeviceID outputDeviceID;
     40     int frameSizeSetting;
     41     uint32_t bufferCount;
     42     UInt32 audioDevicePropertyBufferFrameSize;
     43     AudioDeviceIOProcID ioprocid;
     44     bool enabled;
     45 } coreaudioVoiceOut;
     46 
     47 #if !defined(MAC_OS_VERSION_12_0) \
     48     || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0)
     49 #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
     50 #endif
     51 
     52 static const AudioObjectPropertyAddress voice_addr = {
     53     kAudioHardwarePropertyDefaultOutputDevice,
     54     kAudioObjectPropertyScopeGlobal,
     55     kAudioObjectPropertyElementMain
     56 };
     57 
     58 static OSStatus coreaudio_get_voice(AudioDeviceID *id)
     59 {
     60     UInt32 size = sizeof(*id);
     61 
     62     return AudioObjectGetPropertyData(kAudioObjectSystemObject,
     63                                       &voice_addr,
     64                                       0,
     65                                       NULL,
     66                                       &size,
     67                                       id);
     68 }
     69 
     70 static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
     71                                              AudioValueRange *framerange)
     72 {
     73     UInt32 size = sizeof(*framerange);
     74     AudioObjectPropertyAddress addr = {
     75         kAudioDevicePropertyBufferFrameSizeRange,
     76         kAudioDevicePropertyScopeOutput,
     77         kAudioObjectPropertyElementMain
     78     };
     79 
     80     return AudioObjectGetPropertyData(id,
     81                                       &addr,
     82                                       0,
     83                                       NULL,
     84                                       &size,
     85                                       framerange);
     86 }
     87 
     88 static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
     89 {
     90     UInt32 size = sizeof(*framesize);
     91     AudioObjectPropertyAddress addr = {
     92         kAudioDevicePropertyBufferFrameSize,
     93         kAudioDevicePropertyScopeOutput,
     94         kAudioObjectPropertyElementMain
     95     };
     96 
     97     return AudioObjectGetPropertyData(id,
     98                                       &addr,
     99                                       0,
    100                                       NULL,
    101                                       &size,
    102                                       framesize);
    103 }
    104 
    105 static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
    106 {
    107     UInt32 size = sizeof(*framesize);
    108     AudioObjectPropertyAddress addr = {
    109         kAudioDevicePropertyBufferFrameSize,
    110         kAudioDevicePropertyScopeOutput,
    111         kAudioObjectPropertyElementMain
    112     };
    113 
    114     return AudioObjectSetPropertyData(id,
    115                                       &addr,
    116                                       0,
    117                                       NULL,
    118                                       size,
    119                                       framesize);
    120 }
    121 
    122 static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
    123                                            AudioStreamBasicDescription *d)
    124 {
    125     UInt32 size = sizeof(*d);
    126     AudioObjectPropertyAddress addr = {
    127         kAudioDevicePropertyStreamFormat,
    128         kAudioDevicePropertyScopeOutput,
    129         kAudioObjectPropertyElementMain
    130     };
    131 
    132     return AudioObjectSetPropertyData(id,
    133                                       &addr,
    134                                       0,
    135                                       NULL,
    136                                       size,
    137                                       d);
    138 }
    139 
    140 static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
    141 {
    142     UInt32 size = sizeof(*result);
    143     AudioObjectPropertyAddress addr = {
    144         kAudioDevicePropertyDeviceIsRunning,
    145         kAudioDevicePropertyScopeOutput,
    146         kAudioObjectPropertyElementMain
    147     };
    148 
    149     return AudioObjectGetPropertyData(id,
    150                                       &addr,
    151                                       0,
    152                                       NULL,
    153                                       &size,
    154                                       result);
    155 }
    156 
    157 static void coreaudio_logstatus (OSStatus status)
    158 {
    159     const char *str = "BUG";
    160 
    161     switch (status) {
    162     case kAudioHardwareNoError:
    163         str = "kAudioHardwareNoError";
    164         break;
    165 
    166     case kAudioHardwareNotRunningError:
    167         str = "kAudioHardwareNotRunningError";
    168         break;
    169 
    170     case kAudioHardwareUnspecifiedError:
    171         str = "kAudioHardwareUnspecifiedError";
    172         break;
    173 
    174     case kAudioHardwareUnknownPropertyError:
    175         str = "kAudioHardwareUnknownPropertyError";
    176         break;
    177 
    178     case kAudioHardwareBadPropertySizeError:
    179         str = "kAudioHardwareBadPropertySizeError";
    180         break;
    181 
    182     case kAudioHardwareIllegalOperationError:
    183         str = "kAudioHardwareIllegalOperationError";
    184         break;
    185 
    186     case kAudioHardwareBadDeviceError:
    187         str = "kAudioHardwareBadDeviceError";
    188         break;
    189 
    190     case kAudioHardwareBadStreamError:
    191         str = "kAudioHardwareBadStreamError";
    192         break;
    193 
    194     case kAudioHardwareUnsupportedOperationError:
    195         str = "kAudioHardwareUnsupportedOperationError";
    196         break;
    197 
    198     case kAudioDeviceUnsupportedFormatError:
    199         str = "kAudioDeviceUnsupportedFormatError";
    200         break;
    201 
    202     case kAudioDevicePermissionsError:
    203         str = "kAudioDevicePermissionsError";
    204         break;
    205 
    206     default:
    207         AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
    208         return;
    209     }
    210 
    211     AUD_log (AUDIO_CAP, "Reason: %s\n", str);
    212 }
    213 
    214 static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
    215     OSStatus status,
    216     const char *fmt,
    217     ...
    218     )
    219 {
    220     va_list ap;
    221 
    222     va_start (ap, fmt);
    223     AUD_log (AUDIO_CAP, fmt, ap);
    224     va_end (ap);
    225 
    226     coreaudio_logstatus (status);
    227 }
    228 
    229 static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
    230     OSStatus status,
    231     const char *typ,
    232     const char *fmt,
    233     ...
    234     )
    235 {
    236     va_list ap;
    237 
    238     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
    239 
    240     va_start (ap, fmt);
    241     AUD_vlog (AUDIO_CAP, fmt, ap);
    242     va_end (ap);
    243 
    244     coreaudio_logstatus (status);
    245 }
    246 
    247 #define coreaudio_playback_logerr(status, ...) \
    248     coreaudio_logerr2(status, "playback", __VA_ARGS__)
    249 
    250 static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
    251 {
    252     int err;
    253 
    254     err = pthread_mutex_lock (&core->buf_mutex);
    255     if (err) {
    256         dolog ("Could not lock voice for %s\nReason: %s\n",
    257                fn_name, strerror (err));
    258         return -1;
    259     }
    260     return 0;
    261 }
    262 
    263 static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
    264 {
    265     int err;
    266 
    267     err = pthread_mutex_unlock (&core->buf_mutex);
    268     if (err) {
    269         dolog ("Could not unlock voice for %s\nReason: %s\n",
    270                fn_name, strerror (err));
    271         return -1;
    272     }
    273     return 0;
    274 }
    275 
    276 #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
    277     static ret_type glue(coreaudio_, name)args_decl             \
    278     {                                                           \
    279         coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \
    280         ret_type ret;                                           \
    281                                                                 \
    282         if (coreaudio_buf_lock(core, "coreaudio_" #name)) {         \
    283             return 0;                                           \
    284         }                                                       \
    285                                                                 \
    286         ret = glue(audio_generic_, name)args;                   \
    287                                                                 \
    288         coreaudio_buf_unlock(core, "coreaudio_" #name);             \
    289         return ret;                                             \
    290     }
    291 COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
    292 COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
    293                        (hw, size))
    294 COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
    295                        (HWVoiceOut *hw, void *buf, size_t size),
    296                        (hw, buf, size))
    297 COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
    298                        (hw, buf, size))
    299 #undef COREAUDIO_WRAPPER_FUNC
    300 
    301 /*
    302  * callback to feed audiooutput buffer. called without iothread lock.
    303  * allowed to lock "buf_mutex", but disallowed to have any other locks.
    304  */
    305 static OSStatus audioDeviceIOProc(
    306     AudioDeviceID inDevice,
    307     const AudioTimeStamp *inNow,
    308     const AudioBufferList *inInputData,
    309     const AudioTimeStamp *inInputTime,
    310     AudioBufferList *outOutputData,
    311     const AudioTimeStamp *inOutputTime,
    312     void *hwptr)
    313 {
    314     UInt32 frameCount, pending_frames;
    315     void *out = outOutputData->mBuffers[0].mData;
    316     HWVoiceOut *hw = hwptr;
    317     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
    318     size_t len;
    319 
    320     if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
    321         inInputTime = 0;
    322         return 0;
    323     }
    324 
    325     if (inDevice != core->outputDeviceID) {
    326         coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
    327         return 0;
    328     }
    329 
    330     frameCount = core->audioDevicePropertyBufferFrameSize;
    331     pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
    332 
    333     /* if there are not enough samples, set signal and return */
    334     if (pending_frames < frameCount) {
    335         inInputTime = 0;
    336         coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
    337         return 0;
    338     }
    339 
    340     len = frameCount * hw->info.bytes_per_frame;
    341     while (len) {
    342         size_t write_len, start;
    343 
    344         start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
    345         assert(start < hw->size_emul);
    346 
    347         write_len = MIN(MIN(hw->pending_emul, len),
    348                         hw->size_emul - start);
    349 
    350         memcpy(out, hw->buf_emul + start, write_len);
    351         hw->pending_emul -= write_len;
    352         len -= write_len;
    353         out += write_len;
    354     }
    355 
    356     coreaudio_buf_unlock (core, "audioDeviceIOProc");
    357     return 0;
    358 }
    359 
    360 static OSStatus init_out_device(coreaudioVoiceOut *core)
    361 {
    362     OSStatus status;
    363     AudioValueRange frameRange;
    364 
    365     AudioStreamBasicDescription streamBasicDescription = {
    366         .mBitsPerChannel = core->hw.info.bits,
    367         .mBytesPerFrame = core->hw.info.bytes_per_frame,
    368         .mBytesPerPacket = core->hw.info.bytes_per_frame,
    369         .mChannelsPerFrame = core->hw.info.nchannels,
    370         .mFormatFlags = kLinearPCMFormatFlagIsFloat,
    371         .mFormatID = kAudioFormatLinearPCM,
    372         .mFramesPerPacket = 1,
    373         .mSampleRate = core->hw.info.freq
    374     };
    375 
    376     status = coreaudio_get_voice(&core->outputDeviceID);
    377     if (status != kAudioHardwareNoError) {
    378         coreaudio_playback_logerr (status,
    379                                    "Could not get default output Device\n");
    380         return status;
    381     }
    382     if (core->outputDeviceID == kAudioDeviceUnknown) {
    383         dolog ("Could not initialize playback - Unknown Audiodevice\n");
    384         return status;
    385     }
    386 
    387     /* get minimum and maximum buffer frame sizes */
    388     status = coreaudio_get_framesizerange(core->outputDeviceID,
    389                                           &frameRange);
    390     if (status == kAudioHardwareBadObjectError) {
    391         return 0;
    392     }
    393     if (status != kAudioHardwareNoError) {
    394         coreaudio_playback_logerr (status,
    395                                     "Could not get device buffer frame range\n");
    396         return status;
    397     }
    398 
    399     if (frameRange.mMinimum > core->frameSizeSetting) {
    400         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
    401         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
    402     } else if (frameRange.mMaximum < core->frameSizeSetting) {
    403         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
    404         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
    405     } else {
    406         core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
    407     }
    408 
    409     /* set Buffer Frame Size */
    410     status = coreaudio_set_framesize(core->outputDeviceID,
    411                                      &core->audioDevicePropertyBufferFrameSize);
    412     if (status == kAudioHardwareBadObjectError) {
    413         return 0;
    414     }
    415     if (status != kAudioHardwareNoError) {
    416         coreaudio_playback_logerr (status,
    417                                     "Could not set device buffer frame size %" PRIu32 "\n",
    418                                     (uint32_t)core->audioDevicePropertyBufferFrameSize);
    419         return status;
    420     }
    421 
    422     /* get Buffer Frame Size */
    423     status = coreaudio_get_framesize(core->outputDeviceID,
    424                                      &core->audioDevicePropertyBufferFrameSize);
    425     if (status == kAudioHardwareBadObjectError) {
    426         return 0;
    427     }
    428     if (status != kAudioHardwareNoError) {
    429         coreaudio_playback_logerr (status,
    430                                     "Could not get device buffer frame size\n");
    431         return status;
    432     }
    433     core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
    434 
    435     /* set Samplerate */
    436     status = coreaudio_set_streamformat(core->outputDeviceID,
    437                                         &streamBasicDescription);
    438     if (status == kAudioHardwareBadObjectError) {
    439         return 0;
    440     }
    441     if (status != kAudioHardwareNoError) {
    442         coreaudio_playback_logerr (status,
    443                                    "Could not set samplerate %lf\n",
    444                                    streamBasicDescription.mSampleRate);
    445         core->outputDeviceID = kAudioDeviceUnknown;
    446         return status;
    447     }
    448 
    449     /*
    450      * set Callback.
    451      *
    452      * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
    453      * internal function named HALB_Mutex::Lock(), which locks a mutex in
    454      * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
    455      * AudioObjectGetPropertyData, which is called by coreaudio driver.
    456      * Therefore, the specified callback must be designed to avoid a deadlock
    457      * with the callers of AudioObjectGetPropertyData.
    458      */
    459     core->ioprocid = NULL;
    460     status = AudioDeviceCreateIOProcID(core->outputDeviceID,
    461                                        audioDeviceIOProc,
    462                                        &core->hw,
    463                                        &core->ioprocid);
    464     if (status == kAudioHardwareBadDeviceError) {
    465         return 0;
    466     }
    467     if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
    468         coreaudio_playback_logerr (status, "Could not set IOProc\n");
    469         core->outputDeviceID = kAudioDeviceUnknown;
    470         return status;
    471     }
    472 
    473     return 0;
    474 }
    475 
    476 static void fini_out_device(coreaudioVoiceOut *core)
    477 {
    478     OSStatus status;
    479     UInt32 isrunning;
    480 
    481     /* stop playback */
    482     status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
    483     if (status != kAudioHardwareBadObjectError) {
    484         if (status != kAudioHardwareNoError) {
    485             coreaudio_logerr(status,
    486                              "Could not determine whether Device is playing\n");
    487         }
    488 
    489         if (isrunning) {
    490             status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
    491             if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    492                 coreaudio_logerr(status, "Could not stop playback\n");
    493             }
    494         }
    495     }
    496 
    497     /* remove callback */
    498     status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
    499                                         core->ioprocid);
    500     if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    501         coreaudio_logerr(status, "Could not remove IOProc\n");
    502     }
    503     core->outputDeviceID = kAudioDeviceUnknown;
    504 }
    505 
    506 static void update_device_playback_state(coreaudioVoiceOut *core)
    507 {
    508     OSStatus status;
    509     UInt32 isrunning;
    510 
    511     status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
    512     if (status != kAudioHardwareNoError) {
    513         if (status != kAudioHardwareBadObjectError) {
    514             coreaudio_logerr(status,
    515                              "Could not determine whether Device is playing\n");
    516         }
    517 
    518         return;
    519     }
    520 
    521     if (core->enabled) {
    522         /* start playback */
    523         if (!isrunning) {
    524             status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
    525             if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    526                 coreaudio_logerr (status, "Could not resume playback\n");
    527             }
    528         }
    529     } else {
    530         /* stop playback */
    531         if (isrunning) {
    532             status = AudioDeviceStop(core->outputDeviceID,
    533                                      core->ioprocid);
    534             if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
    535                 coreaudio_logerr(status, "Could not pause playback\n");
    536             }
    537         }
    538     }
    539 }
    540 
    541 /* called without iothread lock. */
    542 static OSStatus handle_voice_change(
    543     AudioObjectID in_object_id,
    544     UInt32 in_number_addresses,
    545     const AudioObjectPropertyAddress *in_addresses,
    546     void *in_client_data)
    547 {
    548     coreaudioVoiceOut *core = in_client_data;
    549 
    550     qemu_mutex_lock_iothread();
    551 
    552     if (core->outputDeviceID) {
    553         fini_out_device(core);
    554     }
    555 
    556     if (!init_out_device(core)) {
    557         update_device_playback_state(core);
    558     }
    559 
    560     qemu_mutex_unlock_iothread();
    561     return 0;
    562 }
    563 
    564 static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
    565                               void *drv_opaque)
    566 {
    567     OSStatus status;
    568     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
    569     int err;
    570     Audiodev *dev = drv_opaque;
    571     AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
    572     struct audsettings obt_as;
    573 
    574     /* create mutex */
    575     err = pthread_mutex_init(&core->buf_mutex, NULL);
    576     if (err) {
    577         dolog("Could not create mutex\nReason: %s\n", strerror (err));
    578         return -1;
    579     }
    580 
    581     obt_as = *as;
    582     as = &obt_as;
    583     as->fmt = AUDIO_FORMAT_F32;
    584     audio_pcm_init_info (&hw->info, as);
    585 
    586     core->frameSizeSetting = audio_buffer_frames(
    587         qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
    588 
    589     core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
    590 
    591     status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
    592                                             &voice_addr, handle_voice_change,
    593                                             core);
    594     if (status != kAudioHardwareNoError) {
    595         coreaudio_playback_logerr (status,
    596                                    "Could not listen to voice property change\n");
    597         return -1;
    598     }
    599 
    600     if (init_out_device(core)) {
    601         status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
    602                                                    &voice_addr,
    603                                                    handle_voice_change,
    604                                                    core);
    605         if (status != kAudioHardwareNoError) {
    606             coreaudio_playback_logerr(status,
    607                                       "Could not remove voice property change listener\n");
    608         }
    609 
    610         return -1;
    611     }
    612 
    613     return 0;
    614 }
    615 
    616 static void coreaudio_fini_out (HWVoiceOut *hw)
    617 {
    618     OSStatus status;
    619     int err;
    620     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
    621 
    622     status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
    623                                                &voice_addr,
    624                                                handle_voice_change,
    625                                                core);
    626     if (status != kAudioHardwareNoError) {
    627         coreaudio_logerr(status, "Could not remove voice property change listener\n");
    628     }
    629 
    630     fini_out_device(core);
    631 
    632     /* destroy mutex */
    633     err = pthread_mutex_destroy(&core->buf_mutex);
    634     if (err) {
    635         dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
    636     }
    637 }
    638 
    639 static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
    640 {
    641     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
    642 
    643     core->enabled = enable;
    644     update_device_playback_state(core);
    645 }
    646 
    647 static void *coreaudio_audio_init(Audiodev *dev)
    648 {
    649     return dev;
    650 }
    651 
    652 static void coreaudio_audio_fini (void *opaque)
    653 {
    654 }
    655 
    656 static struct audio_pcm_ops coreaudio_pcm_ops = {
    657     .init_out = coreaudio_init_out,
    658     .fini_out = coreaudio_fini_out,
    659   /* wrapper for audio_generic_write */
    660     .write    = coreaudio_write,
    661   /* wrapper for audio_generic_buffer_get_free */
    662     .buffer_get_free = coreaudio_buffer_get_free,
    663   /* wrapper for audio_generic_get_buffer_out */
    664     .get_buffer_out = coreaudio_get_buffer_out,
    665   /* wrapper for audio_generic_put_buffer_out */
    666     .put_buffer_out = coreaudio_put_buffer_out,
    667     .enable_out = coreaudio_enable_out
    668 };
    669 
    670 static struct audio_driver coreaudio_audio_driver = {
    671     .name           = "coreaudio",
    672     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
    673     .init           = coreaudio_audio_init,
    674     .fini           = coreaudio_audio_fini,
    675     .pcm_ops        = &coreaudio_pcm_ops,
    676     .can_be_default = 1,
    677     .max_voices_out = 1,
    678     .max_voices_in  = 0,
    679     .voice_size_out = sizeof (coreaudioVoiceOut),
    680     .voice_size_in  = 0
    681 };
    682 
    683 static void register_audio_coreaudio(void)
    684 {
    685     audio_driver_register(&coreaudio_audio_driver);
    686 }
    687 type_init(register_audio_coreaudio);