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
  35#ifndef MAC_OS_X_VERSION_10_6
  36#define MAC_OS_X_VERSION_10_6 1060
  37#endif
  38
  39typedef struct coreaudioVoiceOut {
  40    HWVoiceOut hw;
  41    pthread_mutex_t mutex;
  42    AudioDeviceID outputDeviceID;
  43    UInt32 audioDevicePropertyBufferFrameSize;
  44    AudioStreamBasicDescription outputStreamBasicDescription;
  45    AudioDeviceIOProcID ioprocid;
  46    int live;
  47    int decr;
  48    int rpos;
  49} coreaudioVoiceOut;
  50
  51#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
  52/* The APIs used here only become available from 10.6 */
  53
  54static OSStatus coreaudio_get_voice(AudioDeviceID *id)
  55{
  56    UInt32 size = sizeof(*id);
  57    AudioObjectPropertyAddress addr = {
  58        kAudioHardwarePropertyDefaultOutputDevice,
  59        kAudioObjectPropertyScopeGlobal,
  60        kAudioObjectPropertyElementMaster
  61    };
  62
  63    return AudioObjectGetPropertyData(kAudioObjectSystemObject,
  64                                      &addr,
  65                                      0,
  66                                      NULL,
  67                                      &size,
  68                                      id);
  69}
  70
  71static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
  72                                             AudioValueRange *framerange)
  73{
  74    UInt32 size = sizeof(*framerange);
  75    AudioObjectPropertyAddress addr = {
  76        kAudioDevicePropertyBufferFrameSizeRange,
  77        kAudioDevicePropertyScopeOutput,
  78        kAudioObjectPropertyElementMaster
  79    };
  80
  81    return AudioObjectGetPropertyData(id,
  82                                      &addr,
  83                                      0,
  84                                      NULL,
  85                                      &size,
  86                                      framerange);
  87}
  88
  89static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
  90{
  91    UInt32 size = sizeof(*framesize);
  92    AudioObjectPropertyAddress addr = {
  93        kAudioDevicePropertyBufferFrameSize,
  94        kAudioDevicePropertyScopeOutput,
  95        kAudioObjectPropertyElementMaster
  96    };
  97
  98    return AudioObjectGetPropertyData(id,
  99                                      &addr,
 100                                      0,
 101                                      NULL,
 102                                      &size,
 103                                      framesize);
 104}
 105
 106static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
 107{
 108    UInt32 size = sizeof(*framesize);
 109    AudioObjectPropertyAddress addr = {
 110        kAudioDevicePropertyBufferFrameSize,
 111        kAudioDevicePropertyScopeOutput,
 112        kAudioObjectPropertyElementMaster
 113    };
 114
 115    return AudioObjectSetPropertyData(id,
 116                                      &addr,
 117                                      0,
 118                                      NULL,
 119                                      size,
 120                                      framesize);
 121}
 122
 123static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
 124                                           AudioStreamBasicDescription *d)
 125{
 126    UInt32 size = sizeof(*d);
 127    AudioObjectPropertyAddress addr = {
 128        kAudioDevicePropertyStreamFormat,
 129        kAudioDevicePropertyScopeOutput,
 130        kAudioObjectPropertyElementMaster
 131    };
 132
 133    return AudioObjectGetPropertyData(id,
 134                                      &addr,
 135                                      0,
 136                                      NULL,
 137                                      &size,
 138                                      d);
 139}
 140
 141static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
 142                                           AudioStreamBasicDescription *d)
 143{
 144    UInt32 size = sizeof(*d);
 145    AudioObjectPropertyAddress addr = {
 146        kAudioDevicePropertyStreamFormat,
 147        kAudioDevicePropertyScopeOutput,
 148        kAudioObjectPropertyElementMaster
 149    };
 150
 151    return AudioObjectSetPropertyData(id,
 152                                      &addr,
 153                                      0,
 154                                      NULL,
 155                                      size,
 156                                      d);
 157}
 158
 159static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
 160{
 161    UInt32 size = sizeof(*result);
 162    AudioObjectPropertyAddress addr = {
 163        kAudioDevicePropertyDeviceIsRunning,
 164        kAudioDevicePropertyScopeOutput,
 165        kAudioObjectPropertyElementMaster
 166    };
 167
 168    return AudioObjectGetPropertyData(id,
 169                                      &addr,
 170                                      0,
 171                                      NULL,
 172                                      &size,
 173                                      result);
 174}
 175#else
 176/* Legacy versions of functions using deprecated APIs */
 177
 178static OSStatus coreaudio_get_voice(AudioDeviceID *id)
 179{
 180    UInt32 size = sizeof(*id);
 181
 182    return AudioHardwareGetProperty(
 183        kAudioHardwarePropertyDefaultOutputDevice,
 184        &size,
 185        id);
 186}
 187
 188static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
 189                                             AudioValueRange *framerange)
 190{
 191    UInt32 size = sizeof(*framerange);
 192
 193    return AudioDeviceGetProperty(
 194        id,
 195        0,
 196        0,
 197        kAudioDevicePropertyBufferFrameSizeRange,
 198        &size,
 199        framerange);
 200}
 201
 202static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
 203{
 204    UInt32 size = sizeof(*framesize);
 205
 206    return AudioDeviceGetProperty(
 207        id,
 208        0,
 209        false,
 210        kAudioDevicePropertyBufferFrameSize,
 211        &size,
 212        framesize);
 213}
 214
 215static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
 216{
 217    UInt32 size = sizeof(*framesize);
 218
 219    return AudioDeviceSetProperty(
 220        id,
 221        NULL,
 222        0,
 223        false,
 224        kAudioDevicePropertyBufferFrameSize,
 225        size,
 226        framesize);
 227}
 228
 229static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
 230                                           AudioStreamBasicDescription *d)
 231{
 232    UInt32 size = sizeof(*d);
 233
 234    return AudioDeviceGetProperty(
 235        id,
 236        0,
 237        false,
 238        kAudioDevicePropertyStreamFormat,
 239        &size,
 240        d);
 241}
 242
 243static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
 244                                           AudioStreamBasicDescription *d)
 245{
 246    UInt32 size = sizeof(*d);
 247
 248    return AudioDeviceSetProperty(
 249        id,
 250        0,
 251        0,
 252        0,
 253        kAudioDevicePropertyStreamFormat,
 254        size,
 255        d);
 256}
 257
 258static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
 259{
 260    UInt32 size = sizeof(*result);
 261
 262    return AudioDeviceGetProperty(
 263        id,
 264        0,
 265        0,
 266        kAudioDevicePropertyDeviceIsRunning,
 267        &size,
 268        result);
 269}
 270#endif
 271
 272static void coreaudio_logstatus (OSStatus status)
 273{
 274    const char *str = "BUG";
 275
 276    switch(status) {
 277    case kAudioHardwareNoError:
 278        str = "kAudioHardwareNoError";
 279        break;
 280
 281    case kAudioHardwareNotRunningError:
 282        str = "kAudioHardwareNotRunningError";
 283        break;
 284
 285    case kAudioHardwareUnspecifiedError:
 286        str = "kAudioHardwareUnspecifiedError";
 287        break;
 288
 289    case kAudioHardwareUnknownPropertyError:
 290        str = "kAudioHardwareUnknownPropertyError";
 291        break;
 292
 293    case kAudioHardwareBadPropertySizeError:
 294        str = "kAudioHardwareBadPropertySizeError";
 295        break;
 296
 297    case kAudioHardwareIllegalOperationError:
 298        str = "kAudioHardwareIllegalOperationError";
 299        break;
 300
 301    case kAudioHardwareBadDeviceError:
 302        str = "kAudioHardwareBadDeviceError";
 303        break;
 304
 305    case kAudioHardwareBadStreamError:
 306        str = "kAudioHardwareBadStreamError";
 307        break;
 308
 309    case kAudioHardwareUnsupportedOperationError:
 310        str = "kAudioHardwareUnsupportedOperationError";
 311        break;
 312
 313    case kAudioDeviceUnsupportedFormatError:
 314        str = "kAudioDeviceUnsupportedFormatError";
 315        break;
 316
 317    case kAudioDevicePermissionsError:
 318        str = "kAudioDevicePermissionsError";
 319        break;
 320
 321    default:
 322        AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
 323        return;
 324    }
 325
 326    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
 327}
 328
 329static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
 330    OSStatus status,
 331    const char *fmt,
 332    ...
 333    )
 334{
 335    va_list ap;
 336
 337    va_start (ap, fmt);
 338    AUD_log (AUDIO_CAP, fmt, ap);
 339    va_end (ap);
 340
 341    coreaudio_logstatus (status);
 342}
 343
 344static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
 345    OSStatus status,
 346    const char *typ,
 347    const char *fmt,
 348    ...
 349    )
 350{
 351    va_list ap;
 352
 353    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 354
 355    va_start (ap, fmt);
 356    AUD_vlog (AUDIO_CAP, fmt, ap);
 357    va_end (ap);
 358
 359    coreaudio_logstatus (status);
 360}
 361
 362static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
 363{
 364    OSStatus status;
 365    UInt32 result = 0;
 366    status = coreaudio_get_isrunning(outputDeviceID, &result);
 367    if (status != kAudioHardwareNoError) {
 368        coreaudio_logerr(status,
 369                         "Could not determine whether Device is playing\n");
 370    }
 371    return result;
 372}
 373
 374static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
 375{
 376    int err;
 377
 378    err = pthread_mutex_lock (&core->mutex);
 379    if (err) {
 380        dolog ("Could not lock voice for %s\nReason: %s\n",
 381               fn_name, strerror (err));
 382        return -1;
 383    }
 384    return 0;
 385}
 386
 387static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
 388{
 389    int err;
 390
 391    err = pthread_mutex_unlock (&core->mutex);
 392    if (err) {
 393        dolog ("Could not unlock voice for %s\nReason: %s\n",
 394               fn_name, strerror (err));
 395        return -1;
 396    }
 397    return 0;
 398}
 399
 400static int coreaudio_run_out (HWVoiceOut *hw, int live)
 401{
 402    int decr;
 403    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 404
 405    if (coreaudio_lock (core, "coreaudio_run_out")) {
 406        return 0;
 407    }
 408
 409    if (core->decr > live) {
 410        ldebug ("core->decr %d live %d core->live %d\n",
 411                core->decr,
 412                live,
 413                core->live);
 414    }
 415
 416    decr = audio_MIN (core->decr, live);
 417    core->decr -= decr;
 418
 419    core->live = live - decr;
 420    hw->rpos = core->rpos;
 421
 422    coreaudio_unlock (core, "coreaudio_run_out");
 423    return decr;
 424}
 425
 426/* callback to feed audiooutput buffer */
 427static OSStatus audioDeviceIOProc(
 428    AudioDeviceID inDevice,
 429    const AudioTimeStamp* inNow,
 430    const AudioBufferList* inInputData,
 431    const AudioTimeStamp* inInputTime,
 432    AudioBufferList* outOutputData,
 433    const AudioTimeStamp* inOutputTime,
 434    void* hwptr)
 435{
 436    UInt32 frame, frameCount;
 437    float *out = outOutputData->mBuffers[0].mData;
 438    HWVoiceOut *hw = hwptr;
 439    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
 440    int rpos, live;
 441    struct st_sample *src;
 442#ifndef FLOAT_MIXENG
 443#ifdef RECIPROCAL
 444    const float scale = 1.f / UINT_MAX;
 445#else
 446    const float scale = UINT_MAX;
 447#endif
 448#endif
 449
 450    if (coreaudio_lock (core, "audioDeviceIOProc")) {
 451        inInputTime = 0;
 452        return 0;
 453    }
 454
 455    frameCount = core->audioDevicePropertyBufferFrameSize;
 456    live = core->live;
 457
 458    /* if there are not enough samples, set signal and return */
 459    if (live < frameCount) {
 460        inInputTime = 0;
 461        coreaudio_unlock (core, "audioDeviceIOProc(empty)");
 462        return 0;
 463    }
 464
 465    rpos = core->rpos;
 466    src = hw->mix_buf + rpos;
 467
 468    /* fill buffer */
 469    for (frame = 0; frame < frameCount; frame++) {
 470#ifdef FLOAT_MIXENG
 471        *out++ = src[frame].l; /* left channel */
 472        *out++ = src[frame].r; /* right channel */
 473#else
 474#ifdef RECIPROCAL
 475        *out++ = src[frame].l * scale; /* left channel */
 476        *out++ = src[frame].r * scale; /* right channel */
 477#else
 478        *out++ = src[frame].l / scale; /* left channel */
 479        *out++ = src[frame].r / scale; /* right channel */
 480#endif
 481#endif
 482    }
 483
 484    rpos = (rpos + frameCount) % hw->samples;
 485    core->decr += frameCount;
 486    core->rpos = rpos;
 487
 488    coreaudio_unlock (core, "audioDeviceIOProc");
 489    return 0;
 490}
 491
 492static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
 493{
 494    return audio_pcm_sw_write (sw, buf, len);
 495}
 496
 497static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
 498                              void *drv_opaque)
 499{
 500    OSStatus status;
 501    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 502    int err;
 503    const char *typ = "playback";
 504    AudioValueRange frameRange;
 505    Audiodev *dev = drv_opaque;
 506    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
 507    int frames;
 508
 509    /* create mutex */
 510    err = pthread_mutex_init(&core->mutex, NULL);
 511    if (err) {
 512        dolog("Could not create mutex\nReason: %s\n", strerror (err));
 513        return -1;
 514    }
 515
 516    audio_pcm_init_info (&hw->info, as);
 517
 518    status = coreaudio_get_voice(&core->outputDeviceID);
 519    if (status != kAudioHardwareNoError) {
 520        coreaudio_logerr2 (status, typ,
 521                           "Could not get default output Device\n");
 522        return -1;
 523    }
 524    if (core->outputDeviceID == kAudioDeviceUnknown) {
 525        dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
 526        return -1;
 527    }
 528
 529    /* get minimum and maximum buffer frame sizes */
 530    status = coreaudio_get_framesizerange(core->outputDeviceID,
 531                                          &frameRange);
 532    if (status != kAudioHardwareNoError) {
 533        coreaudio_logerr2 (status, typ,
 534                           "Could not get device buffer frame range\n");
 535        return -1;
 536    }
 537
 538    frames = audio_buffer_frames(
 539        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
 540    if (frameRange.mMinimum > frames) {
 541        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
 542        dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
 543    } else if (frameRange.mMaximum < frames) {
 544        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
 545        dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
 546    }
 547    else {
 548        core->audioDevicePropertyBufferFrameSize = frames;
 549    }
 550
 551    /* set Buffer Frame Size */
 552    status = coreaudio_set_framesize(core->outputDeviceID,
 553                                     &core->audioDevicePropertyBufferFrameSize);
 554    if (status != kAudioHardwareNoError) {
 555        coreaudio_logerr2 (status, typ,
 556                           "Could not set device buffer frame size %" PRIu32 "\n",
 557                           (uint32_t)core->audioDevicePropertyBufferFrameSize);
 558        return -1;
 559    }
 560
 561    /* get Buffer Frame Size */
 562    status = coreaudio_get_framesize(core->outputDeviceID,
 563                                     &core->audioDevicePropertyBufferFrameSize);
 564    if (status != kAudioHardwareNoError) {
 565        coreaudio_logerr2 (status, typ,
 566                           "Could not get device buffer frame size\n");
 567        return -1;
 568    }
 569    hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
 570        core->audioDevicePropertyBufferFrameSize;
 571
 572    /* get StreamFormat */
 573    status = coreaudio_get_streamformat(core->outputDeviceID,
 574                                        &core->outputStreamBasicDescription);
 575    if (status != kAudioHardwareNoError) {
 576        coreaudio_logerr2 (status, typ,
 577                           "Could not get Device Stream properties\n");
 578        core->outputDeviceID = kAudioDeviceUnknown;
 579        return -1;
 580    }
 581
 582    /* set Samplerate */
 583    core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
 584    status = coreaudio_set_streamformat(core->outputDeviceID,
 585                                        &core->outputStreamBasicDescription);
 586    if (status != kAudioHardwareNoError) {
 587        coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
 588                           as->freq);
 589        core->outputDeviceID = kAudioDeviceUnknown;
 590        return -1;
 591    }
 592
 593    /* set Callback */
 594    core->ioprocid = NULL;
 595    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
 596                                       audioDeviceIOProc,
 597                                       hw,
 598                                       &core->ioprocid);
 599    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
 600        coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
 601        core->outputDeviceID = kAudioDeviceUnknown;
 602        return -1;
 603    }
 604
 605    /* start Playback */
 606    if (!isPlaying(core->outputDeviceID)) {
 607        status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
 608        if (status != kAudioHardwareNoError) {
 609            coreaudio_logerr2 (status, typ, "Could not start playback\n");
 610            AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid);
 611            core->outputDeviceID = kAudioDeviceUnknown;
 612            return -1;
 613        }
 614    }
 615
 616    return 0;
 617}
 618
 619static void coreaudio_fini_out (HWVoiceOut *hw)
 620{
 621    OSStatus status;
 622    int err;
 623    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 624
 625    if (!audio_is_cleaning_up()) {
 626        /* stop playback */
 627        if (isPlaying(core->outputDeviceID)) {
 628            status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
 629            if (status != kAudioHardwareNoError) {
 630                coreaudio_logerr (status, "Could not stop playback\n");
 631            }
 632        }
 633
 634        /* remove callback */
 635        status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
 636                                            core->ioprocid);
 637        if (status != kAudioHardwareNoError) {
 638            coreaudio_logerr (status, "Could not remove IOProc\n");
 639        }
 640    }
 641    core->outputDeviceID = kAudioDeviceUnknown;
 642
 643    /* destroy mutex */
 644    err = pthread_mutex_destroy(&core->mutex);
 645    if (err) {
 646        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
 647    }
 648}
 649
 650static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 651{
 652    OSStatus status;
 653    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 654
 655    switch (cmd) {
 656    case VOICE_ENABLE:
 657        /* start playback */
 658        if (!isPlaying(core->outputDeviceID)) {
 659            status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
 660            if (status != kAudioHardwareNoError) {
 661                coreaudio_logerr (status, "Could not resume playback\n");
 662            }
 663        }
 664        break;
 665
 666    case VOICE_DISABLE:
 667        /* stop playback */
 668        if (!audio_is_cleaning_up()) {
 669            if (isPlaying(core->outputDeviceID)) {
 670                status = AudioDeviceStop(core->outputDeviceID,
 671                                         core->ioprocid);
 672                if (status != kAudioHardwareNoError) {
 673                    coreaudio_logerr (status, "Could not pause playback\n");
 674                }
 675            }
 676        }
 677        break;
 678    }
 679    return 0;
 680}
 681
 682static void *coreaudio_audio_init(Audiodev *dev)
 683{
 684    return dev;
 685}
 686
 687static void coreaudio_audio_fini (void *opaque)
 688{
 689}
 690
 691static struct audio_pcm_ops coreaudio_pcm_ops = {
 692    .init_out = coreaudio_init_out,
 693    .fini_out = coreaudio_fini_out,
 694    .run_out  = coreaudio_run_out,
 695    .write    = coreaudio_write,
 696    .ctl_out  = coreaudio_ctl_out
 697};
 698
 699static struct audio_driver coreaudio_audio_driver = {
 700    .name           = "coreaudio",
 701    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
 702    .init           = coreaudio_audio_init,
 703    .fini           = coreaudio_audio_fini,
 704    .pcm_ops        = &coreaudio_pcm_ops,
 705    .can_be_default = 1,
 706    .max_voices_out = 1,
 707    .max_voices_in  = 0,
 708    .voice_size_out = sizeof (coreaudioVoiceOut),
 709    .voice_size_in  = 0
 710};
 711
 712static void register_audio_coreaudio(void)
 713{
 714    audio_driver_register(&coreaudio_audio_driver);
 715}
 716type_init(register_audio_coreaudio);
 717