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