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