xref: /openbmc/qemu/audio/coreaudio.m (revision 2df1eb27)
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
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
51
52static const AudioObjectPropertyAddress voice_addr = {
53    kAudioHardwarePropertyDefaultOutputDevice,
54    kAudioObjectPropertyScopeGlobal,
55    kAudioObjectPropertyElementMain
56};
57
58static OSStatus coreaudio_get_voice(AudioDeviceID *id)
59{
60    UInt32 size = sizeof(*id);
61
62    return AudioObjectGetPropertyData(kAudioObjectSystemObject,
63                                      &voice_addr,
64                                      0,
65                                      NULL,
66                                      &size,
67                                      id);
68}
69
70static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
71                                             AudioValueRange *framerange)
72{
73    UInt32 size = sizeof(*framerange);
74    AudioObjectPropertyAddress addr = {
75        kAudioDevicePropertyBufferFrameSizeRange,
76        kAudioDevicePropertyScopeOutput,
77        kAudioObjectPropertyElementMain
78    };
79
80    return AudioObjectGetPropertyData(id,
81                                      &addr,
82                                      0,
83                                      NULL,
84                                      &size,
85                                      framerange);
86}
87
88static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
89{
90    UInt32 size = sizeof(*framesize);
91    AudioObjectPropertyAddress addr = {
92        kAudioDevicePropertyBufferFrameSize,
93        kAudioDevicePropertyScopeOutput,
94        kAudioObjectPropertyElementMain
95    };
96
97    return AudioObjectGetPropertyData(id,
98                                      &addr,
99                                      0,
100                                      NULL,
101                                      &size,
102                                      framesize);
103}
104
105static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
106{
107    UInt32 size = sizeof(*framesize);
108    AudioObjectPropertyAddress addr = {
109        kAudioDevicePropertyBufferFrameSize,
110        kAudioDevicePropertyScopeOutput,
111        kAudioObjectPropertyElementMain
112    };
113
114    return AudioObjectSetPropertyData(id,
115                                      &addr,
116                                      0,
117                                      NULL,
118                                      size,
119                                      framesize);
120}
121
122static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
123                                           AudioStreamBasicDescription *d)
124{
125    UInt32 size = sizeof(*d);
126    AudioObjectPropertyAddress addr = {
127        kAudioDevicePropertyStreamFormat,
128        kAudioDevicePropertyScopeOutput,
129        kAudioObjectPropertyElementMain
130    };
131
132    return AudioObjectSetPropertyData(id,
133                                      &addr,
134                                      0,
135                                      NULL,
136                                      size,
137                                      d);
138}
139
140static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
141{
142    UInt32 size = sizeof(*result);
143    AudioObjectPropertyAddress addr = {
144        kAudioDevicePropertyDeviceIsRunning,
145        kAudioDevicePropertyScopeOutput,
146        kAudioObjectPropertyElementMain
147    };
148
149    return AudioObjectGetPropertyData(id,
150                                      &addr,
151                                      0,
152                                      NULL,
153                                      &size,
154                                      result);
155}
156
157static void coreaudio_logstatus (OSStatus status)
158{
159    const char *str = "BUG";
160
161    switch (status) {
162    case kAudioHardwareNoError:
163        str = "kAudioHardwareNoError";
164        break;
165
166    case kAudioHardwareNotRunningError:
167        str = "kAudioHardwareNotRunningError";
168        break;
169
170    case kAudioHardwareUnspecifiedError:
171        str = "kAudioHardwareUnspecifiedError";
172        break;
173
174    case kAudioHardwareUnknownPropertyError:
175        str = "kAudioHardwareUnknownPropertyError";
176        break;
177
178    case kAudioHardwareBadPropertySizeError:
179        str = "kAudioHardwareBadPropertySizeError";
180        break;
181
182    case kAudioHardwareIllegalOperationError:
183        str = "kAudioHardwareIllegalOperationError";
184        break;
185
186    case kAudioHardwareBadDeviceError:
187        str = "kAudioHardwareBadDeviceError";
188        break;
189
190    case kAudioHardwareBadStreamError:
191        str = "kAudioHardwareBadStreamError";
192        break;
193
194    case kAudioHardwareUnsupportedOperationError:
195        str = "kAudioHardwareUnsupportedOperationError";
196        break;
197
198    case kAudioDeviceUnsupportedFormatError:
199        str = "kAudioDeviceUnsupportedFormatError";
200        break;
201
202    case kAudioDevicePermissionsError:
203        str = "kAudioDevicePermissionsError";
204        break;
205
206    default:
207        AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
208        return;
209    }
210
211    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
212}
213
214static void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
215    OSStatus status,
216    const char *fmt,
217    ...
218    )
219{
220    va_list ap;
221
222    va_start (ap, fmt);
223    AUD_log (AUDIO_CAP, fmt, ap);
224    va_end (ap);
225
226    coreaudio_logstatus (status);
227}
228
229static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
230    OSStatus status,
231    const char *typ,
232    const char *fmt,
233    ...
234    )
235{
236    va_list ap;
237
238    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
239
240    va_start (ap, fmt);
241    AUD_vlog (AUDIO_CAP, fmt, ap);
242    va_end (ap);
243
244    coreaudio_logstatus (status);
245}
246
247#define coreaudio_playback_logerr(status, ...) \
248    coreaudio_logerr2(status, "playback", __VA_ARGS__)
249
250static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
251{
252    int err;
253
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;
259    }
260    return 0;
261}
262
263static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
264{
265    int err;
266
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;
272    }
273    return 0;
274}
275
276#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
277    static ret_type glue(coreaudio_, name)args_decl             \
278    {                                                           \
279        coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \
280        ret_type ret;                                           \
281                                                                \
282        if (coreaudio_buf_lock(core, "coreaudio_" #name)) {         \
283            return 0;                                           \
284        }                                                       \
285                                                                \
286        ret = glue(audio_generic_, name)args;                   \
287                                                                \
288        coreaudio_buf_unlock(core, "coreaudio_" #name);             \
289        return ret;                                             \
290    }
291COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
292COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
293                       (hw, size))
294COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
295                       (HWVoiceOut *hw, void *buf, size_t size),
296                       (hw, buf, size))
297COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
298                       (hw, buf, size))
299#undef COREAUDIO_WRAPPER_FUNC
300
301/*
302 * callback to feed audiooutput buffer. called without BQL.
303 * allowed to lock "buf_mutex", but disallowed to have any other locks.
304 */
305static 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)
313{
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;
319
320    if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
321        inInputTime = 0;
322        return 0;
323    }
324
325    if (inDevice != core->outputDeviceID) {
326        coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
327        return 0;
328    }
329
330    frameCount = core->audioDevicePropertyBufferFrameSize;
331    pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
332
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;
338    }
339
340    len = frameCount * hw->info.bytes_per_frame;
341    while (len) {
342        size_t write_len, start;
343
344        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
345        assert(start < hw->size_emul);
346
347        write_len = MIN(MIN(hw->pending_emul, len),
348                        hw->size_emul - start);
349
350        memcpy(out, hw->buf_emul + start, write_len);
351        hw->pending_emul -= write_len;
352        len -= write_len;
353        out += write_len;
354    }
355
356    coreaudio_buf_unlock (core, "audioDeviceIOProc");
357    return 0;
358}
359
360static OSStatus init_out_device(coreaudioVoiceOut *core)
361{
362    OSStatus status;
363    AudioValueRange frameRange;
364
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
374    };
375
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;
381    }
382    if (core->outputDeviceID == kAudioDeviceUnknown) {
383        dolog ("Could not initialize playback - Unknown Audiodevice\n");
384        return status;
385    }
386
387    /* get minimum and maximum buffer frame sizes */
388    status = coreaudio_get_framesizerange(core->outputDeviceID,
389                                          &frameRange);
390    if (status == kAudioHardwareBadObjectError) {
391        return 0;
392    }
393    if (status != kAudioHardwareNoError) {
394        coreaudio_playback_logerr (status,
395                                    "Could not get device buffer frame range\n");
396        return status;
397    }
398
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;
407    }
408
409    /* set Buffer Frame Size */
410    status = coreaudio_set_framesize(core->outputDeviceID,
411                                     &core->audioDevicePropertyBufferFrameSize);
412    if (status == kAudioHardwareBadObjectError) {
413        return 0;
414    }
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;
420    }
421
422    /* get Buffer Frame Size */
423    status = coreaudio_get_framesize(core->outputDeviceID,
424                                     &core->audioDevicePropertyBufferFrameSize);
425    if (status == kAudioHardwareBadObjectError) {
426        return 0;
427    }
428    if (status != kAudioHardwareNoError) {
429        coreaudio_playback_logerr (status,
430                                    "Could not get device buffer frame size\n");
431        return status;
432    }
433    core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
434
435    /* set Samplerate */
436    status = coreaudio_set_streamformat(core->outputDeviceID,
437                                        &streamBasicDescription);
438    if (status == kAudioHardwareBadObjectError) {
439        return 0;
440    }
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;
447    }
448
449    /*
450     * set Callback.
451     *
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.
458     */
459    core->ioprocid = NULL;
460    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
461                                       audioDeviceIOProc,
462                                       &core->hw,
463                                       &core->ioprocid);
464    if (status == kAudioHardwareBadDeviceError) {
465        return 0;
466    }
467    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
468        coreaudio_playback_logerr (status, "Could not set IOProc\n");
469        core->outputDeviceID = kAudioDeviceUnknown;
470        return status;
471    }
472
473    return 0;
474}
475
476static void fini_out_device(coreaudioVoiceOut *core)
477{
478    OSStatus status;
479    UInt32 isrunning;
480
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");
487        }
488
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");
493            }
494        }
495    }
496
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");
502    }
503    core->outputDeviceID = kAudioDeviceUnknown;
504}
505
506static void update_device_playback_state(coreaudioVoiceOut *core)
507{
508    OSStatus status;
509    UInt32 isrunning;
510
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");
516        }
517
518        return;
519    }
520
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");
527            }
528        }
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");
536            }
537        }
538    }
539}
540
541/* called without BQL. */
542static OSStatus handle_voice_change(
543    AudioObjectID in_object_id,
544    UInt32 in_number_addresses,
545    const AudioObjectPropertyAddress *in_addresses,
546    void *in_client_data)
547{
548    coreaudioVoiceOut *core = in_client_data;
549
550    bql_lock();
551
552    if (core->outputDeviceID) {
553        fini_out_device(core);
554    }
555
556    if (!init_out_device(core)) {
557        update_device_playback_state(core);
558    }
559
560    bql_unlock();
561    return 0;
562}
563
564static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
565                              void *drv_opaque)
566{
567    OSStatus status;
568    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
569    int err;
570    Audiodev *dev = drv_opaque;
571    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
572    struct audsettings obt_as;
573
574    /* create mutex */
575    err = pthread_mutex_init(&core->buf_mutex, NULL);
576    if (err) {
577        dolog("Could not create mutex\nReason: %s\n", strerror (err));
578        return -1;
579    }
580
581    obt_as = *as;
582    as = &obt_as;
583    as->fmt = AUDIO_FORMAT_F32;
584    audio_pcm_init_info (&hw->info, as);
585
586    core->frameSizeSetting = audio_buffer_frames(
587        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
588
589    core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
590
591    status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
592                                            &voice_addr, handle_voice_change,
593                                            core);
594    if (status != kAudioHardwareNoError) {
595        coreaudio_playback_logerr (status,
596                                   "Could not listen to voice property change\n");
597        return -1;
598    }
599
600    if (init_out_device(core)) {
601        status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
602                                                   &voice_addr,
603                                                   handle_voice_change,
604                                                   core);
605        if (status != kAudioHardwareNoError) {
606            coreaudio_playback_logerr(status,
607                                      "Could not remove voice property change listener\n");
608        }
609
610        return -1;
611    }
612
613    return 0;
614}
615
616static void coreaudio_fini_out (HWVoiceOut *hw)
617{
618    OSStatus status;
619    int err;
620    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
621
622    status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
623                                               &voice_addr,
624                                               handle_voice_change,
625                                               core);
626    if (status != kAudioHardwareNoError) {
627        coreaudio_logerr(status, "Could not remove voice property change listener\n");
628    }
629
630    fini_out_device(core);
631
632    /* destroy mutex */
633    err = pthread_mutex_destroy(&core->buf_mutex);
634    if (err) {
635        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
636    }
637}
638
639static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
640{
641    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
642
643    core->enabled = enable;
644    update_device_playback_state(core);
645}
646
647static void *coreaudio_audio_init(Audiodev *dev, Error **errp)
648{
649    return dev;
650}
651
652static void coreaudio_audio_fini (void *opaque)
653{
654}
655
656static struct audio_pcm_ops coreaudio_pcm_ops = {
657    .init_out = coreaudio_init_out,
658    .fini_out = coreaudio_fini_out,
659  /* wrapper for audio_generic_write */
660    .write    = coreaudio_write,
661  /* wrapper for audio_generic_buffer_get_free */
662    .buffer_get_free = coreaudio_buffer_get_free,
663  /* wrapper for audio_generic_get_buffer_out */
664    .get_buffer_out = coreaudio_get_buffer_out,
665  /* wrapper for audio_generic_put_buffer_out */
666    .put_buffer_out = coreaudio_put_buffer_out,
667    .enable_out = coreaudio_enable_out
668};
669
670static struct audio_driver coreaudio_audio_driver = {
671    .name           = "coreaudio",
672    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
673    .init           = coreaudio_audio_init,
674    .fini           = coreaudio_audio_fini,
675    .pcm_ops        = &coreaudio_pcm_ops,
676    .max_voices_out = 1,
677    .max_voices_in  = 0,
678    .voice_size_out = sizeof (coreaudioVoiceOut),
679    .voice_size_in  = 0
680};
681
682static void register_audio_coreaudio(void)
683{
684    audio_driver_register(&coreaudio_audio_driver);
685}
686type_init(register_audio_coreaudio);
687