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