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