bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 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 | |
Peter Maydell | 6086a56 | 2016-01-18 17:33:52 +0000 | [diff] [blame] | 25 | #include "qemu/osdep.h" |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 26 | #include <CoreAudio/CoreAudio.h> |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 27 | #include <pthread.h> /* pthread_X */ |
| 28 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 29 | #include "qemu/main-loop.h" |
Markus Armbruster | 0b8fa32 | 2019-05-23 16:35:07 +0200 | [diff] [blame] | 30 | #include "qemu/module.h" |
pbrook | 749bc4b | 2007-11-17 17:35:54 +0000 | [diff] [blame] | 31 | #include "audio.h" |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 32 | |
| 33 | #define AUDIO_CAP "coreaudio" |
| 34 | #include "audio_int.h" |
| 35 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 36 | typedef struct coreaudioVoiceOut { |
| 37 | HWVoiceOut hw; |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 38 | pthread_mutex_t buf_mutex; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 39 | AudioDeviceID outputDeviceID; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 40 | int frameSizeSetting; |
| 41 | uint32_t bufferCount; |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 42 | UInt32 audioDevicePropertyBufferFrameSize; |
Peter Maydell | 2f79a18 | 2015-11-28 21:55:24 +0000 | [diff] [blame] | 43 | AudioDeviceIOProcID ioprocid; |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 44 | bool enabled; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 45 | } coreaudioVoiceOut; |
| 46 | |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 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 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 52 | static const AudioObjectPropertyAddress voice_addr = { |
| 53 | kAudioHardwarePropertyDefaultOutputDevice, |
| 54 | kAudioObjectPropertyScopeGlobal, |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 55 | kAudioObjectPropertyElementMain |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 56 | }; |
| 57 | |
Peter Maydell | 624d1fc | 2015-11-28 21:55:21 +0000 | [diff] [blame] | 58 | static OSStatus coreaudio_get_voice(AudioDeviceID *id) |
| 59 | { |
| 60 | UInt32 size = sizeof(*id); |
Peter Maydell | 624d1fc | 2015-11-28 21:55:21 +0000 | [diff] [blame] | 61 | |
| 62 | return AudioObjectGetPropertyData(kAudioObjectSystemObject, |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 63 | &voice_addr, |
Peter Maydell | 624d1fc | 2015-11-28 21:55:21 +0000 | [diff] [blame] | 64 | 0, |
| 65 | NULL, |
| 66 | &size, |
| 67 | id); |
| 68 | } |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 69 | |
| 70 | static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, |
| 71 | AudioValueRange *framerange) |
| 72 | { |
| 73 | UInt32 size = sizeof(*framerange); |
| 74 | AudioObjectPropertyAddress addr = { |
| 75 | kAudioDevicePropertyBufferFrameSizeRange, |
| 76 | kAudioDevicePropertyScopeOutput, |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 77 | kAudioObjectPropertyElementMain |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 78 | }; |
| 79 | |
| 80 | return AudioObjectGetPropertyData(id, |
| 81 | &addr, |
| 82 | 0, |
| 83 | NULL, |
| 84 | &size, |
| 85 | framerange); |
| 86 | } |
| 87 | |
| 88 | static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) |
| 89 | { |
| 90 | UInt32 size = sizeof(*framesize); |
| 91 | AudioObjectPropertyAddress addr = { |
| 92 | kAudioDevicePropertyBufferFrameSize, |
| 93 | kAudioDevicePropertyScopeOutput, |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 94 | kAudioObjectPropertyElementMain |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 95 | }; |
| 96 | |
| 97 | return AudioObjectGetPropertyData(id, |
| 98 | &addr, |
| 99 | 0, |
| 100 | NULL, |
| 101 | &size, |
| 102 | framesize); |
| 103 | } |
| 104 | |
| 105 | static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) |
| 106 | { |
| 107 | UInt32 size = sizeof(*framesize); |
| 108 | AudioObjectPropertyAddress addr = { |
| 109 | kAudioDevicePropertyBufferFrameSize, |
| 110 | kAudioDevicePropertyScopeOutput, |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 111 | kAudioObjectPropertyElementMain |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 112 | }; |
| 113 | |
| 114 | return AudioObjectSetPropertyData(id, |
| 115 | &addr, |
| 116 | 0, |
| 117 | NULL, |
| 118 | size, |
| 119 | framesize); |
| 120 | } |
| 121 | |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 122 | static OSStatus coreaudio_set_streamformat(AudioDeviceID id, |
| 123 | AudioStreamBasicDescription *d) |
| 124 | { |
| 125 | UInt32 size = sizeof(*d); |
| 126 | AudioObjectPropertyAddress addr = { |
| 127 | kAudioDevicePropertyStreamFormat, |
| 128 | kAudioDevicePropertyScopeOutput, |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 129 | kAudioObjectPropertyElementMain |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 130 | }; |
| 131 | |
| 132 | return AudioObjectSetPropertyData(id, |
| 133 | &addr, |
| 134 | 0, |
| 135 | NULL, |
| 136 | size, |
| 137 | d); |
| 138 | } |
| 139 | |
| 140 | static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) |
| 141 | { |
| 142 | UInt32 size = sizeof(*result); |
| 143 | AudioObjectPropertyAddress addr = { |
| 144 | kAudioDevicePropertyDeviceIsRunning, |
| 145 | kAudioDevicePropertyScopeOutput, |
Philippe Mathieu-Daudé | 9f56bd6 | 2022-01-09 14:30:03 +0100 | [diff] [blame] | 146 | kAudioObjectPropertyElementMain |
Peter Maydell | 2d99f62 | 2015-11-28 21:55:23 +0000 | [diff] [blame] | 147 | }; |
| 148 | |
| 149 | return AudioObjectGetPropertyData(id, |
| 150 | &addr, |
| 151 | 0, |
| 152 | NULL, |
| 153 | &size, |
| 154 | result); |
| 155 | } |
Peter Maydell | 95a860f | 2015-11-28 21:55:22 +0000 | [diff] [blame] | 156 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 157 | static void coreaudio_logstatus (OSStatus status) |
| 158 | { |
Alexandre Raymond | d9cbb0f | 2011-05-27 13:22:28 -0400 | [diff] [blame] | 159 | const char *str = "BUG"; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 160 | |
Zhang Han | 3c8de96 | 2021-01-15 09:24:26 +0800 | [diff] [blame] | 161 | switch (status) { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 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: |
Andreas Färber | 744d364 | 2011-06-23 16:24:45 +0200 | [diff] [blame] | 207 | AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 208 | return; |
| 209 | } |
| 210 | |
| 211 | AUD_log (AUDIO_CAP, "Reason: %s\n", str); |
| 212 | } |
| 213 | |
Marc-André Lureau | 9edc631 | 2022-02-20 20:39:25 +0400 | [diff] [blame] | 214 | static void G_GNUC_PRINTF (2, 3) coreaudio_logerr ( |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 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 | |
Marc-André Lureau | 9edc631 | 2022-02-20 20:39:25 +0400 | [diff] [blame] | 229 | static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 ( |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 230 | OSStatus status, |
| 231 | const char *typ, |
| 232 | const char *fmt, |
| 233 | ... |
| 234 | ) |
| 235 | { |
| 236 | va_list ap; |
| 237 | |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 238 | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 239 | |
| 240 | va_start (ap, fmt); |
| 241 | AUD_vlog (AUDIO_CAP, fmt, ap); |
| 242 | va_end (ap); |
| 243 | |
| 244 | coreaudio_logstatus (status); |
| 245 | } |
| 246 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 247 | #define coreaudio_playback_logerr(status, ...) \ |
| 248 | coreaudio_logerr2(status, "playback", __VA_ARGS__) |
| 249 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 250 | static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 251 | { |
| 252 | int err; |
| 253 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 254 | err = pthread_mutex_lock (&core->buf_mutex); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 255 | if (err) { |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 256 | dolog ("Could not lock voice for %s\nReason: %s\n", |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 257 | fn_name, strerror (err)); |
| 258 | return -1; |
| 259 | } |
| 260 | return 0; |
| 261 | } |
| 262 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 263 | static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 264 | { |
| 265 | int err; |
| 266 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 267 | err = pthread_mutex_unlock (&core->buf_mutex); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 268 | if (err) { |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 269 | dolog ("Could not unlock voice for %s\nReason: %s\n", |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 270 | fn_name, strerror (err)); |
| 271 | return -1; |
| 272 | } |
| 273 | return 0; |
| 274 | } |
| 275 | |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 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 | \ |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 282 | if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \ |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 283 | return 0; \ |
| 284 | } \ |
| 285 | \ |
| 286 | ret = glue(audio_generic_, name)args; \ |
| 287 | \ |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 288 | coreaudio_buf_unlock(core, "coreaudio_" #name); \ |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 289 | return ret; \ |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 290 | } |
Volker Rümelin | 9833438 | 2022-03-01 20:13:06 +0100 | [diff] [blame] | 291 | COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw)) |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 292 | COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), |
| 293 | (hw, size)) |
Volker Rümelin | fdc8c5f | 2020-01-23 08:49:39 +0100 | [diff] [blame] | 294 | COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 295 | (HWVoiceOut *hw, void *buf, size_t size), |
| 296 | (hw, buf, size)) |
| 297 | COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), |
| 298 | (hw, buf, size)) |
| 299 | #undef COREAUDIO_WRAPPER_FUNC |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 300 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 301 | /* |
| 302 | * callback to feed audiooutput buffer. called without iothread lock. |
| 303 | * allowed to lock "buf_mutex", but disallowed to have any other locks. |
| 304 | */ |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 305 | static OSStatus audioDeviceIOProc( |
| 306 | AudioDeviceID inDevice, |
Zhang Han | dcf10e4 | 2021-01-15 09:24:27 +0800 | [diff] [blame] | 307 | const AudioTimeStamp *inNow, |
| 308 | const AudioBufferList *inInputData, |
| 309 | const AudioTimeStamp *inInputTime, |
| 310 | AudioBufferList *outOutputData, |
| 311 | const AudioTimeStamp *inOutputTime, |
| 312 | void *hwptr) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 313 | { |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 314 | UInt32 frameCount, pending_frames; |
| 315 | void *out = outOutputData->mBuffers[0].mData; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 316 | HWVoiceOut *hw = hwptr; |
| 317 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 318 | size_t len; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 319 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 320 | if (coreaudio_buf_lock (core, "audioDeviceIOProc")) { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 321 | inInputTime = 0; |
| 322 | return 0; |
| 323 | } |
| 324 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 325 | if (inDevice != core->outputDeviceID) { |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 326 | coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)"); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 327 | return 0; |
| 328 | } |
| 329 | |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 330 | frameCount = core->audioDevicePropertyBufferFrameSize; |
Kővágó, Zoltán | 2b9cce8 | 2019-10-13 21:58:02 +0200 | [diff] [blame] | 331 | pending_frames = hw->pending_emul / hw->info.bytes_per_frame; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 332 | |
| 333 | /* if there are not enough samples, set signal and return */ |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 334 | if (pending_frames < frameCount) { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 335 | inInputTime = 0; |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 336 | coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)"); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 337 | return 0; |
| 338 | } |
| 339 | |
Kővágó, Zoltán | 2b9cce8 | 2019-10-13 21:58:02 +0200 | [diff] [blame] | 340 | len = frameCount * hw->info.bytes_per_frame; |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 341 | while (len) { |
Volker Rümelin | 18404ff | 2022-03-01 20:12:57 +0100 | [diff] [blame] | 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); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 346 | |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 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; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 354 | } |
| 355 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 356 | coreaudio_buf_unlock (core, "audioDeviceIOProc"); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 357 | return 0; |
| 358 | } |
| 359 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 360 | static OSStatus init_out_device(coreaudioVoiceOut *core) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 361 | { |
| 362 | OSStatus status; |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 363 | AudioValueRange frameRange; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 364 | |
Akihiko Odaki | 986bdbc | 2021-06-16 23:17:21 +0900 | [diff] [blame] | 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 | |
Peter Maydell | 88a0f83 | 2015-11-28 21:55:20 +0000 | [diff] [blame] | 376 | status = coreaudio_get_voice(&core->outputDeviceID); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 377 | if (status != kAudioHardwareNoError) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 378 | coreaudio_playback_logerr (status, |
| 379 | "Could not get default output Device\n"); |
| 380 | return status; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 381 | } |
| 382 | if (core->outputDeviceID == kAudioDeviceUnknown) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 383 | dolog ("Could not initialize playback - Unknown Audiodevice\n"); |
| 384 | return status; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 385 | } |
| 386 | |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 387 | /* get minimum and maximum buffer frame sizes */ |
Peter Maydell | 95a860f | 2015-11-28 21:55:22 +0000 | [diff] [blame] | 388 | status = coreaudio_get_framesizerange(core->outputDeviceID, |
| 389 | &frameRange); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 390 | if (status == kAudioHardwareBadObjectError) { |
| 391 | return 0; |
| 392 | } |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 393 | if (status != kAudioHardwareNoError) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 394 | coreaudio_playback_logerr (status, |
| 395 | "Could not get device buffer frame range\n"); |
| 396 | return status; |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 397 | } |
| 398 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 399 | if (frameRange.mMinimum > core->frameSizeSetting) { |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 400 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |
| 401 | dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 402 | } else if (frameRange.mMaximum < core->frameSizeSetting) { |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 403 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; |
| 404 | dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); |
Zhang Han | 6c6886b | 2021-01-15 09:24:25 +0800 | [diff] [blame] | 405 | } else { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 406 | core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting; |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 407 | } |
| 408 | |
| 409 | /* set Buffer Frame Size */ |
Peter Maydell | 95a860f | 2015-11-28 21:55:22 +0000 | [diff] [blame] | 410 | status = coreaudio_set_framesize(core->outputDeviceID, |
| 411 | &core->audioDevicePropertyBufferFrameSize); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 412 | if (status == kAudioHardwareBadObjectError) { |
| 413 | return 0; |
| 414 | } |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 415 | if (status != kAudioHardwareNoError) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 416 | coreaudio_playback_logerr (status, |
| 417 | "Could not set device buffer frame size %" PRIu32 "\n", |
| 418 | (uint32_t)core->audioDevicePropertyBufferFrameSize); |
| 419 | return status; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 420 | } |
| 421 | |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 422 | /* get Buffer Frame Size */ |
Peter Maydell | 95a860f | 2015-11-28 21:55:22 +0000 | [diff] [blame] | 423 | status = coreaudio_get_framesize(core->outputDeviceID, |
| 424 | &core->audioDevicePropertyBufferFrameSize); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 425 | if (status == kAudioHardwareBadObjectError) { |
| 426 | return 0; |
| 427 | } |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 428 | if (status != kAudioHardwareNoError) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 429 | coreaudio_playback_logerr (status, |
| 430 | "Could not get device buffer frame size\n"); |
| 431 | return status; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 432 | } |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 433 | core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 434 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 435 | /* set Samplerate */ |
Peter Maydell | 95a860f | 2015-11-28 21:55:22 +0000 | [diff] [blame] | 436 | status = coreaudio_set_streamformat(core->outputDeviceID, |
Akihiko Odaki | 986bdbc | 2021-06-16 23:17:21 +0900 | [diff] [blame] | 437 | &streamBasicDescription); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 438 | if (status == kAudioHardwareBadObjectError) { |
| 439 | return 0; |
| 440 | } |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 441 | if (status != kAudioHardwareNoError) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 442 | coreaudio_playback_logerr (status, |
| 443 | "Could not set samplerate %lf\n", |
Akihiko Odaki | 986bdbc | 2021-06-16 23:17:21 +0900 | [diff] [blame] | 444 | streamBasicDescription.mSampleRate); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 445 | core->outputDeviceID = kAudioDeviceUnknown; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 446 | return status; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 447 | } |
| 448 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 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 | */ |
Peter Maydell | 2f79a18 | 2015-11-28 21:55:24 +0000 | [diff] [blame] | 459 | core->ioprocid = NULL; |
| 460 | status = AudioDeviceCreateIOProcID(core->outputDeviceID, |
| 461 | audioDeviceIOProc, |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 462 | &core->hw, |
Peter Maydell | 2f79a18 | 2015-11-28 21:55:24 +0000 | [diff] [blame] | 463 | &core->ioprocid); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 464 | if (status == kAudioHardwareBadDeviceError) { |
| 465 | return 0; |
| 466 | } |
Peter Maydell | 2f79a18 | 2015-11-28 21:55:24 +0000 | [diff] [blame] | 467 | if (status != kAudioHardwareNoError || core->ioprocid == NULL) { |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 468 | coreaudio_playback_logerr (status, "Could not set IOProc\n"); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 469 | core->outputDeviceID = kAudioDeviceUnknown; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 470 | return status; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 471 | } |
| 472 | |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 473 | return 0; |
| 474 | } |
| 475 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 476 | static void fini_out_device(coreaudioVoiceOut *core) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 477 | { |
| 478 | OSStatus status; |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 479 | UInt32 isrunning; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 480 | |
Volker Rümelin | ceb1165 | 2020-12-13 14:05:27 +0100 | [diff] [blame] | 481 | /* stop playback */ |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 482 | status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); |
| 483 | if (status != kAudioHardwareBadObjectError) { |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 484 | if (status != kAudioHardwareNoError) { |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 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 | } |
bellard | 5e941d4 | 2005-11-20 16:24:09 +0000 | [diff] [blame] | 494 | } |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 495 | } |
Volker Rümelin | ceb1165 | 2020-12-13 14:05:27 +0100 | [diff] [blame] | 496 | |
| 497 | /* remove callback */ |
| 498 | status = AudioDeviceDestroyIOProcID(core->outputDeviceID, |
| 499 | core->ioprocid); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 500 | if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { |
Volker Rümelin | ceb1165 | 2020-12-13 14:05:27 +0100 | [diff] [blame] | 501 | coreaudio_logerr(status, "Could not remove IOProc\n"); |
| 502 | } |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 503 | core->outputDeviceID = kAudioDeviceUnknown; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 504 | } |
| 505 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 506 | static 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 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 541 | /* called without iothread lock. */ |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 542 | static 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 | { |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 548 | coreaudioVoiceOut *core = in_client_data; |
| 549 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 550 | qemu_mutex_lock_iothread(); |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 551 | |
| 552 | if (core->outputDeviceID) { |
| 553 | fini_out_device(core); |
| 554 | } |
| 555 | |
Akihiko Odaki | 44ccb2d | 2022-03-06 21:34:10 +0900 | [diff] [blame] | 556 | if (!init_out_device(core)) { |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 557 | update_device_playback_state(core); |
| 558 | } |
| 559 | |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 560 | qemu_mutex_unlock_iothread(); |
Akihiko Odaki | 44ccb2d | 2022-03-06 21:34:10 +0900 | [diff] [blame] | 561 | return 0; |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 562 | } |
| 563 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 564 | static 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 */ |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 575 | err = pthread_mutex_init(&core->buf_mutex, NULL); |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 576 | if (err) { |
| 577 | dolog("Could not create mutex\nReason: %s\n", strerror (err)); |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 578 | return -1; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 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; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 590 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 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"); |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 597 | return -1; |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 598 | } |
| 599 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 600 | if (init_out_device(core)) { |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 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 | } |
Akihiko Odaki | bd7819d | 2022-02-26 20:59:53 +0900 | [diff] [blame] | 609 | |
| 610 | return -1; |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 611 | } |
| 612 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 613 | return 0; |
| 614 | } |
| 615 | |
| 616 | static void coreaudio_fini_out (HWVoiceOut *hw) |
| 617 | { |
| 618 | OSStatus status; |
| 619 | int err; |
| 620 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
| 621 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 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 | |
Akihiko Odaki | 7d6948c | 2021-03-12 00:15:11 +0900 | [diff] [blame] | 630 | fini_out_device(core); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 631 | |
| 632 | /* destroy mutex */ |
Akihiko Odaki | eb1a35e | 2021-06-23 05:17:40 +0900 | [diff] [blame] | 633 | err = pthread_mutex_destroy(&core->buf_mutex); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 634 | if (err) { |
bellard | c0fe382 | 2005-11-05 18:55:28 +0000 | [diff] [blame] | 635 | dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 636 | } |
| 637 | } |
| 638 | |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 639 | static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 640 | { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 641 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
| 642 | |
Akihiko Odaki | 3ba6e3f | 2021-03-12 00:15:12 +0900 | [diff] [blame] | 643 | core->enabled = enable; |
| 644 | update_device_playback_state(core); |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 645 | } |
| 646 | |
Kővágó, Zoltán | 7183022 | 2019-03-08 23:34:15 +0100 | [diff] [blame] | 647 | static void *coreaudio_audio_init(Audiodev *dev) |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 648 | { |
Kővágó, Zoltán | 17c56dc | 2019-03-08 23:34:17 +0100 | [diff] [blame] | 649 | return dev; |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 650 | } |
| 651 | |
| 652 | static void coreaudio_audio_fini (void *opaque) |
| 653 | { |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 654 | } |
| 655 | |
blueswir1 | 35f4b58 | 2008-10-06 18:08:30 +0000 | [diff] [blame] | 656 | static struct audio_pcm_ops coreaudio_pcm_ops = { |
Juan Quintela | 1dd3e4d | 2009-08-11 02:31:15 +0200 | [diff] [blame] | 657 | .init_out = coreaudio_init_out, |
| 658 | .fini_out = coreaudio_fini_out, |
Volker Rümelin | fdc8c5f | 2020-01-23 08:49:39 +0100 | [diff] [blame] | 659 | /* wrapper for audio_generic_write */ |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 660 | .write = coreaudio_write, |
Volker Rümelin | 9833438 | 2022-03-01 20:13:06 +0100 | [diff] [blame] | 661 | /* wrapper for audio_generic_buffer_get_free */ |
| 662 | .buffer_get_free = coreaudio_buffer_get_free, |
Volker Rümelin | fdc8c5f | 2020-01-23 08:49:39 +0100 | [diff] [blame] | 663 | /* wrapper for audio_generic_get_buffer_out */ |
Kővágó, Zoltán | 2ceb824 | 2019-09-19 23:24:11 +0200 | [diff] [blame] | 664 | .get_buffer_out = coreaudio_get_buffer_out, |
Volker Rümelin | fdc8c5f | 2020-01-23 08:49:39 +0100 | [diff] [blame] | 665 | /* wrapper for audio_generic_put_buffer_out */ |
| 666 | .put_buffer_out = coreaudio_put_buffer_out, |
Kővágó, Zoltán | 571a8c5 | 2019-09-19 23:24:22 +0200 | [diff] [blame] | 667 | .enable_out = coreaudio_enable_out |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 668 | }; |
| 669 | |
Gerd Hoffmann | d3893a3 | 2018-03-06 08:40:47 +0100 | [diff] [blame] | 670 | static struct audio_driver coreaudio_audio_driver = { |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 671 | .name = "coreaudio", |
| 672 | .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", |
Juan Quintela | bee37f3 | 2009-08-11 02:31:14 +0200 | [diff] [blame] | 673 | .init = coreaudio_audio_init, |
| 674 | .fini = coreaudio_audio_fini, |
| 675 | .pcm_ops = &coreaudio_pcm_ops, |
| 676 | .can_be_default = 1, |
| 677 | .max_voices_out = 1, |
| 678 | .max_voices_in = 0, |
| 679 | .voice_size_out = sizeof (coreaudioVoiceOut), |
| 680 | .voice_size_in = 0 |
bellard | 1d14ffa | 2005-10-30 18:58:22 +0000 | [diff] [blame] | 681 | }; |
Gerd Hoffmann | d3893a3 | 2018-03-06 08:40:47 +0100 | [diff] [blame] | 682 | |
| 683 | static void register_audio_coreaudio(void) |
| 684 | { |
| 685 | audio_driver_register(&coreaudio_audio_driver); |
| 686 | } |
| 687 | type_init(register_audio_coreaudio); |