1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26#include <CoreAudio/CoreAudio.h>
27#include <pthread.h>
28
29#include "qemu/main-loop.h"
30#include "qemu/module.h"
31#include "audio.h"
32
33#define AUDIO_CAP "coreaudio"
34#include "audio_int.h"
35
36typedef struct coreaudioVoiceOut {
37 HWVoiceOut hw;
38 pthread_mutex_t buf_mutex;
39 AudioDeviceID outputDeviceID;
40 int frameSizeSetting;
41 uint32_t bufferCount;
42 UInt32 audioDevicePropertyBufferFrameSize;
43 AudioDeviceIOProcID ioprocid;
44 bool enabled;
45} coreaudioVoiceOut;
46
47static const AudioObjectPropertyAddress voice_addr = {
48 kAudioHardwarePropertyDefaultOutputDevice,
49 kAudioObjectPropertyScopeGlobal,
50 kAudioObjectPropertyElementMaster
51};
52
53static OSStatus coreaudio_get_voice(AudioDeviceID *id)
54{
55 UInt32 size = sizeof(*id);
56
57 return AudioObjectGetPropertyData(kAudioObjectSystemObject,
58 &voice_addr,
59 0,
60 NULL,
61 &size,
62 id);
63}
64
65static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
66 AudioValueRange *framerange)
67{
68 UInt32 size = sizeof(*framerange);
69 AudioObjectPropertyAddress addr = {
70 kAudioDevicePropertyBufferFrameSizeRange,
71 kAudioDevicePropertyScopeOutput,
72 kAudioObjectPropertyElementMaster
73 };
74
75 return AudioObjectGetPropertyData(id,
76 &addr,
77 0,
78 NULL,
79 &size,
80 framerange);
81}
82
83static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
84{
85 UInt32 size = sizeof(*framesize);
86 AudioObjectPropertyAddress addr = {
87 kAudioDevicePropertyBufferFrameSize,
88 kAudioDevicePropertyScopeOutput,
89 kAudioObjectPropertyElementMaster
90 };
91
92 return AudioObjectGetPropertyData(id,
93 &addr,
94 0,
95 NULL,
96 &size,
97 framesize);
98}
99
100static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
101{
102 UInt32 size = sizeof(*framesize);
103 AudioObjectPropertyAddress addr = {
104 kAudioDevicePropertyBufferFrameSize,
105 kAudioDevicePropertyScopeOutput,
106 kAudioObjectPropertyElementMaster
107 };
108
109 return AudioObjectSetPropertyData(id,
110 &addr,
111 0,
112 NULL,
113 size,
114 framesize);
115}
116
117static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
118 AudioStreamBasicDescription *d)
119{
120 UInt32 size = sizeof(*d);
121 AudioObjectPropertyAddress addr = {
122 kAudioDevicePropertyStreamFormat,
123 kAudioDevicePropertyScopeOutput,
124 kAudioObjectPropertyElementMaster
125 };
126
127 return AudioObjectSetPropertyData(id,
128 &addr,
129 0,
130 NULL,
131 size,
132 d);
133}
134
135static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
136{
137 UInt32 size = sizeof(*result);
138 AudioObjectPropertyAddress addr = {
139 kAudioDevicePropertyDeviceIsRunning,
140 kAudioDevicePropertyScopeOutput,
141 kAudioObjectPropertyElementMaster
142 };
143
144 return AudioObjectGetPropertyData(id,
145 &addr,
146 0,
147 NULL,
148 &size,
149 result);
150}
151
152static void coreaudio_logstatus (OSStatus status)
153{
154 const char *str = "BUG";
155
156 switch (status) {
157 case kAudioHardwareNoError:
158 str = "kAudioHardwareNoError";
159 break;
160
161 case kAudioHardwareNotRunningError:
162 str = "kAudioHardwareNotRunningError";
163 break;
164
165 case kAudioHardwareUnspecifiedError:
166 str = "kAudioHardwareUnspecifiedError";
167 break;
168
169 case kAudioHardwareUnknownPropertyError:
170 str = "kAudioHardwareUnknownPropertyError";
171 break;
172
173 case kAudioHardwareBadPropertySizeError:
174 str = "kAudioHardwareBadPropertySizeError";
175 break;
176
177 case kAudioHardwareIllegalOperationError:
178 str = "kAudioHardwareIllegalOperationError";
179 break;
180
181 case kAudioHardwareBadDeviceError:
182 str = "kAudioHardwareBadDeviceError";
183 break;
184
185 case kAudioHardwareBadStreamError:
186 str = "kAudioHardwareBadStreamError";
187 break;
188
189 case kAudioHardwareUnsupportedOperationError:
190 str = "kAudioHardwareUnsupportedOperationError";
191 break;
192
193 case kAudioDeviceUnsupportedFormatError:
194 str = "kAudioDeviceUnsupportedFormatError";
195 break;
196
197 case kAudioDevicePermissionsError:
198 str = "kAudioDevicePermissionsError";
199 break;
200
201 default:
202 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
203 return;
204 }
205
206 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
207}
208
209static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
210 OSStatus status,
211 const char *fmt,
212 ...
213 )
214{
215 va_list ap;
216
217 va_start (ap, fmt);
218 AUD_log (AUDIO_CAP, fmt, ap);
219 va_end (ap);
220
221 coreaudio_logstatus (status);
222}
223
224static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
225 OSStatus status,
226 const char *typ,
227 const char *fmt,
228 ...
229 )
230{
231 va_list ap;
232
233 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
234
235 va_start (ap, fmt);
236 AUD_vlog (AUDIO_CAP, fmt, ap);
237 va_end (ap);
238
239 coreaudio_logstatus (status);
240}
241
242#define coreaudio_playback_logerr(status, ...) \
243 coreaudio_logerr2(status, "playback", __VA_ARGS__)
244
245static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
246{
247 int err;
248
249 err = pthread_mutex_lock (&core->buf_mutex);
250 if (err) {
251 dolog ("Could not lock voice for %s\nReason: %s\n",
252 fn_name, strerror (err));
253 return -1;
254 }
255 return 0;
256}
257
258static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
259{
260 int err;
261
262 err = pthread_mutex_unlock (&core->buf_mutex);
263 if (err) {
264 dolog ("Could not unlock voice for %s\nReason: %s\n",
265 fn_name, strerror (err));
266 return -1;
267 }
268 return 0;
269}
270
271#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
272 static ret_type glue(coreaudio_, name)args_decl \
273 { \
274 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
275 ret_type ret; \
276 \
277 if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
278 return 0; \
279 } \
280 \
281 ret = glue(audio_generic_, name)args; \
282 \
283 coreaudio_buf_unlock(core, "coreaudio_" #name); \
284 return ret; \
285 }
286COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
287 (hw, size))
288COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
289 (HWVoiceOut *hw, void *buf, size_t size),
290 (hw, buf, size))
291COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
292 (hw, buf, size))
293#undef COREAUDIO_WRAPPER_FUNC
294
295
296
297
298
299static OSStatus audioDeviceIOProc(
300 AudioDeviceID inDevice,
301 const AudioTimeStamp *inNow,
302 const AudioBufferList *inInputData,
303 const AudioTimeStamp *inInputTime,
304 AudioBufferList *outOutputData,
305 const AudioTimeStamp *inOutputTime,
306 void *hwptr)
307{
308 UInt32 frameCount, pending_frames;
309 void *out = outOutputData->mBuffers[0].mData;
310 HWVoiceOut *hw = hwptr;
311 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
312 size_t len;
313
314 if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
315 inInputTime = 0;
316 return 0;
317 }
318
319 if (inDevice != core->outputDeviceID) {
320 coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
321 return 0;
322 }
323
324 frameCount = core->audioDevicePropertyBufferFrameSize;
325 pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
326
327
328 if (pending_frames < frameCount) {
329 inInputTime = 0;
330 coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
331 return 0;
332 }
333
334 len = frameCount * hw->info.bytes_per_frame;
335 while (len) {
336 size_t write_len;
337 ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
338 if (start < 0) {
339 start += hw->size_emul;
340 }
341 assert(start >= 0 && start < hw->size_emul);
342
343 write_len = MIN(MIN(hw->pending_emul, len),
344 hw->size_emul - start);
345
346 memcpy(out, hw->buf_emul + start, write_len);
347 hw->pending_emul -= write_len;
348 len -= write_len;
349 out += write_len;
350 }
351
352 coreaudio_buf_unlock (core, "audioDeviceIOProc");
353 return 0;
354}
355
356static OSStatus init_out_device(coreaudioVoiceOut *core)
357{
358 OSStatus status;
359 AudioValueRange frameRange;
360
361 AudioStreamBasicDescription streamBasicDescription = {
362 .mBitsPerChannel = core->hw.info.bits,
363 .mBytesPerFrame = core->hw.info.bytes_per_frame,
364 .mBytesPerPacket = core->hw.info.bytes_per_frame,
365 .mChannelsPerFrame = core->hw.info.nchannels,
366 .mFormatFlags = kLinearPCMFormatFlagIsFloat,
367 .mFormatID = kAudioFormatLinearPCM,
368 .mFramesPerPacket = 1,
369 .mSampleRate = core->hw.info.freq
370 };
371
372 status = coreaudio_get_voice(&core->outputDeviceID);
373 if (status != kAudioHardwareNoError) {
374 coreaudio_playback_logerr (status,
375 "Could not get default output Device\n");
376 return status;
377 }
378 if (core->outputDeviceID == kAudioDeviceUnknown) {
379 dolog ("Could not initialize playback - Unknown Audiodevice\n");
380 return status;
381 }
382
383
384 status = coreaudio_get_framesizerange(core->outputDeviceID,
385 &frameRange);
386 if (status == kAudioHardwareBadObjectError) {
387 return 0;
388 }
389 if (status != kAudioHardwareNoError) {
390 coreaudio_playback_logerr (status,
391 "Could not get device buffer frame range\n");
392 return status;
393 }
394
395 if (frameRange.mMinimum > core->frameSizeSetting) {
396 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
397 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
398 } else if (frameRange.mMaximum < core->frameSizeSetting) {
399 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
400 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
401 } else {
402 core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
403 }
404
405
406 status = coreaudio_set_framesize(core->outputDeviceID,
407 &core->audioDevicePropertyBufferFrameSize);
408 if (status == kAudioHardwareBadObjectError) {
409 return 0;
410 }
411 if (status != kAudioHardwareNoError) {
412 coreaudio_playback_logerr (status,
413 "Could not set device buffer frame size %" PRIu32 "\n",
414 (uint32_t)core->audioDevicePropertyBufferFrameSize);
415 return status;
416 }
417
418
419 status = coreaudio_get_framesize(core->outputDeviceID,
420 &core->audioDevicePropertyBufferFrameSize);
421 if (status == kAudioHardwareBadObjectError) {
422 return 0;
423 }
424 if (status != kAudioHardwareNoError) {
425 coreaudio_playback_logerr (status,
426 "Could not get device buffer frame size\n");
427 return status;
428 }
429 core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
430
431
432 status = coreaudio_set_streamformat(core->outputDeviceID,
433 &streamBasicDescription);
434 if (status == kAudioHardwareBadObjectError) {
435 return 0;
436 }
437 if (status != kAudioHardwareNoError) {
438 coreaudio_playback_logerr (status,
439 "Could not set samplerate %lf\n",
440 streamBasicDescription.mSampleRate);
441 core->outputDeviceID = kAudioDeviceUnknown;
442 return status;
443 }
444
445
446
447
448
449
450
451
452
453
454
455 core->ioprocid = NULL;
456 status = AudioDeviceCreateIOProcID(core->outputDeviceID,
457 audioDeviceIOProc,
458 &core->hw,
459 &core->ioprocid);
460 if (status == kAudioHardwareBadDeviceError) {
461 return 0;
462 }
463 if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
464 coreaudio_playback_logerr (status, "Could not set IOProc\n");
465 core->outputDeviceID = kAudioDeviceUnknown;
466 return status;
467 }
468
469 return 0;
470}
471
472static void fini_out_device(coreaudioVoiceOut *core)
473{
474 OSStatus status;
475 UInt32 isrunning;
476
477
478 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
479 if (status != kAudioHardwareBadObjectError) {
480 if (status != kAudioHardwareNoError) {
481 coreaudio_logerr(status,
482 "Could not determine whether Device is playing\n");
483 }
484
485 if (isrunning) {
486 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
487 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
488 coreaudio_logerr(status, "Could not stop playback\n");
489 }
490 }
491 }
492
493
494 status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
495 core->ioprocid);
496 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
497 coreaudio_logerr(status, "Could not remove IOProc\n");
498 }
499 core->outputDeviceID = kAudioDeviceUnknown;
500}
501
502static void update_device_playback_state(coreaudioVoiceOut *core)
503{
504 OSStatus status;
505 UInt32 isrunning;
506
507 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
508 if (status != kAudioHardwareNoError) {
509 if (status != kAudioHardwareBadObjectError) {
510 coreaudio_logerr(status,
511 "Could not determine whether Device is playing\n");
512 }
513
514 return;
515 }
516
517 if (core->enabled) {
518
519 if (!isrunning) {
520 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
521 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
522 coreaudio_logerr (status, "Could not resume playback\n");
523 }
524 }
525 } else {
526
527 if (isrunning) {
528 status = AudioDeviceStop(core->outputDeviceID,
529 core->ioprocid);
530 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
531 coreaudio_logerr(status, "Could not pause playback\n");
532 }
533 }
534 }
535}
536
537
538static OSStatus handle_voice_change(
539 AudioObjectID in_object_id,
540 UInt32 in_number_addresses,
541 const AudioObjectPropertyAddress *in_addresses,
542 void *in_client_data)
543{
544 OSStatus status;
545 coreaudioVoiceOut *core = in_client_data;
546
547 qemu_mutex_lock_iothread();
548
549 if (core->outputDeviceID) {
550 fini_out_device(core);
551 }
552
553 status = init_out_device(core);
554 if (!status) {
555 update_device_playback_state(core);
556 }
557
558 qemu_mutex_unlock_iothread();
559 return status;
560}
561
562static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
563 void *drv_opaque)
564{
565 OSStatus status;
566 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
567 int err;
568 Audiodev *dev = drv_opaque;
569 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
570 struct audsettings obt_as;
571
572
573 err = pthread_mutex_init(&core->buf_mutex, NULL);
574 if (err) {
575 dolog("Could not create mutex\nReason: %s\n", strerror (err));
576 return -1;
577 }
578
579 obt_as = *as;
580 as = &obt_as;
581 as->fmt = AUDIO_FORMAT_F32;
582 audio_pcm_init_info (&hw->info, as);
583
584 core->frameSizeSetting = audio_buffer_frames(
585 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
586
587 core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
588
589 status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
590 &voice_addr, handle_voice_change,
591 core);
592 if (status != kAudioHardwareNoError) {
593 coreaudio_playback_logerr (status,
594 "Could not listen to voice property change\n");
595 return -1;
596 }
597
598 if (init_out_device(core)) {
599 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
600 &voice_addr,
601 handle_voice_change,
602 core);
603 if (status != kAudioHardwareNoError) {
604 coreaudio_playback_logerr(status,
605 "Could not remove voice property change listener\n");
606 }
607 }
608
609 return 0;
610}
611
612static void coreaudio_fini_out (HWVoiceOut *hw)
613{
614 OSStatus status;
615 int err;
616 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
617
618 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
619 &voice_addr,
620 handle_voice_change,
621 core);
622 if (status != kAudioHardwareNoError) {
623 coreaudio_logerr(status, "Could not remove voice property change listener\n");
624 }
625
626 fini_out_device(core);
627
628
629 err = pthread_mutex_destroy(&core->buf_mutex);
630 if (err) {
631 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
632 }
633}
634
635static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
636{
637 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
638
639 core->enabled = enable;
640 update_device_playback_state(core);
641}
642
643static void *coreaudio_audio_init(Audiodev *dev)
644{
645 return dev;
646}
647
648static void coreaudio_audio_fini (void *opaque)
649{
650}
651
652static struct audio_pcm_ops coreaudio_pcm_ops = {
653 .init_out = coreaudio_init_out,
654 .fini_out = coreaudio_fini_out,
655
656 .write = coreaudio_write,
657
658 .get_buffer_out = coreaudio_get_buffer_out,
659
660 .put_buffer_out = coreaudio_put_buffer_out,
661 .enable_out = coreaudio_enable_out
662};
663
664static struct audio_driver coreaudio_audio_driver = {
665 .name = "coreaudio",
666 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
667 .init = coreaudio_audio_init,
668 .fini = coreaudio_audio_fini,
669 .pcm_ops = &coreaudio_pcm_ops,
670 .can_be_default = 1,
671 .max_voices_out = 1,
672 .max_voices_in = 0,
673 .voice_size_out = sizeof (coreaudioVoiceOut),
674 .voice_size_in = 0
675};
676
677static void register_audio_coreaudio(void)
678{
679 audio_driver_register(&coreaudio_audio_driver);
680}
681type_init(register_audio_coreaudio);
682