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