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