xref: /openbmc/qemu/audio/coreaudio.m (revision 0e376d45)
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/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    kAudioObjectPropertyElementMain
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        kAudioObjectPropertyElementMain
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        kAudioObjectPropertyElementMain
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        kAudioObjectPropertyElementMain
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        kAudioObjectPropertyElementMain
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        kAudioObjectPropertyElementMain
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 G_GNUC_PRINTF (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 G_GNUC_PRINTF (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(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
287COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
288                       (hw, size))
289COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
290                       (HWVoiceOut *hw, void *buf, size_t size),
291                       (hw, buf, size))
292COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
293                       (hw, buf, size))
294#undef COREAUDIO_WRAPPER_FUNC
295
296/*
297 * callback to feed audiooutput buffer. called without BQL.
298 * allowed to lock "buf_mutex", but disallowed to have any other locks.
299 */
300static 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)
308{
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;
314
315    if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
316        inInputTime = 0;
317        return 0;
318    }
319
320    if (inDevice != core->outputDeviceID) {
321        coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
322        return 0;
323    }
324
325    frameCount = core->audioDevicePropertyBufferFrameSize;
326    pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
327
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;
333    }
334
335    len = frameCount * hw->info.bytes_per_frame;
336    while (len) {
337        size_t write_len, start;
338
339        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
340        assert(start < hw->size_emul);
341
342        write_len = MIN(MIN(hw->pending_emul, len),
343                        hw->size_emul - start);
344
345        memcpy(out, hw->buf_emul + start, write_len);
346        hw->pending_emul -= write_len;
347        len -= write_len;
348        out += write_len;
349    }
350
351    coreaudio_buf_unlock (core, "audioDeviceIOProc");
352    return 0;
353}
354
355static OSStatus init_out_device(coreaudioVoiceOut *core)
356{
357    OSStatus status;
358    AudioValueRange frameRange;
359
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
369    };
370
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;
376    }
377    if (core->outputDeviceID == kAudioDeviceUnknown) {
378        dolog ("Could not initialize playback - Unknown Audiodevice\n");
379        return status;
380    }
381
382    /* get minimum and maximum buffer frame sizes */
383    status = coreaudio_get_framesizerange(core->outputDeviceID,
384                                          &frameRange);
385    if (status == kAudioHardwareBadObjectError) {
386        return 0;
387    }
388    if (status != kAudioHardwareNoError) {
389        coreaudio_playback_logerr (status,
390                                    "Could not get device buffer frame range\n");
391        return status;
392    }
393
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;
402    }
403
404    /* set Buffer Frame Size */
405    status = coreaudio_set_framesize(core->outputDeviceID,
406                                     &core->audioDevicePropertyBufferFrameSize);
407    if (status == kAudioHardwareBadObjectError) {
408        return 0;
409    }
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;
415    }
416
417    /* get Buffer Frame Size */
418    status = coreaudio_get_framesize(core->outputDeviceID,
419                                     &core->audioDevicePropertyBufferFrameSize);
420    if (status == kAudioHardwareBadObjectError) {
421        return 0;
422    }
423    if (status != kAudioHardwareNoError) {
424        coreaudio_playback_logerr (status,
425                                    "Could not get device buffer frame size\n");
426        return status;
427    }
428    core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
429
430    /* set Samplerate */
431    status = coreaudio_set_streamformat(core->outputDeviceID,
432                                        &streamBasicDescription);
433    if (status == kAudioHardwareBadObjectError) {
434        return 0;
435    }
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;
442    }
443
444    /*
445     * set Callback.
446     *
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.
453     */
454    core->ioprocid = NULL;
455    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
456                                       audioDeviceIOProc,
457                                       &core->hw,
458                                       &core->ioprocid);
459    if (status == kAudioHardwareBadDeviceError) {
460        return 0;
461    }
462    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
463        coreaudio_playback_logerr (status, "Could not set IOProc\n");
464        core->outputDeviceID = kAudioDeviceUnknown;
465        return status;
466    }
467
468    return 0;
469}
470
471static void fini_out_device(coreaudioVoiceOut *core)
472{
473    OSStatus status;
474    UInt32 isrunning;
475
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");
482        }
483
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");
488            }
489        }
490    }
491
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");
497    }
498    core->outputDeviceID = kAudioDeviceUnknown;
499}
500
501static void update_device_playback_state(coreaudioVoiceOut *core)
502{
503    OSStatus status;
504    UInt32 isrunning;
505
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");
511        }
512
513        return;
514    }
515
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");
522            }
523        }
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");
531            }
532        }
533    }
534}
535
536/* called without BQL. */
537static OSStatus handle_voice_change(
538    AudioObjectID in_object_id,
539    UInt32 in_number_addresses,
540    const AudioObjectPropertyAddress *in_addresses,
541    void *in_client_data)
542{
543    coreaudioVoiceOut *core = in_client_data;
544
545    bql_lock();
546
547    if (core->outputDeviceID) {
548        fini_out_device(core);
549    }
550
551    if (!init_out_device(core)) {
552        update_device_playback_state(core);
553    }
554
555    bql_unlock();
556    return 0;
557}
558
559static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
560                              void *drv_opaque)
561{
562    OSStatus status;
563    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
564    int err;
565    Audiodev *dev = drv_opaque;
566    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
567    struct audsettings obt_as;
568
569    /* create mutex */
570    err = pthread_mutex_init(&core->buf_mutex, NULL);
571    if (err) {
572        dolog("Could not create mutex\nReason: %s\n", strerror (err));
573        return -1;
574    }
575
576    obt_as = *as;
577    as = &obt_as;
578    as->fmt = AUDIO_FORMAT_F32;
579    audio_pcm_init_info (&hw->info, as);
580
581    core->frameSizeSetting = audio_buffer_frames(
582        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
583
584    core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
585
586    status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
587                                            &voice_addr, handle_voice_change,
588                                            core);
589    if (status != kAudioHardwareNoError) {
590        coreaudio_playback_logerr (status,
591                                   "Could not listen to voice property change\n");
592        return -1;
593    }
594
595    if (init_out_device(core)) {
596        status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
597                                                   &voice_addr,
598                                                   handle_voice_change,
599                                                   core);
600        if (status != kAudioHardwareNoError) {
601            coreaudio_playback_logerr(status,
602                                      "Could not remove voice property change listener\n");
603        }
604
605        return -1;
606    }
607
608    return 0;
609}
610
611static void coreaudio_fini_out (HWVoiceOut *hw)
612{
613    OSStatus status;
614    int err;
615    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
616
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");
623    }
624
625    fini_out_device(core);
626
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));
631    }
632}
633
634static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
635{
636    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
637
638    core->enabled = enable;
639    update_device_playback_state(core);
640}
641
642static void *coreaudio_audio_init(Audiodev *dev, Error **errp)
643{
644    return dev;
645}
646
647static void coreaudio_audio_fini (void *opaque)
648{
649}
650
651static 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
663};
664
665static 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    .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