audio: restore mixing-engine playback buffer size
[qemu/ar7.git] / audio / coreaudio.c
blob91ea6ae975e54e6e27269c0df12a706931ed3730
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 static const AudioObjectPropertyAddress voice_addr = {
48 kAudioHardwarePropertyDefaultOutputDevice,
49 kAudioObjectPropertyScopeGlobal,
50 kAudioObjectPropertyElementMaster
53 static OSStatus coreaudio_get_voice(AudioDeviceID *id)
55 UInt32 size = sizeof(*id);
57 return AudioObjectGetPropertyData(kAudioObjectSystemObject,
58 &voice_addr,
60 NULL,
61 &size,
62 id);
65 static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
66 AudioValueRange *framerange)
68 UInt32 size = sizeof(*framerange);
69 AudioObjectPropertyAddress addr = {
70 kAudioDevicePropertyBufferFrameSizeRange,
71 kAudioDevicePropertyScopeOutput,
72 kAudioObjectPropertyElementMaster
75 return AudioObjectGetPropertyData(id,
76 &addr,
78 NULL,
79 &size,
80 framerange);
83 static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
85 UInt32 size = sizeof(*framesize);
86 AudioObjectPropertyAddress addr = {
87 kAudioDevicePropertyBufferFrameSize,
88 kAudioDevicePropertyScopeOutput,
89 kAudioObjectPropertyElementMaster
92 return AudioObjectGetPropertyData(id,
93 &addr,
95 NULL,
96 &size,
97 framesize);
100 static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
102 UInt32 size = sizeof(*framesize);
103 AudioObjectPropertyAddress addr = {
104 kAudioDevicePropertyBufferFrameSize,
105 kAudioDevicePropertyScopeOutput,
106 kAudioObjectPropertyElementMaster
109 return AudioObjectSetPropertyData(id,
110 &addr,
112 NULL,
113 size,
114 framesize);
117 static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
118 AudioStreamBasicDescription *d)
120 UInt32 size = sizeof(*d);
121 AudioObjectPropertyAddress addr = {
122 kAudioDevicePropertyStreamFormat,
123 kAudioDevicePropertyScopeOutput,
124 kAudioObjectPropertyElementMaster
127 return AudioObjectSetPropertyData(id,
128 &addr,
130 NULL,
131 size,
135 static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
137 UInt32 size = sizeof(*result);
138 AudioObjectPropertyAddress addr = {
139 kAudioDevicePropertyDeviceIsRunning,
140 kAudioDevicePropertyScopeOutput,
141 kAudioObjectPropertyElementMaster
144 return AudioObjectGetPropertyData(id,
145 &addr,
147 NULL,
148 &size,
149 result);
152 static void coreaudio_logstatus (OSStatus status)
154 const char *str = "BUG";
156 switch (status) {
157 case kAudioHardwareNoError:
158 str = "kAudioHardwareNoError";
159 break;
161 case kAudioHardwareNotRunningError:
162 str = "kAudioHardwareNotRunningError";
163 break;
165 case kAudioHardwareUnspecifiedError:
166 str = "kAudioHardwareUnspecifiedError";
167 break;
169 case kAudioHardwareUnknownPropertyError:
170 str = "kAudioHardwareUnknownPropertyError";
171 break;
173 case kAudioHardwareBadPropertySizeError:
174 str = "kAudioHardwareBadPropertySizeError";
175 break;
177 case kAudioHardwareIllegalOperationError:
178 str = "kAudioHardwareIllegalOperationError";
179 break;
181 case kAudioHardwareBadDeviceError:
182 str = "kAudioHardwareBadDeviceError";
183 break;
185 case kAudioHardwareBadStreamError:
186 str = "kAudioHardwareBadStreamError";
187 break;
189 case kAudioHardwareUnsupportedOperationError:
190 str = "kAudioHardwareUnsupportedOperationError";
191 break;
193 case kAudioDeviceUnsupportedFormatError:
194 str = "kAudioDeviceUnsupportedFormatError";
195 break;
197 case kAudioDevicePermissionsError:
198 str = "kAudioDevicePermissionsError";
199 break;
201 default:
202 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
203 return;
206 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
209 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
210 OSStatus status,
211 const char *fmt,
215 va_list ap;
217 va_start (ap, fmt);
218 AUD_log (AUDIO_CAP, fmt, ap);
219 va_end (ap);
221 coreaudio_logstatus (status);
224 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
225 OSStatus status,
226 const char *typ,
227 const char *fmt,
231 va_list ap;
233 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
235 va_start (ap, fmt);
236 AUD_vlog (AUDIO_CAP, fmt, ap);
237 va_end (ap);
239 coreaudio_logstatus (status);
242 #define coreaudio_playback_logerr(status, ...) \
243 coreaudio_logerr2(status, "playback", __VA_ARGS__)
245 static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
247 int err;
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;
255 return 0;
258 static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
260 int err;
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;
268 return 0;
271 #define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
272 static ret_type glue(coreaudio_, name)args_decl \
274 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
275 ret_type ret; \
277 if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \
278 return 0; \
281 ret = glue(audio_generic_, name)args; \
283 coreaudio_buf_unlock(core, "coreaudio_" #name); \
284 return ret; \
286 COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
287 COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
288 (hw, size))
289 COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
290 (HWVoiceOut *hw, void *buf, size_t size),
291 (hw, buf, size))
292 COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
293 (hw, buf, size))
294 #undef COREAUDIO_WRAPPER_FUNC
297 * callback to feed audiooutput buffer. called without iothread lock.
298 * allowed to lock "buf_mutex", but disallowed to have any other locks.
300 static OSStatus audioDeviceIOProc(
301 AudioDeviceID inDevice,
302 const AudioTimeStamp *inNow,
303 const AudioBufferList *inInputData,
304 const AudioTimeStamp *inInputTime,
305 AudioBufferList *outOutputData,
306 const AudioTimeStamp *inOutputTime,
307 void *hwptr)
309 UInt32 frameCount, pending_frames;
310 void *out = outOutputData->mBuffers[0].mData;
311 HWVoiceOut *hw = hwptr;
312 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
313 size_t len;
315 if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
316 inInputTime = 0;
317 return 0;
320 if (inDevice != core->outputDeviceID) {
321 coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
322 return 0;
325 frameCount = core->audioDevicePropertyBufferFrameSize;
326 pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
328 /* if there are not enough samples, set signal and return */
329 if (pending_frames < frameCount) {
330 inInputTime = 0;
331 coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
332 return 0;
335 len = frameCount * hw->info.bytes_per_frame;
336 while (len) {
337 size_t write_len, start;
339 start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
340 assert(start < hw->size_emul);
342 write_len = MIN(MIN(hw->pending_emul, len),
343 hw->size_emul - start);
345 memcpy(out, hw->buf_emul + start, write_len);
346 hw->pending_emul -= write_len;
347 len -= write_len;
348 out += write_len;
351 coreaudio_buf_unlock (core, "audioDeviceIOProc");
352 return 0;
355 static OSStatus init_out_device(coreaudioVoiceOut *core)
357 OSStatus status;
358 AudioValueRange frameRange;
360 AudioStreamBasicDescription streamBasicDescription = {
361 .mBitsPerChannel = core->hw.info.bits,
362 .mBytesPerFrame = core->hw.info.bytes_per_frame,
363 .mBytesPerPacket = core->hw.info.bytes_per_frame,
364 .mChannelsPerFrame = core->hw.info.nchannels,
365 .mFormatFlags = kLinearPCMFormatFlagIsFloat,
366 .mFormatID = kAudioFormatLinearPCM,
367 .mFramesPerPacket = 1,
368 .mSampleRate = core->hw.info.freq
371 status = coreaudio_get_voice(&core->outputDeviceID);
372 if (status != kAudioHardwareNoError) {
373 coreaudio_playback_logerr (status,
374 "Could not get default output Device\n");
375 return status;
377 if (core->outputDeviceID == kAudioDeviceUnknown) {
378 dolog ("Could not initialize playback - Unknown Audiodevice\n");
379 return status;
382 /* get minimum and maximum buffer frame sizes */
383 status = coreaudio_get_framesizerange(core->outputDeviceID,
384 &frameRange);
385 if (status == kAudioHardwareBadObjectError) {
386 return 0;
388 if (status != kAudioHardwareNoError) {
389 coreaudio_playback_logerr (status,
390 "Could not get device buffer frame range\n");
391 return status;
394 if (frameRange.mMinimum > core->frameSizeSetting) {
395 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
396 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
397 } else if (frameRange.mMaximum < core->frameSizeSetting) {
398 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
399 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
400 } else {
401 core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
404 /* set Buffer Frame Size */
405 status = coreaudio_set_framesize(core->outputDeviceID,
406 &core->audioDevicePropertyBufferFrameSize);
407 if (status == kAudioHardwareBadObjectError) {
408 return 0;
410 if (status != kAudioHardwareNoError) {
411 coreaudio_playback_logerr (status,
412 "Could not set device buffer frame size %" PRIu32 "\n",
413 (uint32_t)core->audioDevicePropertyBufferFrameSize);
414 return status;
417 /* get Buffer Frame Size */
418 status = coreaudio_get_framesize(core->outputDeviceID,
419 &core->audioDevicePropertyBufferFrameSize);
420 if (status == kAudioHardwareBadObjectError) {
421 return 0;
423 if (status != kAudioHardwareNoError) {
424 coreaudio_playback_logerr (status,
425 "Could not get device buffer frame size\n");
426 return status;
428 core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
430 /* set Samplerate */
431 status = coreaudio_set_streamformat(core->outputDeviceID,
432 &streamBasicDescription);
433 if (status == kAudioHardwareBadObjectError) {
434 return 0;
436 if (status != kAudioHardwareNoError) {
437 coreaudio_playback_logerr (status,
438 "Could not set samplerate %lf\n",
439 streamBasicDescription.mSampleRate);
440 core->outputDeviceID = kAudioDeviceUnknown;
441 return status;
445 * set Callback.
447 * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
448 * internal function named HALB_Mutex::Lock(), which locks a mutex in
449 * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
450 * AudioObjectGetPropertyData, which is called by coreaudio driver.
451 * Therefore, the specified callback must be designed to avoid a deadlock
452 * with the callers of AudioObjectGetPropertyData.
454 core->ioprocid = NULL;
455 status = AudioDeviceCreateIOProcID(core->outputDeviceID,
456 audioDeviceIOProc,
457 &core->hw,
458 &core->ioprocid);
459 if (status == kAudioHardwareBadDeviceError) {
460 return 0;
462 if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
463 coreaudio_playback_logerr (status, "Could not set IOProc\n");
464 core->outputDeviceID = kAudioDeviceUnknown;
465 return status;
468 return 0;
471 static void fini_out_device(coreaudioVoiceOut *core)
473 OSStatus status;
474 UInt32 isrunning;
476 /* stop playback */
477 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
478 if (status != kAudioHardwareBadObjectError) {
479 if (status != kAudioHardwareNoError) {
480 coreaudio_logerr(status,
481 "Could not determine whether Device is playing\n");
484 if (isrunning) {
485 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
486 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
487 coreaudio_logerr(status, "Could not stop playback\n");
492 /* remove callback */
493 status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
494 core->ioprocid);
495 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
496 coreaudio_logerr(status, "Could not remove IOProc\n");
498 core->outputDeviceID = kAudioDeviceUnknown;
501 static void update_device_playback_state(coreaudioVoiceOut *core)
503 OSStatus status;
504 UInt32 isrunning;
506 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
507 if (status != kAudioHardwareNoError) {
508 if (status != kAudioHardwareBadObjectError) {
509 coreaudio_logerr(status,
510 "Could not determine whether Device is playing\n");
513 return;
516 if (core->enabled) {
517 /* start playback */
518 if (!isrunning) {
519 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
520 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
521 coreaudio_logerr (status, "Could not resume playback\n");
524 } else {
525 /* stop playback */
526 if (isrunning) {
527 status = AudioDeviceStop(core->outputDeviceID,
528 core->ioprocid);
529 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
530 coreaudio_logerr(status, "Could not pause playback\n");
536 /* called without iothread lock. */
537 static OSStatus handle_voice_change(
538 AudioObjectID in_object_id,
539 UInt32 in_number_addresses,
540 const AudioObjectPropertyAddress *in_addresses,
541 void *in_client_data)
543 OSStatus status;
544 coreaudioVoiceOut *core = in_client_data;
546 qemu_mutex_lock_iothread();
548 if (core->outputDeviceID) {
549 fini_out_device(core);
552 status = init_out_device(core);
553 if (!status) {
554 update_device_playback_state(core);
557 qemu_mutex_unlock_iothread();
558 return status;
561 static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
562 void *drv_opaque)
564 OSStatus status;
565 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
566 int err;
567 Audiodev *dev = drv_opaque;
568 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
569 struct audsettings obt_as;
571 /* create mutex */
572 err = pthread_mutex_init(&core->buf_mutex, NULL);
573 if (err) {
574 dolog("Could not create mutex\nReason: %s\n", strerror (err));
575 return -1;
578 obt_as = *as;
579 as = &obt_as;
580 as->fmt = AUDIO_FORMAT_F32;
581 audio_pcm_init_info (&hw->info, as);
583 core->frameSizeSetting = audio_buffer_frames(
584 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
586 core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
588 status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
589 &voice_addr, handle_voice_change,
590 core);
591 if (status != kAudioHardwareNoError) {
592 coreaudio_playback_logerr (status,
593 "Could not listen to voice property change\n");
594 return -1;
597 if (init_out_device(core)) {
598 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
599 &voice_addr,
600 handle_voice_change,
601 core);
602 if (status != kAudioHardwareNoError) {
603 coreaudio_playback_logerr(status,
604 "Could not remove voice property change listener\n");
608 return 0;
611 static void coreaudio_fini_out (HWVoiceOut *hw)
613 OSStatus status;
614 int err;
615 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
617 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
618 &voice_addr,
619 handle_voice_change,
620 core);
621 if (status != kAudioHardwareNoError) {
622 coreaudio_logerr(status, "Could not remove voice property change listener\n");
625 fini_out_device(core);
627 /* destroy mutex */
628 err = pthread_mutex_destroy(&core->buf_mutex);
629 if (err) {
630 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
634 static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
636 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
638 core->enabled = enable;
639 update_device_playback_state(core);
642 static void *coreaudio_audio_init(Audiodev *dev)
644 return dev;
647 static void coreaudio_audio_fini (void *opaque)
651 static struct audio_pcm_ops coreaudio_pcm_ops = {
652 .init_out = coreaudio_init_out,
653 .fini_out = coreaudio_fini_out,
654 /* wrapper for audio_generic_write */
655 .write = coreaudio_write,
656 /* wrapper for audio_generic_buffer_get_free */
657 .buffer_get_free = coreaudio_buffer_get_free,
658 /* wrapper for audio_generic_get_buffer_out */
659 .get_buffer_out = coreaudio_get_buffer_out,
660 /* wrapper for audio_generic_put_buffer_out */
661 .put_buffer_out = coreaudio_put_buffer_out,
662 .enable_out = coreaudio_enable_out
665 static struct audio_driver coreaudio_audio_driver = {
666 .name = "coreaudio",
667 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
668 .init = coreaudio_audio_init,
669 .fini = coreaudio_audio_fini,
670 .pcm_ops = &coreaudio_pcm_ops,
671 .can_be_default = 1,
672 .max_voices_out = 1,
673 .max_voices_in = 0,
674 .voice_size_out = sizeof (coreaudioVoiceOut),
675 .voice_size_in = 0
678 static void register_audio_coreaudio(void)
680 audio_driver_register(&coreaudio_audio_driver);
682 type_init(register_audio_coreaudio);