qemu/audio/coreaudio.m
<<
>>
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
  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
  52static const AudioObjectPropertyAddress voice_addr = {
  53    kAudioHardwarePropertyDefaultOutputDevice,
  54    kAudioObjectPropertyScopeGlobal,
  55    kAudioObjectPropertyElementMain
  56};
  57
  58static 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
  70static 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
  88static 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
 105static 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
 122static 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
 140static 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
 157static 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
 214static 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
 229static 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
 250static 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
 263static 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    }
 291COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
 292COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
 293                       (hw, size))
 294COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
 295                       (HWVoiceOut *hw, void *buf, size_t size),
 296                       (hw, buf, size))
 297COREAUDIO_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 */
 305static 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
 360static 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
 476static 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
 506static 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. */
 542static 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
 564static 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
 616static 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
 639static 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
 647static void *coreaudio_audio_init(Audiodev *dev)
 648{
 649    return dev;
 650}
 651
 652static void coreaudio_audio_fini (void *opaque)
 653{
 654}
 655
 656static 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
 670static 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
 683static void register_audio_coreaudio(void)
 684{
 685    audio_driver_register(&coreaudio_audio_driver);
 686}
 687type_init(register_audio_coreaudio);
 688