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