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