audio/coreaudio.c: Factor out uses of AudioDeviceGet/SetProperty
[qemu/ar7.git] / audio / coreaudio.c
blobc7e31eac0f183a1b19754f5c3697f7c5b6de9570
1 /*
2 * QEMU OS X CoreAudio audio driver
4 * Copyright (c) 2005 Mike Kronenberg
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
25 #include <CoreAudio/CoreAudio.h>
26 #include <string.h> /* strerror */
27 #include <pthread.h> /* pthread_X */
29 #include "qemu-common.h"
30 #include "audio.h"
32 #define AUDIO_CAP "coreaudio"
33 #include "audio_int.h"
35 #ifndef MAC_OS_X_VERSION_10_6
36 #define MAC_OS_X_VERSION_10_6 1060
37 #endif
39 static int isAtexit;
41 typedef struct {
42 int buffer_frames;
43 int nbuffers;
44 } CoreaudioConf;
46 typedef struct coreaudioVoiceOut {
47 HWVoiceOut hw;
48 pthread_mutex_t mutex;
49 AudioDeviceID outputDeviceID;
50 UInt32 audioDevicePropertyBufferFrameSize;
51 AudioStreamBasicDescription outputStreamBasicDescription;
52 int live;
53 int decr;
54 int rpos;
55 } coreaudioVoiceOut;
57 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
58 /* The APIs used here only become available from 10.6 */
60 static OSStatus coreaudio_get_voice(AudioDeviceID *id)
62 UInt32 size = sizeof(*id);
63 AudioObjectPropertyAddress addr = {
64 kAudioHardwarePropertyDefaultOutputDevice,
65 kAudioObjectPropertyScopeGlobal,
66 kAudioObjectPropertyElementMaster
69 return AudioObjectGetPropertyData(kAudioObjectSystemObject,
70 &addr,
72 NULL,
73 &size,
74 id);
76 #else
77 /* Legacy versions of functions using deprecated APIs */
79 static OSStatus coreaudio_get_voice(AudioDeviceID *id)
81 UInt32 size = sizeof(*id);
83 return AudioHardwareGetProperty(
84 kAudioHardwarePropertyDefaultOutputDevice,
85 &size,
86 id);
88 #endif
90 static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
91 AudioValueRange *framerange)
93 UInt32 size = sizeof(*framerange);
95 return AudioDeviceGetProperty(
96 id,
99 kAudioDevicePropertyBufferFrameSizeRange,
100 &size,
101 framerange);
104 static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
106 UInt32 size = sizeof(*framesize);
108 return AudioDeviceGetProperty(
111 false,
112 kAudioDevicePropertyBufferFrameSize,
113 &size,
114 framesize);
117 static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
119 UInt32 size = sizeof(*framesize);
121 return AudioDeviceSetProperty(
123 NULL,
125 false,
126 kAudioDevicePropertyBufferFrameSize,
127 size,
128 framesize);
131 static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
132 AudioStreamBasicDescription *d)
134 UInt32 size = sizeof(*d);
136 return AudioDeviceGetProperty(
139 false,
140 kAudioDevicePropertyStreamFormat,
141 &size,
145 static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
146 AudioStreamBasicDescription *d)
148 UInt32 size = sizeof(*d);
150 return AudioDeviceSetProperty(
155 kAudioDevicePropertyStreamFormat,
156 size,
160 static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
162 UInt32 size = sizeof(*result);
164 return AudioDeviceGetProperty(
168 kAudioDevicePropertyDeviceIsRunning,
169 &size,
170 result);
173 static void coreaudio_logstatus (OSStatus status)
175 const char *str = "BUG";
177 switch(status) {
178 case kAudioHardwareNoError:
179 str = "kAudioHardwareNoError";
180 break;
182 case kAudioHardwareNotRunningError:
183 str = "kAudioHardwareNotRunningError";
184 break;
186 case kAudioHardwareUnspecifiedError:
187 str = "kAudioHardwareUnspecifiedError";
188 break;
190 case kAudioHardwareUnknownPropertyError:
191 str = "kAudioHardwareUnknownPropertyError";
192 break;
194 case kAudioHardwareBadPropertySizeError:
195 str = "kAudioHardwareBadPropertySizeError";
196 break;
198 case kAudioHardwareIllegalOperationError:
199 str = "kAudioHardwareIllegalOperationError";
200 break;
202 case kAudioHardwareBadDeviceError:
203 str = "kAudioHardwareBadDeviceError";
204 break;
206 case kAudioHardwareBadStreamError:
207 str = "kAudioHardwareBadStreamError";
208 break;
210 case kAudioHardwareUnsupportedOperationError:
211 str = "kAudioHardwareUnsupportedOperationError";
212 break;
214 case kAudioDeviceUnsupportedFormatError:
215 str = "kAudioDeviceUnsupportedFormatError";
216 break;
218 case kAudioDevicePermissionsError:
219 str = "kAudioDevicePermissionsError";
220 break;
222 default:
223 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
224 return;
227 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
230 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
231 OSStatus status,
232 const char *fmt,
236 va_list ap;
238 va_start (ap, fmt);
239 AUD_log (AUDIO_CAP, fmt, ap);
240 va_end (ap);
242 coreaudio_logstatus (status);
245 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
246 OSStatus status,
247 const char *typ,
248 const char *fmt,
252 va_list ap;
254 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
256 va_start (ap, fmt);
257 AUD_vlog (AUDIO_CAP, fmt, ap);
258 va_end (ap);
260 coreaudio_logstatus (status);
263 static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
265 OSStatus status;
266 UInt32 result = 0;
267 status = coreaudio_get_isrunning(outputDeviceID, &result);
268 if (status != kAudioHardwareNoError) {
269 coreaudio_logerr(status,
270 "Could not determine whether Device is playing\n");
272 return result;
275 static void coreaudio_atexit (void)
277 isAtexit = 1;
280 static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
282 int err;
284 err = pthread_mutex_lock (&core->mutex);
285 if (err) {
286 dolog ("Could not lock voice for %s\nReason: %s\n",
287 fn_name, strerror (err));
288 return -1;
290 return 0;
293 static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
295 int err;
297 err = pthread_mutex_unlock (&core->mutex);
298 if (err) {
299 dolog ("Could not unlock voice for %s\nReason: %s\n",
300 fn_name, strerror (err));
301 return -1;
303 return 0;
306 static int coreaudio_run_out (HWVoiceOut *hw, int live)
308 int decr;
309 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
311 if (coreaudio_lock (core, "coreaudio_run_out")) {
312 return 0;
315 if (core->decr > live) {
316 ldebug ("core->decr %d live %d core->live %d\n",
317 core->decr,
318 live,
319 core->live);
322 decr = audio_MIN (core->decr, live);
323 core->decr -= decr;
325 core->live = live - decr;
326 hw->rpos = core->rpos;
328 coreaudio_unlock (core, "coreaudio_run_out");
329 return decr;
332 /* callback to feed audiooutput buffer */
333 static OSStatus audioDeviceIOProc(
334 AudioDeviceID inDevice,
335 const AudioTimeStamp* inNow,
336 const AudioBufferList* inInputData,
337 const AudioTimeStamp* inInputTime,
338 AudioBufferList* outOutputData,
339 const AudioTimeStamp* inOutputTime,
340 void* hwptr)
342 UInt32 frame, frameCount;
343 float *out = outOutputData->mBuffers[0].mData;
344 HWVoiceOut *hw = hwptr;
345 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
346 int rpos, live;
347 struct st_sample *src;
348 #ifndef FLOAT_MIXENG
349 #ifdef RECIPROCAL
350 const float scale = 1.f / UINT_MAX;
351 #else
352 const float scale = UINT_MAX;
353 #endif
354 #endif
356 if (coreaudio_lock (core, "audioDeviceIOProc")) {
357 inInputTime = 0;
358 return 0;
361 frameCount = core->audioDevicePropertyBufferFrameSize;
362 live = core->live;
364 /* if there are not enough samples, set signal and return */
365 if (live < frameCount) {
366 inInputTime = 0;
367 coreaudio_unlock (core, "audioDeviceIOProc(empty)");
368 return 0;
371 rpos = core->rpos;
372 src = hw->mix_buf + rpos;
374 /* fill buffer */
375 for (frame = 0; frame < frameCount; frame++) {
376 #ifdef FLOAT_MIXENG
377 *out++ = src[frame].l; /* left channel */
378 *out++ = src[frame].r; /* right channel */
379 #else
380 #ifdef RECIPROCAL
381 *out++ = src[frame].l * scale; /* left channel */
382 *out++ = src[frame].r * scale; /* right channel */
383 #else
384 *out++ = src[frame].l / scale; /* left channel */
385 *out++ = src[frame].r / scale; /* right channel */
386 #endif
387 #endif
390 rpos = (rpos + frameCount) % hw->samples;
391 core->decr += frameCount;
392 core->rpos = rpos;
394 coreaudio_unlock (core, "audioDeviceIOProc");
395 return 0;
398 static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
400 return audio_pcm_sw_write (sw, buf, len);
403 static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
404 void *drv_opaque)
406 OSStatus status;
407 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
408 int err;
409 const char *typ = "playback";
410 AudioValueRange frameRange;
411 CoreaudioConf *conf = drv_opaque;
413 /* create mutex */
414 err = pthread_mutex_init(&core->mutex, NULL);
415 if (err) {
416 dolog("Could not create mutex\nReason: %s\n", strerror (err));
417 return -1;
420 audio_pcm_init_info (&hw->info, as);
422 status = coreaudio_get_voice(&core->outputDeviceID);
423 if (status != kAudioHardwareNoError) {
424 coreaudio_logerr2 (status, typ,
425 "Could not get default output Device\n");
426 return -1;
428 if (core->outputDeviceID == kAudioDeviceUnknown) {
429 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
430 return -1;
433 /* get minimum and maximum buffer frame sizes */
434 status = coreaudio_get_framesizerange(core->outputDeviceID,
435 &frameRange);
436 if (status != kAudioHardwareNoError) {
437 coreaudio_logerr2 (status, typ,
438 "Could not get device buffer frame range\n");
439 return -1;
442 if (frameRange.mMinimum > conf->buffer_frames) {
443 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
444 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
446 else if (frameRange.mMaximum < conf->buffer_frames) {
447 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
448 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
450 else {
451 core->audioDevicePropertyBufferFrameSize = conf->buffer_frames;
454 /* set Buffer Frame Size */
455 status = coreaudio_set_framesize(core->outputDeviceID,
456 &core->audioDevicePropertyBufferFrameSize);
457 if (status != kAudioHardwareNoError) {
458 coreaudio_logerr2 (status, typ,
459 "Could not set device buffer frame size %" PRIu32 "\n",
460 (uint32_t)core->audioDevicePropertyBufferFrameSize);
461 return -1;
464 /* get Buffer Frame Size */
465 status = coreaudio_get_framesize(core->outputDeviceID,
466 &core->audioDevicePropertyBufferFrameSize);
467 if (status != kAudioHardwareNoError) {
468 coreaudio_logerr2 (status, typ,
469 "Could not get device buffer frame size\n");
470 return -1;
472 hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
474 /* get StreamFormat */
475 status = coreaudio_get_streamformat(core->outputDeviceID,
476 &core->outputStreamBasicDescription);
477 if (status != kAudioHardwareNoError) {
478 coreaudio_logerr2 (status, typ,
479 "Could not get Device Stream properties\n");
480 core->outputDeviceID = kAudioDeviceUnknown;
481 return -1;
484 /* set Samplerate */
485 core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
486 status = coreaudio_set_streamformat(core->outputDeviceID,
487 &core->outputStreamBasicDescription);
488 if (status != kAudioHardwareNoError) {
489 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
490 as->freq);
491 core->outputDeviceID = kAudioDeviceUnknown;
492 return -1;
495 /* set Callback */
496 status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
497 if (status != kAudioHardwareNoError) {
498 coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
499 core->outputDeviceID = kAudioDeviceUnknown;
500 return -1;
503 /* start Playback */
504 if (!isPlaying(core->outputDeviceID)) {
505 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
506 if (status != kAudioHardwareNoError) {
507 coreaudio_logerr2 (status, typ, "Could not start playback\n");
508 AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
509 core->outputDeviceID = kAudioDeviceUnknown;
510 return -1;
514 return 0;
517 static void coreaudio_fini_out (HWVoiceOut *hw)
519 OSStatus status;
520 int err;
521 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
523 if (!isAtexit) {
524 /* stop playback */
525 if (isPlaying(core->outputDeviceID)) {
526 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
527 if (status != kAudioHardwareNoError) {
528 coreaudio_logerr (status, "Could not stop playback\n");
532 /* remove callback */
533 status = AudioDeviceRemoveIOProc(core->outputDeviceID,
534 audioDeviceIOProc);
535 if (status != kAudioHardwareNoError) {
536 coreaudio_logerr (status, "Could not remove IOProc\n");
539 core->outputDeviceID = kAudioDeviceUnknown;
541 /* destroy mutex */
542 err = pthread_mutex_destroy(&core->mutex);
543 if (err) {
544 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
548 static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
550 OSStatus status;
551 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
553 switch (cmd) {
554 case VOICE_ENABLE:
555 /* start playback */
556 if (!isPlaying(core->outputDeviceID)) {
557 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
558 if (status != kAudioHardwareNoError) {
559 coreaudio_logerr (status, "Could not resume playback\n");
562 break;
564 case VOICE_DISABLE:
565 /* stop playback */
566 if (!isAtexit) {
567 if (isPlaying(core->outputDeviceID)) {
568 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
569 if (status != kAudioHardwareNoError) {
570 coreaudio_logerr (status, "Could not pause playback\n");
574 break;
576 return 0;
579 static CoreaudioConf glob_conf = {
580 .buffer_frames = 512,
581 .nbuffers = 4,
584 static void *coreaudio_audio_init (void)
586 CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf));
587 *conf = glob_conf;
589 atexit(coreaudio_atexit);
590 return conf;
593 static void coreaudio_audio_fini (void *opaque)
595 g_free(opaque);
598 static struct audio_option coreaudio_options[] = {
600 .name = "BUFFER_SIZE",
601 .tag = AUD_OPT_INT,
602 .valp = &glob_conf.buffer_frames,
603 .descr = "Size of the buffer in frames"
606 .name = "BUFFER_COUNT",
607 .tag = AUD_OPT_INT,
608 .valp = &glob_conf.nbuffers,
609 .descr = "Number of buffers"
611 { /* End of list */ }
614 static struct audio_pcm_ops coreaudio_pcm_ops = {
615 .init_out = coreaudio_init_out,
616 .fini_out = coreaudio_fini_out,
617 .run_out = coreaudio_run_out,
618 .write = coreaudio_write,
619 .ctl_out = coreaudio_ctl_out
622 struct audio_driver coreaudio_audio_driver = {
623 .name = "coreaudio",
624 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
625 .options = coreaudio_options,
626 .init = coreaudio_audio_init,
627 .fini = coreaudio_audio_fini,
628 .pcm_ops = &coreaudio_pcm_ops,
629 .can_be_default = 1,
630 .max_voices_out = 1,
631 .max_voices_in = 0,
632 .voice_size_out = sizeof (coreaudioVoiceOut),
633 .voice_size_in = 0