1/* 2 * QEMU OS X CoreAudio audio driver 3 * 4 * Copyright (c) 2005 Mike Kronenberg 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25#include "qemu/osdep.h" 26#include <CoreAudio/CoreAudio.h> 27#include <pthread.h> /* pthread_X */ 28 29#include "qemu/main-loop.h" 30#include "qemu/module.h" 31#include "audio.h" 32 33#define AUDIO_CAP "coreaudio" 34#include "audio_int.h" 35 36typedef struct coreaudioVoiceOut { 37 HWVoiceOut hw; 38 pthread_mutex_t buf_mutex; 39 AudioDeviceID outputDeviceID; 40 int frameSizeSetting; 41 uint32_t bufferCount; 42 UInt32 audioDevicePropertyBufferFrameSize; 43 AudioDeviceIOProcID ioprocid; 44 bool enabled; 45} coreaudioVoiceOut; 46 47#if !defined(MAC_OS_VERSION_12_0) \ 48 || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_12_0) 49#define kAudioObjectPropertyElementMain kAudioObjectPropertyElementMaster 50#endif 51 52static const AudioObjectPropertyAddress voice_addr = { 53 kAudioHardwarePropertyDefaultOutputDevice, 54 kAudioObjectPropertyScopeGlobal, 55 kAudioObjectPropertyElementMain 56}; 57 58static OSStatus coreaudio_get_voice(AudioDeviceID *id) 59{ 60 UInt32 size = sizeof(*id); 61 62 return AudioObjectGetPropertyData(kAudioObjectSystemObject, 63 &voice_addr, 64 0, 65 NULL, 66 &size, 67 id); 68} 69 70static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, 71 AudioValueRange *framerange) 72{ 73 UInt32 size = sizeof(*framerange); 74 AudioObjectPropertyAddress addr = { 75 kAudioDevicePropertyBufferFrameSizeRange, 76 kAudioDevicePropertyScopeOutput, 77 kAudioObjectPropertyElementMain 78 }; 79 80 return AudioObjectGetPropertyData(id, 81 &addr, 82 0, 83 NULL, 84 &size, 85 framerange); 86} 87 88static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) 89{ 90 UInt32 size = sizeof(*framesize); 91 AudioObjectPropertyAddress addr = { 92 kAudioDevicePropertyBufferFrameSize, 93 kAudioDevicePropertyScopeOutput, 94 kAudioObjectPropertyElementMain 95 }; 96 97 return AudioObjectGetPropertyData(id, 98 &addr, 99 0, 100 NULL, 101 &size, 102 framesize); 103} 104 105static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) 106{ 107 UInt32 size = sizeof(*framesize); 108 AudioObjectPropertyAddress addr = { 109 kAudioDevicePropertyBufferFrameSize, 110 kAudioDevicePropertyScopeOutput, 111 kAudioObjectPropertyElementMain 112 }; 113 114 return AudioObjectSetPropertyData(id, 115 &addr, 116 0, 117 NULL, 118 size, 119 framesize); 120} 121 122static OSStatus coreaudio_set_streamformat(AudioDeviceID id, 123 AudioStreamBasicDescription *d) 124{ 125 UInt32 size = sizeof(*d); 126 AudioObjectPropertyAddress addr = { 127 kAudioDevicePropertyStreamFormat, 128 kAudioDevicePropertyScopeOutput, 129 kAudioObjectPropertyElementMain 130 }; 131 132 return AudioObjectSetPropertyData(id, 133 &addr, 134 0, 135 NULL, 136 size, 137 d); 138} 139 140static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) 141{ 142 UInt32 size = sizeof(*result); 143 AudioObjectPropertyAddress addr = { 144 kAudioDevicePropertyDeviceIsRunning, 145 kAudioDevicePropertyScopeOutput, 146 kAudioObjectPropertyElementMain 147 }; 148 149 return AudioObjectGetPropertyData(id, 150 &addr, 151 0, 152 NULL, 153 &size, 154 result); 155} 156 157static void coreaudio_logstatus (OSStatus status) 158{ 159 const char *str = "BUG"; 160 161 switch (status) { 162 case kAudioHardwareNoError: 163 str = "kAudioHardwareNoError"; 164 break; 165 166 case kAudioHardwareNotRunningError: 167 str = "kAudioHardwareNotRunningError"; 168 break; 169 170 case kAudioHardwareUnspecifiedError: 171 str = "kAudioHardwareUnspecifiedError"; 172 break; 173 174 case kAudioHardwareUnknownPropertyError: 175 str = "kAudioHardwareUnknownPropertyError"; 176 break; 177 178 case kAudioHardwareBadPropertySizeError: 179 str = "kAudioHardwareBadPropertySizeError"; 180 break; 181 182 case kAudioHardwareIllegalOperationError: 183 str = "kAudioHardwareIllegalOperationError"; 184 break; 185 186 case kAudioHardwareBadDeviceError: 187 str = "kAudioHardwareBadDeviceError"; 188 break; 189 190 case kAudioHardwareBadStreamError: 191 str = "kAudioHardwareBadStreamError"; 192 break; 193 194 case kAudioHardwareUnsupportedOperationError: 195 str = "kAudioHardwareUnsupportedOperationError"; 196 break; 197 198 case kAudioDeviceUnsupportedFormatError: 199 str = "kAudioDeviceUnsupportedFormatError"; 200 break; 201 202 case kAudioDevicePermissionsError: 203 str = "kAudioDevicePermissionsError"; 204 break; 205 206 default: 207 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); 208 return; 209 } 210 211 AUD_log (AUDIO_CAP, "Reason: %s\n", str); 212} 213 214static void G_GNUC_PRINTF (2, 3) coreaudio_logerr ( 215 OSStatus status, 216 const char *fmt, 217 ... 218 ) 219{ 220 va_list ap; 221 222 va_start (ap, fmt); 223 AUD_log (AUDIO_CAP, fmt, ap); 224 va_end (ap); 225 226 coreaudio_logstatus (status); 227} 228 229static void G_GNUC_PRINTF (3, 4) coreaudio_logerr2 ( 230 OSStatus status, 231 const char *typ, 232 const char *fmt, 233 ... 234 ) 235{ 236 va_list ap; 237 238 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); 239 240 va_start (ap, fmt); 241 AUD_vlog (AUDIO_CAP, fmt, ap); 242 va_end (ap); 243 244 coreaudio_logstatus (status); 245} 246 247#define coreaudio_playback_logerr(status, ...) \ 248 coreaudio_logerr2(status, "playback", __VA_ARGS__) 249 250static int coreaudio_buf_lock (coreaudioVoiceOut *core, const char *fn_name) 251{ 252 int err; 253 254 err = pthread_mutex_lock (&core->buf_mutex); 255 if (err) { 256 dolog ("Could not lock voice for %s\nReason: %s\n", 257 fn_name, strerror (err)); 258 return -1; 259 } 260 return 0; 261} 262 263static int coreaudio_buf_unlock (coreaudioVoiceOut *core, const char *fn_name) 264{ 265 int err; 266 267 err = pthread_mutex_unlock (&core->buf_mutex); 268 if (err) { 269 dolog ("Could not unlock voice for %s\nReason: %s\n", 270 fn_name, strerror (err)); 271 return -1; 272 } 273 return 0; 274} 275 276#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \ 277 static ret_type glue(coreaudio_, name)args_decl \ 278 { \ 279 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \ 280 ret_type ret; \ 281 \ 282 if (coreaudio_buf_lock(core, "coreaudio_" #name)) { \ 283 return 0; \ 284 } \ 285 \ 286 ret = glue(audio_generic_, name)args; \ 287 \ 288 coreaudio_buf_unlock(core, "coreaudio_" #name); \ 289 return ret; \ 290 } 291COREAUDIO_WRAPPER_FUNC(buffer_get_free, size_t, (HWVoiceOut *hw), (hw)) 292COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size), 293 (hw, size)) 294COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t, 295 (HWVoiceOut *hw, void *buf, size_t size), 296 (hw, buf, size)) 297COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size), 298 (hw, buf, size)) 299#undef COREAUDIO_WRAPPER_FUNC 300 301/* 302 * callback to feed audiooutput buffer. called without iothread lock. 303 * allowed to lock "buf_mutex", but disallowed to have any other locks. 304 */ 305static OSStatus audioDeviceIOProc( 306 AudioDeviceID inDevice, 307 const AudioTimeStamp *inNow, 308 const AudioBufferList *inInputData, 309 const AudioTimeStamp *inInputTime, 310 AudioBufferList *outOutputData, 311 const AudioTimeStamp *inOutputTime, 312 void *hwptr) 313{ 314 UInt32 frameCount, pending_frames; 315 void *out = outOutputData->mBuffers[0].mData; 316 HWVoiceOut *hw = hwptr; 317 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; 318 size_t len; 319 320 if (coreaudio_buf_lock (core, "audioDeviceIOProc")) { 321 inInputTime = 0; 322 return 0; 323 } 324 325 if (inDevice != core->outputDeviceID) { 326 coreaudio_buf_unlock (core, "audioDeviceIOProc(old device)"); 327 return 0; 328 } 329 330 frameCount = core->audioDevicePropertyBufferFrameSize; 331 pending_frames = hw->pending_emul / hw->info.bytes_per_frame; 332 333 /* if there are not enough samples, set signal and return */ 334 if (pending_frames < frameCount) { 335 inInputTime = 0; 336 coreaudio_buf_unlock (core, "audioDeviceIOProc(empty)"); 337 return 0; 338 } 339 340 len = frameCount * hw->info.bytes_per_frame; 341 while (len) { 342 size_t write_len, start; 343 344 start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul); 345 assert(start < hw->size_emul); 346 347 write_len = MIN(MIN(hw->pending_emul, len), 348 hw->size_emul - start); 349 350 memcpy(out, hw->buf_emul + start, write_len); 351 hw->pending_emul -= write_len; 352 len -= write_len; 353 out += write_len; 354 } 355 356 coreaudio_buf_unlock (core, "audioDeviceIOProc"); 357 return 0; 358} 359 360static OSStatus init_out_device(coreaudioVoiceOut *core) 361{ 362 OSStatus status; 363 AudioValueRange frameRange; 364 365 AudioStreamBasicDescription streamBasicDescription = { 366 .mBitsPerChannel = core->hw.info.bits, 367 .mBytesPerFrame = core->hw.info.bytes_per_frame, 368 .mBytesPerPacket = core->hw.info.bytes_per_frame, 369 .mChannelsPerFrame = core->hw.info.nchannels, 370 .mFormatFlags = kLinearPCMFormatFlagIsFloat, 371 .mFormatID = kAudioFormatLinearPCM, 372 .mFramesPerPacket = 1, 373 .mSampleRate = core->hw.info.freq 374 }; 375 376 status = coreaudio_get_voice(&core->outputDeviceID); 377 if (status != kAudioHardwareNoError) { 378 coreaudio_playback_logerr (status, 379 "Could not get default output Device\n"); 380 return status; 381 } 382 if (core->outputDeviceID == kAudioDeviceUnknown) { 383 dolog ("Could not initialize playback - Unknown Audiodevice\n"); 384 return status; 385 } 386 387 /* get minimum and maximum buffer frame sizes */ 388 status = coreaudio_get_framesizerange(core->outputDeviceID, 389 &frameRange); 390 if (status == kAudioHardwareBadObjectError) { 391 return 0; 392 } 393 if (status != kAudioHardwareNoError) { 394 coreaudio_playback_logerr (status, 395 "Could not get device buffer frame range\n"); 396 return status; 397 } 398 399 if (frameRange.mMinimum > core->frameSizeSetting) { 400 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; 401 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); 402 } else if (frameRange.mMaximum < core->frameSizeSetting) { 403 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; 404 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); 405 } else { 406 core->audioDevicePropertyBufferFrameSize = core->frameSizeSetting; 407 } 408 409 /* set Buffer Frame Size */ 410 status = coreaudio_set_framesize(core->outputDeviceID, 411 &core->audioDevicePropertyBufferFrameSize); 412 if (status == kAudioHardwareBadObjectError) { 413 return 0; 414 } 415 if (status != kAudioHardwareNoError) { 416 coreaudio_playback_logerr (status, 417 "Could not set device buffer frame size %" PRIu32 "\n", 418 (uint32_t)core->audioDevicePropertyBufferFrameSize); 419 return status; 420 } 421 422 /* get Buffer Frame Size */ 423 status = coreaudio_get_framesize(core->outputDeviceID, 424 &core->audioDevicePropertyBufferFrameSize); 425 if (status == kAudioHardwareBadObjectError) { 426 return 0; 427 } 428 if (status != kAudioHardwareNoError) { 429 coreaudio_playback_logerr (status, 430 "Could not get device buffer frame size\n"); 431 return status; 432 } 433 core->hw.samples = core->bufferCount * core->audioDevicePropertyBufferFrameSize; 434 435 /* set Samplerate */ 436 status = coreaudio_set_streamformat(core->outputDeviceID, 437 &streamBasicDescription); 438 if (status == kAudioHardwareBadObjectError) { 439 return 0; 440 } 441 if (status != kAudioHardwareNoError) { 442 coreaudio_playback_logerr (status, 443 "Could not set samplerate %lf\n", 444 streamBasicDescription.mSampleRate); 445 core->outputDeviceID = kAudioDeviceUnknown; 446 return status; 447 } 448 449 /* 450 * set Callback. 451 * 452 * On macOS 11.3.1, Core Audio calls AudioDeviceIOProc after calling an 453 * internal function named HALB_Mutex::Lock(), which locks a mutex in 454 * HALB_IOThread::Entry(void*). HALB_Mutex::Lock() is also called in 455 * AudioObjectGetPropertyData, which is called by coreaudio driver. 456 * Therefore, the specified callback must be designed to avoid a deadlock 457 * with the callers of AudioObjectGetPropertyData. 458 */ 459 core->ioprocid = NULL; 460 status = AudioDeviceCreateIOProcID(core->outputDeviceID, 461 audioDeviceIOProc, 462 &core->hw, 463 &core->ioprocid); 464 if (status == kAudioHardwareBadDeviceError) { 465 return 0; 466 } 467 if (status != kAudioHardwareNoError || core->ioprocid == NULL) { 468 coreaudio_playback_logerr (status, "Could not set IOProc\n"); 469 core->outputDeviceID = kAudioDeviceUnknown; 470 return status; 471 } 472 473 return 0; 474} 475 476static void fini_out_device(coreaudioVoiceOut *core) 477{ 478 OSStatus status; 479 UInt32 isrunning; 480 481 /* stop playback */ 482 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); 483 if (status != kAudioHardwareBadObjectError) { 484 if (status != kAudioHardwareNoError) { 485 coreaudio_logerr(status, 486 "Could not determine whether Device is playing\n"); 487 } 488 489 if (isrunning) { 490 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); 491 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { 492 coreaudio_logerr(status, "Could not stop playback\n"); 493 } 494 } 495 } 496 497 /* remove callback */ 498 status = AudioDeviceDestroyIOProcID(core->outputDeviceID, 499 core->ioprocid); 500 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { 501 coreaudio_logerr(status, "Could not remove IOProc\n"); 502 } 503 core->outputDeviceID = kAudioDeviceUnknown; 504} 505 506static void update_device_playback_state(coreaudioVoiceOut *core) 507{ 508 OSStatus status; 509 UInt32 isrunning; 510 511 status = coreaudio_get_isrunning(core->outputDeviceID, &isrunning); 512 if (status != kAudioHardwareNoError) { 513 if (status != kAudioHardwareBadObjectError) { 514 coreaudio_logerr(status, 515 "Could not determine whether Device is playing\n"); 516 } 517 518 return; 519 } 520 521 if (core->enabled) { 522 /* start playback */ 523 if (!isrunning) { 524 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); 525 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { 526 coreaudio_logerr (status, "Could not resume playback\n"); 527 } 528 } 529 } else { 530 /* stop playback */ 531 if (isrunning) { 532 status = AudioDeviceStop(core->outputDeviceID, 533 core->ioprocid); 534 if (status != kAudioHardwareBadDeviceError && status != kAudioHardwareNoError) { 535 coreaudio_logerr(status, "Could not pause playback\n"); 536 } 537 } 538 } 539} 540 541/* called without iothread lock. */ 542static OSStatus handle_voice_change( 543 AudioObjectID in_object_id, 544 UInt32 in_number_addresses, 545 const AudioObjectPropertyAddress *in_addresses, 546 void *in_client_data) 547{ 548 coreaudioVoiceOut *core = in_client_data; 549 550 qemu_mutex_lock_iothread(); 551 552 if (core->outputDeviceID) { 553 fini_out_device(core); 554 } 555 556 if (!init_out_device(core)) { 557 update_device_playback_state(core); 558 } 559 560 qemu_mutex_unlock_iothread(); 561 return 0; 562} 563 564static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, 565 void *drv_opaque) 566{ 567 OSStatus status; 568 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; 569 int err; 570 Audiodev *dev = drv_opaque; 571 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; 572 struct audsettings obt_as; 573 574 /* create mutex */ 575 err = pthread_mutex_init(&core->buf_mutex, NULL); 576 if (err) { 577 dolog("Could not create mutex\nReason: %s\n", strerror (err)); 578 return -1; 579 } 580 581 obt_as = *as; 582 as = &obt_as; 583 as->fmt = AUDIO_FORMAT_F32; 584 audio_pcm_init_info (&hw->info, as); 585 586 core->frameSizeSetting = audio_buffer_frames( 587 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); 588 589 core->bufferCount = cpdo->has_buffer_count ? cpdo->buffer_count : 4; 590 591 status = AudioObjectAddPropertyListener(kAudioObjectSystemObject, 592 &voice_addr, handle_voice_change, 593 core); 594 if (status != kAudioHardwareNoError) { 595 coreaudio_playback_logerr (status, 596 "Could not listen to voice property change\n"); 597 return -1; 598 } 599 600 if (init_out_device(core)) { 601 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, 602 &voice_addr, 603 handle_voice_change, 604 core); 605 if (status != kAudioHardwareNoError) { 606 coreaudio_playback_logerr(status, 607 "Could not remove voice property change listener\n"); 608 } 609 610 return -1; 611 } 612 613 return 0; 614} 615 616static void coreaudio_fini_out (HWVoiceOut *hw) 617{ 618 OSStatus status; 619 int err; 620 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; 621 622 status = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, 623 &voice_addr, 624 handle_voice_change, 625 core); 626 if (status != kAudioHardwareNoError) { 627 coreaudio_logerr(status, "Could not remove voice property change listener\n"); 628 } 629 630 fini_out_device(core); 631 632 /* destroy mutex */ 633 err = pthread_mutex_destroy(&core->buf_mutex); 634 if (err) { 635 dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); 636 } 637} 638 639static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) 640{ 641 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; 642 643 core->enabled = enable; 644 update_device_playback_state(core); 645} 646 647static void *coreaudio_audio_init(Audiodev *dev) 648{ 649 return dev; 650} 651 652static void coreaudio_audio_fini (void *opaque) 653{ 654} 655 656static struct audio_pcm_ops coreaudio_pcm_ops = { 657 .init_out = coreaudio_init_out, 658 .fini_out = coreaudio_fini_out, 659 /* wrapper for audio_generic_write */ 660 .write = coreaudio_write, 661 /* wrapper for audio_generic_buffer_get_free */ 662 .buffer_get_free = coreaudio_buffer_get_free, 663 /* wrapper for audio_generic_get_buffer_out */ 664 .get_buffer_out = coreaudio_get_buffer_out, 665 /* wrapper for audio_generic_put_buffer_out */ 666 .put_buffer_out = coreaudio_put_buffer_out, 667 .enable_out = coreaudio_enable_out 668}; 669 670static struct audio_driver coreaudio_audio_driver = { 671 .name = "coreaudio", 672 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", 673 .init = coreaudio_audio_init, 674 .fini = coreaudio_audio_fini, 675 .pcm_ops = &coreaudio_pcm_ops, 676 .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 681}; 682 683static void register_audio_coreaudio(void) 684{ 685 audio_driver_register(&coreaudio_audio_driver); 686} 687type_init(register_audio_coreaudio); 688