1/*
2 * QEMU OS X CoreAudio audio driver
3 *
4 * Copyright (c) 2008 The Android Open Source Project
5 * Copyright (c) 2005 Mike Kronenberg
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 */
25
26#include <CoreAudio/CoreAudio.h>
27#include <string.h>             /* strerror */
28#include <pthread.h>            /* pthread_X */
29
30#include "qemu-common.h"
31#include "audio.h"
32
33#define AUDIO_CAP "coreaudio"
34#include "audio_int.h"
35
36#if 0
37#  define  D(...)  fprintf(stderr, __VA_ARGS__)
38#else
39#  define  D(...)  ((void)0)
40#endif
41
42struct {
43    int out_buffer_frames;
44    int out_nbuffers;
45    int in_buffer_frames;
46    int in_nbuffers;
47    int isAtexit;
48} conf = {
49    .out_buffer_frames = 512,
50    .out_nbuffers = 4,
51    .in_buffer_frames = 512,
52    .in_nbuffers = 4,
53    .isAtexit = 0
54};
55
56/***************************************************************************************/
57/***************************************************************************************/
58/***                                                                                 ***/
59/***       U T I L I T Y   R O U T I N E S                                           ***/
60/***                                                                                 ***/
61/***************************************************************************************/
62/***************************************************************************************/
63
64static void coreaudio_logstatus (OSStatus status)
65{
66    char *str = "BUG";
67
68    switch(status) {
69    case kAudioHardwareNoError:
70        str = "kAudioHardwareNoError";
71        break;
72
73    case kAudioHardwareNotRunningError:
74        str = "kAudioHardwareNotRunningError";
75        break;
76
77    case kAudioHardwareUnspecifiedError:
78        str = "kAudioHardwareUnspecifiedError";
79        break;
80
81    case kAudioHardwareUnknownPropertyError:
82        str = "kAudioHardwareUnknownPropertyError";
83        break;
84
85    case kAudioHardwareBadPropertySizeError:
86        str = "kAudioHardwareBadPropertySizeError";
87        break;
88
89    case kAudioHardwareIllegalOperationError:
90        str = "kAudioHardwareIllegalOperationError";
91        break;
92
93    case kAudioHardwareBadDeviceError:
94        str = "kAudioHardwareBadDeviceError";
95        break;
96
97    case kAudioHardwareBadStreamError:
98        str = "kAudioHardwareBadStreamError";
99        break;
100
101    case kAudioHardwareUnsupportedOperationError:
102        str = "kAudioHardwareUnsupportedOperationError";
103        break;
104
105    case kAudioDeviceUnsupportedFormatError:
106        str = "kAudioDeviceUnsupportedFormatError";
107        break;
108
109    case kAudioDevicePermissionsError:
110        str = "kAudioDevicePermissionsError";
111        break;
112
113    default:
114        AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
115        return;
116    }
117
118    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
119}
120
121static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
122    OSStatus status,
123    const char *fmt,
124    ...
125    )
126{
127    va_list ap;
128
129    va_start (ap, fmt);
130    AUD_log (AUDIO_CAP, fmt, ap);
131    va_end (ap);
132
133    coreaudio_logstatus (status);
134}
135
136static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
137    OSStatus status,
138    const char *typ,
139    const char *fmt,
140    ...
141    )
142{
143    va_list ap;
144
145    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
146
147    va_start (ap, fmt);
148    AUD_vlog (AUDIO_CAP, fmt, ap);
149    va_end (ap);
150
151    coreaudio_logstatus (status);
152}
153
154/***************************************************************************************/
155/***************************************************************************************/
156/***                                                                                 ***/
157/***       S H A R E D   I N / O U T   V O I C E                                     ***/
158/***                                                                                 ***/
159/***************************************************************************************/
160/***************************************************************************************/
161
162typedef struct coreAudioVoice {
163    pthread_mutex_t              mutex;
164    AudioDeviceID                deviceID;
165    Boolean                      isInput;
166    UInt32                       bufferFrameSize;
167    AudioStreamBasicDescription  streamBasicDescription;
168    AudioDeviceIOProc            ioproc;
169    int                          live;
170    int                          decr;
171    int                          pos;
172} coreaudioVoice;
173
174static inline UInt32
175coreaudio_voice_isPlaying (coreaudioVoice *core)
176{
177    OSStatus status;
178    UInt32 result = 0;
179    UInt32 propertySize = sizeof(core->deviceID);
180    status = AudioDeviceGetProperty(
181        core->deviceID, 0, core->isInput,
182        kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
183    if (status != kAudioHardwareNoError) {
184        coreaudio_logerr(status,
185                         "Could not determine whether Device is playing\n");
186    }
187    return result;
188}
189
190static void coreaudio_atexit (void)
191{
192    conf.isAtexit = 1;
193}
194
195static int coreaudio_voice_lock (coreaudioVoice *core, const char *fn_name)
196{
197    int err;
198
199    err = pthread_mutex_lock (&core->mutex);
200    if (err) {
201        dolog ("Could not lock voice for %s\nReason: %s\n",
202               fn_name, strerror (err));
203        return -1;
204    }
205    return 0;
206}
207
208static int
209coreaudio_voice_unlock (coreaudioVoice *core, const char *fn_name)
210{
211    int err;
212
213    err = pthread_mutex_unlock (&core->mutex);
214    if (err) {
215        dolog ("Could not unlock voice for %s\nReason: %s\n",
216               fn_name, strerror (err));
217        return -1;
218    }
219    return 0;
220}
221
222static int
223coreaudio_voice_ctl (coreaudioVoice*  core, int cmd)
224{
225    OSStatus status;
226
227    switch (cmd) {
228    case VOICE_ENABLE:
229        /* start playback */
230        D("%s: %s started\n", __FUNCTION__, core->isInput ? "input" : "output");
231        if (!coreaudio_voice_isPlaying(core)) {
232            status = AudioDeviceStart(core->deviceID, core->ioproc);
233            if (status != kAudioHardwareNoError) {
234                coreaudio_logerr (status, "Could not resume playback\n");
235            }
236        }
237        break;
238
239    case VOICE_DISABLE:
240        /* stop playback */
241        D("%s: %s stopped\n", __FUNCTION__, core->isInput ? "input" : "output");
242        if (!conf.isAtexit) {
243            if (coreaudio_voice_isPlaying(core)) {
244                status = AudioDeviceStop(core->deviceID, core->ioproc);
245                if (status != kAudioHardwareNoError) {
246                    coreaudio_logerr (status, "Could not pause playback\n");
247                }
248            }
249        }
250        break;
251    }
252    return 0;
253}
254
255static void
256coreaudio_voice_fini (coreaudioVoice*  core)
257{
258    OSStatus status;
259    int err;
260
261    if (!conf.isAtexit) {
262        /* stop playback */
263        coreaudio_voice_ctl(core, VOICE_DISABLE);
264
265        /* remove callback */
266        status = AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
267        if (status != kAudioHardwareNoError) {
268            coreaudio_logerr (status, "Could not remove IOProc\n");
269        }
270    }
271    core->deviceID = kAudioDeviceUnknown;
272
273    /* destroy mutex */
274    err = pthread_mutex_destroy(&core->mutex);
275    if (err) {
276        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
277    }
278}
279
280
281static int
282coreaudio_voice_init (coreaudioVoice*    core,
283                      struct audsettings*  as,
284                      int                frameSize,
285                      AudioDeviceIOProc  ioproc,
286                      void*              hw,
287                      int                input)
288{
289    OSStatus  status;
290    UInt32    propertySize;
291    int       err;
292    int       bits = 8;
293    AudioValueRange frameRange;
294    const char*  typ = input ? "input" : "playback";
295
296    core->isInput = input ? true : false;
297
298    /* create mutex */
299    err = pthread_mutex_init(&core->mutex, NULL);
300    if (err) {
301        dolog("Could not create mutex\nReason: %s\n", strerror (err));
302        return -1;
303    }
304
305    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
306        bits = 16;
307    }
308
309    // TODO: audio_pcm_init_info (&hw->info, as);
310    /* open default output device */
311   /* note: we use DefaultSystemOutputDevice because DefaultOutputDevice seems to
312    * always link to the internal speakers, and not the ones selected through system properties
313    * go figure...
314    */
315    propertySize = sizeof(core->deviceID);
316    status = AudioHardwareGetProperty(
317        input ? kAudioHardwarePropertyDefaultInputDevice :
318                kAudioHardwarePropertyDefaultSystemOutputDevice,
319        &propertySize,
320        &core->deviceID);
321    if (status != kAudioHardwareNoError) {
322        coreaudio_logerr2 (status, typ,
323                           "Could not get default %s device\n", typ);
324        return -1;
325    }
326    if (core->deviceID == kAudioDeviceUnknown) {
327        dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
328        return -1;
329    }
330
331    /* get minimum and maximum buffer frame sizes */
332    propertySize = sizeof(frameRange);
333    status = AudioDeviceGetProperty(
334        core->deviceID,
335        0,
336        core->isInput,
337        kAudioDevicePropertyBufferFrameSizeRange,
338        &propertySize,
339        &frameRange);
340    if (status != kAudioHardwareNoError) {
341        coreaudio_logerr2 (status, typ,
342                           "Could not get device buffer frame range\n");
343        return -1;
344    }
345
346    if (frameRange.mMinimum > frameSize) {
347        core->bufferFrameSize = (UInt32) frameRange.mMinimum;
348        dolog ("warning: Upsizing Output Buffer Frames to %f\n", frameRange.mMinimum);
349    }
350    else if (frameRange.mMaximum < frameSize) {
351        core->bufferFrameSize = (UInt32) frameRange.mMaximum;
352        dolog ("warning: Downsizing Output Buffer Frames to %f\n", frameRange.mMaximum);
353    }
354    else {
355        core->bufferFrameSize = frameSize;
356    }
357
358    /* set Buffer Frame Size */
359    propertySize = sizeof(core->bufferFrameSize);
360    status = AudioDeviceSetProperty(
361        core->deviceID,
362        NULL,
363        0,
364        core->isInput,
365        kAudioDevicePropertyBufferFrameSize,
366        propertySize,
367        &core->bufferFrameSize);
368    if (status != kAudioHardwareNoError) {
369        coreaudio_logerr2 (status, typ,
370                           "Could not set device buffer frame size %ld\n",
371                           core->bufferFrameSize);
372        return -1;
373    }
374
375    /* get Buffer Frame Size */
376    propertySize = sizeof(core->bufferFrameSize);
377    status = AudioDeviceGetProperty(
378        core->deviceID,
379        0,
380        core->isInput,
381        kAudioDevicePropertyBufferFrameSize,
382        &propertySize,
383        &core->bufferFrameSize);
384    if (status != kAudioHardwareNoError) {
385        coreaudio_logerr2 (status, typ,
386                           "Could not get device buffer frame size\n");
387        return -1;
388    }
389    // TODO: hw->samples = *pNBuffers * core->bufferFrameSize;
390
391    /* get StreamFormat */
392    propertySize = sizeof(core->streamBasicDescription);
393    status = AudioDeviceGetProperty(
394        core->deviceID,
395        0,
396        core->isInput,
397        kAudioDevicePropertyStreamFormat,
398        &propertySize,
399        &core->streamBasicDescription);
400    if (status != kAudioHardwareNoError) {
401        coreaudio_logerr2 (status, typ,
402                           "Could not get Device Stream properties\n");
403        core->deviceID = kAudioDeviceUnknown;
404        return -1;
405    }
406
407    /* set Samplerate */
408    core->streamBasicDescription.mSampleRate = (Float64) as->freq;
409    propertySize = sizeof(core->streamBasicDescription);
410    status = AudioDeviceSetProperty(
411        core->deviceID,
412        0,
413        0,
414        core->isInput,
415        kAudioDevicePropertyStreamFormat,
416        propertySize,
417        &core->streamBasicDescription);
418    if (status != kAudioHardwareNoError) {
419        coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
420                           as->freq);
421        core->deviceID = kAudioDeviceUnknown;
422        return -1;
423    }
424
425    /* set Callback */
426    core->ioproc = ioproc;
427    status = AudioDeviceAddIOProc(core->deviceID, ioproc, hw);
428    if (status != kAudioHardwareNoError) {
429        coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
430        core->deviceID = kAudioDeviceUnknown;
431        return -1;
432    }
433
434    /* start Playback */
435    if (!input && !coreaudio_voice_isPlaying(core)) {
436        status = AudioDeviceStart(core->deviceID, core->ioproc);
437        if (status != kAudioHardwareNoError) {
438            coreaudio_logerr2 (status, typ, "Could not start playback\n");
439            AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
440            core->deviceID = kAudioDeviceUnknown;
441            return -1;
442        }
443    }
444
445    return 0;
446}
447
448
449/***************************************************************************************/
450/***************************************************************************************/
451/***                                                                                 ***/
452/***       O U T P U T   V O I C E                                                   ***/
453/***                                                                                 ***/
454/***************************************************************************************/
455/***************************************************************************************/
456
457typedef struct coreaudioVoiceOut {
458    HWVoiceOut                   hw;
459    coreaudioVoice               core[1];
460} coreaudioVoiceOut;
461
462#define  CORE_OUT(hw)  ((coreaudioVoiceOut*)(hw))->core
463
464
465static int coreaudio_run_out (HWVoiceOut *hw, int live)
466{
467    int decr;
468    coreaudioVoice *core = CORE_OUT(hw);
469
470    if (coreaudio_voice_lock (core, "coreaudio_run_out")) {
471        return 0;
472    }
473
474    if (core->decr > live) {
475        ldebug ("core->decr %d live %d core->live %d\n",
476                core->decr,
477                live,
478                core->live);
479    }
480
481    decr = audio_MIN (core->decr, live);
482    core->decr -= decr;
483
484    core->live = live - decr;
485    hw->rpos = core->pos;
486
487    coreaudio_voice_unlock (core, "coreaudio_run_out");
488    return decr;
489}
490
491/* callback to feed audiooutput buffer */
492static OSStatus audioOutDeviceIOProc(
493    AudioDeviceID inDevice,
494    const AudioTimeStamp* inNow,
495    const AudioBufferList* inInputData,
496    const AudioTimeStamp* inInputTime,
497    AudioBufferList* outOutputData,
498    const AudioTimeStamp* inOutputTime,
499    void* hwptr)
500{
501    UInt32 frame, frameCount;
502    float *out = outOutputData->mBuffers[0].mData;
503    HWVoiceOut *hw = hwptr;
504    coreaudioVoice *core = CORE_OUT(hw);
505    int rpos, live;
506    struct st_sample *src;
507#ifndef FLOAT_MIXENG
508#ifdef RECIPROCAL
509    const float scale = 1.f / UINT_MAX;
510#else
511    const float scale = UINT_MAX;
512#endif
513#endif
514
515    if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
516        inInputTime = 0;
517        return 0;
518    }
519
520    frameCount = core->bufferFrameSize;
521    live = core->live;
522
523    /* if there are not enough samples, set signal and return */
524    if (live < frameCount) {
525        inInputTime = 0;
526        coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
527        return 0;
528    }
529
530    rpos = core->pos;
531    src = hw->mix_buf + rpos;
532
533    /* fill buffer */
534    for (frame = 0; frame < frameCount; frame++) {
535#ifdef FLOAT_MIXENG
536        *out++ = src[frame].l; /* left channel */
537        *out++ = src[frame].r; /* right channel */
538#else
539#ifdef RECIPROCAL
540        *out++ = src[frame].l * scale; /* left channel */
541        *out++ = src[frame].r * scale; /* right channel */
542#else
543        *out++ = src[frame].l / scale; /* left channel */
544        *out++ = src[frame].r / scale; /* right channel */
545#endif
546#endif
547    }
548
549    rpos = (rpos + frameCount) % hw->samples;
550    core->decr += frameCount;
551    core->pos = rpos;
552
553    coreaudio_voice_unlock (core, "audioDeviceIOProc");
554    return 0;
555}
556
557static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
558{
559    return audio_pcm_sw_write (sw, buf, len);
560}
561
562static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
563{
564    coreaudioVoice*  core = CORE_OUT(hw);
565    int err;
566
567    audio_pcm_init_info (&hw->info, as);
568
569    err = coreaudio_voice_init (core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0);
570    if (err < 0)
571        return err;
572
573    hw->samples = core->bufferFrameSize * conf.out_nbuffers;
574    return 0;
575}
576
577static void coreaudio_fini_out (HWVoiceOut *hw)
578{
579    coreaudioVoice *core = CORE_OUT(hw);
580
581    coreaudio_voice_fini (core);
582}
583
584static int
585coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
586{
587    coreaudioVoice *core = CORE_OUT(hw);
588
589    return coreaudio_voice_ctl (core, cmd);
590}
591
592/***************************************************************************************/
593/***************************************************************************************/
594/***                                                                                 ***/
595/***       I N P U T   V O I C E                                                     ***/
596/***                                                                                 ***/
597/***************************************************************************************/
598/***************************************************************************************/
599
600
601
602typedef struct coreaudioVoiceIn {
603    HWVoiceIn        hw;
604    coreaudioVoice   core[1];
605} coreaudioVoiceIn;
606
607#define  CORE_IN(hw)  ((coreaudioVoiceIn *) (hw))->core
608
609
610static int coreaudio_run_in (HWVoiceIn *hw, int live)
611{
612    int decr;
613
614    coreaudioVoice *core = CORE_IN(hw);
615
616    if (coreaudio_voice_lock (core, "coreaudio_run_in")) {
617        return 0;
618    }
619    D("%s: core.decr=%d core.pos=%d\n", __FUNCTION__, core->decr, core->pos);
620    decr        = core->decr;
621    core->decr -= decr;
622    hw->wpos    = core->pos;
623
624    coreaudio_voice_unlock (core, "coreaudio_run_in");
625    return decr;
626}
627
628
629/* callback to feed audiooutput buffer */
630static OSStatus audioInDeviceIOProc(
631    AudioDeviceID inDevice,
632    const AudioTimeStamp* inNow,
633    const AudioBufferList* inInputData,
634    const AudioTimeStamp* inInputTime,
635    AudioBufferList* outOutputData,
636    const AudioTimeStamp* inOutputTime,
637    void* hwptr)
638{
639    UInt32 frame, frameCount;
640    float *in = inInputData->mBuffers[0].mData;
641    HWVoiceIn *hw = hwptr;
642    coreaudioVoice *core = CORE_IN(hw);
643    int wpos, avail;
644    struct st_sample *dst;
645#ifndef FLOAT_MIXENG
646#ifdef RECIPROCAL
647    const float scale = 1.f / UINT_MAX;
648#else
649    const float scale = UINT_MAX;
650#endif
651#endif
652
653    if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
654        inInputTime = 0;
655        return 0;
656    }
657
658    frameCount = core->bufferFrameSize;
659    avail      = hw->samples - hw->total_samples_captured - core->decr;
660
661    D("%s: enter avail=%d core.decr=%d core.pos=%d hw.samples=%d hw.total_samples_captured=%d frameCount=%d\n",
662      __FUNCTION__, avail, core->decr, core->pos, hw->samples, hw->total_samples_captured, (int)frameCount);
663
664    /* if there are not enough samples, set signal and return */
665    if (avail < frameCount) {
666        inInputTime = 0;
667        coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
668        return 0;
669    }
670
671    wpos = core->pos;
672    dst  = hw->conv_buf + wpos;
673
674    /* fill buffer */
675    for (frame = 0; frame < frameCount; frame++) {
676#ifdef FLOAT_MIXENG
677        dst[frame].l = *in++; /* left channel */
678        dst[frame].r = *in++; /* right channel */
679#else
680#ifdef RECIPROCAL
681        dst[frame].l = *in++ * scale; /* left channel */
682        dst[frame].r = *in++ * scale; /* right channel */
683#else
684        dst[frame].l = *in++ / scale; /* left channel */
685        dst[frame].r = *in++ / scale; /* right channel */
686#endif
687#endif
688    }
689
690    wpos = (wpos + frameCount) % hw->samples;
691    core->decr += frameCount;
692    core->pos   = wpos;
693
694    D("exit: core.decr=%d core.pos=%d\n", core->decr, core->pos);
695    coreaudio_voice_unlock (core, "audioDeviceIOProc");
696    return 0;
697}
698
699static int
700coreaudio_read (SWVoiceIn *sw, void *buf, int len)
701{
702    int  result = audio_pcm_sw_read (sw, buf, len);
703    D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result);
704    return result;
705}
706
707static int
708coreaudio_init_in (HWVoiceIn *hw, struct audsettings *as)
709{
710    coreaudioVoice*  core = CORE_IN(hw);
711    int              err;
712
713    audio_pcm_init_info (&hw->info, as);
714
715    err = coreaudio_voice_init (core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1);
716    if (err < 0) {
717        return err;
718    }
719
720    hw->samples = core->bufferFrameSize * conf.in_nbuffers;
721    return 0;
722}
723
724static void
725coreaudio_fini_in (HWVoiceIn *hw)
726{
727
728    coreaudioVoice*  core = CORE_IN(hw);
729
730    coreaudio_voice_fini(core);
731}
732
733static int
734coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
735{
736    coreaudioVoice*  core = CORE_IN(hw);
737
738    return coreaudio_voice_ctl(core, cmd);
739}
740
741static void *coreaudio_audio_init (void)
742{
743    atexit(coreaudio_atexit);
744    return &coreaudio_audio_init;
745}
746
747static void coreaudio_audio_fini (void *opaque)
748{
749    (void) opaque;
750}
751
752static struct audio_option coreaudio_options[] = {
753    {
754        .name  = "OUT_BUFFER_SIZE",
755        .tag   = AUD_OPT_INT,
756        .valp  = &conf.out_buffer_frames,
757        .descr = "Size of the output buffer in frames"
758    },
759    {
760        .name  = "OUT_BUFFER_COUNT",
761        .tag   = AUD_OPT_INT,
762        .valp  = &conf.out_nbuffers,
763        .descr = "Number of output buffers"
764    },
765    {
766        .name  = "IN_BUFFER_SIZE",
767        .tag   = AUD_OPT_INT,
768        .valp  = &conf.in_buffer_frames,
769        .descr = "Size of the input buffer in frames"
770    },
771    {
772        .name  = "IN_BUFFER_COUNT",
773        .tag   = AUD_OPT_INT,
774        .valp  = &conf.in_nbuffers,
775        .descr = "Number of input buffers"
776    },
777    { /* End of list */ }
778};
779
780static struct audio_pcm_ops coreaudio_pcm_ops = {
781    .init_out = coreaudio_init_out,
782    .fini_out = coreaudio_fini_out,
783    .run_out  = coreaudio_run_out,
784    .write    = coreaudio_write,
785    .ctl_out  = coreaudio_ctl_out,
786
787    .init_in = coreaudio_init_in,
788    .fini_in = coreaudio_fini_in,
789    .run_in  = coreaudio_run_in,
790    .read    = coreaudio_read,
791    .ctl_in  = coreaudio_ctl_in
792};
793
794struct audio_driver coreaudio_audio_driver = {
795    .name           = "coreaudio",
796    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
797    .options        = coreaudio_options,
798    .init           = coreaudio_audio_init,
799    .fini           = coreaudio_audio_fini,
800    .pcm_ops        = &coreaudio_pcm_ops,
801    .can_be_default = 1,
802    .max_voices_out = 1,
803    .max_voices_in  = 1,
804    .voice_size_out = sizeof (coreaudioVoiceOut),
805    .voice_size_in  = sizeof (coreaudioVoiceIn),
806};
807