cubeb_audiounit.cpp (123615B)
1 /* 2 * Copyright © 2011 Mozilla Foundation 3 * 4 * This program is made available under an ISC-style license. See the 5 * accompanying file LICENSE for details. 6 */ 7 #undef NDEBUG 8 9 #include <AudioUnit/AudioUnit.h> 10 #include <TargetConditionals.h> 11 #include <assert.h> 12 #include <mach/mach_time.h> 13 #include <pthread.h> 14 #include <stdlib.h> 15 #if !TARGET_OS_IPHONE 16 #include <AvailabilityMacros.h> 17 #include <CoreAudio/AudioHardware.h> 18 #include <CoreAudio/HostTime.h> 19 #include <CoreFoundation/CoreFoundation.h> 20 #endif 21 #include "cubeb-internal.h" 22 #include "cubeb/cubeb.h" 23 #include "cubeb_mixer.h" 24 #include <AudioToolbox/AudioToolbox.h> 25 #include <CoreAudio/CoreAudioTypes.h> 26 #if !TARGET_OS_IPHONE 27 #include "cubeb_osx_run_loop.h" 28 #endif 29 #include "cubeb_resampler.h" 30 #include "cubeb_ring_array.h" 31 #include <algorithm> 32 #include <atomic> 33 #include <set> 34 #include <string> 35 #include <sys/time.h> 36 #include <vector> 37 38 using namespace std; 39 40 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 41 typedef UInt32 AudioFormatFlags; 42 #endif 43 44 #define AU_OUT_BUS 0 45 #define AU_IN_BUS 1 46 47 const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb"; 48 const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice"; 49 50 #ifdef ALOGV 51 #undef ALOGV 52 #endif 53 #define ALOGV(msg, ...) \ 54 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), \ 55 ^{ \ 56 LOGV(msg, ##__VA_ARGS__); \ 57 }) 58 59 #ifdef ALOG 60 #undef ALOG 61 #endif 62 #define ALOG(msg, ...) \ 63 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), \ 64 ^{ \ 65 LOG(msg, ##__VA_ARGS__); \ 66 }) 67 68 /* Testing empirically, some headsets report a minimal latency that is very 69 * low, but this does not work in practice. Lie and say the minimum is 256 70 * frames. */ 71 const uint32_t SAFE_MIN_LATENCY_FRAMES = 128; 72 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512; 73 74 const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = { 75 kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, 76 kAudioObjectPropertyElementMaster}; 77 78 const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = { 79 kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, 80 kAudioObjectPropertyElementMaster}; 81 82 const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = { 83 kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, 84 kAudioObjectPropertyElementMaster}; 85 86 const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = { 87 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, 88 kAudioObjectPropertyElementMaster}; 89 90 const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = { 91 kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput, 92 kAudioObjectPropertyElementMaster}; 93 94 const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = { 95 kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, 96 kAudioObjectPropertyElementMaster}; 97 98 typedef uint32_t device_flags_value; 99 100 enum device_flags { 101 DEV_UNKNOWN = 0x00, /* Unknown */ 102 DEV_INPUT = 0x01, /* Record device like mic */ 103 DEV_OUTPUT = 0x02, /* Playback device like speakers */ 104 DEV_SYSTEM_DEFAULT = 0x04, /* System default device */ 105 DEV_SELECTED_DEFAULT = 106 0x08, /* User selected to use the system default device */ 107 }; 108 109 void 110 audiounit_stream_stop_internal(cubeb_stream * stm); 111 static int 112 audiounit_stream_start_internal(cubeb_stream * stm); 113 static void 114 audiounit_close_stream(cubeb_stream * stm); 115 static int 116 audiounit_setup_stream(cubeb_stream * stm); 117 static vector<AudioObjectID> 118 audiounit_get_devices_of_type(cubeb_device_type devtype); 119 static UInt32 120 audiounit_get_device_presentation_latency(AudioObjectID devid, 121 AudioObjectPropertyScope scope); 122 123 #if !TARGET_OS_IPHONE 124 static AudioObjectID 125 audiounit_get_default_device_id(cubeb_device_type type); 126 static int 127 audiounit_uninstall_device_changed_callback(cubeb_stream * stm); 128 static int 129 audiounit_uninstall_system_changed_callback(cubeb_stream * stm); 130 static void 131 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags); 132 #endif 133 134 extern cubeb_ops const audiounit_ops; 135 136 struct cubeb { 137 cubeb_ops const * ops = &audiounit_ops; 138 owned_critical_section mutex; 139 int active_streams = 0; 140 uint32_t global_latency_frames = 0; 141 cubeb_device_collection_changed_callback input_collection_changed_callback = 142 nullptr; 143 void * input_collection_changed_user_ptr = nullptr; 144 cubeb_device_collection_changed_callback output_collection_changed_callback = 145 nullptr; 146 void * output_collection_changed_user_ptr = nullptr; 147 // Store list of devices to detect changes 148 vector<AudioObjectID> input_device_array; 149 vector<AudioObjectID> output_device_array; 150 // The queue should be released when it’s no longer needed. 151 dispatch_queue_t serial_queue = 152 dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL); 153 // Current used channel layout 154 atomic<cubeb_channel_layout> layout{CUBEB_LAYOUT_UNDEFINED}; 155 uint32_t channels = 0; 156 }; 157 158 static unique_ptr<AudioChannelLayout, decltype(&free)> 159 make_sized_audio_channel_layout(size_t sz) 160 { 161 assert(sz >= sizeof(AudioChannelLayout)); 162 AudioChannelLayout * acl = 163 reinterpret_cast<AudioChannelLayout *>(calloc(1, sz)); 164 assert(acl); // Assert the allocation works. 165 return unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free); 166 } 167 168 enum class io_side { 169 INPUT, 170 OUTPUT, 171 }; 172 173 static char const * 174 to_string(io_side side) 175 { 176 switch (side) { 177 case io_side::INPUT: 178 return "input"; 179 case io_side::OUTPUT: 180 return "output"; 181 } 182 } 183 184 struct device_info { 185 AudioDeviceID id = kAudioObjectUnknown; 186 device_flags_value flags = DEV_UNKNOWN; 187 }; 188 189 struct property_listener { 190 AudioDeviceID device_id; 191 const AudioObjectPropertyAddress * property_address; 192 AudioObjectPropertyListenerProc callback; 193 cubeb_stream * stream; 194 195 property_listener(AudioDeviceID id, 196 const AudioObjectPropertyAddress * address, 197 AudioObjectPropertyListenerProc proc, cubeb_stream * stm) 198 : device_id(id), property_address(address), callback(proc), stream(stm) 199 { 200 } 201 }; 202 203 struct cubeb_stream { 204 explicit cubeb_stream(cubeb * context); 205 206 /* Note: Must match cubeb_stream layout in cubeb.c. */ 207 cubeb * context; 208 void * user_ptr = nullptr; 209 /**/ 210 211 cubeb_data_callback data_callback = nullptr; 212 cubeb_state_callback state_callback = nullptr; 213 cubeb_device_changed_callback device_changed_callback = nullptr; 214 owned_critical_section device_changed_callback_lock; 215 /* Stream creation parameters */ 216 cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, 217 CUBEB_LAYOUT_UNDEFINED, 218 CUBEB_STREAM_PREF_NONE}; 219 cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, 220 CUBEB_LAYOUT_UNDEFINED, 221 CUBEB_STREAM_PREF_NONE}; 222 device_info input_device; 223 device_info output_device; 224 /* Format descriptions */ 225 AudioStreamBasicDescription input_desc; 226 AudioStreamBasicDescription output_desc; 227 /* I/O AudioUnits */ 228 AudioUnit input_unit = nullptr; 229 AudioUnit output_unit = nullptr; 230 /* I/O device sample rate */ 231 Float64 input_hw_rate = 0; 232 Float64 output_hw_rate = 0; 233 /* Expected I/O thread interleave, 234 * calculated from I/O hw rate. */ 235 int expected_output_callbacks_in_a_row = 0; 236 owned_critical_section mutex; 237 // Hold the input samples in every input callback iteration. 238 // Only accessed on input/output callback thread and during initial configure. 239 unique_ptr<auto_array_wrapper> input_linear_buffer; 240 /* Frame counters */ 241 atomic<uint64_t> frames_played{0}; 242 uint64_t frames_queued = 0; 243 // How many frames got read from the input since the stream started (includes 244 // padded silence) 245 atomic<int64_t> frames_read{0}; 246 // How many frames got written to the output device since the stream started 247 atomic<int64_t> frames_written{0}; 248 atomic<bool> shutdown{true}; 249 atomic<bool> draining{false}; 250 atomic<bool> reinit_pending{false}; 251 atomic<bool> destroy_pending{false}; 252 /* Latency requested by the user. */ 253 uint32_t latency_frames = 0; 254 atomic<uint32_t> current_latency_frames{0}; 255 atomic<uint32_t> total_output_latency_frames{0}; 256 unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler; 257 /* This is true if a device change callback is currently running. */ 258 atomic<bool> switching_device{false}; 259 atomic<bool> buffer_size_change_state{false}; 260 AudioDeviceID aggregate_device_id = 261 kAudioObjectUnknown; // the aggregate device id 262 AudioObjectID plugin_id = 263 kAudioObjectUnknown; // used to create aggregate device 264 /* Mixer interface */ 265 unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer; 266 /* Buffer where remixing/resampling will occur when upmixing is required */ 267 /* Only accessed from callback thread */ 268 unique_ptr<uint8_t[]> temp_buffer; 269 size_t temp_buffer_size = 0; // size in bytes. 270 /* Listeners indicating what system events are monitored. */ 271 unique_ptr<property_listener> default_input_listener; 272 unique_ptr<property_listener> default_output_listener; 273 unique_ptr<property_listener> input_alive_listener; 274 unique_ptr<property_listener> input_source_listener; 275 unique_ptr<property_listener> output_source_listener; 276 }; 277 278 bool 279 has_input(cubeb_stream * stm) 280 { 281 return stm->input_stream_params.rate != 0; 282 } 283 284 bool 285 has_output(cubeb_stream * stm) 286 { 287 return stm->output_stream_params.rate != 0; 288 } 289 290 cubeb_channel 291 channel_label_to_cubeb_channel(UInt32 label) 292 { 293 switch (label) { 294 case kAudioChannelLabel_Left: 295 return CHANNEL_FRONT_LEFT; 296 case kAudioChannelLabel_Right: 297 return CHANNEL_FRONT_RIGHT; 298 case kAudioChannelLabel_Center: 299 return CHANNEL_FRONT_CENTER; 300 case kAudioChannelLabel_LFEScreen: 301 return CHANNEL_LOW_FREQUENCY; 302 case kAudioChannelLabel_LeftSurround: 303 return CHANNEL_BACK_LEFT; 304 case kAudioChannelLabel_RightSurround: 305 return CHANNEL_BACK_RIGHT; 306 case kAudioChannelLabel_LeftCenter: 307 return CHANNEL_FRONT_LEFT_OF_CENTER; 308 case kAudioChannelLabel_RightCenter: 309 return CHANNEL_FRONT_RIGHT_OF_CENTER; 310 case kAudioChannelLabel_CenterSurround: 311 return CHANNEL_BACK_CENTER; 312 case kAudioChannelLabel_LeftSurroundDirect: 313 return CHANNEL_SIDE_LEFT; 314 case kAudioChannelLabel_RightSurroundDirect: 315 return CHANNEL_SIDE_RIGHT; 316 case kAudioChannelLabel_TopCenterSurround: 317 return CHANNEL_TOP_CENTER; 318 case kAudioChannelLabel_VerticalHeightLeft: 319 return CHANNEL_TOP_FRONT_LEFT; 320 case kAudioChannelLabel_VerticalHeightCenter: 321 return CHANNEL_TOP_FRONT_CENTER; 322 case kAudioChannelLabel_VerticalHeightRight: 323 return CHANNEL_TOP_FRONT_RIGHT; 324 case kAudioChannelLabel_TopBackLeft: 325 return CHANNEL_TOP_BACK_LEFT; 326 case kAudioChannelLabel_TopBackCenter: 327 return CHANNEL_TOP_BACK_CENTER; 328 case kAudioChannelLabel_TopBackRight: 329 return CHANNEL_TOP_BACK_RIGHT; 330 default: 331 return CHANNEL_UNKNOWN; 332 } 333 } 334 335 AudioChannelLabel 336 cubeb_channel_to_channel_label(cubeb_channel channel) 337 { 338 switch (channel) { 339 case CHANNEL_FRONT_LEFT: 340 return kAudioChannelLabel_Left; 341 case CHANNEL_FRONT_RIGHT: 342 return kAudioChannelLabel_Right; 343 case CHANNEL_FRONT_CENTER: 344 return kAudioChannelLabel_Center; 345 case CHANNEL_LOW_FREQUENCY: 346 return kAudioChannelLabel_LFEScreen; 347 case CHANNEL_BACK_LEFT: 348 return kAudioChannelLabel_LeftSurround; 349 case CHANNEL_BACK_RIGHT: 350 return kAudioChannelLabel_RightSurround; 351 case CHANNEL_FRONT_LEFT_OF_CENTER: 352 return kAudioChannelLabel_LeftCenter; 353 case CHANNEL_FRONT_RIGHT_OF_CENTER: 354 return kAudioChannelLabel_RightCenter; 355 case CHANNEL_BACK_CENTER: 356 return kAudioChannelLabel_CenterSurround; 357 case CHANNEL_SIDE_LEFT: 358 return kAudioChannelLabel_LeftSurroundDirect; 359 case CHANNEL_SIDE_RIGHT: 360 return kAudioChannelLabel_RightSurroundDirect; 361 case CHANNEL_TOP_CENTER: 362 return kAudioChannelLabel_TopCenterSurround; 363 case CHANNEL_TOP_FRONT_LEFT: 364 return kAudioChannelLabel_VerticalHeightLeft; 365 case CHANNEL_TOP_FRONT_CENTER: 366 return kAudioChannelLabel_VerticalHeightCenter; 367 case CHANNEL_TOP_FRONT_RIGHT: 368 return kAudioChannelLabel_VerticalHeightRight; 369 case CHANNEL_TOP_BACK_LEFT: 370 return kAudioChannelLabel_TopBackLeft; 371 case CHANNEL_TOP_BACK_CENTER: 372 return kAudioChannelLabel_TopBackCenter; 373 case CHANNEL_TOP_BACK_RIGHT: 374 return kAudioChannelLabel_TopBackRight; 375 default: 376 return kAudioChannelLabel_Unknown; 377 } 378 } 379 380 bool 381 is_common_sample_rate(Float64 sample_rate) 382 { 383 /* Some commonly used sample rates and their multiples and divisors. */ 384 return sample_rate == 8000 || sample_rate == 16000 || sample_rate == 22050 || 385 sample_rate == 32000 || sample_rate == 44100 || sample_rate == 48000 || 386 sample_rate == 88200 || sample_rate == 96000; 387 } 388 389 #if TARGET_OS_IPHONE 390 typedef UInt32 AudioDeviceID; 391 typedef UInt32 AudioObjectID; 392 393 #define AudioGetCurrentHostTime mach_absolute_time 394 395 #endif 396 397 uint64_t 398 ConvertHostTimeToNanos(uint64_t host_time) 399 { 400 static struct mach_timebase_info timebase_info; 401 static bool initialized = false; 402 if (!initialized) { 403 mach_timebase_info(&timebase_info); 404 initialized = true; 405 } 406 407 long double answer = host_time; 408 if (timebase_info.numer != timebase_info.denom) { 409 answer *= timebase_info.numer; 410 answer /= timebase_info.denom; 411 } 412 return (uint64_t)answer; 413 } 414 415 static void 416 audiounit_increment_active_streams(cubeb * ctx) 417 { 418 ctx->mutex.assert_current_thread_owns(); 419 ctx->active_streams += 1; 420 } 421 422 static void 423 audiounit_decrement_active_streams(cubeb * ctx) 424 { 425 ctx->mutex.assert_current_thread_owns(); 426 ctx->active_streams -= 1; 427 } 428 429 static int 430 audiounit_active_streams(cubeb * ctx) 431 { 432 ctx->mutex.assert_current_thread_owns(); 433 return ctx->active_streams; 434 } 435 436 static void 437 audiounit_set_global_latency(cubeb * ctx, uint32_t latency_frames) 438 { 439 ctx->mutex.assert_current_thread_owns(); 440 assert(audiounit_active_streams(ctx) == 1); 441 ctx->global_latency_frames = latency_frames; 442 } 443 444 static void 445 audiounit_make_silent(AudioBuffer * ioData) 446 { 447 assert(ioData); 448 assert(ioData->mData); 449 memset(ioData->mData, 0, ioData->mDataByteSize); 450 } 451 452 static OSStatus 453 audiounit_render_input(cubeb_stream * stm, AudioUnitRenderActionFlags * flags, 454 AudioTimeStamp const * tstamp, UInt32 bus, 455 UInt32 input_frames) 456 { 457 /* Create the AudioBufferList to store input. */ 458 AudioBufferList input_buffer_list; 459 input_buffer_list.mBuffers[0].mDataByteSize = 460 stm->input_desc.mBytesPerFrame * input_frames; 461 input_buffer_list.mBuffers[0].mData = nullptr; 462 input_buffer_list.mBuffers[0].mNumberChannels = 463 stm->input_desc.mChannelsPerFrame; 464 input_buffer_list.mNumberBuffers = 1; 465 466 /* Render input samples */ 467 OSStatus r = AudioUnitRender(stm->input_unit, flags, tstamp, bus, 468 input_frames, &input_buffer_list); 469 470 if (r != noErr) { 471 LOG("AudioUnitRender rv=%d", r); 472 if (r != kAudioUnitErr_CannotDoInCurrentContext) { 473 return r; 474 } 475 if (stm->output_unit) { 476 // kAudioUnitErr_CannotDoInCurrentContext is returned when using a BT 477 // headset and the profile is changed from A2DP to HFP/HSP. The previous 478 // output device is no longer valid and must be reset. 479 audiounit_reinit_stream_async(stm, DEV_INPUT | DEV_OUTPUT); 480 } 481 // For now state that no error occurred and feed silence, stream will be 482 // resumed once reinit has completed. 483 ALOGV("(%p) input: reinit pending feeding silence instead", stm); 484 stm->input_linear_buffer->push_silence(input_frames * 485 stm->input_desc.mChannelsPerFrame); 486 } else { 487 /* Copy input data in linear buffer. */ 488 stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData, 489 input_frames * 490 stm->input_desc.mChannelsPerFrame); 491 } 492 493 /* Advance input frame counter. */ 494 assert(input_frames > 0); 495 stm->frames_read += input_frames; 496 497 ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, " 498 "total frames %lu.", 499 stm, (unsigned int)input_buffer_list.mNumberBuffers, 500 (unsigned int)input_buffer_list.mBuffers[0].mDataByteSize, 501 (unsigned int)input_buffer_list.mBuffers[0].mNumberChannels, 502 (unsigned int)input_frames, 503 stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame); 504 505 return noErr; 506 } 507 508 static OSStatus 509 audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags, 510 AudioTimeStamp const * tstamp, UInt32 bus, 511 UInt32 input_frames, AudioBufferList * /* bufs */) 512 { 513 cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr); 514 515 assert(stm->input_unit != NULL); 516 assert(AU_IN_BUS == bus); 517 518 if (stm->shutdown) { 519 ALOG("(%p) input shutdown", stm); 520 return noErr; 521 } 522 523 if (stm->draining) { 524 OSStatus r = AudioOutputUnitStop(stm->input_unit); 525 assert(r == 0); 526 // Only fire state callback in input-only stream. For duplex stream, 527 // the state callback will be fired in output callback. 528 if (stm->output_unit == NULL) { 529 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); 530 } 531 return noErr; 532 } 533 534 OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames); 535 if (r != noErr) { 536 return r; 537 } 538 539 // Full Duplex. We'll call data_callback in the AudioUnit output callback. 540 if (stm->output_unit != NULL) { 541 return noErr; 542 } 543 544 /* Input only. Call the user callback through resampler. 545 Resampler will deliver input buffer in the correct rate. */ 546 assert(input_frames <= stm->input_linear_buffer->length() / 547 stm->input_desc.mChannelsPerFrame); 548 long total_input_frames = 549 stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; 550 long outframes = cubeb_resampler_fill(stm->resampler.get(), 551 stm->input_linear_buffer->data(), 552 &total_input_frames, NULL, 0); 553 if (outframes < 0) { 554 stm->shutdown = true; 555 OSStatus r = AudioOutputUnitStop(stm->input_unit); 556 assert(r == 0); 557 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 558 return noErr; 559 } 560 stm->draining = outframes < total_input_frames; 561 562 // Reset input buffer 563 stm->input_linear_buffer->clear(); 564 565 return noErr; 566 } 567 568 static void 569 audiounit_mix_output_buffer(cubeb_stream * stm, size_t output_frames, 570 void * input_buffer, size_t input_buffer_size, 571 void * output_buffer, size_t output_buffer_size) 572 { 573 assert(input_buffer_size >= 574 cubeb_sample_size(stm->output_stream_params.format) * 575 stm->output_stream_params.channels * output_frames); 576 assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames); 577 578 int r = cubeb_mixer_mix(stm->mixer.get(), output_frames, input_buffer, 579 input_buffer_size, output_buffer, output_buffer_size); 580 if (r != 0) { 581 LOG("Remix error = %d", r); 582 } 583 } 584 585 // Return how many input frames (sampled at input_hw_rate) are needed to provide 586 // output_frames (sampled at output_stream_params.rate) 587 static int64_t 588 minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames) 589 { 590 if (stm->input_hw_rate == stm->output_stream_params.rate) { 591 // Fast path. 592 return output_frames; 593 } 594 return ceil(stm->input_hw_rate * output_frames / 595 stm->output_stream_params.rate); 596 } 597 598 static OSStatus 599 audiounit_output_callback(void * user_ptr, 600 AudioUnitRenderActionFlags * /* flags */, 601 AudioTimeStamp const * tstamp, UInt32 bus, 602 UInt32 output_frames, AudioBufferList * outBufferList) 603 { 604 assert(AU_OUT_BUS == bus); 605 assert(outBufferList->mNumberBuffers == 1); 606 607 cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr); 608 609 uint64_t now = ConvertHostTimeToNanos(mach_absolute_time()); 610 uint64_t audio_output_time = ConvertHostTimeToNanos(tstamp->mHostTime); 611 uint64_t output_latency_ns = audio_output_time - now; 612 613 const int ns2s = 1e9; 614 // The total output latency is the timestamp difference + the stream latency + 615 // the hardware latency. 616 stm->total_output_latency_frames = 617 output_latency_ns * stm->output_hw_rate / ns2s + 618 stm->current_latency_frames; 619 620 ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input " 621 "frames %lu.", 622 stm, (unsigned int)outBufferList->mNumberBuffers, 623 (unsigned int)outBufferList->mBuffers[0].mDataByteSize, 624 (unsigned int)outBufferList->mBuffers[0].mNumberChannels, 625 (unsigned int)output_frames, 626 has_input(stm) ? stm->input_linear_buffer->length() / 627 stm->input_desc.mChannelsPerFrame 628 : 0); 629 630 long input_frames = 0; 631 void *output_buffer = NULL, *input_buffer = NULL; 632 633 if (stm->shutdown) { 634 ALOG("(%p) output shutdown.", stm); 635 audiounit_make_silent(&outBufferList->mBuffers[0]); 636 return noErr; 637 } 638 639 if (stm->draining) { 640 OSStatus r = AudioOutputUnitStop(stm->output_unit); 641 assert(r == 0); 642 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); 643 audiounit_make_silent(&outBufferList->mBuffers[0]); 644 return noErr; 645 } 646 647 /* Get output buffer. */ 648 if (stm->mixer) { 649 // If remixing needs to occur, we can't directly work in our final 650 // destination buffer as data may be overwritten or too small to start with. 651 size_t size_needed = output_frames * stm->output_stream_params.channels * 652 cubeb_sample_size(stm->output_stream_params.format); 653 if (stm->temp_buffer_size < size_needed) { 654 stm->temp_buffer.reset(new uint8_t[size_needed]); 655 stm->temp_buffer_size = size_needed; 656 } 657 output_buffer = stm->temp_buffer.get(); 658 } else { 659 output_buffer = outBufferList->mBuffers[0].mData; 660 } 661 662 stm->frames_written += output_frames; 663 664 /* If Full duplex get also input buffer */ 665 if (stm->input_unit != NULL) { 666 /* If the output callback came first and this is a duplex stream, we need to 667 * fill in some additional silence in the resampler. 668 * Otherwise, if we had more than expected callbacks in a row, or we're 669 * currently switching, we add some silence as well to compensate for the 670 * fact that we're lacking some input data. */ 671 uint32_t input_frames_needed = 672 minimum_resampling_input_frames(stm, stm->frames_written); 673 long missing_frames = input_frames_needed - stm->frames_read; 674 if (missing_frames > 0) { 675 stm->input_linear_buffer->push_silence(missing_frames * 676 stm->input_desc.mChannelsPerFrame); 677 stm->frames_read = input_frames_needed; 678 679 ALOG("(%p) %s pushed %ld frames of input silence.", stm, 680 stm->frames_read == 0 ? "Input hasn't started," 681 : stm->switching_device ? "Device switching," 682 : "Drop out,", 683 missing_frames); 684 } 685 input_buffer = stm->input_linear_buffer->data(); 686 // Number of input frames in the buffer. It will change to actually used 687 // frames inside fill 688 input_frames = 689 stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; 690 } 691 692 /* Call user callback through resampler. */ 693 long outframes = cubeb_resampler_fill(stm->resampler.get(), input_buffer, 694 input_buffer ? &input_frames : NULL, 695 output_buffer, output_frames); 696 697 if (input_buffer) { 698 // Pop from the buffer the frames used by the the resampler. 699 stm->input_linear_buffer->pop(input_frames * 700 stm->input_desc.mChannelsPerFrame); 701 } 702 703 if (outframes < 0 || outframes > output_frames) { 704 stm->shutdown = true; 705 OSStatus r = AudioOutputUnitStop(stm->output_unit); 706 assert(r == 0); 707 if (stm->input_unit) { 708 r = AudioOutputUnitStop(stm->input_unit); 709 assert(r == 0); 710 } 711 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 712 audiounit_make_silent(&outBufferList->mBuffers[0]); 713 return noErr; 714 } 715 716 stm->draining = (UInt32)outframes < output_frames; 717 stm->frames_played = stm->frames_queued; 718 stm->frames_queued += outframes; 719 720 /* Post process output samples. */ 721 if (stm->draining) { 722 /* Clear missing frames (silence) */ 723 size_t channels = stm->output_stream_params.channels; 724 size_t missing_samples = (output_frames - outframes) * channels; 725 size_t size_sample = cubeb_sample_size(stm->output_stream_params.format); 726 /* number of bytes that have been filled with valid audio by the callback. 727 */ 728 size_t audio_byte_count = outframes * channels * size_sample; 729 PodZero((uint8_t *)output_buffer + audio_byte_count, 730 missing_samples * size_sample); 731 } 732 733 /* Mixing */ 734 if (stm->mixer) { 735 audiounit_mix_output_buffer(stm, output_frames, output_buffer, 736 stm->temp_buffer_size, 737 outBufferList->mBuffers[0].mData, 738 outBufferList->mBuffers[0].mDataByteSize); 739 } 740 741 return noErr; 742 } 743 744 extern "C" { 745 int 746 audiounit_init(cubeb ** context, char const * /* context_name */) 747 { 748 #if !TARGET_OS_IPHONE 749 cubeb_set_coreaudio_notification_runloop(); 750 #endif 751 752 *context = new cubeb; 753 754 return CUBEB_OK; 755 } 756 } 757 758 static char const * 759 audiounit_get_backend_id(cubeb * /* ctx */) 760 { 761 return "audiounit"; 762 } 763 764 #if !TARGET_OS_IPHONE 765 766 static int 767 audiounit_stream_get_volume(cubeb_stream * stm, float * volume); 768 static int 769 audiounit_stream_set_volume(cubeb_stream * stm, float volume); 770 771 static int 772 audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side) 773 { 774 assert(stm); 775 776 device_info * info = nullptr; 777 cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN; 778 779 if (side == io_side::INPUT) { 780 info = &stm->input_device; 781 type = CUBEB_DEVICE_TYPE_INPUT; 782 } else if (side == io_side::OUTPUT) { 783 info = &stm->output_device; 784 type = CUBEB_DEVICE_TYPE_OUTPUT; 785 } 786 memset(info, 0, sizeof(device_info)); 787 info->id = id; 788 789 if (side == io_side::INPUT) { 790 info->flags |= DEV_INPUT; 791 } else if (side == io_side::OUTPUT) { 792 info->flags |= DEV_OUTPUT; 793 } 794 795 AudioDeviceID default_device_id = audiounit_get_default_device_id(type); 796 if (default_device_id == kAudioObjectUnknown) { 797 return CUBEB_ERROR; 798 } 799 if (id == kAudioObjectUnknown) { 800 info->id = default_device_id; 801 info->flags |= DEV_SELECTED_DEFAULT; 802 } 803 804 if (info->id == default_device_id) { 805 info->flags |= DEV_SYSTEM_DEFAULT; 806 } 807 808 assert(info->id); 809 assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) || 810 !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT); 811 812 return CUBEB_OK; 813 } 814 815 static int 816 audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags) 817 { 818 auto_lock context_lock(stm->context->mutex); 819 assert((flags & DEV_INPUT && stm->input_unit) || 820 (flags & DEV_OUTPUT && stm->output_unit)); 821 if (!stm->shutdown) { 822 audiounit_stream_stop_internal(stm); 823 } 824 825 int r = audiounit_uninstall_device_changed_callback(stm); 826 if (r != CUBEB_OK) { 827 LOG("(%p) Could not uninstall all device change listeners.", stm); 828 } 829 830 { 831 auto_lock lock(stm->mutex); 832 float volume = 0.0; 833 int vol_rv = CUBEB_ERROR; 834 if (stm->output_unit) { 835 vol_rv = audiounit_stream_get_volume(stm, &volume); 836 } 837 838 audiounit_close_stream(stm); 839 840 /* Reinit occurs in one of the following case: 841 * - When the device is not alive any more 842 * - When the default system device change. 843 * - The bluetooth device changed from A2DP to/from HFP/HSP profile 844 * We first attempt to re-use the same device id, should that fail we will 845 * default to the (potentially new) default device. */ 846 AudioDeviceID input_device = 847 flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown; 848 if (flags & DEV_INPUT) { 849 r = audiounit_set_device_info(stm, input_device, io_side::INPUT); 850 if (r != CUBEB_OK) { 851 LOG("(%p) Set input device info failed. This can happen when last " 852 "media device is unplugged", 853 stm); 854 return CUBEB_ERROR; 855 } 856 } 857 858 /* Always use the default output on reinit. This is not correct in every 859 * case but it is sufficient for Firefox and prevent reinit from reporting 860 * failures. It will change soon when reinit mechanism will be updated. */ 861 r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT); 862 if (r != CUBEB_OK) { 863 LOG("(%p) Set output device info failed. This can happen when last media " 864 "device is unplugged", 865 stm); 866 return CUBEB_ERROR; 867 } 868 869 if (audiounit_setup_stream(stm) != CUBEB_OK) { 870 LOG("(%p) Stream reinit failed.", stm); 871 if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) { 872 // Attempt to re-use the same device-id failed, so attempt again with 873 // default input device. 874 audiounit_close_stream(stm); 875 if (audiounit_set_device_info(stm, kAudioObjectUnknown, 876 io_side::INPUT) != CUBEB_OK || 877 audiounit_setup_stream(stm) != CUBEB_OK) { 878 LOG("(%p) Second stream reinit failed.", stm); 879 return CUBEB_ERROR; 880 } 881 } 882 } 883 884 if (vol_rv == CUBEB_OK) { 885 audiounit_stream_set_volume(stm, volume); 886 } 887 888 // If the stream was running, start it again. 889 if (!stm->shutdown) { 890 r = audiounit_stream_start_internal(stm); 891 if (r != CUBEB_OK) { 892 return CUBEB_ERROR; 893 } 894 } 895 } 896 return CUBEB_OK; 897 } 898 899 static void 900 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags) 901 { 902 if (std::atomic_exchange(&stm->reinit_pending, true)) { 903 // A reinit task is already pending, nothing more to do. 904 ALOG("(%p) re-init stream task already pending, cancelling request", stm); 905 return; 906 } 907 908 // Use a new thread, through the queue, to avoid deadlock when calling 909 // Get/SetProperties method from inside notify callback 910 dispatch_async(stm->context->serial_queue, ^() { 911 if (stm->destroy_pending) { 912 ALOG("(%p) stream pending destroy, cancelling reinit task", stm); 913 return; 914 } 915 916 if (audiounit_reinit_stream(stm, flags) != CUBEB_OK) { 917 if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) { 918 LOG("(%p) Could not uninstall system changed callback", stm); 919 } 920 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); 921 LOG("(%p) Could not reopen the stream after switching.", stm); 922 } 923 stm->switching_device = false; 924 stm->reinit_pending = false; 925 }); 926 } 927 928 static char const * 929 event_addr_to_string(AudioObjectPropertySelector selector) 930 { 931 switch (selector) { 932 case kAudioHardwarePropertyDefaultOutputDevice: 933 return "kAudioHardwarePropertyDefaultOutputDevice"; 934 case kAudioHardwarePropertyDefaultInputDevice: 935 return "kAudioHardwarePropertyDefaultInputDevice"; 936 case kAudioDevicePropertyDeviceIsAlive: 937 return "kAudioDevicePropertyDeviceIsAlive"; 938 case kAudioDevicePropertyDataSource: 939 return "kAudioDevicePropertyDataSource"; 940 default: 941 return "Unknown"; 942 } 943 } 944 945 static OSStatus 946 audiounit_property_listener_callback( 947 AudioObjectID id, UInt32 address_count, 948 const AudioObjectPropertyAddress * addresses, void * user) 949 { 950 cubeb_stream * stm = (cubeb_stream *)user; 951 if (stm->switching_device) { 952 LOG("Switching is already taking place. Skip Event %s for id=%d", 953 event_addr_to_string(addresses[0].mSelector), id); 954 return noErr; 955 } 956 stm->switching_device = true; 957 958 LOG("(%p) Audio device changed, %u events.", stm, 959 (unsigned int)address_count); 960 for (UInt32 i = 0; i < address_count; i++) { 961 switch (addresses[i].mSelector) { 962 case kAudioHardwarePropertyDefaultOutputDevice: { 963 LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice " 964 "for id=%d", 965 (unsigned int)i, id); 966 } break; 967 case kAudioHardwarePropertyDefaultInputDevice: { 968 LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice " 969 "for id=%d", 970 (unsigned int)i, id); 971 } break; 972 case kAudioDevicePropertyDeviceIsAlive: { 973 LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for " 974 "id=%d", 975 (unsigned int)i, id); 976 // If this is the default input device ignore the event, 977 // kAudioHardwarePropertyDefaultInputDevice will take care of the switch 978 if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) { 979 LOG("It's the default input device, ignore the event"); 980 stm->switching_device = false; 981 return noErr; 982 } 983 } break; 984 case kAudioDevicePropertyDataSource: { 985 LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", 986 (unsigned int)i, id); 987 } break; 988 default: 989 LOG("Event[%u] - mSelector == Unexpected Event id %d, return", 990 (unsigned int)i, addresses[i].mSelector); 991 stm->switching_device = false; 992 return noErr; 993 } 994 } 995 996 // Allow restart to choose the new default 997 device_flags_value switch_side = DEV_UNKNOWN; 998 if (has_input(stm)) { 999 switch_side |= DEV_INPUT; 1000 } 1001 if (has_output(stm)) { 1002 switch_side |= DEV_OUTPUT; 1003 } 1004 1005 for (UInt32 i = 0; i < address_count; i++) { 1006 switch (addresses[i].mSelector) { 1007 case kAudioHardwarePropertyDefaultOutputDevice: 1008 case kAudioHardwarePropertyDefaultInputDevice: 1009 case kAudioDevicePropertyDeviceIsAlive: 1010 /* fall through */ 1011 case kAudioDevicePropertyDataSource: { 1012 auto_lock dev_cb_lock(stm->device_changed_callback_lock); 1013 if (stm->device_changed_callback) { 1014 stm->device_changed_callback(stm->user_ptr); 1015 } 1016 break; 1017 } 1018 } 1019 } 1020 1021 audiounit_reinit_stream_async(stm, switch_side); 1022 1023 return noErr; 1024 } 1025 1026 OSStatus 1027 audiounit_add_listener(const property_listener * listener) 1028 { 1029 assert(listener); 1030 return AudioObjectAddPropertyListener(listener->device_id, 1031 listener->property_address, 1032 listener->callback, listener->stream); 1033 } 1034 1035 OSStatus 1036 audiounit_remove_listener(const property_listener * listener) 1037 { 1038 assert(listener); 1039 return AudioObjectRemovePropertyListener( 1040 listener->device_id, listener->property_address, listener->callback, 1041 listener->stream); 1042 } 1043 1044 static int 1045 audiounit_install_device_changed_callback(cubeb_stream * stm) 1046 { 1047 OSStatus rv; 1048 int r = CUBEB_OK; 1049 1050 if (stm->output_unit) { 1051 /* This event will notify us when the data source on the same device 1052 * changes, for example when the user plugs in a normal (non-usb) headset in 1053 * the headphone jack. */ 1054 stm->output_source_listener.reset(new property_listener( 1055 stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS, 1056 &audiounit_property_listener_callback, stm)); 1057 rv = audiounit_add_listener(stm->output_source_listener.get()); 1058 if (rv != noErr) { 1059 stm->output_source_listener.reset(); 1060 LOG("AudioObjectAddPropertyListener/output/" 1061 "kAudioDevicePropertyDataSource rv=%d, device id=%d", 1062 rv, stm->output_device.id); 1063 r = CUBEB_ERROR; 1064 } 1065 } 1066 1067 if (stm->input_unit) { 1068 /* This event will notify us when the data source on the input device 1069 * changes. */ 1070 stm->input_source_listener.reset(new property_listener( 1071 stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS, 1072 &audiounit_property_listener_callback, stm)); 1073 rv = audiounit_add_listener(stm->input_source_listener.get()); 1074 if (rv != noErr) { 1075 stm->input_source_listener.reset(); 1076 LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource " 1077 "rv=%d, device id=%d", 1078 rv, stm->input_device.id); 1079 r = CUBEB_ERROR; 1080 } 1081 1082 /* Event to notify when the input is going away. */ 1083 stm->input_alive_listener.reset(new property_listener( 1084 stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS, 1085 &audiounit_property_listener_callback, stm)); 1086 rv = audiounit_add_listener(stm->input_alive_listener.get()); 1087 if (rv != noErr) { 1088 stm->input_alive_listener.reset(); 1089 LOG("AudioObjectAddPropertyListener/input/" 1090 "kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", 1091 rv, stm->input_device.id); 1092 r = CUBEB_ERROR; 1093 } 1094 } 1095 1096 return r; 1097 } 1098 1099 static int 1100 audiounit_install_system_changed_callback(cubeb_stream * stm) 1101 { 1102 OSStatus r; 1103 1104 if (stm->output_unit) { 1105 /* This event will notify us when the default audio device changes, 1106 * for example when the user plugs in a USB headset and the system chooses 1107 * it automatically as the default, or when another device is chosen in the 1108 * dropdown list. */ 1109 stm->default_output_listener.reset(new property_listener( 1110 kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS, 1111 &audiounit_property_listener_callback, stm)); 1112 r = audiounit_add_listener(stm->default_output_listener.get()); 1113 if (r != noErr) { 1114 stm->default_output_listener.reset(); 1115 LOG("AudioObjectAddPropertyListener/output/" 1116 "kAudioHardwarePropertyDefaultOutputDevice rv=%d", 1117 r); 1118 return CUBEB_ERROR; 1119 } 1120 } 1121 1122 if (stm->input_unit) { 1123 /* This event will notify us when the default input device changes. */ 1124 stm->default_input_listener.reset(new property_listener( 1125 kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS, 1126 &audiounit_property_listener_callback, stm)); 1127 r = audiounit_add_listener(stm->default_input_listener.get()); 1128 if (r != noErr) { 1129 stm->default_input_listener.reset(); 1130 LOG("AudioObjectAddPropertyListener/input/" 1131 "kAudioHardwarePropertyDefaultInputDevice rv=%d", 1132 r); 1133 return CUBEB_ERROR; 1134 } 1135 } 1136 1137 return CUBEB_OK; 1138 } 1139 1140 static int 1141 audiounit_uninstall_device_changed_callback(cubeb_stream * stm) 1142 { 1143 OSStatus rv; 1144 // Failing to uninstall listeners is not a fatal error. 1145 int r = CUBEB_OK; 1146 1147 if (stm->output_source_listener) { 1148 rv = audiounit_remove_listener(stm->output_source_listener.get()); 1149 if (rv != noErr) { 1150 LOG("AudioObjectRemovePropertyListener/output/" 1151 "kAudioDevicePropertyDataSource rv=%d, device id=%d", 1152 rv, stm->output_device.id); 1153 r = CUBEB_ERROR; 1154 } 1155 stm->output_source_listener.reset(); 1156 } 1157 1158 if (stm->input_source_listener) { 1159 rv = audiounit_remove_listener(stm->input_source_listener.get()); 1160 if (rv != noErr) { 1161 LOG("AudioObjectRemovePropertyListener/input/" 1162 "kAudioDevicePropertyDataSource rv=%d, device id=%d", 1163 rv, stm->input_device.id); 1164 r = CUBEB_ERROR; 1165 } 1166 stm->input_source_listener.reset(); 1167 } 1168 1169 if (stm->input_alive_listener) { 1170 rv = audiounit_remove_listener(stm->input_alive_listener.get()); 1171 if (rv != noErr) { 1172 LOG("AudioObjectRemovePropertyListener/input/" 1173 "kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", 1174 rv, stm->input_device.id); 1175 r = CUBEB_ERROR; 1176 } 1177 stm->input_alive_listener.reset(); 1178 } 1179 1180 return r; 1181 } 1182 1183 static int 1184 audiounit_uninstall_system_changed_callback(cubeb_stream * stm) 1185 { 1186 OSStatus r; 1187 1188 if (stm->default_output_listener) { 1189 r = audiounit_remove_listener(stm->default_output_listener.get()); 1190 if (r != noErr) { 1191 return CUBEB_ERROR; 1192 } 1193 stm->default_output_listener.reset(); 1194 } 1195 1196 if (stm->default_input_listener) { 1197 r = audiounit_remove_listener(stm->default_input_listener.get()); 1198 if (r != noErr) { 1199 return CUBEB_ERROR; 1200 } 1201 stm->default_input_listener.reset(); 1202 } 1203 return CUBEB_OK; 1204 } 1205 1206 /* Get the acceptable buffer size (in frames) that this device can work with. */ 1207 static int 1208 audiounit_get_acceptable_latency_range(AudioValueRange * latency_range) 1209 { 1210 UInt32 size; 1211 OSStatus r; 1212 AudioDeviceID output_device_id; 1213 AudioObjectPropertyAddress output_device_buffer_size_range = { 1214 kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyScopeOutput, 1215 kAudioObjectPropertyElementMaster}; 1216 1217 output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); 1218 if (output_device_id == kAudioObjectUnknown) { 1219 LOG("Could not get default output device id."); 1220 return CUBEB_ERROR; 1221 } 1222 1223 /* Get the buffer size range this device supports */ 1224 size = sizeof(*latency_range); 1225 1226 r = AudioObjectGetPropertyData(output_device_id, 1227 &output_device_buffer_size_range, 0, NULL, 1228 &size, latency_range); 1229 if (r != noErr) { 1230 LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r); 1231 return CUBEB_ERROR; 1232 } 1233 1234 return CUBEB_OK; 1235 } 1236 #endif /* !TARGET_OS_IPHONE */ 1237 1238 static AudioObjectID 1239 audiounit_get_default_device_id(cubeb_device_type type) 1240 { 1241 const AudioObjectPropertyAddress * adr; 1242 if (type == CUBEB_DEVICE_TYPE_OUTPUT) { 1243 adr = &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS; 1244 } else if (type == CUBEB_DEVICE_TYPE_INPUT) { 1245 adr = &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS; 1246 } else { 1247 return kAudioObjectUnknown; 1248 } 1249 1250 AudioDeviceID devid; 1251 UInt32 size = sizeof(AudioDeviceID); 1252 if (AudioObjectGetPropertyData(kAudioObjectSystemObject, adr, 0, NULL, &size, 1253 &devid) != noErr) { 1254 return kAudioObjectUnknown; 1255 } 1256 1257 return devid; 1258 } 1259 1260 int 1261 audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) 1262 { 1263 #if TARGET_OS_IPHONE 1264 // TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels] 1265 *max_channels = 2; 1266 #else 1267 UInt32 size; 1268 OSStatus r; 1269 AudioDeviceID output_device_id; 1270 AudioStreamBasicDescription stream_format; 1271 AudioObjectPropertyAddress stream_format_address = { 1272 kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, 1273 kAudioObjectPropertyElementMaster}; 1274 1275 assert(ctx && max_channels); 1276 1277 output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); 1278 if (output_device_id == kAudioObjectUnknown) { 1279 return CUBEB_ERROR; 1280 } 1281 1282 size = sizeof(stream_format); 1283 1284 r = AudioObjectGetPropertyData(output_device_id, &stream_format_address, 0, 1285 NULL, &size, &stream_format); 1286 if (r != noErr) { 1287 LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r); 1288 return CUBEB_ERROR; 1289 } 1290 1291 *max_channels = stream_format.mChannelsPerFrame; 1292 #endif 1293 return CUBEB_OK; 1294 } 1295 1296 static int 1297 audiounit_get_min_latency(cubeb * /* ctx */, cubeb_stream_params /* params */, 1298 uint32_t * latency_frames) 1299 { 1300 #if TARGET_OS_IPHONE 1301 // TODO: [[AVAudioSession sharedInstance] inputLatency] 1302 return CUBEB_ERROR_NOT_SUPPORTED; 1303 #else 1304 AudioValueRange latency_range; 1305 if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) { 1306 LOG("Could not get acceptable latency range."); 1307 return CUBEB_ERROR; 1308 } 1309 1310 *latency_frames = 1311 max<uint32_t>(latency_range.mMinimum, SAFE_MIN_LATENCY_FRAMES); 1312 #endif 1313 1314 return CUBEB_OK; 1315 } 1316 1317 static int 1318 audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate) 1319 { 1320 #if TARGET_OS_IPHONE 1321 // TODO 1322 return CUBEB_ERROR_NOT_SUPPORTED; 1323 #else 1324 UInt32 size; 1325 OSStatus r; 1326 Float64 fsamplerate; 1327 AudioDeviceID output_device_id; 1328 AudioObjectPropertyAddress samplerate_address = { 1329 kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, 1330 kAudioObjectPropertyElementMaster}; 1331 1332 output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); 1333 if (output_device_id == kAudioObjectUnknown) { 1334 return CUBEB_ERROR; 1335 } 1336 1337 size = sizeof(fsamplerate); 1338 r = AudioObjectGetPropertyData(output_device_id, &samplerate_address, 0, NULL, 1339 &size, &fsamplerate); 1340 1341 if (r != noErr) { 1342 return CUBEB_ERROR; 1343 } 1344 1345 *rate = static_cast<uint32_t>(fsamplerate); 1346 #endif 1347 return CUBEB_OK; 1348 } 1349 1350 static cubeb_channel_layout 1351 audiounit_convert_channel_layout(AudioChannelLayout * layout) 1352 { 1353 // When having one or two channel, force mono or stereo. Some devices (namely, 1354 // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for 1355 // some reason. 1356 if (layout->mNumberChannelDescriptions == 1) { 1357 return CUBEB_LAYOUT_MONO; 1358 } else if (layout->mNumberChannelDescriptions == 2) { 1359 return CUBEB_LAYOUT_STEREO; 1360 } 1361 1362 if (layout->mChannelLayoutTag != 1363 kAudioChannelLayoutTag_UseChannelDescriptions) { 1364 // kAudioChannelLayoutTag_UseChannelBitmap 1365 // kAudioChannelLayoutTag_Mono 1366 // kAudioChannelLayoutTag_Stereo 1367 // .... 1368 LOG("Only handle UseChannelDescriptions for now.\n"); 1369 return CUBEB_LAYOUT_UNDEFINED; 1370 } 1371 1372 cubeb_channel_layout cl = 0; 1373 for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) { 1374 cubeb_channel cc = channel_label_to_cubeb_channel( 1375 layout->mChannelDescriptions[i].mChannelLabel); 1376 if (cc == CHANNEL_UNKNOWN) { 1377 return CUBEB_LAYOUT_UNDEFINED; 1378 } 1379 cl |= cc; 1380 } 1381 1382 return cl; 1383 } 1384 1385 static cubeb_channel_layout 1386 audiounit_get_preferred_channel_layout(AudioUnit output_unit) 1387 { 1388 OSStatus rv = noErr; 1389 UInt32 size = 0; 1390 rv = AudioUnitGetPropertyInfo( 1391 output_unit, kAudioDevicePropertyPreferredChannelLayout, 1392 kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr); 1393 if (rv != noErr) { 1394 LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout " 1395 "rv=%d", 1396 rv); 1397 return CUBEB_LAYOUT_UNDEFINED; 1398 } 1399 assert(size > 0); 1400 1401 auto layout = make_sized_audio_channel_layout(size); 1402 rv = AudioUnitGetProperty( 1403 output_unit, kAudioDevicePropertyPreferredChannelLayout, 1404 kAudioUnitScope_Output, AU_OUT_BUS, layout.get(), &size); 1405 if (rv != noErr) { 1406 LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d", 1407 rv); 1408 return CUBEB_LAYOUT_UNDEFINED; 1409 } 1410 1411 return audiounit_convert_channel_layout(layout.get()); 1412 } 1413 1414 static cubeb_channel_layout 1415 audiounit_get_current_channel_layout(AudioUnit output_unit) 1416 { 1417 OSStatus rv = noErr; 1418 UInt32 size = 0; 1419 rv = AudioUnitGetPropertyInfo( 1420 output_unit, kAudioUnitProperty_AudioChannelLayout, 1421 kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr); 1422 if (rv != noErr) { 1423 LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", 1424 rv); 1425 // This property isn't known before macOS 10.12, attempt another method. 1426 return audiounit_get_preferred_channel_layout(output_unit); 1427 } 1428 assert(size > 0); 1429 1430 auto layout = make_sized_audio_channel_layout(size); 1431 rv = AudioUnitGetProperty(output_unit, kAudioUnitProperty_AudioChannelLayout, 1432 kAudioUnitScope_Output, AU_OUT_BUS, layout.get(), 1433 &size); 1434 if (rv != noErr) { 1435 LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv); 1436 return CUBEB_LAYOUT_UNDEFINED; 1437 } 1438 1439 return audiounit_convert_channel_layout(layout.get()); 1440 } 1441 1442 static int 1443 audiounit_create_unit(AudioUnit * unit, device_info * device); 1444 1445 static OSStatus 1446 audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype); 1447 1448 static void 1449 audiounit_destroy(cubeb * ctx) 1450 { 1451 { 1452 auto_lock lock(ctx->mutex); 1453 1454 // Disabling this assert for bug 1083664 -- we seem to leak a stream 1455 // assert(ctx->active_streams == 0); 1456 if (audiounit_active_streams(ctx) > 0) { 1457 LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, 1458 audiounit_active_streams(ctx)); 1459 } 1460 1461 // Destroying a cubeb context with device collection callbacks registered 1462 // is misuse of the API, assert then attempt to clean up. 1463 assert(!ctx->input_collection_changed_callback && 1464 !ctx->input_collection_changed_user_ptr && 1465 !ctx->output_collection_changed_callback && 1466 !ctx->output_collection_changed_user_ptr); 1467 1468 /* Unregister the callback if necessary. */ 1469 if (ctx->input_collection_changed_callback) { 1470 audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT); 1471 } 1472 if (ctx->output_collection_changed_callback) { 1473 audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_OUTPUT); 1474 } 1475 } 1476 1477 dispatch_release(ctx->serial_queue); 1478 1479 delete ctx; 1480 } 1481 1482 static void 1483 audiounit_stream_destroy(cubeb_stream * stm); 1484 1485 static int 1486 audio_stream_desc_init(AudioStreamBasicDescription * ss, 1487 const cubeb_stream_params * stream_params) 1488 { 1489 switch (stream_params->format) { 1490 case CUBEB_SAMPLE_S16LE: 1491 ss->mBitsPerChannel = 16; 1492 ss->mFormatFlags = kAudioFormatFlagIsSignedInteger; 1493 break; 1494 case CUBEB_SAMPLE_S16BE: 1495 ss->mBitsPerChannel = 16; 1496 ss->mFormatFlags = 1497 kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian; 1498 break; 1499 case CUBEB_SAMPLE_FLOAT32LE: 1500 ss->mBitsPerChannel = 32; 1501 ss->mFormatFlags = kAudioFormatFlagIsFloat; 1502 break; 1503 case CUBEB_SAMPLE_FLOAT32BE: 1504 ss->mBitsPerChannel = 32; 1505 ss->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsBigEndian; 1506 break; 1507 default: 1508 return CUBEB_ERROR_INVALID_FORMAT; 1509 } 1510 1511 ss->mFormatID = kAudioFormatLinearPCM; 1512 ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked; 1513 ss->mSampleRate = stream_params->rate; 1514 ss->mChannelsPerFrame = stream_params->channels; 1515 1516 ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame; 1517 ss->mFramesPerPacket = 1; 1518 ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket; 1519 1520 ss->mReserved = 0; 1521 1522 return CUBEB_OK; 1523 } 1524 1525 void 1526 audiounit_init_mixer(cubeb_stream * stm) 1527 { 1528 // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio 1529 // data, it silently drop the channels so we need to remix the 1530 // audio data by ourselves to keep all the information. 1531 stm->mixer.reset(cubeb_mixer_create( 1532 stm->output_stream_params.format, stm->output_stream_params.channels, 1533 stm->output_stream_params.layout, stm->context->channels, 1534 stm->context->layout)); 1535 assert(stm->mixer); 1536 } 1537 1538 static int 1539 audiounit_set_channel_layout(AudioUnit unit, io_side side, 1540 cubeb_channel_layout layout) 1541 { 1542 if (side != io_side::OUTPUT) { 1543 return CUBEB_ERROR; 1544 } 1545 1546 if (layout == CUBEB_LAYOUT_UNDEFINED) { 1547 // We leave everything as-is... 1548 return CUBEB_OK; 1549 } 1550 1551 OSStatus r; 1552 uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout); 1553 1554 // We do not use CoreAudio standard layout for lack of documentation on what 1555 // the actual channel orders are. So we set a custom layout. 1556 size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[nb_channels]); 1557 auto au_layout = make_sized_audio_channel_layout(size); 1558 au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; 1559 au_layout->mNumberChannelDescriptions = nb_channels; 1560 1561 uint32_t channels = 0; 1562 cubeb_channel_layout channelMap = layout; 1563 for (uint32_t i = 0; channelMap != 0; ++i) { 1564 XASSERT(channels < nb_channels); 1565 uint32_t channel = (channelMap & 1) << i; 1566 if (channel != 0) { 1567 au_layout->mChannelDescriptions[channels].mChannelLabel = 1568 cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel)); 1569 au_layout->mChannelDescriptions[channels].mChannelFlags = 1570 kAudioChannelFlags_AllOff; 1571 channels++; 1572 } 1573 channelMap = channelMap >> 1; 1574 } 1575 1576 r = AudioUnitSetProperty(unit, kAudioUnitProperty_AudioChannelLayout, 1577 kAudioUnitScope_Input, AU_OUT_BUS, au_layout.get(), 1578 size); 1579 if (r != noErr) { 1580 LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", 1581 to_string(side), r); 1582 return CUBEB_ERROR; 1583 } 1584 1585 return CUBEB_OK; 1586 } 1587 1588 void 1589 audiounit_layout_init(cubeb_stream * stm, io_side side) 1590 { 1591 // We currently don't support the input layout setting. 1592 if (side == io_side::INPUT) { 1593 return; 1594 } 1595 1596 stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit); 1597 1598 audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT, 1599 stm->context->layout); 1600 } 1601 1602 static vector<AudioObjectID> 1603 audiounit_get_sub_devices(AudioDeviceID device_id) 1604 { 1605 vector<AudioDeviceID> sub_devices; 1606 AudioObjectPropertyAddress property_address = { 1607 kAudioAggregateDevicePropertyActiveSubDeviceList, 1608 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; 1609 UInt32 size = 0; 1610 OSStatus rv = AudioObjectGetPropertyDataSize(device_id, &property_address, 0, 1611 nullptr, &size); 1612 1613 if (rv != noErr) { 1614 sub_devices.push_back(device_id); 1615 return sub_devices; 1616 } 1617 1618 uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID)); 1619 sub_devices.resize(count); 1620 rv = AudioObjectGetPropertyData(device_id, &property_address, 0, nullptr, 1621 &size, sub_devices.data()); 1622 if (rv != noErr) { 1623 sub_devices.clear(); 1624 sub_devices.push_back(device_id); 1625 } else { 1626 LOG("Found %u sub-devices", count); 1627 } 1628 return sub_devices; 1629 } 1630 1631 static int 1632 audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, 1633 AudioDeviceID * aggregate_device_id) 1634 { 1635 AudioObjectPropertyAddress address_plugin_bundle_id = { 1636 kAudioHardwarePropertyPlugInForBundleID, kAudioObjectPropertyScopeGlobal, 1637 kAudioObjectPropertyElementMaster}; 1638 UInt32 size = 0; 1639 OSStatus r = AudioObjectGetPropertyDataSize( 1640 kAudioObjectSystemObject, &address_plugin_bundle_id, 0, NULL, &size); 1641 if (r != noErr) { 1642 LOG("AudioObjectGetPropertyDataSize/" 1643 "kAudioHardwarePropertyPlugInForBundleID, rv=%d", 1644 r); 1645 return CUBEB_ERROR; 1646 } 1647 1648 AudioValueTranslation translation_value; 1649 CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio"); 1650 translation_value.mInputData = &in_bundle_ref; 1651 translation_value.mInputDataSize = sizeof(in_bundle_ref); 1652 translation_value.mOutputData = plugin_id; 1653 translation_value.mOutputDataSize = sizeof(*plugin_id); 1654 1655 r = AudioObjectGetPropertyData(kAudioObjectSystemObject, 1656 &address_plugin_bundle_id, 0, nullptr, &size, 1657 &translation_value); 1658 if (r != noErr) { 1659 LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, " 1660 "rv=%d", 1661 r); 1662 return CUBEB_ERROR; 1663 } 1664 1665 AudioObjectPropertyAddress create_aggregate_device_address = { 1666 kAudioPlugInCreateAggregateDevice, kAudioObjectPropertyScopeGlobal, 1667 kAudioObjectPropertyElementMaster}; 1668 r = AudioObjectGetPropertyDataSize( 1669 *plugin_id, &create_aggregate_device_address, 0, nullptr, &size); 1670 if (r != noErr) { 1671 LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, " 1672 "rv=%d", 1673 r); 1674 return CUBEB_ERROR; 1675 } 1676 1677 CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable( 1678 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, 1679 &kCFTypeDictionaryValueCallBacks); 1680 struct timeval timestamp; 1681 gettimeofday(×tamp, NULL); 1682 long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec; 1683 CFStringRef aggregate_device_name = CFStringCreateWithFormat( 1684 NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id); 1685 CFDictionaryAddValue(aggregate_device_dict, 1686 CFSTR(kAudioAggregateDeviceNameKey), 1687 aggregate_device_name); 1688 CFRelease(aggregate_device_name); 1689 1690 CFStringRef aggregate_device_UID = 1691 CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), 1692 PRIVATE_AGGREGATE_DEVICE_NAME, time_id); 1693 CFDictionaryAddValue(aggregate_device_dict, 1694 CFSTR(kAudioAggregateDeviceUIDKey), 1695 aggregate_device_UID); 1696 CFRelease(aggregate_device_UID); 1697 1698 int private_value = 1; 1699 CFNumberRef aggregate_device_private_key = 1700 CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value); 1701 CFDictionaryAddValue(aggregate_device_dict, 1702 CFSTR(kAudioAggregateDeviceIsPrivateKey), 1703 aggregate_device_private_key); 1704 CFRelease(aggregate_device_private_key); 1705 1706 int stacked_value = 0; 1707 CFNumberRef aggregate_device_stacked_key = 1708 CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value); 1709 CFDictionaryAddValue(aggregate_device_dict, 1710 CFSTR(kAudioAggregateDeviceIsStackedKey), 1711 aggregate_device_stacked_key); 1712 CFRelease(aggregate_device_stacked_key); 1713 1714 r = AudioObjectGetPropertyData(*plugin_id, &create_aggregate_device_address, 1715 sizeof(aggregate_device_dict), 1716 &aggregate_device_dict, &size, 1717 aggregate_device_id); 1718 CFRelease(aggregate_device_dict); 1719 if (r != noErr) { 1720 LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", 1721 r); 1722 return CUBEB_ERROR; 1723 } 1724 LOG("New aggregate device %u", *aggregate_device_id); 1725 1726 return CUBEB_OK; 1727 } 1728 1729 // The returned CFStringRef object needs to be released (via CFRelease) 1730 // if it's not NULL, since the reference count of the returned CFStringRef 1731 // object is increased. 1732 static CFStringRef 1733 get_device_name(AudioDeviceID id) 1734 { 1735 UInt32 size = sizeof(CFStringRef); 1736 CFStringRef UIname = nullptr; 1737 AudioObjectPropertyAddress address_uuid = {kAudioDevicePropertyDeviceUID, 1738 kAudioObjectPropertyScopeGlobal, 1739 kAudioObjectPropertyElementMaster}; 1740 OSStatus err = 1741 AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname); 1742 return (err == noErr) ? UIname : NULL; 1743 } 1744 1745 static int 1746 audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id, 1747 AudioDeviceID input_device_id, 1748 AudioDeviceID output_device_id) 1749 { 1750 LOG("Add devices input %u and output %u into aggregate device %u", 1751 input_device_id, output_device_id, aggregate_device_id); 1752 const vector<AudioDeviceID> output_sub_devices = 1753 audiounit_get_sub_devices(output_device_id); 1754 const vector<AudioDeviceID> input_sub_devices = 1755 audiounit_get_sub_devices(input_device_id); 1756 1757 CFMutableArrayRef aggregate_sub_devices_array = 1758 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 1759 /* The order of the items in the array is significant and is used to determine 1760 the order of the streams of the AudioAggregateDevice. */ 1761 for (UInt32 i = 0; i < output_sub_devices.size(); i++) { 1762 CFStringRef ref = get_device_name(output_sub_devices[i]); 1763 if (ref == NULL) { 1764 CFRelease(aggregate_sub_devices_array); 1765 return CUBEB_ERROR; 1766 } 1767 CFArrayAppendValue(aggregate_sub_devices_array, ref); 1768 CFRelease(ref); 1769 } 1770 for (UInt32 i = 0; i < input_sub_devices.size(); i++) { 1771 CFStringRef ref = get_device_name(input_sub_devices[i]); 1772 if (ref == NULL) { 1773 CFRelease(aggregate_sub_devices_array); 1774 return CUBEB_ERROR; 1775 } 1776 CFArrayAppendValue(aggregate_sub_devices_array, ref); 1777 CFRelease(ref); 1778 } 1779 1780 AudioObjectPropertyAddress aggregate_sub_device_list = { 1781 kAudioAggregateDevicePropertyFullSubDeviceList, 1782 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; 1783 UInt32 size = sizeof(CFMutableArrayRef); 1784 OSStatus rv = AudioObjectSetPropertyData( 1785 aggregate_device_id, &aggregate_sub_device_list, 0, nullptr, size, 1786 &aggregate_sub_devices_array); 1787 CFRelease(aggregate_sub_devices_array); 1788 if (rv != noErr) { 1789 LOG("AudioObjectSetPropertyData/" 1790 "kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", 1791 rv); 1792 return CUBEB_ERROR; 1793 } 1794 1795 return CUBEB_OK; 1796 } 1797 1798 static int 1799 audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id) 1800 { 1801 assert(aggregate_device_id != kAudioObjectUnknown); 1802 AudioObjectPropertyAddress master_aggregate_sub_device = { 1803 kAudioAggregateDevicePropertyMasterSubDevice, 1804 kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; 1805 1806 // Master become the 1st output sub device 1807 AudioDeviceID output_device_id = 1808 audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); 1809 const vector<AudioDeviceID> output_sub_devices = 1810 audiounit_get_sub_devices(output_device_id); 1811 CFStringRef master_sub_device = get_device_name(output_sub_devices[0]); 1812 1813 UInt32 size = sizeof(CFStringRef); 1814 OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id, 1815 &master_aggregate_sub_device, 0, 1816 NULL, size, &master_sub_device); 1817 if (master_sub_device) { 1818 CFRelease(master_sub_device); 1819 } 1820 if (rv != noErr) { 1821 LOG("AudioObjectSetPropertyData/" 1822 "kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", 1823 rv); 1824 return CUBEB_ERROR; 1825 } 1826 1827 return CUBEB_OK; 1828 } 1829 1830 static int 1831 audiounit_activate_clock_drift_compensation( 1832 const AudioDeviceID aggregate_device_id) 1833 { 1834 assert(aggregate_device_id != kAudioObjectUnknown); 1835 AudioObjectPropertyAddress address_owned = { 1836 kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, 1837 kAudioObjectPropertyElementMaster}; 1838 1839 UInt32 qualifier_data_size = sizeof(AudioObjectID); 1840 AudioClassID class_id = kAudioSubDeviceClassID; 1841 void * qualifier_data = &class_id; 1842 UInt32 size = 0; 1843 OSStatus rv = AudioObjectGetPropertyDataSize( 1844 aggregate_device_id, &address_owned, qualifier_data_size, qualifier_data, 1845 &size); 1846 if (rv != noErr) { 1847 LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, " 1848 "rv=%d", 1849 rv); 1850 return CUBEB_ERROR; 1851 } 1852 1853 UInt32 subdevices_num = 0; 1854 subdevices_num = size / sizeof(AudioObjectID); 1855 AudioObjectID sub_devices[subdevices_num]; 1856 size = sizeof(sub_devices); 1857 1858 rv = AudioObjectGetPropertyData(aggregate_device_id, &address_owned, 1859 qualifier_data_size, qualifier_data, &size, 1860 sub_devices); 1861 if (rv != noErr) { 1862 LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", 1863 rv); 1864 return CUBEB_ERROR; 1865 } 1866 1867 AudioObjectPropertyAddress address_drift = { 1868 kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, 1869 kAudioObjectPropertyElementMaster}; 1870 1871 // Start from the second device since the first is the master clock 1872 for (UInt32 i = 1; i < subdevices_num; ++i) { 1873 UInt32 drift_compensation_value = 1; 1874 rv = AudioObjectSetPropertyData(sub_devices[i], &address_drift, 0, nullptr, 1875 sizeof(UInt32), &drift_compensation_value); 1876 if (rv != noErr) { 1877 LOG("AudioObjectSetPropertyData/" 1878 "kAudioSubDevicePropertyDriftCompensation, rv=%d", 1879 rv); 1880 return CUBEB_OK; 1881 } 1882 } 1883 return CUBEB_OK; 1884 } 1885 1886 static int 1887 audiounit_destroy_aggregate_device(AudioObjectID plugin_id, 1888 AudioDeviceID * aggregate_device_id); 1889 static void 1890 audiounit_get_available_samplerate(AudioObjectID devid, 1891 AudioObjectPropertyScope scope, 1892 uint32_t * min, uint32_t * max, 1893 uint32_t * def); 1894 static int 1895 audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, 1896 AudioObjectID devid, cubeb_device_type type); 1897 static void 1898 audiounit_device_destroy(cubeb_device_info * device); 1899 1900 static void 1901 audiounit_workaround_for_airpod(cubeb_stream * stm) 1902 { 1903 cubeb_device_info input_device_info; 1904 audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id, 1905 CUBEB_DEVICE_TYPE_INPUT); 1906 1907 cubeb_device_info output_device_info; 1908 audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id, 1909 CUBEB_DEVICE_TYPE_OUTPUT); 1910 1911 std::string input_name_str(input_device_info.friendly_name); 1912 std::string output_name_str(output_device_info.friendly_name); 1913 1914 if (input_name_str.find("AirPods") != std::string::npos && 1915 output_name_str.find("AirPods") != std::string::npos) { 1916 uint32_t input_min_rate = 0; 1917 uint32_t input_max_rate = 0; 1918 uint32_t input_nominal_rate = 0; 1919 audiounit_get_available_samplerate( 1920 stm->input_device.id, kAudioObjectPropertyScopeGlobal, &input_min_rate, 1921 &input_max_rate, &input_nominal_rate); 1922 LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u", 1923 stm, stm->input_device.id, input_device_info.friendly_name, 1924 input_min_rate, input_max_rate, input_nominal_rate); 1925 uint32_t output_min_rate = 0; 1926 uint32_t output_max_rate = 0; 1927 uint32_t output_nominal_rate = 0; 1928 audiounit_get_available_samplerate( 1929 stm->output_device.id, kAudioObjectPropertyScopeGlobal, 1930 &output_min_rate, &output_max_rate, &output_nominal_rate); 1931 LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u", 1932 stm, stm->output_device.id, output_device_info.friendly_name, 1933 output_min_rate, output_max_rate, output_nominal_rate); 1934 1935 Float64 rate = input_nominal_rate; 1936 AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate, 1937 kAudioObjectPropertyScopeGlobal, 1938 kAudioObjectPropertyElementMaster}; 1939 1940 OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id, &addr, 0, 1941 nullptr, sizeof(Float64), &rate); 1942 if (rv != noErr) { 1943 LOG("Non fatal error, " 1944 "AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, " 1945 "rv=%d", 1946 rv); 1947 } 1948 } 1949 audiounit_device_destroy(&input_device_info); 1950 audiounit_device_destroy(&output_device_info); 1951 } 1952 1953 /* 1954 * Aggregate Device is a virtual audio interface which utilizes inputs and 1955 * outputs of one or more physical audio interfaces. It is possible to use the 1956 * clock of one of the devices as a master clock for all the combined devices 1957 * and enable drift compensation for the devices that are not designated clock 1958 * master. 1959 * 1960 * Creating a new aggregate device programmatically requires [0][1]: 1961 * 1. Locate the base plug-in ("com.apple.audio.CoreAudio") 1962 * 2. Create a dictionary that describes the aggregate device 1963 * (don't add sub-devices in that step, prone to fail [0]) 1964 * 3. Ask the base plug-in to create the aggregate device (blank) 1965 * 4. Add the array of sub-devices. 1966 * 5. Set the master device (1st output device in our case) 1967 * 6. Enable drift compensation for the non-master devices 1968 * 1969 * [0] https://lists.apple.com/archives/coreaudio-api/2006/Apr/msg00092.html 1970 * [1] https://lists.apple.com/archives/coreaudio-api/2005/Jul/msg00150.html 1971 * [2] CoreAudio.framework/Headers/AudioHardware.h 1972 * */ 1973 static int 1974 audiounit_create_aggregate_device(cubeb_stream * stm) 1975 { 1976 int r = audiounit_create_blank_aggregate_device(&stm->plugin_id, 1977 &stm->aggregate_device_id); 1978 if (r != CUBEB_OK) { 1979 LOG("(%p) Failed to create blank aggregate device", stm); 1980 return CUBEB_ERROR; 1981 } 1982 1983 r = audiounit_set_aggregate_sub_device_list( 1984 stm->aggregate_device_id, stm->input_device.id, stm->output_device.id); 1985 if (r != CUBEB_OK) { 1986 LOG("(%p) Failed to set aggregate sub-device list", stm); 1987 audiounit_destroy_aggregate_device(stm->plugin_id, 1988 &stm->aggregate_device_id); 1989 return CUBEB_ERROR; 1990 } 1991 1992 r = audiounit_set_master_aggregate_device(stm->aggregate_device_id); 1993 if (r != CUBEB_OK) { 1994 LOG("(%p) Failed to set master sub-device for aggregate device", stm); 1995 audiounit_destroy_aggregate_device(stm->plugin_id, 1996 &stm->aggregate_device_id); 1997 return CUBEB_ERROR; 1998 } 1999 2000 r = audiounit_activate_clock_drift_compensation(stm->aggregate_device_id); 2001 if (r != CUBEB_OK) { 2002 LOG("(%p) Failed to activate clock drift compensation for aggregate device", 2003 stm); 2004 audiounit_destroy_aggregate_device(stm->plugin_id, 2005 &stm->aggregate_device_id); 2006 return CUBEB_ERROR; 2007 } 2008 2009 audiounit_workaround_for_airpod(stm); 2010 2011 return CUBEB_OK; 2012 } 2013 2014 static int 2015 audiounit_destroy_aggregate_device(AudioObjectID plugin_id, 2016 AudioDeviceID * aggregate_device_id) 2017 { 2018 assert(aggregate_device_id && *aggregate_device_id != kAudioDeviceUnknown && 2019 plugin_id != kAudioObjectUnknown); 2020 AudioObjectPropertyAddress destroy_aggregate_device_addr = { 2021 kAudioPlugInDestroyAggregateDevice, kAudioObjectPropertyScopeGlobal, 2022 kAudioObjectPropertyElementMaster}; 2023 UInt32 size; 2024 OSStatus rv = AudioObjectGetPropertyDataSize( 2025 plugin_id, &destroy_aggregate_device_addr, 0, NULL, &size); 2026 if (rv != noErr) { 2027 LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, " 2028 "rv=%d", 2029 rv); 2030 return CUBEB_ERROR; 2031 } 2032 2033 rv = AudioObjectGetPropertyData(plugin_id, &destroy_aggregate_device_addr, 0, 2034 NULL, &size, aggregate_device_id); 2035 if (rv != noErr) { 2036 LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", 2037 rv); 2038 return CUBEB_ERROR; 2039 } 2040 2041 LOG("Destroyed aggregate device %d", *aggregate_device_id); 2042 *aggregate_device_id = kAudioObjectUnknown; 2043 return CUBEB_OK; 2044 } 2045 2046 static int 2047 audiounit_new_unit_instance(AudioUnit * unit, device_info * device) 2048 { 2049 AudioComponentDescription desc; 2050 AudioComponent comp; 2051 OSStatus rv; 2052 2053 desc.componentType = kAudioUnitType_Output; 2054 #if TARGET_OS_IPHONE 2055 desc.componentSubType = kAudioUnitSubType_RemoteIO; 2056 #else 2057 // Use the DefaultOutputUnit for output when no device is specified 2058 // so we retain automatic output device switching when the default 2059 // changes. Once we have complete support for device notifications 2060 // and switching, we can use the AUHAL for everything. 2061 if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) { 2062 desc.componentSubType = kAudioUnitSubType_DefaultOutput; 2063 } else { 2064 desc.componentSubType = kAudioUnitSubType_HALOutput; 2065 } 2066 #endif 2067 desc.componentManufacturer = kAudioUnitManufacturer_Apple; 2068 desc.componentFlags = 0; 2069 desc.componentFlagsMask = 0; 2070 comp = AudioComponentFindNext(NULL, &desc); 2071 if (comp == NULL) { 2072 LOG("Could not find matching audio hardware."); 2073 return CUBEB_ERROR; 2074 } 2075 2076 rv = AudioComponentInstanceNew(comp, unit); 2077 if (rv != noErr) { 2078 LOG("AudioComponentInstanceNew rv=%d", rv); 2079 return CUBEB_ERROR; 2080 } 2081 return CUBEB_OK; 2082 } 2083 2084 enum enable_state { 2085 DISABLE, 2086 ENABLE, 2087 }; 2088 2089 static int 2090 audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state) 2091 { 2092 OSStatus rv; 2093 UInt32 enable = state; 2094 rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, 2095 (side == io_side::INPUT) ? kAudioUnitScope_Input 2096 : kAudioUnitScope_Output, 2097 (side == io_side::INPUT) ? AU_IN_BUS : AU_OUT_BUS, 2098 &enable, sizeof(UInt32)); 2099 if (rv != noErr) { 2100 LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv); 2101 return CUBEB_ERROR; 2102 } 2103 return CUBEB_OK; 2104 } 2105 2106 static int 2107 audiounit_create_unit(AudioUnit * unit, device_info * device) 2108 { 2109 assert(*unit == nullptr); 2110 assert(device); 2111 2112 OSStatus rv; 2113 int r; 2114 2115 r = audiounit_new_unit_instance(unit, device); 2116 if (r != CUBEB_OK) { 2117 return r; 2118 } 2119 assert(*unit); 2120 2121 if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) { 2122 return CUBEB_OK; 2123 } 2124 2125 if (device->flags & DEV_INPUT) { 2126 r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE); 2127 if (r != CUBEB_OK) { 2128 LOG("Failed to enable audiounit input scope"); 2129 return r; 2130 } 2131 r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE); 2132 if (r != CUBEB_OK) { 2133 LOG("Failed to disable audiounit output scope"); 2134 return r; 2135 } 2136 } else if (device->flags & DEV_OUTPUT) { 2137 r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE); 2138 if (r != CUBEB_OK) { 2139 LOG("Failed to enable audiounit output scope"); 2140 return r; 2141 } 2142 r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE); 2143 if (r != CUBEB_OK) { 2144 LOG("Failed to disable audiounit input scope"); 2145 return r; 2146 } 2147 } else { 2148 assert(false); 2149 } 2150 2151 rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice, 2152 kAudioUnitScope_Global, 0, &device->id, 2153 sizeof(AudioDeviceID)); 2154 if (rv != noErr) { 2155 LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d", 2156 rv); 2157 return CUBEB_ERROR; 2158 } 2159 2160 return CUBEB_OK; 2161 } 2162 2163 static int 2164 audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity) 2165 { 2166 uint32_t size = 2167 capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame; 2168 if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) { 2169 stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size)); 2170 } else { 2171 stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size)); 2172 } 2173 assert(stream->input_linear_buffer->length() == 0); 2174 2175 return CUBEB_OK; 2176 } 2177 2178 static uint32_t 2179 audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames) 2180 { 2181 // For the 1st stream set anything within safe min-max 2182 assert(audiounit_active_streams(stm->context) > 0); 2183 if (audiounit_active_streams(stm->context) == 1) { 2184 return max(min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES), 2185 SAFE_MIN_LATENCY_FRAMES); 2186 } 2187 assert(stm->output_unit); 2188 2189 // If more than one stream operates in parallel 2190 // allow only lower values of latency 2191 int r; 2192 UInt32 output_buffer_size = 0; 2193 UInt32 size = sizeof(output_buffer_size); 2194 if (stm->output_unit) { 2195 r = AudioUnitGetProperty( 2196 stm->output_unit, kAudioDevicePropertyBufferFrameSize, 2197 kAudioUnitScope_Output, AU_OUT_BUS, &output_buffer_size, &size); 2198 if (r != noErr) { 2199 LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize " 2200 "rv=%d", 2201 r); 2202 return 0; 2203 } 2204 2205 output_buffer_size = 2206 max(min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES), 2207 SAFE_MIN_LATENCY_FRAMES); 2208 } 2209 2210 UInt32 input_buffer_size = 0; 2211 if (stm->input_unit) { 2212 r = AudioUnitGetProperty( 2213 stm->input_unit, kAudioDevicePropertyBufferFrameSize, 2214 kAudioUnitScope_Input, AU_IN_BUS, &input_buffer_size, &size); 2215 if (r != noErr) { 2216 LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize " 2217 "rv=%d", 2218 r); 2219 return 0; 2220 } 2221 2222 input_buffer_size = 2223 max(min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES), 2224 SAFE_MIN_LATENCY_FRAMES); 2225 } 2226 2227 // Every following active streams can only set smaller latency 2228 UInt32 upper_latency_limit = 0; 2229 if (input_buffer_size != 0 && output_buffer_size != 0) { 2230 upper_latency_limit = min<uint32_t>(input_buffer_size, output_buffer_size); 2231 } else if (input_buffer_size != 0) { 2232 upper_latency_limit = input_buffer_size; 2233 } else if (output_buffer_size != 0) { 2234 upper_latency_limit = output_buffer_size; 2235 } else { 2236 upper_latency_limit = SAFE_MAX_LATENCY_FRAMES; 2237 } 2238 2239 return max(min<uint32_t>(latency_frames, upper_latency_limit), 2240 SAFE_MIN_LATENCY_FRAMES); 2241 } 2242 2243 /* 2244 * Change buffer size is prone to deadlock thus we change it 2245 * following the steps: 2246 * - register a listener for the buffer size property 2247 * - change the property 2248 * - wait until the listener is executed 2249 * - property has changed, remove the listener 2250 * */ 2251 static void 2252 buffer_size_changed_callback(void * inClientData, AudioUnit inUnit, 2253 AudioUnitPropertyID inPropertyID, 2254 AudioUnitScope inScope, AudioUnitElement inElement) 2255 { 2256 cubeb_stream * stm = (cubeb_stream *)inClientData; 2257 2258 AudioUnit au = inUnit; 2259 AudioUnitScope au_scope = kAudioUnitScope_Input; 2260 AudioUnitElement au_element = inElement; 2261 char const * au_type = "output"; 2262 2263 if (AU_IN_BUS == inElement) { 2264 au_scope = kAudioUnitScope_Output; 2265 au_type = "input"; 2266 } 2267 2268 switch (inPropertyID) { 2269 2270 case kAudioDevicePropertyBufferFrameSize: { 2271 if (inScope != au_scope) { 2272 break; 2273 } 2274 UInt32 new_buffer_size; 2275 UInt32 outSize = sizeof(UInt32); 2276 OSStatus r = 2277 AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, au_scope, 2278 au_element, &new_buffer_size, &outSize); 2279 if (r != noErr) { 2280 LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current " 2281 "buffer size", 2282 stm); 2283 } else { 2284 LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size " 2285 "= %d for scope %d", 2286 stm, au_type, new_buffer_size, inScope); 2287 } 2288 stm->buffer_size_change_state = true; 2289 break; 2290 } 2291 } 2292 } 2293 2294 static int 2295 audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, 2296 io_side side) 2297 { 2298 AudioUnit au = stm->output_unit; 2299 AudioUnitScope au_scope = kAudioUnitScope_Input; 2300 AudioUnitElement au_element = AU_OUT_BUS; 2301 2302 if (side == io_side::INPUT) { 2303 au = stm->input_unit; 2304 au_scope = kAudioUnitScope_Output; 2305 au_element = AU_IN_BUS; 2306 } 2307 2308 uint32_t buffer_frames = 0; 2309 UInt32 size = sizeof(buffer_frames); 2310 int r = AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, 2311 au_scope, au_element, &buffer_frames, &size); 2312 if (r != noErr) { 2313 LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", 2314 to_string(side), r); 2315 return CUBEB_ERROR; 2316 } 2317 2318 if (new_size_frames == buffer_frames) { 2319 LOG("(%p) No need to update %s buffer size already %u frames", stm, 2320 to_string(side), buffer_frames); 2321 return CUBEB_OK; 2322 } 2323 2324 r = AudioUnitAddPropertyListener(au, kAudioDevicePropertyBufferFrameSize, 2325 buffer_size_changed_callback, stm); 2326 if (r != noErr) { 2327 LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize " 2328 "rv=%d", 2329 to_string(side), r); 2330 return CUBEB_ERROR; 2331 } 2332 2333 stm->buffer_size_change_state = false; 2334 2335 r = AudioUnitSetProperty(au, kAudioDevicePropertyBufferFrameSize, au_scope, 2336 au_element, &new_size_frames, 2337 sizeof(new_size_frames)); 2338 if (r != noErr) { 2339 LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", 2340 to_string(side), r); 2341 2342 r = AudioUnitRemovePropertyListenerWithUserData( 2343 au, kAudioDevicePropertyBufferFrameSize, buffer_size_changed_callback, 2344 stm); 2345 if (r != noErr) { 2346 LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize " 2347 "rv=%d", 2348 to_string(side), r); 2349 } 2350 2351 return CUBEB_ERROR; 2352 } 2353 2354 int count = 0; 2355 while (!stm->buffer_size_change_state && count++ < 30) { 2356 struct timespec req, rem; 2357 req.tv_sec = 0; 2358 req.tv_nsec = 100000000L; // 0.1 sec 2359 if (nanosleep(&req, &rem) < 0) { 2360 LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time " 2361 "%ld nano secs \n", 2362 stm, rem.tv_nsec); 2363 } 2364 LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count); 2365 } 2366 2367 r = AudioUnitRemovePropertyListenerWithUserData( 2368 au, kAudioDevicePropertyBufferFrameSize, buffer_size_changed_callback, 2369 stm); 2370 if (r != noErr) { 2371 LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize " 2372 "rv=%d", 2373 to_string(side), r); 2374 return CUBEB_ERROR; 2375 } 2376 2377 if (!stm->buffer_size_change_state && count >= 30) { 2378 LOG("(%p) Error, did not get buffer size change callback ...", stm); 2379 return CUBEB_ERROR; 2380 } 2381 2382 LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side), 2383 new_size_frames); 2384 return CUBEB_OK; 2385 } 2386 2387 static int 2388 audiounit_configure_input(cubeb_stream * stm) 2389 { 2390 assert(stm && stm->input_unit); 2391 2392 int r = 0; 2393 UInt32 size; 2394 AURenderCallbackStruct aurcbs_in; 2395 2396 LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in " 2397 "frames %u.", 2398 stm, stm->input_stream_params.rate, stm->input_stream_params.channels, 2399 stm->input_stream_params.format, stm->latency_frames); 2400 2401 /* Get input device sample rate. */ 2402 AudioStreamBasicDescription input_hw_desc; 2403 size = sizeof(AudioStreamBasicDescription); 2404 r = AudioUnitGetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat, 2405 kAudioUnitScope_Input, AU_IN_BUS, &input_hw_desc, 2406 &size); 2407 if (r != noErr) { 2408 LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r); 2409 return CUBEB_ERROR; 2410 } 2411 stm->input_hw_rate = input_hw_desc.mSampleRate; 2412 LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate); 2413 2414 /* Set format description according to the input params. */ 2415 r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params); 2416 if (r != CUBEB_OK) { 2417 LOG("(%p) Setting format description for input failed.", stm); 2418 return r; 2419 } 2420 2421 // Use latency to set buffer size 2422 r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::INPUT); 2423 if (r != CUBEB_OK) { 2424 LOG("(%p) Error in change input buffer size.", stm); 2425 return CUBEB_ERROR; 2426 } 2427 2428 AudioStreamBasicDescription src_desc = stm->input_desc; 2429 /* Input AudioUnit must be configured with device's sample rate. 2430 we will resample inside input callback. */ 2431 src_desc.mSampleRate = stm->input_hw_rate; 2432 2433 r = AudioUnitSetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat, 2434 kAudioUnitScope_Output, AU_IN_BUS, &src_desc, 2435 sizeof(AudioStreamBasicDescription)); 2436 if (r != noErr) { 2437 LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r); 2438 return CUBEB_ERROR; 2439 } 2440 2441 /* Frames per buffer in the input callback. */ 2442 r = AudioUnitSetProperty( 2443 stm->input_unit, kAudioUnitProperty_MaximumFramesPerSlice, 2444 kAudioUnitScope_Global, AU_IN_BUS, &stm->latency_frames, sizeof(UInt32)); 2445 if (r != noErr) { 2446 LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice " 2447 "rv=%d", 2448 r); 2449 return CUBEB_ERROR; 2450 } 2451 2452 // Input only capacity 2453 unsigned int array_capacity = 1; 2454 if (has_output(stm)) { 2455 // Full-duplex increase capacity 2456 array_capacity = 8; 2457 } 2458 if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) { 2459 return CUBEB_ERROR; 2460 } 2461 2462 aurcbs_in.inputProc = audiounit_input_callback; 2463 aurcbs_in.inputProcRefCon = stm; 2464 2465 r = AudioUnitSetProperty( 2466 stm->input_unit, kAudioOutputUnitProperty_SetInputCallback, 2467 kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_in, sizeof(aurcbs_in)); 2468 if (r != noErr) { 2469 LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback " 2470 "rv=%d", 2471 r); 2472 return CUBEB_ERROR; 2473 } 2474 2475 stm->frames_read = 0; 2476 2477 LOG("(%p) Input audiounit init successfully.", stm); 2478 2479 return CUBEB_OK; 2480 } 2481 2482 static int 2483 audiounit_configure_output(cubeb_stream * stm) 2484 { 2485 assert(stm && stm->output_unit); 2486 2487 int r; 2488 AURenderCallbackStruct aurcbs_out; 2489 UInt32 size; 2490 2491 LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in " 2492 "frames %u.", 2493 stm, stm->output_stream_params.rate, stm->output_stream_params.channels, 2494 stm->output_stream_params.format, stm->latency_frames); 2495 2496 r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params); 2497 if (r != CUBEB_OK) { 2498 LOG("(%p) Could not initialize the audio stream description.", stm); 2499 return r; 2500 } 2501 2502 /* Get output device sample rate. */ 2503 AudioStreamBasicDescription output_hw_desc; 2504 size = sizeof(AudioStreamBasicDescription); 2505 memset(&output_hw_desc, 0, size); 2506 r = AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat, 2507 kAudioUnitScope_Output, AU_OUT_BUS, &output_hw_desc, 2508 &size); 2509 if (r != noErr) { 2510 LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r); 2511 return CUBEB_ERROR; 2512 } 2513 stm->output_hw_rate = output_hw_desc.mSampleRate; 2514 if (!is_common_sample_rate(stm->output_desc.mSampleRate)) { 2515 /* For uncommon sample rates, we may run into issues with the OS 2516 resampler if we don't do the resampling ourselves, so set the 2517 AudioUnit sample rate to the hardware rate and resample. */ 2518 stm->output_desc.mSampleRate = stm->output_hw_rate; 2519 } 2520 LOG("(%p) Output device sampling rate: %.2f", stm, 2521 output_hw_desc.mSampleRate); 2522 stm->context->channels = output_hw_desc.mChannelsPerFrame; 2523 2524 // Set the input layout to match the output device layout. 2525 audiounit_layout_init(stm, io_side::OUTPUT); 2526 if (stm->context->channels != stm->output_stream_params.channels || 2527 stm->context->layout != stm->output_stream_params.layout) { 2528 LOG("Incompatible channel layouts detected, setting up remixer"); 2529 audiounit_init_mixer(stm); 2530 // We will be remixing the data before it reaches the output device. 2531 // We need to adjust the number of channels and other 2532 // AudioStreamDescription details. 2533 stm->output_desc.mChannelsPerFrame = stm->context->channels; 2534 stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) * 2535 stm->output_desc.mChannelsPerFrame; 2536 stm->output_desc.mBytesPerPacket = 2537 stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket; 2538 } else { 2539 stm->mixer = nullptr; 2540 } 2541 2542 r = AudioUnitSetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat, 2543 kAudioUnitScope_Input, AU_OUT_BUS, &stm->output_desc, 2544 sizeof(AudioStreamBasicDescription)); 2545 if (r != noErr) { 2546 LOG("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r); 2547 return CUBEB_ERROR; 2548 } 2549 2550 r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::OUTPUT); 2551 if (r != CUBEB_OK) { 2552 LOG("(%p) Error in change output buffer size.", stm); 2553 return CUBEB_ERROR; 2554 } 2555 2556 /* Frames per buffer in the input callback. */ 2557 r = AudioUnitSetProperty( 2558 stm->output_unit, kAudioUnitProperty_MaximumFramesPerSlice, 2559 kAudioUnitScope_Global, AU_OUT_BUS, &stm->latency_frames, sizeof(UInt32)); 2560 if (r != noErr) { 2561 LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice " 2562 "rv=%d", 2563 r); 2564 return CUBEB_ERROR; 2565 } 2566 2567 aurcbs_out.inputProc = audiounit_output_callback; 2568 aurcbs_out.inputProcRefCon = stm; 2569 r = AudioUnitSetProperty( 2570 stm->output_unit, kAudioUnitProperty_SetRenderCallback, 2571 kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_out, sizeof(aurcbs_out)); 2572 if (r != noErr) { 2573 LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback " 2574 "rv=%d", 2575 r); 2576 return CUBEB_ERROR; 2577 } 2578 2579 stm->frames_written = 0; 2580 2581 LOG("(%p) Output audiounit init successfully.", stm); 2582 return CUBEB_OK; 2583 } 2584 2585 static int 2586 audiounit_setup_stream(cubeb_stream * stm) 2587 { 2588 stm->mutex.assert_current_thread_owns(); 2589 2590 if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) || 2591 (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) { 2592 LOG("(%p) Loopback not supported for audiounit.", stm); 2593 return CUBEB_ERROR_NOT_SUPPORTED; 2594 } 2595 2596 int r = 0; 2597 2598 device_info in_dev_info = stm->input_device; 2599 device_info out_dev_info = stm->output_device; 2600 2601 if (has_input(stm) && has_output(stm) && 2602 stm->input_device.id != stm->output_device.id) { 2603 r = audiounit_create_aggregate_device(stm); 2604 if (r != CUBEB_OK) { 2605 stm->aggregate_device_id = kAudioObjectUnknown; 2606 LOG("(%p) Create aggregate devices failed.", stm); 2607 // !!!NOTE: It is not necessary to return here. If it does not 2608 // return it will fallback to the old implementation. The intention 2609 // is to investigate how often it fails. I plan to remove 2610 // it after a couple of weeks. 2611 return r; 2612 } else { 2613 in_dev_info.id = out_dev_info.id = stm->aggregate_device_id; 2614 in_dev_info.flags = DEV_INPUT; 2615 out_dev_info.flags = DEV_OUTPUT; 2616 } 2617 } 2618 2619 if (has_input(stm)) { 2620 r = audiounit_create_unit(&stm->input_unit, &in_dev_info); 2621 if (r != CUBEB_OK) { 2622 LOG("(%p) AudioUnit creation for input failed.", stm); 2623 return r; 2624 } 2625 } 2626 2627 if (has_output(stm)) { 2628 r = audiounit_create_unit(&stm->output_unit, &out_dev_info); 2629 if (r != CUBEB_OK) { 2630 LOG("(%p) AudioUnit creation for output failed.", stm); 2631 return r; 2632 } 2633 } 2634 2635 /* Latency cannot change if another stream is operating in parallel. In this 2636 * case latency is set to the other stream value. */ 2637 if (audiounit_active_streams(stm->context) > 1) { 2638 LOG("(%p) More than one active stream, use global latency.", stm); 2639 stm->latency_frames = stm->context->global_latency_frames; 2640 } else { 2641 /* Silently clamp the latency down to the platform default, because we 2642 * synthetize the clock from the callbacks, and we want the clock to update 2643 * often. */ 2644 stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames); 2645 assert(stm->latency_frames); // Ugly error check 2646 audiounit_set_global_latency(stm->context, stm->latency_frames); 2647 } 2648 2649 /* Configure I/O stream */ 2650 if (has_input(stm)) { 2651 r = audiounit_configure_input(stm); 2652 if (r != CUBEB_OK) { 2653 LOG("(%p) Configure audiounit input failed.", stm); 2654 return r; 2655 } 2656 } 2657 2658 if (has_output(stm)) { 2659 r = audiounit_configure_output(stm); 2660 if (r != CUBEB_OK) { 2661 LOG("(%p) Configure audiounit output failed.", stm); 2662 return r; 2663 } 2664 } 2665 2666 // Setting the latency doesn't work well for USB headsets (eg. plantronics). 2667 // Keep the default latency for now. 2668 #if 0 2669 buffer_size = latency; 2670 2671 /* Get the range of latency this particular device can work with, and clamp 2672 * the requested latency to this acceptable range. */ 2673 #if !TARGET_OS_IPHONE 2674 if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) { 2675 return CUBEB_ERROR; 2676 } 2677 2678 if (buffer_size < (unsigned int) latency_range.mMinimum) { 2679 buffer_size = (unsigned int) latency_range.mMinimum; 2680 } else if (buffer_size > (unsigned int) latency_range.mMaximum) { 2681 buffer_size = (unsigned int) latency_range.mMaximum; 2682 } 2683 2684 /** 2685 * Get the default buffer size. If our latency request is below the default, 2686 * set it. Otherwise, use the default latency. 2687 **/ 2688 size = sizeof(default_buffer_size); 2689 if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize, 2690 kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) { 2691 return CUBEB_ERROR; 2692 } 2693 2694 if (buffer_size < default_buffer_size) { 2695 /* Set the maximum number of frame that the render callback will ask for, 2696 * effectively setting the latency of the stream. This is process-wide. */ 2697 if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize, 2698 kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) { 2699 return CUBEB_ERROR; 2700 } 2701 } 2702 #else // TARGET_OS_IPHONE 2703 //TODO: [[AVAudioSession sharedInstance] inputLatency] 2704 // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios 2705 #endif 2706 #endif 2707 2708 /* We use a resampler because input AudioUnit operates 2709 * reliable only in the capture device sample rate. 2710 * Resampler will convert it to the user sample rate 2711 * and deliver it to the callback. */ 2712 uint32_t target_sample_rate; 2713 if (has_input(stm)) { 2714 target_sample_rate = stm->input_stream_params.rate; 2715 } else { 2716 assert(has_output(stm)); 2717 target_sample_rate = stm->output_stream_params.rate; 2718 } 2719 2720 cubeb_stream_params input_unconverted_params; 2721 if (has_input(stm)) { 2722 input_unconverted_params = stm->input_stream_params; 2723 /* Use the rate of the input device. */ 2724 input_unconverted_params.rate = stm->input_hw_rate; 2725 } 2726 2727 cubeb_stream_params output_unconverted_params; 2728 if (has_output(stm)) { 2729 output_unconverted_params = stm->output_stream_params; 2730 output_unconverted_params.rate = stm->output_desc.mSampleRate; 2731 } 2732 2733 /* Create resampler. */ 2734 stm->resampler.reset(cubeb_resampler_create( 2735 stm, has_input(stm) ? &input_unconverted_params : NULL, 2736 has_output(stm) ? &output_unconverted_params : NULL, target_sample_rate, 2737 stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP, 2738 CUBEB_RESAMPLER_RECLOCK_NONE)); 2739 if (!stm->resampler) { 2740 LOG("(%p) Could not create resampler.", stm); 2741 return CUBEB_ERROR; 2742 } 2743 2744 if (stm->input_unit != NULL) { 2745 r = AudioUnitInitialize(stm->input_unit); 2746 if (r != noErr) { 2747 LOG("AudioUnitInitialize/input rv=%d", r); 2748 return CUBEB_ERROR; 2749 } 2750 } 2751 2752 if (stm->output_unit != NULL) { 2753 r = AudioUnitInitialize(stm->output_unit); 2754 if (r != noErr) { 2755 LOG("AudioUnitInitialize/output rv=%d", r); 2756 return CUBEB_ERROR; 2757 } 2758 2759 stm->current_latency_frames = audiounit_get_device_presentation_latency( 2760 stm->output_device.id, kAudioDevicePropertyScopeOutput); 2761 2762 Float64 unit_s; 2763 UInt32 size = sizeof(unit_s); 2764 if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, 2765 kAudioUnitScope_Global, 0, &unit_s, 2766 &size) == noErr) { 2767 stm->current_latency_frames += 2768 static_cast<uint32_t>(unit_s * stm->output_desc.mSampleRate); 2769 } 2770 } 2771 2772 if (stm->input_unit && stm->output_unit) { 2773 // According to the I/O hardware rate it is expected a specific pattern of 2774 // callbacks for example is input is 44100 and output is 48000 we expected 2775 // no more than 2 out callback in a row. 2776 stm->expected_output_callbacks_in_a_row = 2777 ceilf(stm->output_hw_rate / stm->input_hw_rate); 2778 } 2779 2780 r = audiounit_install_device_changed_callback(stm); 2781 if (r != CUBEB_OK) { 2782 LOG("(%p) Could not install all device change callback.", stm); 2783 } 2784 2785 return CUBEB_OK; 2786 } 2787 2788 cubeb_stream::cubeb_stream(cubeb * context) 2789 : context(context), resampler(nullptr, cubeb_resampler_destroy), 2790 mixer(nullptr, cubeb_mixer_destroy) 2791 { 2792 PodZero(&input_desc, 1); 2793 PodZero(&output_desc, 1); 2794 } 2795 2796 static void 2797 audiounit_stream_destroy_internal(cubeb_stream * stm); 2798 2799 static int 2800 audiounit_stream_init(cubeb * context, cubeb_stream ** stream, 2801 char const * /* stream_name */, cubeb_devid input_device, 2802 cubeb_stream_params * input_stream_params, 2803 cubeb_devid output_device, 2804 cubeb_stream_params * output_stream_params, 2805 unsigned int latency_frames, 2806 cubeb_data_callback data_callback, 2807 cubeb_state_callback state_callback, void * user_ptr) 2808 { 2809 assert(context); 2810 auto_lock context_lock(context->mutex); 2811 audiounit_increment_active_streams(context); 2812 unique_ptr<cubeb_stream, decltype(&audiounit_stream_destroy)> stm( 2813 new cubeb_stream(context), audiounit_stream_destroy_internal); 2814 int r; 2815 *stream = NULL; 2816 assert(latency_frames > 0); 2817 2818 /* These could be different in the future if we have both 2819 * full-duplex stream and different devices for input vs output. */ 2820 stm->data_callback = data_callback; 2821 stm->state_callback = state_callback; 2822 stm->user_ptr = user_ptr; 2823 stm->latency_frames = latency_frames; 2824 2825 if ((input_device && !input_stream_params) || 2826 (output_device && !output_stream_params)) { 2827 return CUBEB_ERROR_INVALID_PARAMETER; 2828 } 2829 if (input_stream_params) { 2830 stm->input_stream_params = *input_stream_params; 2831 r = audiounit_set_device_info( 2832 stm.get(), reinterpret_cast<uintptr_t>(input_device), io_side::INPUT); 2833 if (r != CUBEB_OK) { 2834 LOG("(%p) Fail to set device info for input.", stm.get()); 2835 return r; 2836 } 2837 } 2838 if (output_stream_params) { 2839 stm->output_stream_params = *output_stream_params; 2840 r = audiounit_set_device_info( 2841 stm.get(), reinterpret_cast<uintptr_t>(output_device), io_side::OUTPUT); 2842 if (r != CUBEB_OK) { 2843 LOG("(%p) Fail to set device info for output.", stm.get()); 2844 return r; 2845 } 2846 } 2847 2848 { 2849 // It's not critical to lock here, because no other thread has been started 2850 // yet, but it allows to assert that the lock has been taken in 2851 // `audiounit_setup_stream`. 2852 auto_lock lock(stm->mutex); 2853 r = audiounit_setup_stream(stm.get()); 2854 } 2855 2856 if (r != CUBEB_OK) { 2857 LOG("(%p) Could not setup the audiounit stream.", stm.get()); 2858 return r; 2859 } 2860 2861 r = audiounit_install_system_changed_callback(stm.get()); 2862 if (r != CUBEB_OK) { 2863 LOG("(%p) Could not install the device change callback.", stm.get()); 2864 return r; 2865 } 2866 2867 *stream = stm.release(); 2868 LOG("(%p) Cubeb stream init successful.", *stream); 2869 return CUBEB_OK; 2870 } 2871 2872 static void 2873 audiounit_close_stream(cubeb_stream * stm) 2874 { 2875 stm->mutex.assert_current_thread_owns(); 2876 2877 if (stm->input_unit) { 2878 AudioUnitUninitialize(stm->input_unit); 2879 AudioComponentInstanceDispose(stm->input_unit); 2880 stm->input_unit = nullptr; 2881 } 2882 2883 stm->input_linear_buffer.reset(); 2884 2885 if (stm->output_unit) { 2886 AudioUnitUninitialize(stm->output_unit); 2887 AudioComponentInstanceDispose(stm->output_unit); 2888 stm->output_unit = nullptr; 2889 } 2890 2891 stm->resampler.reset(); 2892 stm->mixer.reset(); 2893 2894 if (stm->aggregate_device_id != kAudioObjectUnknown) { 2895 audiounit_destroy_aggregate_device(stm->plugin_id, 2896 &stm->aggregate_device_id); 2897 stm->aggregate_device_id = kAudioObjectUnknown; 2898 } 2899 } 2900 2901 static void 2902 audiounit_stream_destroy_internal(cubeb_stream * stm) 2903 { 2904 stm->context->mutex.assert_current_thread_owns(); 2905 2906 int r = audiounit_uninstall_system_changed_callback(stm); 2907 if (r != CUBEB_OK) { 2908 LOG("(%p) Could not uninstall the device changed callback", stm); 2909 } 2910 r = audiounit_uninstall_device_changed_callback(stm); 2911 if (r != CUBEB_OK) { 2912 LOG("(%p) Could not uninstall all device change listeners", stm); 2913 } 2914 2915 auto_lock lock(stm->mutex); 2916 audiounit_close_stream(stm); 2917 assert(audiounit_active_streams(stm->context) >= 1); 2918 audiounit_decrement_active_streams(stm->context); 2919 } 2920 2921 static void 2922 audiounit_stream_destroy(cubeb_stream * stm) 2923 { 2924 int r = audiounit_uninstall_system_changed_callback(stm); 2925 if (r != CUBEB_OK) { 2926 LOG("(%p) Could not uninstall the device changed callback", stm); 2927 } 2928 r = audiounit_uninstall_device_changed_callback(stm); 2929 if (r != CUBEB_OK) { 2930 LOG("(%p) Could not uninstall all device change listeners", stm); 2931 } 2932 2933 if (!stm->shutdown.load()) { 2934 auto_lock context_lock(stm->context->mutex); 2935 audiounit_stream_stop_internal(stm); 2936 stm->shutdown = true; 2937 } 2938 2939 stm->destroy_pending = true; 2940 // Execute close in serial queue to avoid collision 2941 // with reinit when un/plug devices 2942 dispatch_sync(stm->context->serial_queue, ^() { 2943 auto_lock context_lock(stm->context->mutex); 2944 audiounit_stream_destroy_internal(stm); 2945 }); 2946 2947 LOG("Cubeb stream (%p) destroyed successful.", stm); 2948 delete stm; 2949 } 2950 2951 static int 2952 audiounit_stream_start_internal(cubeb_stream * stm) 2953 { 2954 OSStatus r; 2955 if (stm->input_unit != NULL) { 2956 r = AudioOutputUnitStart(stm->input_unit); 2957 if (r != noErr) { 2958 LOG("AudioOutputUnitStart (input) rv=%d", r); 2959 return CUBEB_ERROR; 2960 } 2961 } 2962 if (stm->output_unit != NULL) { 2963 r = AudioOutputUnitStart(stm->output_unit); 2964 if (r != noErr) { 2965 LOG("AudioOutputUnitStart (output) rv=%d", r); 2966 return CUBEB_ERROR; 2967 } 2968 } 2969 return CUBEB_OK; 2970 } 2971 2972 static int 2973 audiounit_stream_start(cubeb_stream * stm) 2974 { 2975 auto_lock context_lock(stm->context->mutex); 2976 stm->shutdown = false; 2977 stm->draining = false; 2978 2979 int r = audiounit_stream_start_internal(stm); 2980 if (r != CUBEB_OK) { 2981 return r; 2982 } 2983 2984 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); 2985 2986 LOG("Cubeb stream (%p) started successfully.", stm); 2987 return CUBEB_OK; 2988 } 2989 2990 void 2991 audiounit_stream_stop_internal(cubeb_stream * stm) 2992 { 2993 OSStatus r; 2994 if (stm->input_unit != NULL) { 2995 r = AudioOutputUnitStop(stm->input_unit); 2996 assert(r == 0); 2997 } 2998 if (stm->output_unit != NULL) { 2999 r = AudioOutputUnitStop(stm->output_unit); 3000 assert(r == 0); 3001 } 3002 } 3003 3004 static int 3005 audiounit_stream_stop(cubeb_stream * stm) 3006 { 3007 auto_lock context_lock(stm->context->mutex); 3008 stm->shutdown = true; 3009 3010 audiounit_stream_stop_internal(stm); 3011 3012 stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); 3013 3014 LOG("Cubeb stream (%p) stopped successfully.", stm); 3015 return CUBEB_OK; 3016 } 3017 3018 static int 3019 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) 3020 { 3021 assert(stm); 3022 if (stm->current_latency_frames > stm->frames_played) { 3023 *position = 0; 3024 } else { 3025 *position = stm->frames_played - stm->current_latency_frames; 3026 } 3027 return CUBEB_OK; 3028 } 3029 3030 int 3031 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) 3032 { 3033 #if TARGET_OS_IPHONE 3034 // TODO 3035 return CUBEB_ERROR_NOT_SUPPORTED; 3036 #else 3037 *latency = stm->total_output_latency_frames; 3038 return CUBEB_OK; 3039 #endif 3040 } 3041 3042 static int 3043 audiounit_stream_get_volume(cubeb_stream * stm, float * volume) 3044 { 3045 assert(stm->output_unit); 3046 OSStatus r = AudioUnitGetParameter(stm->output_unit, kHALOutputParam_Volume, 3047 kAudioUnitScope_Global, 0, volume); 3048 if (r != noErr) { 3049 LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r); 3050 return CUBEB_ERROR; 3051 } 3052 return CUBEB_OK; 3053 } 3054 3055 static int 3056 audiounit_stream_set_volume(cubeb_stream * stm, float volume) 3057 { 3058 assert(stm->output_unit); 3059 OSStatus r; 3060 r = AudioUnitSetParameter(stm->output_unit, kHALOutputParam_Volume, 3061 kAudioUnitScope_Global, 0, volume, 0); 3062 3063 if (r != noErr) { 3064 LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r); 3065 return CUBEB_ERROR; 3066 } 3067 return CUBEB_OK; 3068 } 3069 3070 unique_ptr<char[]> 3071 convert_uint32_into_string(UInt32 data) 3072 { 3073 // Simply create an empty string if no data. 3074 size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32. 3075 auto str = unique_ptr<char[]>{new char[size + 1]}; // + 1 for '\0'. 3076 str[size] = '\0'; 3077 if (size < 4) { 3078 return str; 3079 } 3080 3081 // Reverse 0xWXYZ into 0xZYXW. 3082 str[0] = (char)(data >> 24); 3083 str[1] = (char)(data >> 16); 3084 str[2] = (char)(data >> 8); 3085 str[3] = (char)(data); 3086 return str; 3087 } 3088 3089 int 3090 audiounit_get_default_device_datasource(cubeb_device_type type, UInt32 * data) 3091 { 3092 AudioDeviceID id = audiounit_get_default_device_id(type); 3093 if (id == kAudioObjectUnknown) { 3094 return CUBEB_ERROR; 3095 } 3096 3097 UInt32 size = sizeof(*data); 3098 /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */ 3099 OSStatus r = AudioObjectGetPropertyData( 3100 id, 3101 type == CUBEB_DEVICE_TYPE_INPUT ? &INPUT_DATA_SOURCE_PROPERTY_ADDRESS 3102 : &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS, 3103 0, NULL, &size, data); 3104 if (r != noErr) { 3105 *data = 0; 3106 } 3107 3108 return CUBEB_OK; 3109 } 3110 3111 int 3112 audiounit_get_default_device_name(cubeb_stream * stm, 3113 cubeb_device * const device, 3114 cubeb_device_type type) 3115 { 3116 assert(stm); 3117 assert(device); 3118 3119 UInt32 data; 3120 int r = audiounit_get_default_device_datasource(type, &data); 3121 if (r != CUBEB_OK) { 3122 return r; 3123 } 3124 char ** name = type == CUBEB_DEVICE_TYPE_INPUT ? &device->input_name 3125 : &device->output_name; 3126 *name = convert_uint32_into_string(data).release(); 3127 if (!strlen(*name)) { // empty string. 3128 LOG("(%p) name of %s device is empty!", stm, 3129 type == CUBEB_DEVICE_TYPE_INPUT ? "input" : "output"); 3130 } 3131 return CUBEB_OK; 3132 } 3133 3134 int 3135 audiounit_stream_get_current_device(cubeb_stream * stm, 3136 cubeb_device ** const device) 3137 { 3138 #if TARGET_OS_IPHONE 3139 // TODO 3140 return CUBEB_ERROR_NOT_SUPPORTED; 3141 #else 3142 *device = new cubeb_device; 3143 if (!*device) { 3144 return CUBEB_ERROR; 3145 } 3146 PodZero(*device, 1); 3147 3148 int r = 3149 audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_OUTPUT); 3150 if (r != CUBEB_OK) { 3151 return r; 3152 } 3153 3154 r = audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_INPUT); 3155 if (r != CUBEB_OK) { 3156 return r; 3157 } 3158 3159 return CUBEB_OK; 3160 #endif 3161 } 3162 3163 int 3164 audiounit_stream_device_destroy(cubeb_stream * /* stream */, 3165 cubeb_device * device) 3166 { 3167 delete[] device->output_name; 3168 delete[] device->input_name; 3169 delete device; 3170 return CUBEB_OK; 3171 } 3172 3173 int 3174 audiounit_stream_register_device_changed_callback( 3175 cubeb_stream * stream, 3176 cubeb_device_changed_callback device_changed_callback) 3177 { 3178 auto_lock dev_cb_lock(stream->device_changed_callback_lock); 3179 /* Note: second register without unregister first causes 'nope' error. 3180 * Current implementation requires unregister before register a new cb. */ 3181 assert(!device_changed_callback || !stream->device_changed_callback); 3182 stream->device_changed_callback = device_changed_callback; 3183 return CUBEB_OK; 3184 } 3185 3186 static char * 3187 audiounit_strref_to_cstr_utf8(CFStringRef strref) 3188 { 3189 CFIndex len, size; 3190 char * ret; 3191 if (strref == NULL) { 3192 return NULL; 3193 } 3194 3195 len = CFStringGetLength(strref); 3196 // Add 1 to size to allow for '\0' termination character. 3197 size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1; 3198 ret = new char[size]; 3199 3200 if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) { 3201 delete[] ret; 3202 ret = NULL; 3203 } 3204 3205 return ret; 3206 } 3207 3208 static uint32_t 3209 audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope) 3210 { 3211 AudioObjectPropertyAddress adr = {0, scope, 3212 kAudioObjectPropertyElementMaster}; 3213 UInt32 size = 0; 3214 uint32_t i, ret = 0; 3215 3216 adr.mSelector = kAudioDevicePropertyStreamConfiguration; 3217 3218 if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && 3219 size > 0) { 3220 AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size)); 3221 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == 3222 noErr) { 3223 for (i = 0; i < list->mNumberBuffers; i++) 3224 ret += list->mBuffers[i].mNumberChannels; 3225 } 3226 } 3227 3228 return ret; 3229 } 3230 3231 static void 3232 audiounit_get_available_samplerate(AudioObjectID devid, 3233 AudioObjectPropertyScope scope, 3234 uint32_t * min, uint32_t * max, 3235 uint32_t * def) 3236 { 3237 AudioObjectPropertyAddress adr = {0, scope, 3238 kAudioObjectPropertyElementMaster}; 3239 3240 adr.mSelector = kAudioDevicePropertyNominalSampleRate; 3241 if (AudioObjectHasProperty(devid, &adr)) { 3242 UInt32 size = sizeof(Float64); 3243 Float64 fvalue = 0.0; 3244 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == 3245 noErr) { 3246 *def = fvalue; 3247 } 3248 } 3249 3250 adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; 3251 UInt32 size = 0; 3252 AudioValueRange range; 3253 if (AudioObjectHasProperty(devid, &adr) && 3254 AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) { 3255 uint32_t count = size / sizeof(AudioValueRange); 3256 vector<AudioValueRange> ranges(count); 3257 range.mMinimum = 9999999999.0; 3258 range.mMaximum = 0.0; 3259 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, 3260 ranges.data()) == noErr) { 3261 for (uint32_t i = 0; i < count; i++) { 3262 if (ranges[i].mMaximum > range.mMaximum) 3263 range.mMaximum = ranges[i].mMaximum; 3264 if (ranges[i].mMinimum < range.mMinimum) 3265 range.mMinimum = ranges[i].mMinimum; 3266 } 3267 } 3268 *max = static_cast<uint32_t>(range.mMaximum); 3269 *min = static_cast<uint32_t>(range.mMinimum); 3270 } else { 3271 *min = *max = 0; 3272 } 3273 } 3274 3275 static UInt32 3276 audiounit_get_device_presentation_latency(AudioObjectID devid, 3277 AudioObjectPropertyScope scope) 3278 { 3279 AudioObjectPropertyAddress adr = {0, scope, 3280 kAudioObjectPropertyElementMaster}; 3281 UInt32 size, dev, stream = 0; 3282 AudioStreamID sid[1]; 3283 3284 adr.mSelector = kAudioDevicePropertyLatency; 3285 size = sizeof(UInt32); 3286 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) { 3287 dev = 0; 3288 } 3289 3290 adr.mSelector = kAudioDevicePropertyStreams; 3291 size = sizeof(sid); 3292 if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) { 3293 adr.mSelector = kAudioStreamPropertyLatency; 3294 size = sizeof(UInt32); 3295 AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream); 3296 } 3297 3298 return dev + stream; 3299 } 3300 3301 static int 3302 audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, 3303 AudioObjectID devid, cubeb_device_type type) 3304 { 3305 AudioObjectPropertyAddress adr = {0, 0, kAudioObjectPropertyElementMaster}; 3306 UInt32 size; 3307 3308 if (type == CUBEB_DEVICE_TYPE_OUTPUT) { 3309 adr.mScope = kAudioDevicePropertyScopeOutput; 3310 } else if (type == CUBEB_DEVICE_TYPE_INPUT) { 3311 adr.mScope = kAudioDevicePropertyScopeInput; 3312 } else { 3313 return CUBEB_ERROR; 3314 } 3315 3316 UInt32 ch = audiounit_get_channel_count(devid, adr.mScope); 3317 if (ch == 0) { 3318 return CUBEB_ERROR; 3319 } 3320 3321 PodZero(dev_info, 1); 3322 3323 CFStringRef device_id_str = nullptr; 3324 size = sizeof(CFStringRef); 3325 adr.mSelector = kAudioDevicePropertyDeviceUID; 3326 OSStatus ret = 3327 AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str); 3328 if (ret == noErr && device_id_str != NULL) { 3329 dev_info->device_id = audiounit_strref_to_cstr_utf8(device_id_str); 3330 static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)), 3331 "cubeb_devid can't represent devid"); 3332 dev_info->devid = reinterpret_cast<cubeb_devid>(devid); 3333 dev_info->group_id = dev_info->device_id; 3334 CFRelease(device_id_str); 3335 } 3336 3337 CFStringRef friendly_name_str = nullptr; 3338 UInt32 ds; 3339 size = sizeof(UInt32); 3340 adr.mSelector = kAudioDevicePropertyDataSource; 3341 ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds); 3342 if (ret == noErr) { 3343 AudioValueTranslation trl = {&ds, sizeof(ds), &friendly_name_str, 3344 sizeof(CFStringRef)}; 3345 adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString; 3346 size = sizeof(AudioValueTranslation); 3347 AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl); 3348 } 3349 3350 // If there is no datasource for this device, fall back to the 3351 // device name. 3352 if (!friendly_name_str) { 3353 size = sizeof(CFStringRef); 3354 adr.mSelector = kAudioObjectPropertyName; 3355 AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &friendly_name_str); 3356 } 3357 3358 if (friendly_name_str) { 3359 dev_info->friendly_name = audiounit_strref_to_cstr_utf8(friendly_name_str); 3360 CFRelease(friendly_name_str); 3361 } else { 3362 // Couldn't get a datasource name nor a device name, return a 3363 // valid string of length 0. 3364 char * fallback_name = new char[1]; 3365 fallback_name[0] = '\0'; 3366 dev_info->friendly_name = fallback_name; 3367 } 3368 3369 CFStringRef vendor_name_str = nullptr; 3370 size = sizeof(CFStringRef); 3371 adr.mSelector = kAudioObjectPropertyManufacturer; 3372 ret = 3373 AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str); 3374 if (ret == noErr && vendor_name_str != NULL) { 3375 dev_info->vendor_name = audiounit_strref_to_cstr_utf8(vendor_name_str); 3376 CFRelease(vendor_name_str); 3377 } 3378 3379 dev_info->type = type; 3380 dev_info->state = CUBEB_DEVICE_STATE_ENABLED; 3381 dev_info->preferred = (devid == audiounit_get_default_device_id(type)) 3382 ? CUBEB_DEVICE_PREF_ALL 3383 : CUBEB_DEVICE_PREF_NONE; 3384 3385 dev_info->max_channels = ch; 3386 dev_info->format = 3387 (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */ 3388 /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */ 3389 dev_info->default_format = CUBEB_DEVICE_FMT_F32NE; 3390 audiounit_get_available_samplerate(devid, adr.mScope, &dev_info->min_rate, 3391 &dev_info->max_rate, 3392 &dev_info->default_rate); 3393 3394 UInt32 latency = audiounit_get_device_presentation_latency(devid, adr.mScope); 3395 3396 AudioValueRange range; 3397 adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange; 3398 size = sizeof(AudioValueRange); 3399 ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range); 3400 if (ret == noErr) { 3401 dev_info->latency_lo = latency + range.mMinimum; 3402 dev_info->latency_hi = latency + range.mMaximum; 3403 } else { 3404 dev_info->latency_lo = 3405 10 * dev_info->default_rate / 1000; /* Default to 10ms */ 3406 dev_info->latency_hi = 3407 100 * dev_info->default_rate / 1000; /* Default to 100ms */ 3408 } 3409 3410 return CUBEB_OK; 3411 } 3412 3413 bool 3414 is_aggregate_device(cubeb_device_info * device_info) 3415 { 3416 assert(device_info->friendly_name); 3417 return !strncmp(device_info->friendly_name, PRIVATE_AGGREGATE_DEVICE_NAME, 3418 strlen(PRIVATE_AGGREGATE_DEVICE_NAME)); 3419 } 3420 3421 static int 3422 audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type, 3423 cubeb_device_collection * collection) 3424 { 3425 vector<AudioObjectID> input_devs; 3426 vector<AudioObjectID> output_devs; 3427 3428 // Count number of input and output devices. This is not 3429 // necessarily the same as the count of raw devices supported by the 3430 // system since, for example, with Soundflower installed, some 3431 // devices may report as being both input *and* output and cubeb 3432 // separates those into two different devices. 3433 3434 if (type & CUBEB_DEVICE_TYPE_OUTPUT) { 3435 output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); 3436 } 3437 3438 if (type & CUBEB_DEVICE_TYPE_INPUT) { 3439 input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); 3440 } 3441 3442 auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()]; 3443 collection->count = 0; 3444 3445 if (type & CUBEB_DEVICE_TYPE_OUTPUT) { 3446 for (auto dev : output_devs) { 3447 auto device = &devices[collection->count]; 3448 auto err = audiounit_create_device_from_hwdev(device, dev, 3449 CUBEB_DEVICE_TYPE_OUTPUT); 3450 if (err != CUBEB_OK || is_aggregate_device(device)) { 3451 continue; 3452 } 3453 collection->count += 1; 3454 } 3455 } 3456 3457 if (type & CUBEB_DEVICE_TYPE_INPUT) { 3458 for (auto dev : input_devs) { 3459 auto device = &devices[collection->count]; 3460 auto err = audiounit_create_device_from_hwdev(device, dev, 3461 CUBEB_DEVICE_TYPE_INPUT); 3462 if (err != CUBEB_OK || is_aggregate_device(device)) { 3463 continue; 3464 } 3465 collection->count += 1; 3466 } 3467 } 3468 3469 if (collection->count > 0) { 3470 collection->device = devices; 3471 } else { 3472 delete[] devices; 3473 collection->device = NULL; 3474 } 3475 3476 return CUBEB_OK; 3477 } 3478 3479 static void 3480 audiounit_device_destroy(cubeb_device_info * device) 3481 { 3482 delete[] device->device_id; 3483 delete[] device->friendly_name; 3484 delete[] device->vendor_name; 3485 } 3486 3487 static int 3488 audiounit_device_collection_destroy(cubeb * /* context */, 3489 cubeb_device_collection * collection) 3490 { 3491 for (size_t i = 0; i < collection->count; i++) { 3492 audiounit_device_destroy(&collection->device[i]); 3493 } 3494 delete[] collection->device; 3495 3496 return CUBEB_OK; 3497 } 3498 3499 static vector<AudioObjectID> 3500 audiounit_get_devices_of_type(cubeb_device_type devtype) 3501 { 3502 UInt32 size = 0; 3503 OSStatus ret = AudioObjectGetPropertyDataSize( 3504 kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size); 3505 if (ret != noErr) { 3506 return vector<AudioObjectID>(); 3507 } 3508 vector<AudioObjectID> devices(size / sizeof(AudioObjectID)); 3509 ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, 3510 &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size, 3511 devices.data()); 3512 if (ret != noErr) { 3513 return vector<AudioObjectID>(); 3514 } 3515 3516 // Remove the aggregate device from the list of devices (if any). 3517 for (auto it = devices.begin(); it != devices.end();) { 3518 CFStringRef name = get_device_name(*it); 3519 if (name && CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location != 3520 kCFNotFound) { 3521 it = devices.erase(it); 3522 } else { 3523 it++; 3524 } 3525 if (name) { 3526 CFRelease(name); 3527 } 3528 } 3529 3530 /* Expected sorted but did not find anything in the docs. */ 3531 sort(devices.begin(), devices.end(), 3532 [](AudioObjectID a, AudioObjectID b) { return a < b; }); 3533 3534 if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) { 3535 return devices; 3536 } 3537 3538 AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) 3539 ? kAudioDevicePropertyScopeInput 3540 : kAudioDevicePropertyScopeOutput; 3541 3542 vector<AudioObjectID> devices_in_scope; 3543 for (uint32_t i = 0; i < devices.size(); ++i) { 3544 /* For device in the given scope channel must be > 0. */ 3545 if (audiounit_get_channel_count(devices[i], scope) > 0) { 3546 devices_in_scope.push_back(devices[i]); 3547 } 3548 } 3549 3550 return devices_in_scope; 3551 } 3552 3553 static OSStatus 3554 audiounit_collection_changed_callback( 3555 AudioObjectID /* inObjectID */, UInt32 /* inNumberAddresses */, 3556 const AudioObjectPropertyAddress * /* inAddresses */, void * inClientData) 3557 { 3558 cubeb * context = static_cast<cubeb *>(inClientData); 3559 3560 // This can be called from inside an AudioUnit function, dispatch to another 3561 // queue. 3562 dispatch_async(context->serial_queue, ^() { 3563 auto_lock lock(context->mutex); 3564 if (!context->input_collection_changed_callback && 3565 !context->output_collection_changed_callback) { 3566 /* Listener removed while waiting in mutex, abort. */ 3567 return; 3568 } 3569 if (context->input_collection_changed_callback) { 3570 vector<AudioObjectID> devices = 3571 audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); 3572 /* Elements in the vector expected sorted. */ 3573 if (context->input_device_array != devices) { 3574 context->input_device_array = devices; 3575 context->input_collection_changed_callback( 3576 context, context->input_collection_changed_user_ptr); 3577 } 3578 } 3579 if (context->output_collection_changed_callback) { 3580 vector<AudioObjectID> devices = 3581 audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); 3582 /* Elements in the vector expected sorted. */ 3583 if (context->output_device_array != devices) { 3584 context->output_device_array = devices; 3585 context->output_collection_changed_callback( 3586 context, context->output_collection_changed_user_ptr); 3587 } 3588 } 3589 }); 3590 return noErr; 3591 } 3592 3593 static OSStatus 3594 audiounit_add_device_listener( 3595 cubeb * context, cubeb_device_type devtype, 3596 cubeb_device_collection_changed_callback collection_changed_callback, 3597 void * user_ptr) 3598 { 3599 context->mutex.assert_current_thread_owns(); 3600 assert(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)); 3601 /* Note: second register without unregister first causes 'nope' error. 3602 * Current implementation requires unregister before register a new cb. */ 3603 assert((devtype & CUBEB_DEVICE_TYPE_INPUT) && 3604 !context->input_collection_changed_callback || 3605 (devtype & CUBEB_DEVICE_TYPE_OUTPUT) && 3606 !context->output_collection_changed_callback); 3607 3608 if (!context->input_collection_changed_callback && 3609 !context->output_collection_changed_callback) { 3610 OSStatus ret = AudioObjectAddPropertyListener( 3611 kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, 3612 audiounit_collection_changed_callback, context); 3613 if (ret != noErr) { 3614 return ret; 3615 } 3616 } 3617 if (devtype & CUBEB_DEVICE_TYPE_INPUT) { 3618 /* Expected empty after unregister. */ 3619 assert(context->input_device_array.empty()); 3620 context->input_device_array = 3621 audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); 3622 context->input_collection_changed_callback = collection_changed_callback; 3623 context->input_collection_changed_user_ptr = user_ptr; 3624 } 3625 if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) { 3626 /* Expected empty after unregister. */ 3627 assert(context->output_device_array.empty()); 3628 context->output_device_array = 3629 audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); 3630 context->output_collection_changed_callback = collection_changed_callback; 3631 context->output_collection_changed_user_ptr = user_ptr; 3632 } 3633 return noErr; 3634 } 3635 3636 static OSStatus 3637 audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype) 3638 { 3639 context->mutex.assert_current_thread_owns(); 3640 3641 if (devtype & CUBEB_DEVICE_TYPE_INPUT) { 3642 context->input_collection_changed_callback = nullptr; 3643 context->input_collection_changed_user_ptr = nullptr; 3644 context->input_device_array.clear(); 3645 } 3646 if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) { 3647 context->output_collection_changed_callback = nullptr; 3648 context->output_collection_changed_user_ptr = nullptr; 3649 context->output_device_array.clear(); 3650 } 3651 3652 if (context->input_collection_changed_callback || 3653 context->output_collection_changed_callback) { 3654 return noErr; 3655 } 3656 /* Note: unregister a non registered cb is not a problem, not checking. */ 3657 return AudioObjectRemovePropertyListener( 3658 kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, 3659 audiounit_collection_changed_callback, context); 3660 } 3661 3662 int 3663 audiounit_register_device_collection_changed( 3664 cubeb * context, cubeb_device_type devtype, 3665 cubeb_device_collection_changed_callback collection_changed_callback, 3666 void * user_ptr) 3667 { 3668 if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) { 3669 return CUBEB_ERROR_INVALID_PARAMETER; 3670 } 3671 OSStatus ret; 3672 auto_lock lock(context->mutex); 3673 if (collection_changed_callback) { 3674 ret = audiounit_add_device_listener(context, devtype, 3675 collection_changed_callback, user_ptr); 3676 } else { 3677 ret = audiounit_remove_device_listener(context, devtype); 3678 } 3679 return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR; 3680 } 3681 3682 cubeb_ops const audiounit_ops = { 3683 /*.init =*/audiounit_init, 3684 /*.get_backend_id =*/audiounit_get_backend_id, 3685 /*.get_max_channel_count =*/audiounit_get_max_channel_count, 3686 /*.get_min_latency =*/audiounit_get_min_latency, 3687 /*.get_preferred_sample_rate =*/audiounit_get_preferred_sample_rate, 3688 /*.get_supported_input_processing_params =*/NULL, 3689 /*.enumerate_devices =*/audiounit_enumerate_devices, 3690 /*.device_collection_destroy =*/audiounit_device_collection_destroy, 3691 /*.destroy =*/audiounit_destroy, 3692 /*.stream_init =*/audiounit_stream_init, 3693 /*.stream_destroy =*/audiounit_stream_destroy, 3694 /*.stream_start =*/audiounit_stream_start, 3695 /*.stream_stop =*/audiounit_stream_stop, 3696 /*.stream_get_position =*/audiounit_stream_get_position, 3697 /*.stream_get_latency =*/audiounit_stream_get_latency, 3698 /*.stream_get_input_latency =*/NULL, 3699 /*.stream_set_volume =*/audiounit_stream_set_volume, 3700 /*.stream_set_name =*/NULL, 3701 /*.stream_get_current_device =*/audiounit_stream_get_current_device, 3702 /*.stream_set_input_mute =*/NULL, 3703 /*.stream_set_input_processing_params =*/NULL, 3704 /*.stream_device_destroy =*/audiounit_stream_device_destroy, 3705 /*.stream_register_device_changed_callback =*/ 3706 audiounit_stream_register_device_changed_callback, 3707 /*.register_device_collection_changed =*/ 3708 audiounit_register_device_collection_changed};