audio/coreaudio: Remove a deprecation warning on macOS 12
[qemu/ar7.git] / audio / coreaudio.c
blob23d7593eb9bce4de09a1e9ae6e3db9f78bb0a1e6
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 "qemu/osdep.h"
26 #include <CoreAudio/CoreAudio.h>
27 #include <pthread.h> /* pthread_X */
29 #include "qemu/main-loop.h"
30 #include "qemu/module.h"
31 #include "audio.h"
33 #define AUDIO_CAP "coreaudio"
34 #include "audio_int.h"
36 typedef 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;
47 #if !defined(MAC_OS_VERSION_12_0) \
48 || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0)
49 #define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster
50 #endif
52 static const AudioObjectPropertyAddress voice_addr = {
53 kAudioHardwarePropertyDefaultOutputDevice,
54 kAudioObjectPropertyScopeGlobal,
55 kAudioObjectPropertyElementMain
58 static OSStatus coreaudio_get_voice(AudioDeviceID *id)
60 UInt32 size = sizeof(*id);
62 return AudioObjectGetPropertyData(kAudioObjectSystemObject,
63 &voice_addr,
65 NULL,
66 &size,
67 id);
70 static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
71 AudioValueRange *framerange)
73 UInt32 size = sizeof(*framerange);
74 AudioObjectPropertyAddress addr = {
75 kAudioDevicePropertyBufferFrameSizeRange,
76 kAudioDevicePropertyScopeOutput,
77 kAudioObjectPropertyElementMain
80 return AudioObjectGetPropertyData(id,
81 &addr,
83 NULL,
84 &size,
85 framerange);
88 static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
90 UInt32 size = sizeof(*framesize);
91 AudioObjectPropertyAddress addr = {
92 kAudioDevicePropertyBufferFrameSize,
93 kAudioDevicePropertyScopeOutput,
94 kAudioObjectPropertyElementMain
97 return AudioObjectGetPropertyData(id,
98 &addr,
100 NULL,
101 &size,
102 framesize);
105 static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
107 UInt32 size = sizeof(*framesize);
108 AudioObjectPropertyAddress addr = {
109 kAudioDevicePropertyBufferFrameSize,
110 kAudioDevicePropertyScopeOutput,
111 kAudioObjectPropertyElementMain
114 return AudioObjectSetPropertyData(id,
115 &addr,
117 NULL,
118 size,
119 framesize);
122 static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
123 AudioStreamBasicDescription *d)
125 UInt32 size = sizeof(*d);
126 AudioObjectPropertyAddress addr = {
127 kAudioDevicePropertyStreamFormat,
128 kAudioDevicePropertyScopeOutput,
129 kAudioObjectPropertyElementMain
132 return AudioObjectSetPropertyData(id,
133 &addr,
135 NULL,
136 size,
140 static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
142 UInt32 size = sizeof(*result);
143 AudioObjectPropertyAddress addr = {
144 kAudioDevicePropertyDeviceIsRunning,
145 kAudioDevicePropertyScopeOutput,
146 kAudioObjectPropertyElementMain
149 return AudioObjectGetPropertyData(id,
150 &addr,
152 NULL,
153 &size,
154 result);
157 static void coreaudio_logstatus (OSStatus status)
159 const char *str = "BUG";
161 switch (status) {
162 case kAudioHardwareNoError:
163 str = "kAudioHardwareNoError";
164 break;
166 case kAudioHardwareNotRunningError:
167 str = "kAudioHardwareNotRunningError";
168 break;
170 case kAudioHardwareUnspecifiedError:
171 str = "kAudioHardwareUnspecifiedError";
172 break;
174 case kAudioHardwareUnknownPropertyError:
175 str = "kAudioHardwareUnknownPropertyError";
176 break;
178 case kAudioHardwareBadPropertySizeError:
179 str = "kAudioHardwareBadPropertySizeError";
180 break;
182 case kAudioHardwareIllegalOperationError:
183 str = "kAudioHardwareIllegalOperationError";
184 break;
186 case kAudioHardwareBadDeviceError:
187 str = "kAudioHardwareBadDeviceError";
188 break;
190 case kAudioHardwareBadStreamError:
191 str = "kAudioHardwareBadStreamError";
192 break;
194 case kAudioHardwareUnsupportedOperationError:
195 str = "kAudioHardwareUnsupportedOperationError";
196 break;
198 case kAudioDeviceUnsupportedFormatError:
199 str = "kAudioDeviceUnsupportedFormatError";
200 break;
202 case kAudioDevicePermissionsError:
203 str = "kAudioDevicePermissionsError";
204 break;
206 default:
207 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
208 return;
211 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
214 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
215 OSStatus status,
216 const char *fmt,
220 va_list ap;
222 va_start (ap, fmt);
223 AUD_log (AUDIO_CAP, fmt, ap);
224 va_end (ap);
226 coreaudio_logstatus (status);
229 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
230 OSStatus status,
231 const char *typ,
232 const char *fmt,
236 va_list ap;
238 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
240 va_start (ap, fmt);
241 AUD_vlog (AUDIO_CAP, fmt, ap);
242 va_end (ap);
244 coreaudio_logstatus (status);
247 #define coreaudio_playback_logerr(status, ...) \
248 coreaudio_logerr2(status, "playback", __VA_ARGS__)
250 static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
252 int err;
254 err = pthread_mutex_lock (&core->buf_mutex);
255 if (err) {
256 dolog ("Could not lock voice for %s\nReason: %s\n",
257 fn_name, strerror (err));
258 return -1;
260 return 0;
263 static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
265 int err;
267 err = pthread_mutex_unlock (&core->buf_mutex);
268 if (err) {
269 dolog ("Could not unlock voice for %s\nReason: %s\n",
270 fn_name, strerror (err));
271 return -1;
273 return 0;
276 #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
277 static ret_type glue(coreaudio_, name)args_decl \
279 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
280 ret_type ret; \
282 if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
283 return 0; \
286 ret = glue(audio_generic_, name)args; \
288 coreaudio_buf_unlock(core, "coreaudio_" #name); \
289 return ret; \
291 COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
292 COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
293 (hw, size))
294 COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
295 (HWVoiceOut *hw, void *buf, size_t size),
296 (hw, buf, size))
297 COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
298 (hw, buf, size))
299 #undef COREAUDIO_WRAPPER_FUNC
302 * callback to feed audiooutput buffer. called without iothread lock.
303 * allowed to lock "buf_mutex", but disallowed to have any other locks.
305 static OSStatus audioDeviceIOProc(
306 AudioDeviceID inDevice,
307 const AudioTimeStamp *inNow,
308 const AudioBufferList *inInputData,
309 const AudioTimeStamp *inInputTime,
310 AudioBufferList *outOutputData,
311 const AudioTimeStamp *inOutputTime,
312 void *hwptr)
314 UInt32 frameCount, pending_frames;
315 void *out = outOutputData->mBuffers[0].mData;
316 HWVoiceOut *hw = hwptr;
317 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
318 size_t len;
320 if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
321 inInputTime = 0;
322 return 0;
325 if (inDevice != core->outputDeviceID) {
326 coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
327 return 0;
330 frameCount = core->audioDevicePropertyBufferFrameSize;
331 pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
333 /* if there are not enough samples, set signal and return */
334 if (pending_frames < frameCount) {
335 inInputTime = 0;
336 coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
337 return 0;
340 len = frameCount * hw->info.bytes_per_frame;
341 while (len) {
342 size_t write_len, start;
344 start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
345 assert(start < hw->size_emul);
347 write_len = MIN(MIN(hw->pending_emul, len),
348 hw->size_emul - start);
350 memcpy(out, hw->buf_emul + start, write_len);
351 hw->pending_emul -= write_len;
352 len -= write_len;
353 out += write_len;
356 coreaudio_buf_unlock (core, "audioDeviceIOProc");
357 return 0;
360 static OSStatus init_out_device(coreaudioVoiceOut *core)
362 OSStatus status;
363 AudioValueRange frameRange;
365 AudioStreamBasicDescription streamBasicDescription = {
366 .mBitsPerChannel = core->hw.info.bits,
367 .mBytesPerFrame = core->hw.info.bytes_per_frame,
368 .mBytesPerPacket = core->hw.info.bytes_per_frame,
369 .mChannelsPerFrame = core->hw.info.nchannels,
370 .mFormatFlags = kLinearPCMFormatFlagIsFloat,
371 .mFormatID = kAudioFormatLinearPCM,
372 .mFramesPerPacket = 1,
373 .mSampleRate = core->hw.info.freq
376 status = coreaudio_get_voice(&core->outputDeviceID);
377 if (status != kAudioHardwareNoError) {
378 coreaudio_playback_logerr (status,
379 "Could not get default output Device\n");
380 return status;
382 if (core->outputDeviceID == kAudioDeviceUnknown) {
383 dolog ("Could not initialize playback - Unknown Audiodevice\n");
384 return status;
387 /* get minimum and maximum buffer frame sizes */
388 status = coreaudio_get_framesizerange(core->outputDeviceID,
389 &frameRange);
390 if (status == kAudioHardwareBadObjectError) {
391 return 0;
393 if (status != kAudioHardwareNoError) {
394 coreaudio_playback_logerr (status,
395 "Could not get device buffer frame range\n");
396 return status;
399 if (frameRange.mMinimum > core->frameSizeSetting) {
400 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
401 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
402 } else if (frameRange.mMaximum < core->frameSizeSetting) {
403 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
404 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
405 } else {
406 core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
409 /* set Buffer Frame Size */
410 status = coreaudio_set_framesize(core->outputDeviceID,
411 &core->audioDevicePropertyBufferFrameSize);
412 if (status == kAudioHardwareBadObjectError) {
413 return 0;
415 if (status != kAudioHardwareNoError) {
416 coreaudio_playback_logerr (status,
417 "Could not set device buffer frame size %" PRIu32 "\n",
418 (uint32_t)core->audioDevicePropertyBufferFrameSize);
419 return status;
422 /* get Buffer Frame Size */
423 status = coreaudio_get_framesize(core->outputDeviceID,
424 &core->audioDevicePropertyBufferFrameSize);
425 if (status == kAudioHardwareBadObjectError) {
426 return 0;
428 if (status != kAudioHardwareNoError) {
429 coreaudio_playback_logerr (status,
430 "Could not get device buffer frame size\n");
431 return status;
433 core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
435 /* set Samplerate */
436 status = coreaudio_set_streamformat(core->outputDeviceID,
437 &streamBasicDescription);
438 if (status == kAudioHardwareBadObjectError) {
439 return 0;
441 if (status != kAudioHardwareNoError) {
442 coreaudio_playback_logerr (status,
443 "Could not set samplerate %lf\n",
444 streamBasicDescription.mSampleRate);
445 core->outputDeviceID = kAudioDeviceUnknown;
446 return status;
450 * set Callback.
452 * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
453 * internal function named HALB_Mutex::Lock(), which locks a mutex in
454 * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
455 * AudioObjectGetPropertyData, which is called by coreaudio driver.
456 * Therefore, the specified callback must be designed to avoid a deadlock
457 * with the callers of AudioObjectGetPropertyData.
459 core->ioprocid = NULL;
460 status = AudioDeviceCreateIOProcID(core->outputDeviceID,
461 audioDeviceIOProc,
462 &core->hw,
463 &core->ioprocid);
464 if (status == kAudioHardwareBadDeviceError) {
465 return 0;
467 if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
468 coreaudio_playback_logerr (status, "Could not set IOProc\n");
469 core->outputDeviceID = kAudioDeviceUnknown;
470 return status;
473 return 0;
476 static void fini_out_device(coreaudioVoiceOut *core)
478 OSStatus status;
479 UInt32 isrunning;
481 /* stop playback */
482 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
483 if (status != kAudioHardwareBadObjectError) {
484 if (status != kAudioHardwareNoError) {
485 coreaudio_logerr(status,
486 "Could not determine whether Device is playing\n");
489 if (isrunning) {
490 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
491 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
492 coreaudio_logerr(status, "Could not stop playback\n");
497 /* remove callback */
498 status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
499 core->ioprocid);
500 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
501 coreaudio_logerr(status, "Could not remove IOProc\n");
503 core->outputDeviceID = kAudioDeviceUnknown;
506 static void update_device_playback_state(coreaudioVoiceOut *core)
508 OSStatus status;
509 UInt32 isrunning;
511 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
512 if (status != kAudioHardwareNoError) {
513 if (status != kAudioHardwareBadObjectError) {
514 coreaudio_logerr(status,
515 "Could not determine whether Device is playing\n");
518 return;
521 if (core->enabled) {
522 /* start playback */
523 if (!isrunning) {
524 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
525 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
526 coreaudio_logerr (status, "Could not resume playback\n");
529 } else {
530 /* stop playback */
531 if (isrunning) {
532 status = AudioDeviceStop(core->outputDeviceID,
533 core->ioprocid);
534 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
535 coreaudio_logerr(status, "Could not pause playback\n");
541 /* called without iothread lock. */
542 static OSStatus handle_voice_change(
543 AudioObjectID in_object_id,
544 UInt32 in_number_addresses,
545 const AudioObjectPropertyAddress *in_addresses,
546 void *in_client_data)
548 OSStatus status;
549 coreaudioVoiceOut *core = in_client_data;
551 qemu_mutex_lock_iothread();
553 if (core->outputDeviceID) {
554 fini_out_device(core);
557 status = init_out_device(core);
558 if (!status) {
559 update_device_playback_state(core);
562 qemu_mutex_unlock_iothread();
563 return status;
566 static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
567 void *drv_opaque)
569 OSStatus status;
570 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
571 int err;
572 Audiodev *dev = drv_opaque;
573 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
574 struct audsettings obt_as;
576 /* create mutex */
577 err = pthread_mutex_init(&core->buf_mutex, NULL);
578 if (err) {
579 dolog("Could not create mutex\nReason: %s\n", strerror (err));
580 return -1;
583 obt_as = *as;
584 as = &obt_as;
585 as->fmt = AUDIO_FORMAT_F32;
586 audio_pcm_init_info (&hw->info, as);
588 core->frameSizeSetting = audio_buffer_frames(
589 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
591 core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
593 status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
594 &voice_addr, handle_voice_change,
595 core);
596 if (status != kAudioHardwareNoError) {
597 coreaudio_playback_logerr (status,
598 "Could not listen to voice property change\n");
599 return -1;
602 if (init_out_device(core)) {
603 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
604 &voice_addr,
605 handle_voice_change,
606 core);
607 if (status != kAudioHardwareNoError) {
608 coreaudio_playback_logerr(status,
609 "Could not remove voice property change listener\n");
612 return -1;
615 return 0;
618 static void coreaudio_fini_out (HWVoiceOut *hw)
620 OSStatus status;
621 int err;
622 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
624 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
625 &voice_addr,
626 handle_voice_change,
627 core);
628 if (status != kAudioHardwareNoError) {
629 coreaudio_logerr(status, "Could not remove voice property change listener\n");
632 fini_out_device(core);
634 /* destroy mutex */
635 err = pthread_mutex_destroy(&core->buf_mutex);
636 if (err) {
637 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
641 static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
643 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
645 core->enabled = enable;
646 update_device_playback_state(core);
649 static void *coreaudio_audio_init(Audiodev *dev)
651 return dev;
654 static void coreaudio_audio_fini (void *opaque)
658 static struct audio_pcm_ops coreaudio_pcm_ops = {
659 .init_out = coreaudio_init_out,
660 .fini_out = coreaudio_fini_out,
661 /* wrapper for audio_generic_write */
662 .write = coreaudio_write,
663 /* wrapper for audio_generic_buffer_get_free */
664 .buffer_get_free = coreaudio_buffer_get_free,
665 /* wrapper for audio_generic_get_buffer_out */
666 .get_buffer_out = coreaudio_get_buffer_out,
667 /* wrapper for audio_generic_put_buffer_out */
668 .put_buffer_out = coreaudio_put_buffer_out,
669 .enable_out = coreaudio_enable_out
672 static struct audio_driver coreaudio_audio_driver = {
673 .name = "coreaudio",
674 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
675 .init = coreaudio_audio_init,
676 .fini = coreaudio_audio_fini,
677 .pcm_ops = &coreaudio_pcm_ops,
678 .can_be_default = 1,
679 .max_voices_out = 1,
680 .max_voices_in = 0,
681 .voice_size_out = sizeof (coreaudioVoiceOut),
682 .voice_size_in = 0
685 static void register_audio_coreaudio(void)
687 audio_driver_register(&coreaudio_audio_driver);
689 type_init(register_audio_coreaudio);