xref: /openbmc/qemu/audio/coreaudio.m (revision ff6d8490e33acf44ed8afd549e203a42d6f813b5)
18b46d7e2SPhilippe Mathieu-Daudé/*
28b46d7e2SPhilippe Mathieu-Daudé * QEMU OS X CoreAudio audio driver
38b46d7e2SPhilippe Mathieu-Daudé *
48b46d7e2SPhilippe Mathieu-Daudé * Copyright (c) 2005 Mike Kronenberg
58b46d7e2SPhilippe Mathieu-Daudé *
68b46d7e2SPhilippe Mathieu-Daudé * Permission is hereby granted, free of charge, to any person obtaining a copy
78b46d7e2SPhilippe Mathieu-Daudé * of this software and associated documentation files (the "Software"), to deal
88b46d7e2SPhilippe Mathieu-Daudé * in the Software without restriction, including without limitation the rights
98b46d7e2SPhilippe Mathieu-Daudé * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
108b46d7e2SPhilippe Mathieu-Daudé * copies of the Software, and to permit persons to whom the Software is
118b46d7e2SPhilippe Mathieu-Daudé * furnished to do so, subject to the following conditions:
128b46d7e2SPhilippe Mathieu-Daudé *
138b46d7e2SPhilippe Mathieu-Daudé * The above copyright notice and this permission notice shall be included in
148b46d7e2SPhilippe Mathieu-Daudé * all copies or substantial portions of the Software.
158b46d7e2SPhilippe Mathieu-Daudé *
168b46d7e2SPhilippe Mathieu-Daudé * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178b46d7e2SPhilippe Mathieu-Daudé * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188b46d7e2SPhilippe Mathieu-Daudé * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
198b46d7e2SPhilippe Mathieu-Daudé * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
208b46d7e2SPhilippe Mathieu-Daudé * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
218b46d7e2SPhilippe Mathieu-Daudé * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
228b46d7e2SPhilippe Mathieu-Daudé * THE SOFTWARE.
238b46d7e2SPhilippe Mathieu-Daudé */
248b46d7e2SPhilippe Mathieu-Daudé
258b46d7e2SPhilippe Mathieu-Daudé#include "qemu/osdep.h"
268b46d7e2SPhilippe Mathieu-Daudé#include <CoreAudio/CoreAudio.h>
278b46d7e2SPhilippe Mathieu-Daudé#include <pthread.h>            /* pthread_X */
288b46d7e2SPhilippe Mathieu-Daudé
298b46d7e2SPhilippe Mathieu-Daudé#include "qemu/main-loop.h"
308b46d7e2SPhilippe Mathieu-Daudé#include "qemu/module.h"
318b46d7e2SPhilippe Mathieu-Daudé#include "audio.h"
328b46d7e2SPhilippe Mathieu-Daudé
338b46d7e2SPhilippe Mathieu-Daudé#define AUDIO_CAP "coreaudio"
348b46d7e2SPhilippe Mathieu-Daudé#include "audio_int.h"
358b46d7e2SPhilippe Mathieu-Daudé
368b46d7e2SPhilippe Mathieu-Daudétypedef struct coreaudioVoiceOut {
378b46d7e2SPhilippe Mathieu-Daudé    HWVoiceOut hw;
388b46d7e2SPhilippe Mathieu-Daudé    pthread_mutex_t buf_mutex;
398b46d7e2SPhilippe Mathieu-Daudé    AudioDeviceID outputDeviceID;
408b46d7e2SPhilippe Mathieu-Daudé    int frameSizeSetting;
418b46d7e2SPhilippe Mathieu-Daudé    uint32_t bufferCount;
428b46d7e2SPhilippe Mathieu-Daudé    UInt32 audioDevicePropertyBufferFrameSize;
438b46d7e2SPhilippe Mathieu-Daudé    AudioDeviceIOProcID ioprocid;
448b46d7e2SPhilippe Mathieu-Daudé    bool enabled;
458b46d7e2SPhilippe Mathieu-Daudé} coreaudioVoiceOut;
468b46d7e2SPhilippe Mathieu-Daudé
478b46d7e2SPhilippe Mathieu-Daudéstatic const AudioObjectPropertyAddress voice_addr = {
488b46d7e2SPhilippe Mathieu-Daudé    kAudioHardwarePropertyDefaultOutputDevice,
498b46d7e2SPhilippe Mathieu-Daudé    kAudioObjectPropertyScopeGlobal,
508b46d7e2SPhilippe Mathieu-Daudé    kAudioObjectPropertyElementMain
518b46d7e2SPhilippe Mathieu-Daudé};
528b46d7e2SPhilippe Mathieu-Daudé
538b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus coreaudio_get_voice(AudioDeviceID *id)
548b46d7e2SPhilippe Mathieu-Daudé{
558b46d7e2SPhilippe Mathieu-Daudé    UInt32 size = sizeof(*id);
568b46d7e2SPhilippe Mathieu-Daudé
578b46d7e2SPhilippe Mathieu-Daudé    return AudioObjectGetPropertyData(kAudioObjectSystemObject,
588b46d7e2SPhilippe Mathieu-Daudé                                      &voice_addr,
598b46d7e2SPhilippe Mathieu-Daudé                                      0,
608b46d7e2SPhilippe Mathieu-Daudé                                      NULL,
618b46d7e2SPhilippe Mathieu-Daudé                                      &size,
628b46d7e2SPhilippe Mathieu-Daudé                                      id);
638b46d7e2SPhilippe Mathieu-Daudé}
648b46d7e2SPhilippe Mathieu-Daudé
658b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
668b46d7e2SPhilippe Mathieu-Daudé                                             AudioValueRange *framerange)
678b46d7e2SPhilippe Mathieu-Daudé{
688b46d7e2SPhilippe Mathieu-Daudé    UInt32 size = sizeof(*framerange);
698b46d7e2SPhilippe Mathieu-Daudé    AudioObjectPropertyAddress addr = {
708b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyBufferFrameSizeRange,
718b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyScopeOutput,
728b46d7e2SPhilippe Mathieu-Daudé        kAudioObjectPropertyElementMain
738b46d7e2SPhilippe Mathieu-Daudé    };
748b46d7e2SPhilippe Mathieu-Daudé
758b46d7e2SPhilippe Mathieu-Daudé    return AudioObjectGetPropertyData(id,
768b46d7e2SPhilippe Mathieu-Daudé                                      &addr,
778b46d7e2SPhilippe Mathieu-Daudé                                      0,
788b46d7e2SPhilippe Mathieu-Daudé                                      NULL,
798b46d7e2SPhilippe Mathieu-Daudé                                      &size,
808b46d7e2SPhilippe Mathieu-Daudé                                      framerange);
818b46d7e2SPhilippe Mathieu-Daudé}
828b46d7e2SPhilippe Mathieu-Daudé
838b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
848b46d7e2SPhilippe Mathieu-Daudé{
858b46d7e2SPhilippe Mathieu-Daudé    UInt32 size = sizeof(*framesize);
868b46d7e2SPhilippe Mathieu-Daudé    AudioObjectPropertyAddress addr = {
878b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyBufferFrameSize,
888b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyScopeOutput,
898b46d7e2SPhilippe Mathieu-Daudé        kAudioObjectPropertyElementMain
908b46d7e2SPhilippe Mathieu-Daudé    };
918b46d7e2SPhilippe Mathieu-Daudé
928b46d7e2SPhilippe Mathieu-Daudé    return AudioObjectGetPropertyData(id,
938b46d7e2SPhilippe Mathieu-Daudé                                      &addr,
948b46d7e2SPhilippe Mathieu-Daudé                                      0,
958b46d7e2SPhilippe Mathieu-Daudé                                      NULL,
968b46d7e2SPhilippe Mathieu-Daudé                                      &size,
978b46d7e2SPhilippe Mathieu-Daudé                                      framesize);
988b46d7e2SPhilippe Mathieu-Daudé}
998b46d7e2SPhilippe Mathieu-Daudé
1008b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
1018b46d7e2SPhilippe Mathieu-Daudé{
1028b46d7e2SPhilippe Mathieu-Daudé    UInt32 size = sizeof(*framesize);
1038b46d7e2SPhilippe Mathieu-Daudé    AudioObjectPropertyAddress addr = {
1048b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyBufferFrameSize,
1058b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyScopeOutput,
1068b46d7e2SPhilippe Mathieu-Daudé        kAudioObjectPropertyElementMain
1078b46d7e2SPhilippe Mathieu-Daudé    };
1088b46d7e2SPhilippe Mathieu-Daudé
1098b46d7e2SPhilippe Mathieu-Daudé    return AudioObjectSetPropertyData(id,
1108b46d7e2SPhilippe Mathieu-Daudé                                      &addr,
1118b46d7e2SPhilippe Mathieu-Daudé                                      0,
1128b46d7e2SPhilippe Mathieu-Daudé                                      NULL,
1138b46d7e2SPhilippe Mathieu-Daudé                                      size,
1148b46d7e2SPhilippe Mathieu-Daudé                                      framesize);
1158b46d7e2SPhilippe Mathieu-Daudé}
1168b46d7e2SPhilippe Mathieu-Daudé
1178b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus coreaudio_set_streamformat(AudioDeviceID id,
1188b46d7e2SPhilippe Mathieu-Daudé                                           AudioStreamBasicDescription *d)
1198b46d7e2SPhilippe Mathieu-Daudé{
1208b46d7e2SPhilippe Mathieu-Daudé    UInt32 size = sizeof(*d);
1218b46d7e2SPhilippe Mathieu-Daudé    AudioObjectPropertyAddress addr = {
1228b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyStreamFormat,
1238b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyScopeOutput,
1248b46d7e2SPhilippe Mathieu-Daudé        kAudioObjectPropertyElementMain
1258b46d7e2SPhilippe Mathieu-Daudé    };
1268b46d7e2SPhilippe Mathieu-Daudé
1278b46d7e2SPhilippe Mathieu-Daudé    return AudioObjectSetPropertyData(id,
1288b46d7e2SPhilippe Mathieu-Daudé                                      &addr,
1298b46d7e2SPhilippe Mathieu-Daudé                                      0,
1308b46d7e2SPhilippe Mathieu-Daudé                                      NULL,
1318b46d7e2SPhilippe Mathieu-Daudé                                      size,
1328b46d7e2SPhilippe Mathieu-Daudé                                      d);
1338b46d7e2SPhilippe Mathieu-Daudé}
1348b46d7e2SPhilippe Mathieu-Daudé
1358b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
1368b46d7e2SPhilippe Mathieu-Daudé{
1378b46d7e2SPhilippe Mathieu-Daudé    UInt32 size = sizeof(*result);
1388b46d7e2SPhilippe Mathieu-Daudé    AudioObjectPropertyAddress addr = {
1398b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyDeviceIsRunning,
1408b46d7e2SPhilippe Mathieu-Daudé        kAudioDevicePropertyScopeOutput,
1418b46d7e2SPhilippe Mathieu-Daudé        kAudioObjectPropertyElementMain
1428b46d7e2SPhilippe Mathieu-Daudé    };
1438b46d7e2SPhilippe Mathieu-Daudé
1448b46d7e2SPhilippe Mathieu-Daudé    return AudioObjectGetPropertyData(id,
1458b46d7e2SPhilippe Mathieu-Daudé                                      &addr,
1468b46d7e2SPhilippe Mathieu-Daudé                                      0,
1478b46d7e2SPhilippe Mathieu-Daudé                                      NULL,
1488b46d7e2SPhilippe Mathieu-Daudé                                      &size,
1498b46d7e2SPhilippe Mathieu-Daudé                                      result);
1508b46d7e2SPhilippe Mathieu-Daudé}
1518b46d7e2SPhilippe Mathieu-Daudé
1528b46d7e2SPhilippe Mathieu-Daudéstatic void coreaudio_logstatus (OSStatus status)
1538b46d7e2SPhilippe Mathieu-Daudé{
1548b46d7e2SPhilippe Mathieu-Daudé    const char *str = "BUG";
1558b46d7e2SPhilippe Mathieu-Daudé
1568b46d7e2SPhilippe Mathieu-Daudé    switch (status) {
1578b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareNoError:
1588b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareNoError";
1598b46d7e2SPhilippe Mathieu-Daudé        break;
1608b46d7e2SPhilippe Mathieu-Daudé
1618b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareNotRunningError:
1628b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareNotRunningError";
1638b46d7e2SPhilippe Mathieu-Daudé        break;
1648b46d7e2SPhilippe Mathieu-Daudé
1658b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareUnspecifiedError:
1668b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareUnspecifiedError";
1678b46d7e2SPhilippe Mathieu-Daudé        break;
1688b46d7e2SPhilippe Mathieu-Daudé
1698b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareUnknownPropertyError:
1708b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareUnknownPropertyError";
1718b46d7e2SPhilippe Mathieu-Daudé        break;
1728b46d7e2SPhilippe Mathieu-Daudé
1738b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareBadPropertySizeError:
1748b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareBadPropertySizeError";
1758b46d7e2SPhilippe Mathieu-Daudé        break;
1768b46d7e2SPhilippe Mathieu-Daudé
1778b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareIllegalOperationError:
1788b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareIllegalOperationError";
1798b46d7e2SPhilippe Mathieu-Daudé        break;
1808b46d7e2SPhilippe Mathieu-Daudé
1818b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareBadDeviceError:
1828b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareBadDeviceError";
1838b46d7e2SPhilippe Mathieu-Daudé        break;
1848b46d7e2SPhilippe Mathieu-Daudé
1858b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareBadStreamError:
1868b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareBadStreamError";
1878b46d7e2SPhilippe Mathieu-Daudé        break;
1888b46d7e2SPhilippe Mathieu-Daudé
1898b46d7e2SPhilippe Mathieu-Daudé    case kAudioHardwareUnsupportedOperationError:
1908b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioHardwareUnsupportedOperationError";
1918b46d7e2SPhilippe Mathieu-Daudé        break;
1928b46d7e2SPhilippe Mathieu-Daudé
1938b46d7e2SPhilippe Mathieu-Daudé    case kAudioDeviceUnsupportedFormatError:
1948b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioDeviceUnsupportedFormatError";
1958b46d7e2SPhilippe Mathieu-Daudé        break;
1968b46d7e2SPhilippe Mathieu-Daudé
1978b46d7e2SPhilippe Mathieu-Daudé    case kAudioDevicePermissionsError:
1988b46d7e2SPhilippe Mathieu-Daudé        str = "kAudioDevicePermissionsError";
1998b46d7e2SPhilippe Mathieu-Daudé        break;
2008b46d7e2SPhilippe Mathieu-Daudé
2018b46d7e2SPhilippe Mathieu-Daudé    default:
2028b46d7e2SPhilippe Mathieu-Daudé        AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
2038b46d7e2SPhilippe Mathieu-Daudé        return;
2048b46d7e2SPhilippe Mathieu-Daudé    }
2058b46d7e2SPhilippe Mathieu-Daudé
2068b46d7e2SPhilippe Mathieu-Daudé    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
2078b46d7e2SPhilippe Mathieu-Daudé}
2088b46d7e2SPhilippe Mathieu-Daudé
2099edc6313SMarc-André Lureaustatic void G_GNUC_PRINTF (2, 3) coreaudio_logerr (
2108b46d7e2SPhilippe Mathieu-Daudé    OSStatus status,
2118b46d7e2SPhilippe Mathieu-Daudé    const char *fmt,
2128b46d7e2SPhilippe Mathieu-Daudé    ...
2138b46d7e2SPhilippe Mathieu-Daudé    )
2148b46d7e2SPhilippe Mathieu-Daudé{
2158b46d7e2SPhilippe Mathieu-Daudé    va_list ap;
2168b46d7e2SPhilippe Mathieu-Daudé
2178b46d7e2SPhilippe Mathieu-Daudé    va_start (ap, fmt);
2188b46d7e2SPhilippe Mathieu-Daudé    AUD_log (AUDIO_CAP, fmt, ap);
2198b46d7e2SPhilippe Mathieu-Daudé    va_end (ap);
2208b46d7e2SPhilippe Mathieu-Daudé
2218b46d7e2SPhilippe Mathieu-Daudé    coreaudio_logstatus (status);
2228b46d7e2SPhilippe Mathieu-Daudé}
2238b46d7e2SPhilippe Mathieu-Daudé
2249edc6313SMarc-André Lureaustatic void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 (
2258b46d7e2SPhilippe Mathieu-Daudé    OSStatus status,
2268b46d7e2SPhilippe Mathieu-Daudé    const char *typ,
2278b46d7e2SPhilippe Mathieu-Daudé    const char *fmt,
2288b46d7e2SPhilippe Mathieu-Daudé    ...
2298b46d7e2SPhilippe Mathieu-Daudé    )
2308b46d7e2SPhilippe Mathieu-Daudé{
2318b46d7e2SPhilippe Mathieu-Daudé    va_list ap;
2328b46d7e2SPhilippe Mathieu-Daudé
2338b46d7e2SPhilippe Mathieu-Daudé    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
2348b46d7e2SPhilippe Mathieu-Daudé
2358b46d7e2SPhilippe Mathieu-Daudé    va_start (ap, fmt);
2368b46d7e2SPhilippe Mathieu-Daudé    AUD_vlog (AUDIO_CAP, fmt, ap);
2378b46d7e2SPhilippe Mathieu-Daudé    va_end (ap);
2388b46d7e2SPhilippe Mathieu-Daudé
2398b46d7e2SPhilippe Mathieu-Daudé    coreaudio_logstatus (status);
2408b46d7e2SPhilippe Mathieu-Daudé}
2418b46d7e2SPhilippe Mathieu-Daudé
2428b46d7e2SPhilippe Mathieu-Daudé#define coreaudio_playback_logerr(status, ...) \
2438b46d7e2SPhilippe Mathieu-Daudé    coreaudio_logerr2(status, "playback", __VA_ARGS__)
2448b46d7e2SPhilippe Mathieu-Daudé
2458b46d7e2SPhilippe Mathieu-Daudéstatic int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name)
2468b46d7e2SPhilippe Mathieu-Daudé{
2478b46d7e2SPhilippe Mathieu-Daudé    int err;
2488b46d7e2SPhilippe Mathieu-Daudé
2498b46d7e2SPhilippe Mathieu-Daudé    err = pthread_mutex_lock (&core->buf_mutex);
2508b46d7e2SPhilippe Mathieu-Daudé    if (err) {
2518b46d7e2SPhilippe Mathieu-Daudé        dolog ("Could not lock voice for %s\nReason: %s\n",
2528b46d7e2SPhilippe Mathieu-Daudé               fn_name, strerror (err));
2538b46d7e2SPhilippe Mathieu-Daudé        return -1;
2548b46d7e2SPhilippe Mathieu-Daudé    }
2558b46d7e2SPhilippe Mathieu-Daudé    return 0;
2568b46d7e2SPhilippe Mathieu-Daudé}
2578b46d7e2SPhilippe Mathieu-Daudé
2588b46d7e2SPhilippe Mathieu-Daudéstatic int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name)
2598b46d7e2SPhilippe Mathieu-Daudé{
2608b46d7e2SPhilippe Mathieu-Daudé    int err;
2618b46d7e2SPhilippe Mathieu-Daudé
2628b46d7e2SPhilippe Mathieu-Daudé    err = pthread_mutex_unlock (&core->buf_mutex);
2638b46d7e2SPhilippe Mathieu-Daudé    if (err) {
2648b46d7e2SPhilippe Mathieu-Daudé        dolog ("Could not unlock voice for %s\nReason: %s\n",
2658b46d7e2SPhilippe Mathieu-Daudé               fn_name, strerror (err));
2668b46d7e2SPhilippe Mathieu-Daudé        return -1;
2678b46d7e2SPhilippe Mathieu-Daudé    }
2688b46d7e2SPhilippe Mathieu-Daudé    return 0;
2698b46d7e2SPhilippe Mathieu-Daudé}
2708b46d7e2SPhilippe Mathieu-Daudé
2718b46d7e2SPhilippe Mathieu-Daudé#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
2728b46d7e2SPhilippe Mathieu-Daudé    static ret_type glue(coreaudio_, name)args_decl             \
2738b46d7e2SPhilippe Mathieu-Daudé    {                                                           \
2748b46d7e2SPhilippe Mathieu-Daudé        coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;     \
2758b46d7e2SPhilippe Mathieu-Daudé        ret_type ret;                                           \
2768b46d7e2SPhilippe Mathieu-Daudé                                                                \
2778b46d7e2SPhilippe Mathieu-Daudé        if (coreaudio_buf_lock(core, "coreaudio_" #name)) {         \
2788b46d7e2SPhilippe Mathieu-Daudé            return 0;                                           \
2798b46d7e2SPhilippe Mathieu-Daudé        }                                                       \
2808b46d7e2SPhilippe Mathieu-Daudé                                                                \
2818b46d7e2SPhilippe Mathieu-Daudé        ret = glue(audio_generic_, name)args;                   \
2828b46d7e2SPhilippe Mathieu-Daudé                                                                \
2838b46d7e2SPhilippe Mathieu-Daudé        coreaudio_buf_unlock(core, "coreaudio_" #name);             \
2848b46d7e2SPhilippe Mathieu-Daudé        return ret;                                             \
2858b46d7e2SPhilippe Mathieu-Daudé    }
2868b46d7e2SPhilippe Mathieu-DaudéCOREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw))
2878b46d7e2SPhilippe Mathieu-DaudéCOREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
2888b46d7e2SPhilippe Mathieu-Daudé                       (hw, size))
2898b46d7e2SPhilippe Mathieu-DaudéCOREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
2908b46d7e2SPhilippe Mathieu-Daudé                       (HWVoiceOut *hw, void *buf, size_t size),
2918b46d7e2SPhilippe Mathieu-Daudé                       (hw, buf, size))
2928b46d7e2SPhilippe Mathieu-DaudéCOREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
2938b46d7e2SPhilippe Mathieu-Daudé                       (hw, buf, size))
2948b46d7e2SPhilippe Mathieu-Daudé#undef COREAUDIO_WRAPPER_FUNC
2958b46d7e2SPhilippe Mathieu-Daudé
2968b46d7e2SPhilippe Mathieu-Daudé/*
297*a4a411fbSStefan Hajnoczi * callback to feed audiooutput buffer. called without BQL.
2988b46d7e2SPhilippe Mathieu-Daudé * allowed to lock "buf_mutex", but disallowed to have any other locks.
2998b46d7e2SPhilippe Mathieu-Daudé */
3008b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus audioDeviceIOProc(
3018b46d7e2SPhilippe Mathieu-Daudé    AudioDeviceID inDevice,
3028b46d7e2SPhilippe Mathieu-Daudé    const AudioTimeStamp *inNow,
3038b46d7e2SPhilippe Mathieu-Daudé    const AudioBufferList *inInputData,
3048b46d7e2SPhilippe Mathieu-Daudé    const AudioTimeStamp *inInputTime,
3058b46d7e2SPhilippe Mathieu-Daudé    AudioBufferList *outOutputData,
3068b46d7e2SPhilippe Mathieu-Daudé    const AudioTimeStamp *inOutputTime,
3078b46d7e2SPhilippe Mathieu-Daudé    void *hwptr)
3088b46d7e2SPhilippe Mathieu-Daudé{
3098b46d7e2SPhilippe Mathieu-Daudé    UInt32 frameCount, pending_frames;
3108b46d7e2SPhilippe Mathieu-Daudé    void *out = outOutputData->mBuffers[0].mData;
3118b46d7e2SPhilippe Mathieu-Daudé    HWVoiceOut *hw = hwptr;
3128b46d7e2SPhilippe Mathieu-Daudé    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
3138b46d7e2SPhilippe Mathieu-Daudé    size_t len;
3148b46d7e2SPhilippe Mathieu-Daudé
3158b46d7e2SPhilippe Mathieu-Daudé    if (coreaudio_buf_lock (core, "audioDeviceIOProc")) {
3168b46d7e2SPhilippe Mathieu-Daudé        inInputTime = 0;
3178b46d7e2SPhilippe Mathieu-Daudé        return 0;
3188b46d7e2SPhilippe Mathieu-Daudé    }
3198b46d7e2SPhilippe Mathieu-Daudé
3208b46d7e2SPhilippe Mathieu-Daudé    if (inDevice != core->outputDeviceID) {
3218b46d7e2SPhilippe Mathieu-Daudé        coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)");
3228b46d7e2SPhilippe Mathieu-Daudé        return 0;
3238b46d7e2SPhilippe Mathieu-Daudé    }
3248b46d7e2SPhilippe Mathieu-Daudé
3258b46d7e2SPhilippe Mathieu-Daudé    frameCount = core->audioDevicePropertyBufferFrameSize;
3268b46d7e2SPhilippe Mathieu-Daudé    pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
3278b46d7e2SPhilippe Mathieu-Daudé
3288b46d7e2SPhilippe Mathieu-Daudé    /* if there are not enough samples, set signal and return */
3298b46d7e2SPhilippe Mathieu-Daudé    if (pending_frames < frameCount) {
3308b46d7e2SPhilippe Mathieu-Daudé        inInputTime = 0;
3318b46d7e2SPhilippe Mathieu-Daudé        coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)");
3328b46d7e2SPhilippe Mathieu-Daudé        return 0;
3338b46d7e2SPhilippe Mathieu-Daudé    }
3348b46d7e2SPhilippe Mathieu-Daudé
3358b46d7e2SPhilippe Mathieu-Daudé    len = frameCount * hw->info.bytes_per_frame;
3368b46d7e2SPhilippe Mathieu-Daudé    while (len) {
3378b46d7e2SPhilippe Mathieu-Daudé        size_t write_len, start;
3388b46d7e2SPhilippe Mathieu-Daudé
3398b46d7e2SPhilippe Mathieu-Daudé        start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
3408b46d7e2SPhilippe Mathieu-Daudé        assert(start < hw->size_emul);
3418b46d7e2SPhilippe Mathieu-Daudé
3428b46d7e2SPhilippe Mathieu-Daudé        write_len = MIN(MIN(hw->pending_emul, len),
3438b46d7e2SPhilippe Mathieu-Daudé                        hw->size_emul - start);
3448b46d7e2SPhilippe Mathieu-Daudé
3458b46d7e2SPhilippe Mathieu-Daudé        memcpy(out, hw->buf_emul + start, write_len);
3468b46d7e2SPhilippe Mathieu-Daudé        hw->pending_emul -= write_len;
3478b46d7e2SPhilippe Mathieu-Daudé        len -= write_len;
3488b46d7e2SPhilippe Mathieu-Daudé        out += write_len;
3498b46d7e2SPhilippe Mathieu-Daudé    }
3508b46d7e2SPhilippe Mathieu-Daudé
3518b46d7e2SPhilippe Mathieu-Daudé    coreaudio_buf_unlock (core, "audioDeviceIOProc");
3528b46d7e2SPhilippe Mathieu-Daudé    return 0;
3538b46d7e2SPhilippe Mathieu-Daudé}
3548b46d7e2SPhilippe Mathieu-Daudé
3558b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus init_out_device(coreaudioVoiceOut *core)
3568b46d7e2SPhilippe Mathieu-Daudé{
3578b46d7e2SPhilippe Mathieu-Daudé    OSStatus status;
3588b46d7e2SPhilippe Mathieu-Daudé    AudioValueRange frameRange;
3598b46d7e2SPhilippe Mathieu-Daudé
3608b46d7e2SPhilippe Mathieu-Daudé    AudioStreamBasicDescription streamBasicDescription = {
3618b46d7e2SPhilippe Mathieu-Daudé        .mBitsPerChannel = core->hw.info.bits,
3628b46d7e2SPhilippe Mathieu-Daudé        .mBytesPerFrame = core->hw.info.bytes_per_frame,
3638b46d7e2SPhilippe Mathieu-Daudé        .mBytesPerPacket = core->hw.info.bytes_per_frame,
3648b46d7e2SPhilippe Mathieu-Daudé        .mChannelsPerFrame = core->hw.info.nchannels,
3658b46d7e2SPhilippe Mathieu-Daudé        .mFormatFlags = kLinearPCMFormatFlagIsFloat,
3668b46d7e2SPhilippe Mathieu-Daudé        .mFormatID = kAudioFormatLinearPCM,
3678b46d7e2SPhilippe Mathieu-Daudé        .mFramesPerPacket = 1,
3688b46d7e2SPhilippe Mathieu-Daudé        .mSampleRate = core->hw.info.freq
3698b46d7e2SPhilippe Mathieu-Daudé    };
3708b46d7e2SPhilippe Mathieu-Daudé
3718b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_get_voice(&core->outputDeviceID);
3728b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
3738b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status,
3748b46d7e2SPhilippe Mathieu-Daudé                                   "Could not get default output Device\n");
3758b46d7e2SPhilippe Mathieu-Daudé        return status;
3768b46d7e2SPhilippe Mathieu-Daudé    }
3778b46d7e2SPhilippe Mathieu-Daudé    if (core->outputDeviceID == kAudioDeviceUnknown) {
3788b46d7e2SPhilippe Mathieu-Daudé        dolog ("Could not initialize playback - Unknown Audiodevice\n");
3798b46d7e2SPhilippe Mathieu-Daudé        return status;
3808b46d7e2SPhilippe Mathieu-Daudé    }
3818b46d7e2SPhilippe Mathieu-Daudé
3828b46d7e2SPhilippe Mathieu-Daudé    /* get minimum and maximum buffer frame sizes */
3838b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_get_framesizerange(core->outputDeviceID,
3848b46d7e2SPhilippe Mathieu-Daudé                                          &frameRange);
3858b46d7e2SPhilippe Mathieu-Daudé    if (status == kAudioHardwareBadObjectError) {
3868b46d7e2SPhilippe Mathieu-Daudé        return 0;
3878b46d7e2SPhilippe Mathieu-Daudé    }
3888b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
3898b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status,
3908b46d7e2SPhilippe Mathieu-Daudé                                    "Could not get device buffer frame range\n");
3918b46d7e2SPhilippe Mathieu-Daudé        return status;
3928b46d7e2SPhilippe Mathieu-Daudé    }
3938b46d7e2SPhilippe Mathieu-Daudé
3948b46d7e2SPhilippe Mathieu-Daudé    if (frameRange.mMinimum > core->frameSizeSetting) {
3958b46d7e2SPhilippe Mathieu-Daudé        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
3968b46d7e2SPhilippe Mathieu-Daudé        dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
3978b46d7e2SPhilippe Mathieu-Daudé    } else if (frameRange.mMaximum < core->frameSizeSetting) {
3988b46d7e2SPhilippe Mathieu-Daudé        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
3998b46d7e2SPhilippe Mathieu-Daudé        dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
4008b46d7e2SPhilippe Mathieu-Daudé    } else {
4018b46d7e2SPhilippe Mathieu-Daudé        core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting;
4028b46d7e2SPhilippe Mathieu-Daudé    }
4038b46d7e2SPhilippe Mathieu-Daudé
4048b46d7e2SPhilippe Mathieu-Daudé    /* set Buffer Frame Size */
4058b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_set_framesize(core->outputDeviceID,
4068b46d7e2SPhilippe Mathieu-Daudé                                     &core->audioDevicePropertyBufferFrameSize);
4078b46d7e2SPhilippe Mathieu-Daudé    if (status == kAudioHardwareBadObjectError) {
4088b46d7e2SPhilippe Mathieu-Daudé        return 0;
4098b46d7e2SPhilippe Mathieu-Daudé    }
4108b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
4118b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status,
4128b46d7e2SPhilippe Mathieu-Daudé                                    "Could not set device buffer frame size %" PRIu32 "\n",
4138b46d7e2SPhilippe Mathieu-Daudé                                    (uint32_t)core->audioDevicePropertyBufferFrameSize);
4148b46d7e2SPhilippe Mathieu-Daudé        return status;
4158b46d7e2SPhilippe Mathieu-Daudé    }
4168b46d7e2SPhilippe Mathieu-Daudé
4178b46d7e2SPhilippe Mathieu-Daudé    /* get Buffer Frame Size */
4188b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_get_framesize(core->outputDeviceID,
4198b46d7e2SPhilippe Mathieu-Daudé                                     &core->audioDevicePropertyBufferFrameSize);
4208b46d7e2SPhilippe Mathieu-Daudé    if (status == kAudioHardwareBadObjectError) {
4218b46d7e2SPhilippe Mathieu-Daudé        return 0;
4228b46d7e2SPhilippe Mathieu-Daudé    }
4238b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
4248b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status,
4258b46d7e2SPhilippe Mathieu-Daudé                                    "Could not get device buffer frame size\n");
4268b46d7e2SPhilippe Mathieu-Daudé        return status;
4278b46d7e2SPhilippe Mathieu-Daudé    }
4288b46d7e2SPhilippe Mathieu-Daudé    core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize;
4298b46d7e2SPhilippe Mathieu-Daudé
4308b46d7e2SPhilippe Mathieu-Daudé    /* set Samplerate */
4318b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_set_streamformat(core->outputDeviceID,
4328b46d7e2SPhilippe Mathieu-Daudé                                        &streamBasicDescription);
4338b46d7e2SPhilippe Mathieu-Daudé    if (status == kAudioHardwareBadObjectError) {
4348b46d7e2SPhilippe Mathieu-Daudé        return 0;
4358b46d7e2SPhilippe Mathieu-Daudé    }
4368b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
4378b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status,
4388b46d7e2SPhilippe Mathieu-Daudé                                   "Could not set samplerate %lf\n",
4398b46d7e2SPhilippe Mathieu-Daudé                                   streamBasicDescription.mSampleRate);
4408b46d7e2SPhilippe Mathieu-Daudé        core->outputDeviceID = kAudioDeviceUnknown;
4418b46d7e2SPhilippe Mathieu-Daudé        return status;
4428b46d7e2SPhilippe Mathieu-Daudé    }
4438b46d7e2SPhilippe Mathieu-Daudé
4448b46d7e2SPhilippe Mathieu-Daudé    /*
4458b46d7e2SPhilippe Mathieu-Daudé     * set Callback.
4468b46d7e2SPhilippe Mathieu-Daudé     *
4478b46d7e2SPhilippe Mathieu-Daudé     * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an
4488b46d7e2SPhilippe Mathieu-Daudé     * internal function named HALB_Mutex::Lock(), which locks a mutex in
4498b46d7e2SPhilippe Mathieu-Daudé     * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in
4508b46d7e2SPhilippe Mathieu-Daudé     * AudioObjectGetPropertyData, which is called by coreaudio driver.
4518b46d7e2SPhilippe Mathieu-Daudé     * Therefore, the specified callback must be designed to avoid a deadlock
4528b46d7e2SPhilippe Mathieu-Daudé     * with the callers of AudioObjectGetPropertyData.
4538b46d7e2SPhilippe Mathieu-Daudé     */
4548b46d7e2SPhilippe Mathieu-Daudé    core->ioprocid = NULL;
4558b46d7e2SPhilippe Mathieu-Daudé    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
4568b46d7e2SPhilippe Mathieu-Daudé                                       audioDeviceIOProc,
4578b46d7e2SPhilippe Mathieu-Daudé                                       &core->hw,
4588b46d7e2SPhilippe Mathieu-Daudé                                       &core->ioprocid);
4598b46d7e2SPhilippe Mathieu-Daudé    if (status == kAudioHardwareBadDeviceError) {
4608b46d7e2SPhilippe Mathieu-Daudé        return 0;
4618b46d7e2SPhilippe Mathieu-Daudé    }
4628b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
4638b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status, "Could not set IOProc\n");
4648b46d7e2SPhilippe Mathieu-Daudé        core->outputDeviceID = kAudioDeviceUnknown;
4658b46d7e2SPhilippe Mathieu-Daudé        return status;
4668b46d7e2SPhilippe Mathieu-Daudé    }
4678b46d7e2SPhilippe Mathieu-Daudé
4688b46d7e2SPhilippe Mathieu-Daudé    return 0;
4698b46d7e2SPhilippe Mathieu-Daudé}
4708b46d7e2SPhilippe Mathieu-Daudé
4718b46d7e2SPhilippe Mathieu-Daudéstatic void fini_out_device(coreaudioVoiceOut *core)
4728b46d7e2SPhilippe Mathieu-Daudé{
4738b46d7e2SPhilippe Mathieu-Daudé    OSStatus status;
4748b46d7e2SPhilippe Mathieu-Daudé    UInt32 isrunning;
4758b46d7e2SPhilippe Mathieu-Daudé
4768b46d7e2SPhilippe Mathieu-Daudé    /* stop playback */
4778b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
4788b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareBadObjectError) {
4798b46d7e2SPhilippe Mathieu-Daudé        if (status != kAudioHardwareNoError) {
4808b46d7e2SPhilippe Mathieu-Daudé            coreaudio_logerr(status,
4818b46d7e2SPhilippe Mathieu-Daudé                             "Could not determine whether Device is playing\n");
4828b46d7e2SPhilippe Mathieu-Daudé        }
4838b46d7e2SPhilippe Mathieu-Daudé
4848b46d7e2SPhilippe Mathieu-Daudé        if (isrunning) {
4858b46d7e2SPhilippe Mathieu-Daudé            status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
4868b46d7e2SPhilippe Mathieu-Daudé            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
4878b46d7e2SPhilippe Mathieu-Daudé                coreaudio_logerr(status, "Could not stop playback\n");
4888b46d7e2SPhilippe Mathieu-Daudé            }
4898b46d7e2SPhilippe Mathieu-Daudé        }
4908b46d7e2SPhilippe Mathieu-Daudé    }
4918b46d7e2SPhilippe Mathieu-Daudé
4928b46d7e2SPhilippe Mathieu-Daudé    /* remove callback */
4938b46d7e2SPhilippe Mathieu-Daudé    status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
4948b46d7e2SPhilippe Mathieu-Daudé                                        core->ioprocid);
4958b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
4968b46d7e2SPhilippe Mathieu-Daudé        coreaudio_logerr(status, "Could not remove IOProc\n");
4978b46d7e2SPhilippe Mathieu-Daudé    }
4988b46d7e2SPhilippe Mathieu-Daudé    core->outputDeviceID = kAudioDeviceUnknown;
4998b46d7e2SPhilippe Mathieu-Daudé}
5008b46d7e2SPhilippe Mathieu-Daudé
5018b46d7e2SPhilippe Mathieu-Daudéstatic void update_device_playback_state(coreaudioVoiceOut *core)
5028b46d7e2SPhilippe Mathieu-Daudé{
5038b46d7e2SPhilippe Mathieu-Daudé    OSStatus status;
5048b46d7e2SPhilippe Mathieu-Daudé    UInt32 isrunning;
5058b46d7e2SPhilippe Mathieu-Daudé
5068b46d7e2SPhilippe Mathieu-Daudé    status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning);
5078b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
5088b46d7e2SPhilippe Mathieu-Daudé        if (status != kAudioHardwareBadObjectError) {
5098b46d7e2SPhilippe Mathieu-Daudé            coreaudio_logerr(status,
5108b46d7e2SPhilippe Mathieu-Daudé                             "Could not determine whether Device is playing\n");
5118b46d7e2SPhilippe Mathieu-Daudé        }
5128b46d7e2SPhilippe Mathieu-Daudé
5138b46d7e2SPhilippe Mathieu-Daudé        return;
5148b46d7e2SPhilippe Mathieu-Daudé    }
5158b46d7e2SPhilippe Mathieu-Daudé
5168b46d7e2SPhilippe Mathieu-Daudé    if (core->enabled) {
5178b46d7e2SPhilippe Mathieu-Daudé        /* start playback */
5188b46d7e2SPhilippe Mathieu-Daudé        if (!isrunning) {
5198b46d7e2SPhilippe Mathieu-Daudé            status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
5208b46d7e2SPhilippe Mathieu-Daudé            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
5218b46d7e2SPhilippe Mathieu-Daudé                coreaudio_logerr (status, "Could not resume playback\n");
5228b46d7e2SPhilippe Mathieu-Daudé            }
5238b46d7e2SPhilippe Mathieu-Daudé        }
5248b46d7e2SPhilippe Mathieu-Daudé    } else {
5258b46d7e2SPhilippe Mathieu-Daudé        /* stop playback */
5268b46d7e2SPhilippe Mathieu-Daudé        if (isrunning) {
5278b46d7e2SPhilippe Mathieu-Daudé            status = AudioDeviceStop(core->outputDeviceID,
5288b46d7e2SPhilippe Mathieu-Daudé                                     core->ioprocid);
5298b46d7e2SPhilippe Mathieu-Daudé            if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) {
5308b46d7e2SPhilippe Mathieu-Daudé                coreaudio_logerr(status, "Could not pause playback\n");
5318b46d7e2SPhilippe Mathieu-Daudé            }
5328b46d7e2SPhilippe Mathieu-Daudé        }
5338b46d7e2SPhilippe Mathieu-Daudé    }
5348b46d7e2SPhilippe Mathieu-Daudé}
5358b46d7e2SPhilippe Mathieu-Daudé
536*a4a411fbSStefan Hajnoczi/* called without BQL. */
5378b46d7e2SPhilippe Mathieu-Daudéstatic OSStatus handle_voice_change(
5388b46d7e2SPhilippe Mathieu-Daudé    AudioObjectID in_object_id,
5398b46d7e2SPhilippe Mathieu-Daudé    UInt32 in_number_addresses,
5408b46d7e2SPhilippe Mathieu-Daudé    const AudioObjectPropertyAddress *in_addresses,
5418b46d7e2SPhilippe Mathieu-Daudé    void *in_client_data)
5428b46d7e2SPhilippe Mathieu-Daudé{
5438b46d7e2SPhilippe Mathieu-Daudé    coreaudioVoiceOut *core = in_client_data;
5448b46d7e2SPhilippe Mathieu-Daudé
545195801d7SStefan Hajnoczi    bql_lock();
5468b46d7e2SPhilippe Mathieu-Daudé
5478b46d7e2SPhilippe Mathieu-Daudé    if (core->outputDeviceID) {
5488b46d7e2SPhilippe Mathieu-Daudé        fini_out_device(core);
5498b46d7e2SPhilippe Mathieu-Daudé    }
5508b46d7e2SPhilippe Mathieu-Daudé
5518b46d7e2SPhilippe Mathieu-Daudé    if (!init_out_device(core)) {
5528b46d7e2SPhilippe Mathieu-Daudé        update_device_playback_state(core);
5538b46d7e2SPhilippe Mathieu-Daudé    }
5548b46d7e2SPhilippe Mathieu-Daudé
555195801d7SStefan Hajnoczi    bql_unlock();
5568b46d7e2SPhilippe Mathieu-Daudé    return 0;
5578b46d7e2SPhilippe Mathieu-Daudé}
5588b46d7e2SPhilippe Mathieu-Daudé
5598b46d7e2SPhilippe Mathieu-Daudéstatic int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
5608b46d7e2SPhilippe Mathieu-Daudé                              void *drv_opaque)
5618b46d7e2SPhilippe Mathieu-Daudé{
5628b46d7e2SPhilippe Mathieu-Daudé    OSStatus status;
5638b46d7e2SPhilippe Mathieu-Daudé    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
5648b46d7e2SPhilippe Mathieu-Daudé    int err;
5658b46d7e2SPhilippe Mathieu-Daudé    Audiodev *dev = drv_opaque;
5668b46d7e2SPhilippe Mathieu-Daudé    AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
5678b46d7e2SPhilippe Mathieu-Daudé    struct audsettings obt_as;
5688b46d7e2SPhilippe Mathieu-Daudé
5698b46d7e2SPhilippe Mathieu-Daudé    /* create mutex */
5708b46d7e2SPhilippe Mathieu-Daudé    err = pthread_mutex_init(&core->buf_mutex, NULL);
5718b46d7e2SPhilippe Mathieu-Daudé    if (err) {
5728b46d7e2SPhilippe Mathieu-Daudé        dolog("Could not create mutex\nReason: %s\n", strerror (err));
5738b46d7e2SPhilippe Mathieu-Daudé        return -1;
5748b46d7e2SPhilippe Mathieu-Daudé    }
5758b46d7e2SPhilippe Mathieu-Daudé
5768b46d7e2SPhilippe Mathieu-Daudé    obt_as = *as;
5778b46d7e2SPhilippe Mathieu-Daudé    as = &obt_as;
5788b46d7e2SPhilippe Mathieu-Daudé    as->fmt = AUDIO_FORMAT_F32;
5798b46d7e2SPhilippe Mathieu-Daudé    audio_pcm_init_info (&hw->info, as);
5808b46d7e2SPhilippe Mathieu-Daudé
5818b46d7e2SPhilippe Mathieu-Daudé    core->frameSizeSetting = audio_buffer_frames(
5828b46d7e2SPhilippe Mathieu-Daudé        qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
5838b46d7e2SPhilippe Mathieu-Daudé
5848b46d7e2SPhilippe Mathieu-Daudé    core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4;
5858b46d7e2SPhilippe Mathieu-Daudé
5868b46d7e2SPhilippe Mathieu-Daudé    status = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
5878b46d7e2SPhilippe Mathieu-Daudé                                            &voice_addr, handle_voice_change,
5888b46d7e2SPhilippe Mathieu-Daudé                                            core);
5898b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
5908b46d7e2SPhilippe Mathieu-Daudé        coreaudio_playback_logerr (status,
5918b46d7e2SPhilippe Mathieu-Daudé                                   "Could not listen to voice property change\n");
5928b46d7e2SPhilippe Mathieu-Daudé        return -1;
5938b46d7e2SPhilippe Mathieu-Daudé    }
5948b46d7e2SPhilippe Mathieu-Daudé
5958b46d7e2SPhilippe Mathieu-Daudé    if (init_out_device(core)) {
5968b46d7e2SPhilippe Mathieu-Daudé        status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
5978b46d7e2SPhilippe Mathieu-Daudé                                                   &voice_addr,
5988b46d7e2SPhilippe Mathieu-Daudé                                                   handle_voice_change,
5998b46d7e2SPhilippe Mathieu-Daudé                                                   core);
6008b46d7e2SPhilippe Mathieu-Daudé        if (status != kAudioHardwareNoError) {
6018b46d7e2SPhilippe Mathieu-Daudé            coreaudio_playback_logerr(status,
6028b46d7e2SPhilippe Mathieu-Daudé                                      "Could not remove voice property change listener\n");
6038b46d7e2SPhilippe Mathieu-Daudé        }
6048b46d7e2SPhilippe Mathieu-Daudé
6058b46d7e2SPhilippe Mathieu-Daudé        return -1;
6068b46d7e2SPhilippe Mathieu-Daudé    }
6078b46d7e2SPhilippe Mathieu-Daudé
6088b46d7e2SPhilippe Mathieu-Daudé    return 0;
6098b46d7e2SPhilippe Mathieu-Daudé}
6108b46d7e2SPhilippe Mathieu-Daudé
6118b46d7e2SPhilippe Mathieu-Daudéstatic void coreaudio_fini_out (HWVoiceOut *hw)
6128b46d7e2SPhilippe Mathieu-Daudé{
6138b46d7e2SPhilippe Mathieu-Daudé    OSStatus status;
6148b46d7e2SPhilippe Mathieu-Daudé    int err;
6158b46d7e2SPhilippe Mathieu-Daudé    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
6168b46d7e2SPhilippe Mathieu-Daudé
6178b46d7e2SPhilippe Mathieu-Daudé    status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
6188b46d7e2SPhilippe Mathieu-Daudé                                               &voice_addr,
6198b46d7e2SPhilippe Mathieu-Daudé                                               handle_voice_change,
6208b46d7e2SPhilippe Mathieu-Daudé                                               core);
6218b46d7e2SPhilippe Mathieu-Daudé    if (status != kAudioHardwareNoError) {
6228b46d7e2SPhilippe Mathieu-Daudé        coreaudio_logerr(status, "Could not remove voice property change listener\n");
6238b46d7e2SPhilippe Mathieu-Daudé    }
6248b46d7e2SPhilippe Mathieu-Daudé
6258b46d7e2SPhilippe Mathieu-Daudé    fini_out_device(core);
6268b46d7e2SPhilippe Mathieu-Daudé
6278b46d7e2SPhilippe Mathieu-Daudé    /* destroy mutex */
6288b46d7e2SPhilippe Mathieu-Daudé    err = pthread_mutex_destroy(&core->buf_mutex);
6298b46d7e2SPhilippe Mathieu-Daudé    if (err) {
6308b46d7e2SPhilippe Mathieu-Daudé        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
6318b46d7e2SPhilippe Mathieu-Daudé    }
6328b46d7e2SPhilippe Mathieu-Daudé}
6338b46d7e2SPhilippe Mathieu-Daudé
6348b46d7e2SPhilippe Mathieu-Daudéstatic void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
6358b46d7e2SPhilippe Mathieu-Daudé{
6368b46d7e2SPhilippe Mathieu-Daudé    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
6378b46d7e2SPhilippe Mathieu-Daudé
6388b46d7e2SPhilippe Mathieu-Daudé    core->enabled = enable;
6398b46d7e2SPhilippe Mathieu-Daudé    update_device_playback_state(core);
6408b46d7e2SPhilippe Mathieu-Daudé}
6418b46d7e2SPhilippe Mathieu-Daudé
642f6061733SPaolo Bonzinistatic void *coreaudio_audio_init(Audiodev *dev, Error **errp)
6438b46d7e2SPhilippe Mathieu-Daudé{
6448b46d7e2SPhilippe Mathieu-Daudé    return dev;
6458b46d7e2SPhilippe Mathieu-Daudé}
6468b46d7e2SPhilippe Mathieu-Daudé
6478b46d7e2SPhilippe Mathieu-Daudéstatic void coreaudio_audio_fini (void *opaque)
6488b46d7e2SPhilippe Mathieu-Daudé{
6498b46d7e2SPhilippe Mathieu-Daudé}
6508b46d7e2SPhilippe Mathieu-Daudé
6518b46d7e2SPhilippe Mathieu-Daudéstatic struct audio_pcm_ops coreaudio_pcm_ops = {
6528b46d7e2SPhilippe Mathieu-Daudé    .init_out = coreaudio_init_out,
6538b46d7e2SPhilippe Mathieu-Daudé    .fini_out = coreaudio_fini_out,
6548b46d7e2SPhilippe Mathieu-Daudé  /* wrapper for audio_generic_write */
6558b46d7e2SPhilippe Mathieu-Daudé    .write    = coreaudio_write,
6568b46d7e2SPhilippe Mathieu-Daudé  /* wrapper for audio_generic_buffer_get_free */
6578b46d7e2SPhilippe Mathieu-Daudé    .buffer_get_free = coreaudio_buffer_get_free,
6588b46d7e2SPhilippe Mathieu-Daudé  /* wrapper for audio_generic_get_buffer_out */
6598b46d7e2SPhilippe Mathieu-Daudé    .get_buffer_out = coreaudio_get_buffer_out,
6608b46d7e2SPhilippe Mathieu-Daudé  /* wrapper for audio_generic_put_buffer_out */
6618b46d7e2SPhilippe Mathieu-Daudé    .put_buffer_out = coreaudio_put_buffer_out,
6628b46d7e2SPhilippe Mathieu-Daudé    .enable_out = coreaudio_enable_out
6638b46d7e2SPhilippe Mathieu-Daudé};
6648b46d7e2SPhilippe Mathieu-Daudé
6658b46d7e2SPhilippe Mathieu-Daudéstatic struct audio_driver coreaudio_audio_driver = {
6668b46d7e2SPhilippe Mathieu-Daudé    .name           = "coreaudio",
6678b46d7e2SPhilippe Mathieu-Daudé    .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
6688b46d7e2SPhilippe Mathieu-Daudé    .init           = coreaudio_audio_init,
6698b46d7e2SPhilippe Mathieu-Daudé    .fini           = coreaudio_audio_fini,
6708b46d7e2SPhilippe Mathieu-Daudé    .pcm_ops        = &coreaudio_pcm_ops,
6718b46d7e2SPhilippe Mathieu-Daudé    .max_voices_out = 1,
6728b46d7e2SPhilippe Mathieu-Daudé    .max_voices_in  = 0,
6738b46d7e2SPhilippe Mathieu-Daudé    .voice_size_out = sizeof (coreaudioVoiceOut),
6748b46d7e2SPhilippe Mathieu-Daudé    .voice_size_in  = 0
6758b46d7e2SPhilippe Mathieu-Daudé};
6768b46d7e2SPhilippe Mathieu-Daudé
6778b46d7e2SPhilippe Mathieu-Daudéstatic void register_audio_coreaudio(void)
6788b46d7e2SPhilippe Mathieu-Daudé{
6798b46d7e2SPhilippe Mathieu-Daudé    audio_driver_register(&coreaudio_audio_driver);
6808b46d7e2SPhilippe Mathieu-Daudé}
6818b46d7e2SPhilippe Mathieu-Daudétype_init(register_audio_coreaudio);
682