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/module.h"
  30#include "audio.h"
  31
  32#define AUDIO_CAP "coreaudio"
  33#include "audio_int.h"
  34
  35typedef struct coreaudioVoiceOut {
  36    HWVoiceOut hw;
  37    pthread_mutex_t mutex;
  38    AudioDeviceID outputDeviceID;
  39    int frameSizeSetting;
  40    uint32_t bufferCount;
  41    UInt32 audioDevicePropertyBufferFrameSize;
  42    AudioStreamBasicDescription outputStreamBasicDescription;
  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_get_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 AudioObjectGetPropertyData(id,
 128                                      &addr,
 129                                      0,
 130                                      NULL,
 131                                      &size,
 132                                      d);
 133}
 134
 135static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
 136                                           AudioStreamBasicDescription *d)
 137{
 138    UInt32 size = sizeof(*d);
 139    AudioObjectPropertyAddress addr = {
 140        kAudioDevicePropertyStreamFormat,
 141        kAudioDevicePropertyScopeOutput,
 142        kAudioObjectPropertyElementMaster
 143    };
 144
 145    return AudioObjectSetPropertyData(id,
 146                                      &addr,
 147                                      0,
 148                                      NULL,
 149                                      size,
 150                                      d);
 151}
 152
 153static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
 154{
 155    UInt32 size = sizeof(*result);
 156    AudioObjectPropertyAddress addr = {
 157        kAudioDevicePropertyDeviceIsRunning,
 158        kAudioDevicePropertyScopeOutput,
 159        kAudioObjectPropertyElementMaster
 160    };
 161
 162    return AudioObjectGetPropertyData(id,
 163                                      &addr,
 164                                      0,
 165                                      NULL,
 166                                      &size,
 167                                      result);
 168}
 169
 170static void coreaudio_logstatus (OSStatus status)
 171{
 172    const char *str = "BUG";
 173
 174    switch (status) {
 175    case kAudioHardwareNoError:
 176        str = "kAudioHardwareNoError";
 177        break;
 178
 179    case kAudioHardwareNotRunningError:
 180        str = "kAudioHardwareNotRunningError";
 181        break;
 182
 183    case kAudioHardwareUnspecifiedError:
 184        str = "kAudioHardwareUnspecifiedError";
 185        break;
 186
 187    case kAudioHardwareUnknownPropertyError:
 188        str = "kAudioHardwareUnknownPropertyError";
 189        break;
 190
 191    case kAudioHardwareBadPropertySizeError:
 192        str = "kAudioHardwareBadPropertySizeError";
 193        break;
 194
 195    case kAudioHardwareIllegalOperationError:
 196        str = "kAudioHardwareIllegalOperationError";
 197        break;
 198
 199    case kAudioHardwareBadDeviceError:
 200        str = "kAudioHardwareBadDeviceError";
 201        break;
 202
 203    case kAudioHardwareBadStreamError:
 204        str = "kAudioHardwareBadStreamError";
 205        break;
 206
 207    case kAudioHardwareUnsupportedOperationError:
 208        str = "kAudioHardwareUnsupportedOperationError";
 209        break;
 210
 211    case kAudioDeviceUnsupportedFormatError:
 212        str = "kAudioDeviceUnsupportedFormatError";
 213        break;
 214
 215    case kAudioDevicePermissionsError:
 216        str = "kAudioDevicePermissionsError";
 217        break;
 218
 219    default:
 220        AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
 221        return;
 222    }
 223
 224    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
 225}
 226
 227static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
 228    OSStatus status,
 229    const char *fmt,
 230    ...
 231    )
 232{
 233    va_list ap;
 234
 235    va_start (ap, fmt);
 236    AUD_log (AUDIO_CAP, fmt, ap);
 237    va_end (ap);
 238
 239    coreaudio_logstatus (status);
 240}
 241
 242static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
 243    OSStatus status,
 244    const char *typ,
 245    const char *fmt,
 246    ...
 247    )
 248{
 249    va_list ap;
 250
 251    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 252
 253    va_start (ap, fmt);
 254    AUD_vlog (AUDIO_CAP, fmt, ap);
 255    va_end (ap);
 256
 257    coreaudio_logstatus (status);
 258}
 259
 260#define coreaudio_playback_logerr(status, ...) \
 261    coreaudio_logerr2(status, "playback", __VA_ARGS__)
 262
 263static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
 264{
 265    int err;
 266
 267    err = pthread_mutex_lock (&core->mutex);
 268    if (err) {
 269        dolog ("Could not lock voice for %s\nReason: %s\n",
 270               fn_name, strerror (err));
 271        return -1;
 272    }
 273    return 0;
 274}
 275
 276static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
 277{
 278    int err;
 279
 280    err = pthread_mutex_unlock (&core->mutex);
 281    if (err) {
 282        dolog ("Could not unlock voice for %s\nReason: %s\n",
 283               fn_name, strerror (err));
 284        return -1;
 285    }
 286    return 0;
 287}
 288
 289#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
 290    static ret_type glue(coreaudio_, name)args_decl             \
 291    {                                                           \
 292        coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \
 293        ret_type ret;                                           \
 294                                                                \
 295        if (coreaudio_lock(core, "coreaudio_" #name)) {         \
 296            return 0;                                           \
 297        }                                                       \
 298                                                                \
 299        ret = glue(audio_generic_, name)args;                   \
 300                                                                \
 301        coreaudio_unlock(core, "coreaudio_" #name);             \
 302        return ret;                                             \
 303    }
 304COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
 305                       (hw, size))
 306COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
 307                       (HWVoiceOut *hw, void *buf, size_t size),
 308                       (hw, buf, size))
 309COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
 310                       (hw, buf, size))
 311#undef COREAUDIO_WRAPPER_FUNC
 312
 313/* callback to feed audiooutput buffer */
 314static OSStatus audioDeviceIOProc(
 315    AudioDeviceID inDevice,
 316    const AudioTimeStamp *inNow,
 317    const AudioBufferList *inInputData,
 318    const AudioTimeStamp *inInputTime,
 319    AudioBufferList *outOutputData,
 320    const AudioTimeStamp *inOutputTime,
 321    void *hwptr)
 322{
 323    UInt32 frameCount, pending_frames;
 324    void *out = outOutputData->mBuffers[0].mData;
 325    HWVoiceOut *hw = hwptr;
 326    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
 327    size_t len;
 328
 329    if (coreaudio_lock (core, "audioDeviceIOProc")) {
 330        inInputTime = 0;
 331        return 0;
 332    }
 333
 334    if (inDevice != core->outputDeviceID) {
 335        coreaudio_unlock (core, "audioDeviceIOProc(old device)");
 336        return 0;
 337    }
 338
 339    frameCount = core->audioDevicePropertyBufferFrameSize;
 340    pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
 341
 342    /* if there are not enough samples, set signal and return */
 343    if (pending_frames < frameCount) {
 344        inInputTime = 0;
 345        coreaudio_unlock (core, "audioDeviceIOProc(empty)");
 346        return 0;
 347    }
 348
 349    len = frameCount * hw->info.bytes_per_frame;
 350    while (len) {
 351        size_t write_len;
 352        ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
 353        if (start < 0) {
 354            start += hw->size_emul;
 355        }
 356        assert(start >= 0 && start < hw->size_emul);
 357
 358        write_len = MIN(MIN(hw->pending_emul, len),
 359                        hw->size_emul - start);
 360
 361        memcpy(out, hw->buf_emul + start, write_len);
 362        hw->pending_emul -= write_len;
 363        len -= write_len;
 364        out += write_len;
 365    }
 366
 367    coreaudio_unlock (core, "audioDeviceIOProc");
 368    return 0;
 369}
 370
 371static OSStatus init_out_device(coreaudioVoiceOut *core)
 372{
 373    OSStatus status;
 374    AudioValueRange frameRange;
 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    /* get StreamFormat */
 436    status = coreaudio_get_streamformat(core->outputDeviceID,
 437                                        &core->outputStreamBasicDescription);
 438    if (status == kAudioHardwareBadObjectError) {
 439        return 0;
 440    }
 441    if (status != kAudioHardwareNoError) {
 442        coreaudio_playback_logerr (status,
 443                                    "Could not get Device Stream properties\n");
 444        core->outputDeviceID = kAudioDeviceUnknown;
 445        return status;
 446    }
 447
 448    /* set Samplerate */
 449    status = coreaudio_set_streamformat(core->outputDeviceID,
 450                                        &core->outputStreamBasicDescription);
 451    if (status == kAudioHardwareBadObjectError) {
 452        return 0;
 453    }
 454    if (status != kAudioHardwareNoError) {
 455        coreaudio_playback_logerr (status,
 456                                   "Could not set samplerate %lf\n",
 457                                   core->outputStreamBasicDescription.mSampleRate);
 458        core->outputDeviceID = kAudioDeviceUnknown;
 459        return status;
 460    }
 461
 462    /* set Callback */
 463    core->ioprocid = NULL;
 464    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
 465                                       audioDeviceIOProc,
 466                                       &core->hw,
 467                                       &core->ioprocid);
 468    if (status == kAudioHardwareBadDeviceError) {
 469        return 0;
 470    }
 471    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
 472        coreaudio_playback_logerr (status, "Could not set IOProc\n");
 473        core->outputDeviceID = kAudioDeviceUnknown;
 474        return status;
 475    }
 476
 477    return 0;
 478}
 479
 480static void fini_out_device(coreaudioVoiceOut *core)
 481{
 482    OSStatus status;
 483    UInt32 isrunning;
 484
 485    /* stop playback */
 486    status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
 487    if (status != kAudioHardwareBadObjectError) {
 488        if (status != kAudioHardwareNoError) {
 489            coreaudio_logerr(status,
 490                             "Could not determine whether Device is playing\n");
 491        }
 492
 493        if (isrunning) {
 494            status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
 495            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
 496                coreaudio_logerr(status, "Could not stop playback\n");
 497            }
 498        }
 499    }
 500
 501    /* remove callback */
 502    status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
 503                                        core->ioprocid);
 504    if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
 505        coreaudio_logerr(status, "Could not remove IOProc\n");
 506    }
 507    core->outputDeviceID = kAudioDeviceUnknown;
 508}
 509
 510static void update_device_playback_state(coreaudioVoiceOut *core)
 511{
 512    OSStatus status;
 513    UInt32 isrunning;
 514
 515    status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
 516    if (status != kAudioHardwareNoError) {
 517        if (status != kAudioHardwareBadObjectError) {
 518            coreaudio_logerr(status,
 519                             "Could not determine whether Device is playing\n");
 520        }
 521
 522        return;
 523    }
 524
 525    if (core->enabled) {
 526        /* start playback */
 527        if (!isrunning) {
 528            status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
 529            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
 530                coreaudio_logerr (status, "Could not resume playback\n");
 531            }
 532        }
 533    } else {
 534        /* stop playback */
 535        if (isrunning) {
 536            status = AudioDeviceStop(core->outputDeviceID,
 537                                     core->ioprocid);
 538            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
 539                coreaudio_logerr(status, "Could not pause playback\n");
 540            }
 541        }
 542    }
 543}
 544
 545static OSStatus handle_voice_change(
 546    AudioObjectID in_object_id,
 547    UInt32 in_number_addresses,
 548    const AudioObjectPropertyAddress *in_addresses,
 549    void *in_client_data)
 550{
 551    OSStatus status;
 552    coreaudioVoiceOut *core = in_client_data;
 553
 554    if (coreaudio_lock(core, __func__)) {
 555        abort();
 556    }
 557
 558    if (core->outputDeviceID) {
 559        fini_out_device(core);
 560    }
 561
 562    status = init_out_device(core);
 563    if (!status) {
 564        update_device_playback_state(core);
 565    }
 566
 567    coreaudio_unlock (core, __func__);
 568    return status;
 569}
 570
 571static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 572                              void *drv_opaque)
 573{
 574    OSStatus status;
 575    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 576    int err;
 577    Audiodev *dev = drv_opaque;
 578    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
 579    struct audsettings obt_as;
 580
 581    /* create mutex */
 582    err = pthread_mutex_init(&core->mutex, NULL);
 583    if (err) {
 584        dolog("Could not create mutex\nReason: %s\n", strerror (err));
 585        goto mutex_error;
 586    }
 587
 588    if (coreaudio_lock(core, __func__)) {
 589        goto lock_error;
 590    }
 591
 592    obt_as = *as;
 593    as = &obt_as;
 594    as->fmt = AUDIO_FORMAT_F32;
 595    audio_pcm_init_info (&hw->info, as);
 596
 597    core->frameSizeSetting = audio_buffer_frames(
 598        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
 599
 600    core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
 601    core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
 602
 603    status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
 604                                            &voice_addr, handle_voice_change,
 605                                            core);
 606    if (status != kAudioHardwareNoError) {
 607        coreaudio_playback_logerr (status,
 608                                   "Could not listen to voice property change\n");
 609        goto listener_error;
 610    }
 611
 612    if (init_out_device(core)) {
 613        goto device_error;
 614    }
 615
 616    coreaudio_unlock(core, __func__);
 617    return 0;
 618
 619device_error:
 620    status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
 621                                               &voice_addr,
 622                                               handle_voice_change,
 623                                               core);
 624    if (status != kAudioHardwareNoError) {
 625        coreaudio_playback_logerr(status,
 626                                  "Could not remove voice property change listener\n");
 627    }
 628
 629listener_error:
 630    coreaudio_unlock(core, __func__);
 631
 632lock_error:
 633    err = pthread_mutex_destroy(&core->mutex);
 634    if (err) {
 635        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
 636    }
 637
 638mutex_error:
 639    return -1;
 640}
 641
 642static void coreaudio_fini_out (HWVoiceOut *hw)
 643{
 644    OSStatus status;
 645    int err;
 646    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 647
 648    if (coreaudio_lock(core, __func__)) {
 649        abort();
 650    }
 651
 652    status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
 653                                               &voice_addr,
 654                                               handle_voice_change,
 655                                               core);
 656    if (status != kAudioHardwareNoError) {
 657        coreaudio_logerr(status, "Could not remove voice property change listener\n");
 658    }
 659
 660    fini_out_device(core);
 661
 662    coreaudio_unlock(core, __func__);
 663
 664    /* destroy mutex */
 665    err = pthread_mutex_destroy(&core->mutex);
 666    if (err) {
 667        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
 668    }
 669}
 670
 671static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
 672{
 673    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 674
 675    if (coreaudio_lock(core, __func__)) {
 676        abort();
 677    }
 678
 679    core->enabled = enable;
 680    update_device_playback_state(core);
 681
 682    coreaudio_unlock(core, __func__);
 683}
 684
 685static void *coreaudio_audio_init(Audiodev *dev)
 686{
 687    return dev;
 688}
 689
 690static void coreaudio_audio_fini (void *opaque)
 691{
 692}
 693
 694static struct audio_pcm_ops coreaudio_pcm_ops = {
 695    .init_out = coreaudio_init_out,
 696    .fini_out = coreaudio_fini_out,
 697  /* wrapper for audio_generic_write */
 698    .write    = coreaudio_write,
 699  /* wrapper for audio_generic_get_buffer_out */
 700    .get_buffer_out = coreaudio_get_buffer_out,
 701  /* wrapper for audio_generic_put_buffer_out */
 702    .put_buffer_out = coreaudio_put_buffer_out,
 703    .enable_out = coreaudio_enable_out
 704};
 705
 706static struct audio_driver coreaudio_audio_driver = {
 707    .name           = "coreaudio",
 708    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
 709    .init           = coreaudio_audio_init,
 710    .fini           = coreaudio_audio_fini,
 711    .pcm_ops        = &coreaudio_pcm_ops,
 712    .can_be_default = 1,
 713    .max_voices_out = 1,
 714    .max_voices_in  = 0,
 715    .voice_size_out = sizeof (coreaudioVoiceOut),
 716    .voice_size_in  = 0
 717};
 718
 719static void register_audio_coreaudio(void)
 720{
 721    audio_driver_register(&coreaudio_audio_driver);
 722}
 723type_init(register_audio_coreaudio);
 724