1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "media/audio/mac/audio_manager_mac.h" 6 7 #include <CoreAudio/AudioHardware.h> 8 #include <string> 9 10 #include "base/bind.h" 11 #include "base/command_line.h" 12 #include "base/mac/mac_logging.h" 13 #include "base/mac/scoped_cftyperef.h" 14 #include "base/strings/sys_string_conversions.h" 15 #include "media/audio/audio_parameters.h" 16 #include "media/audio/mac/audio_auhal_mac.h" 17 #include "media/audio/mac/audio_input_mac.h" 18 #include "media/audio/mac/audio_low_latency_input_mac.h" 19 #include "media/audio/mac/audio_low_latency_output_mac.h" 20 #include "media/audio/mac/audio_synchronized_mac.h" 21 #include "media/audio/mac/audio_unified_mac.h" 22 #include "media/base/bind_to_loop.h" 23 #include "media/base/channel_layout.h" 24 #include "media/base/limits.h" 25 #include "media/base/media_switches.h" 26 27 namespace media { 28 29 // Maximum number of output streams that can be open simultaneously. 30 static const int kMaxOutputStreams = 50; 31 32 // Default buffer size in samples for low-latency input and output streams. 33 static const int kDefaultLowLatencyBufferSize = 128; 34 35 // Default sample-rate on most Apple hardware. 36 static const int kFallbackSampleRate = 44100; 37 38 static bool HasAudioHardware(AudioObjectPropertySelector selector) { 39 AudioDeviceID output_device_id = kAudioObjectUnknown; 40 const AudioObjectPropertyAddress property_address = { 41 selector, 42 kAudioObjectPropertyScopeGlobal, // mScope 43 kAudioObjectPropertyElementMaster // mElement 44 }; 45 UInt32 output_device_id_size = static_cast<UInt32>(sizeof(output_device_id)); 46 OSStatus err = AudioObjectGetPropertyData(kAudioObjectSystemObject, 47 &property_address, 48 0, // inQualifierDataSize 49 NULL, // inQualifierData 50 &output_device_id_size, 51 &output_device_id); 52 return err == kAudioHardwareNoError && 53 output_device_id != kAudioObjectUnknown; 54 } 55 56 // Returns true if the default input device is the same as 57 // the default output device. 58 bool AudioManagerMac::HasUnifiedDefaultIO() { 59 AudioDeviceID input_id, output_id; 60 if (!GetDefaultInputDevice(&input_id) || !GetDefaultOutputDevice(&output_id)) 61 return false; 62 63 return input_id == output_id; 64 } 65 66 // Retrieves information on audio devices, and prepends the default 67 // device to the list if the list is non-empty. 68 static void GetAudioDeviceInfo(bool is_input, 69 media::AudioDeviceNames* device_names) { 70 // Query the number of total devices. 71 AudioObjectPropertyAddress property_address = { 72 kAudioHardwarePropertyDevices, 73 kAudioObjectPropertyScopeGlobal, 74 kAudioObjectPropertyElementMaster 75 }; 76 UInt32 size = 0; 77 OSStatus result = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, 78 &property_address, 79 0, 80 NULL, 81 &size); 82 if (result || !size) 83 return; 84 85 int device_count = size / sizeof(AudioDeviceID); 86 87 // Get the array of device ids for all the devices, which includes both 88 // input devices and output devices. 89 scoped_ptr_malloc<AudioDeviceID> 90 devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); 91 AudioDeviceID* device_ids = devices.get(); 92 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 93 &property_address, 94 0, 95 NULL, 96 &size, 97 device_ids); 98 if (result) 99 return; 100 101 // Iterate over all available devices to gather information. 102 for (int i = 0; i < device_count; ++i) { 103 // Get the number of input or output channels of the device. 104 property_address.mScope = is_input ? 105 kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; 106 property_address.mSelector = kAudioDevicePropertyStreams; 107 size = 0; 108 result = AudioObjectGetPropertyDataSize(device_ids[i], 109 &property_address, 110 0, 111 NULL, 112 &size); 113 if (result || !size) 114 continue; 115 116 // Get device UID. 117 CFStringRef uid = NULL; 118 size = sizeof(uid); 119 property_address.mSelector = kAudioDevicePropertyDeviceUID; 120 property_address.mScope = kAudioObjectPropertyScopeGlobal; 121 result = AudioObjectGetPropertyData(device_ids[i], 122 &property_address, 123 0, 124 NULL, 125 &size, 126 &uid); 127 if (result) 128 continue; 129 130 // Get device name. 131 CFStringRef name = NULL; 132 property_address.mSelector = kAudioObjectPropertyName; 133 property_address.mScope = kAudioObjectPropertyScopeGlobal; 134 result = AudioObjectGetPropertyData(device_ids[i], 135 &property_address, 136 0, 137 NULL, 138 &size, 139 &name); 140 if (result) { 141 if (uid) 142 CFRelease(uid); 143 continue; 144 } 145 146 // Store the device name and UID. 147 media::AudioDeviceName device_name; 148 device_name.device_name = base::SysCFStringRefToUTF8(name); 149 device_name.unique_id = base::SysCFStringRefToUTF8(uid); 150 device_names->push_back(device_name); 151 152 // We are responsible for releasing the returned CFObject. See the 153 // comment in the AudioHardware.h for constant 154 // kAudioDevicePropertyDeviceUID. 155 if (uid) 156 CFRelease(uid); 157 if (name) 158 CFRelease(name); 159 } 160 161 if (!device_names->empty()) { 162 // Prepend the default device to the list since we always want it to be 163 // on the top of the list for all platforms. There is no duplicate 164 // counting here since the default device has been abstracted out before. 165 media::AudioDeviceName name; 166 name.device_name = AudioManagerBase::kDefaultDeviceName; 167 name.unique_id = AudioManagerBase::kDefaultDeviceId; 168 device_names->push_front(name); 169 } 170 } 171 172 static AudioDeviceID GetAudioDeviceIdByUId(bool is_input, 173 const std::string& device_id) { 174 AudioObjectPropertyAddress property_address = { 175 kAudioHardwarePropertyDevices, 176 kAudioObjectPropertyScopeGlobal, 177 kAudioObjectPropertyElementMaster 178 }; 179 AudioDeviceID audio_device_id = kAudioObjectUnknown; 180 UInt32 device_size = sizeof(audio_device_id); 181 OSStatus result = -1; 182 183 if (device_id == AudioManagerBase::kDefaultDeviceId || device_id.empty()) { 184 // Default Device. 185 property_address.mSelector = is_input ? 186 kAudioHardwarePropertyDefaultInputDevice : 187 kAudioHardwarePropertyDefaultOutputDevice; 188 189 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 190 &property_address, 191 0, 192 0, 193 &device_size, 194 &audio_device_id); 195 } else { 196 // Non-default device. 197 base::ScopedCFTypeRef<CFStringRef> uid( 198 base::SysUTF8ToCFStringRef(device_id)); 199 AudioValueTranslation value; 200 value.mInputData = &uid; 201 value.mInputDataSize = sizeof(CFStringRef); 202 value.mOutputData = &audio_device_id; 203 value.mOutputDataSize = device_size; 204 UInt32 translation_size = sizeof(AudioValueTranslation); 205 206 property_address.mSelector = kAudioHardwarePropertyDeviceForUID; 207 result = AudioObjectGetPropertyData(kAudioObjectSystemObject, 208 &property_address, 209 0, 210 0, 211 &translation_size, 212 &value); 213 } 214 215 if (result) { 216 OSSTATUS_DLOG(WARNING, result) << "Unable to query device " << device_id 217 << " for AudioDeviceID"; 218 } 219 220 return audio_device_id; 221 } 222 223 AudioManagerMac::AudioManagerMac(AudioLogFactory* audio_log_factory) 224 : AudioManagerBase(audio_log_factory), 225 current_sample_rate_(0) { 226 current_output_device_ = kAudioDeviceUnknown; 227 228 SetMaxOutputStreamsAllowed(kMaxOutputStreams); 229 230 // Task must be posted last to avoid races from handing out "this" to the 231 // audio thread. Always PostTask even if we're on the right thread since 232 // AudioManager creation is on the startup path and this may be slow. 233 GetMessageLoop()->PostTask(FROM_HERE, base::Bind( 234 &AudioManagerMac::CreateDeviceListener, base::Unretained(this))); 235 } 236 237 AudioManagerMac::~AudioManagerMac() { 238 if (GetMessageLoop()->BelongsToCurrentThread()) { 239 DestroyDeviceListener(); 240 } else { 241 // It's safe to post a task here since Shutdown() will wait for all tasks to 242 // complete before returning. 243 GetMessageLoop()->PostTask(FROM_HERE, base::Bind( 244 &AudioManagerMac::DestroyDeviceListener, base::Unretained(this))); 245 } 246 247 Shutdown(); 248 } 249 250 bool AudioManagerMac::HasAudioOutputDevices() { 251 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice); 252 } 253 254 bool AudioManagerMac::HasAudioInputDevices() { 255 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice); 256 } 257 258 // TODO(xians): There are several places on the OSX specific code which 259 // could benefit from these helper functions. 260 bool AudioManagerMac::GetDefaultInputDevice( 261 AudioDeviceID* device) { 262 return GetDefaultDevice(device, true); 263 } 264 265 bool AudioManagerMac::GetDefaultOutputDevice( 266 AudioDeviceID* device) { 267 return GetDefaultDevice(device, false); 268 } 269 270 bool AudioManagerMac::GetDefaultDevice( 271 AudioDeviceID* device, bool input) { 272 CHECK(device); 273 274 // Obtain the current output device selected by the user. 275 AudioObjectPropertyAddress pa; 276 pa.mSelector = input ? kAudioHardwarePropertyDefaultInputDevice : 277 kAudioHardwarePropertyDefaultOutputDevice; 278 pa.mScope = kAudioObjectPropertyScopeGlobal; 279 pa.mElement = kAudioObjectPropertyElementMaster; 280 281 UInt32 size = sizeof(*device); 282 283 OSStatus result = AudioObjectGetPropertyData( 284 kAudioObjectSystemObject, 285 &pa, 286 0, 287 0, 288 &size, 289 device); 290 291 if ((result != kAudioHardwareNoError) || (*device == kAudioDeviceUnknown)) { 292 DLOG(ERROR) << "Error getting default AudioDevice."; 293 return false; 294 } 295 296 return true; 297 } 298 299 bool AudioManagerMac::GetDefaultOutputChannels( 300 int* channels) { 301 AudioDeviceID device; 302 if (!GetDefaultOutputDevice(&device)) 303 return false; 304 305 return GetDeviceChannels(device, 306 kAudioDevicePropertyScopeOutput, 307 channels); 308 } 309 310 bool AudioManagerMac::GetDeviceChannels( 311 AudioDeviceID device, 312 AudioObjectPropertyScope scope, 313 int* channels) { 314 CHECK(channels); 315 316 // Get stream configuration. 317 AudioObjectPropertyAddress pa; 318 pa.mSelector = kAudioDevicePropertyStreamConfiguration; 319 pa.mScope = scope; 320 pa.mElement = kAudioObjectPropertyElementMaster; 321 322 UInt32 size; 323 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 324 if (result != noErr || !size) 325 return false; 326 327 // Allocate storage. 328 scoped_ptr<uint8[]> list_storage(new uint8[size]); 329 AudioBufferList& buffer_list = 330 *reinterpret_cast<AudioBufferList*>(list_storage.get()); 331 332 result = AudioObjectGetPropertyData( 333 device, 334 &pa, 335 0, 336 0, 337 &size, 338 &buffer_list); 339 if (result != noErr) 340 return false; 341 342 // Determine number of input channels. 343 int channels_per_frame = buffer_list.mNumberBuffers > 0 ? 344 buffer_list.mBuffers[0].mNumberChannels : 0; 345 if (channels_per_frame == 1 && buffer_list.mNumberBuffers > 1) { 346 // Non-interleaved. 347 *channels = buffer_list.mNumberBuffers; 348 } else { 349 // Interleaved. 350 *channels = channels_per_frame; 351 } 352 353 return true; 354 } 355 356 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id) { 357 Float64 nominal_sample_rate; 358 UInt32 info_size = sizeof(nominal_sample_rate); 359 360 static const AudioObjectPropertyAddress kNominalSampleRateAddress = { 361 kAudioDevicePropertyNominalSampleRate, 362 kAudioObjectPropertyScopeGlobal, 363 kAudioObjectPropertyElementMaster 364 }; 365 OSStatus result = AudioObjectGetPropertyData( 366 device_id, 367 &kNominalSampleRateAddress, 368 0, 369 0, 370 &info_size, 371 &nominal_sample_rate); 372 if (result != noErr) { 373 OSSTATUS_DLOG(WARNING, result) 374 << "Could not get default sample rate for device: " << device_id; 375 return 0; 376 } 377 378 return static_cast<int>(nominal_sample_rate); 379 } 380 381 int AudioManagerMac::HardwareSampleRate() { 382 // Determine the default output device's sample-rate. 383 AudioDeviceID device_id = kAudioObjectUnknown; 384 if (!GetDefaultOutputDevice(&device_id)) 385 return kFallbackSampleRate; 386 387 return HardwareSampleRateForDevice(device_id); 388 } 389 390 void AudioManagerMac::GetAudioInputDeviceNames( 391 media::AudioDeviceNames* device_names) { 392 DCHECK(device_names->empty()); 393 GetAudioDeviceInfo(true, device_names); 394 } 395 396 void AudioManagerMac::GetAudioOutputDeviceNames( 397 media::AudioDeviceNames* device_names) { 398 DCHECK(device_names->empty()); 399 GetAudioDeviceInfo(false, device_names); 400 } 401 402 AudioParameters AudioManagerMac::GetInputStreamParameters( 403 const std::string& device_id) { 404 // Due to the sharing of the input and output buffer sizes, we need to choose 405 // the input buffer size based on the output sample rate. See 406 // http://crbug.com/154352. 407 const int buffer_size = ChooseBufferSize( 408 AUAudioOutputStream::HardwareSampleRate()); 409 410 AudioDeviceID device = GetAudioDeviceIdByUId(true, device_id); 411 if (device == kAudioObjectUnknown) { 412 DLOG(ERROR) << "Invalid device " << device_id; 413 return AudioParameters(); 414 } 415 416 int channels = 0; 417 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 418 if (GetDeviceChannels(device, kAudioDevicePropertyScopeInput, &channels) && 419 channels <= 2) { 420 channel_layout = GuessChannelLayout(channels); 421 } else { 422 DLOG(ERROR) << "Failed to get the device channels, use stereo as default " 423 << "for device " << device_id; 424 } 425 426 int sample_rate = HardwareSampleRateForDevice(device); 427 if (!sample_rate) 428 sample_rate = kFallbackSampleRate; 429 430 // TODO(xians): query the native channel layout for the specific device. 431 return AudioParameters( 432 AudioParameters::AUDIO_PCM_LOW_LATENCY, channel_layout, 433 sample_rate, 16, buffer_size); 434 } 435 436 std::string AudioManagerMac::GetAssociatedOutputDeviceID( 437 const std::string& input_device_id) { 438 AudioDeviceID device = GetAudioDeviceIdByUId(true, input_device_id); 439 if (device == kAudioObjectUnknown) 440 return std::string(); 441 442 UInt32 size = 0; 443 AudioObjectPropertyAddress pa = { 444 kAudioDevicePropertyRelatedDevices, 445 kAudioDevicePropertyScopeOutput, 446 kAudioObjectPropertyElementMaster 447 }; 448 OSStatus result = AudioObjectGetPropertyDataSize(device, &pa, 0, 0, &size); 449 if (result || !size) 450 return std::string(); 451 452 int device_count = size / sizeof(AudioDeviceID); 453 scoped_ptr_malloc<AudioDeviceID> 454 devices(reinterpret_cast<AudioDeviceID*>(malloc(size))); 455 result = AudioObjectGetPropertyData( 456 device, &pa, 0, NULL, &size, devices.get()); 457 if (result) 458 return std::string(); 459 460 std::vector<std::string> associated_devices; 461 for (int i = 0; i < device_count; ++i) { 462 // Get the number of output channels of the device. 463 pa.mSelector = kAudioDevicePropertyStreams; 464 size = 0; 465 result = AudioObjectGetPropertyDataSize(devices.get()[i], 466 &pa, 467 0, 468 NULL, 469 &size); 470 if (result || !size) 471 continue; // Skip if there aren't any output channels. 472 473 // Get device UID. 474 CFStringRef uid = NULL; 475 size = sizeof(uid); 476 pa.mSelector = kAudioDevicePropertyDeviceUID; 477 result = AudioObjectGetPropertyData(devices.get()[i], 478 &pa, 479 0, 480 NULL, 481 &size, 482 &uid); 483 if (result || !uid) 484 continue; 485 486 std::string ret(base::SysCFStringRefToUTF8(uid)); 487 CFRelease(uid); 488 associated_devices.push_back(ret); 489 } 490 491 // No matching device found. 492 if (associated_devices.empty()) 493 return std::string(); 494 495 // Return the device if there is only one associated device. 496 if (associated_devices.size() == 1) 497 return associated_devices[0]; 498 499 // When there are multiple associated devices, we currently do not have a way 500 // to detect if a device (e.g. a digital output device) is actually connected 501 // to an endpoint, so we cannot randomly pick a device. 502 // We pick the device iff the associated device is the default output device. 503 const std::string default_device = GetDefaultOutputDeviceID(); 504 for (std::vector<std::string>::const_iterator iter = 505 associated_devices.begin(); 506 iter != associated_devices.end(); ++iter) { 507 if (default_device == *iter) 508 return *iter; 509 } 510 511 // Failed to figure out which is the matching device, return an emtpy string. 512 return std::string(); 513 } 514 515 AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( 516 const AudioParameters& params) { 517 return MakeLowLatencyOutputStream(params, std::string(), std::string()); 518 } 519 520 AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( 521 const AudioParameters& params, 522 const std::string& device_id, 523 const std::string& input_device_id) { 524 // Handle basic output with no input channels. 525 if (params.input_channels() == 0) { 526 AudioDeviceID device = GetAudioDeviceIdByUId(false, device_id); 527 if (device == kAudioObjectUnknown) { 528 DLOG(ERROR) << "Failed to open output device: " << device_id; 529 return NULL; 530 } 531 return new AUHALStream(this, params, device); 532 } 533 534 DLOG_IF(ERROR, !device_id.empty()) << "Not implemented!"; 535 536 // TODO(xians): support more than stereo input. 537 if (params.input_channels() != 2) { 538 // WebAudio is currently hard-coded to 2 channels so we should not 539 // see this case. 540 NOTREACHED() << "Only stereo input is currently supported!"; 541 return NULL; 542 } 543 544 AudioDeviceID device = kAudioObjectUnknown; 545 if (HasUnifiedDefaultIO()) { 546 // For I/O, the simplest case is when the default input and output 547 // devices are the same. 548 GetDefaultOutputDevice(&device); 549 VLOG(0) << "UNIFIED: default input and output devices are identical"; 550 } else { 551 // Some audio hardware is presented as separate input and output devices 552 // even though they are really the same physical hardware and 553 // share the same "clock domain" at the lowest levels of the driver. 554 // A common of example of this is the "built-in" audio hardware: 555 // "Built-in Line Input" 556 // "Built-in Output" 557 // We would like to use an "aggregate" device for these situations, since 558 // CoreAudio will make the most efficient use of the shared "clock domain" 559 // so we get the lowest latency and use fewer threads. 560 device = aggregate_device_manager_.GetDefaultAggregateDevice(); 561 if (device != kAudioObjectUnknown) 562 VLOG(0) << "Using AGGREGATE audio device"; 563 } 564 565 if (device != kAudioObjectUnknown && 566 input_device_id == AudioManagerBase::kDefaultDeviceId) 567 return new AUHALStream(this, params, device); 568 569 // Fallback to AudioSynchronizedStream which will handle completely 570 // different and arbitrary combinations of input and output devices 571 // even running at different sample-rates. 572 // kAudioDeviceUnknown translates to "use default" here. 573 // TODO(xians): consider tracking UMA stats on AUHALStream 574 // versus AudioSynchronizedStream. 575 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, input_device_id); 576 if (audio_device_id == kAudioObjectUnknown) 577 return NULL; 578 579 return new AudioSynchronizedStream(this, 580 params, 581 audio_device_id, 582 kAudioDeviceUnknown); 583 } 584 585 std::string AudioManagerMac::GetDefaultOutputDeviceID() { 586 AudioDeviceID device_id = kAudioObjectUnknown; 587 if (!GetDefaultOutputDevice(&device_id)) 588 return std::string(); 589 590 const AudioObjectPropertyAddress property_address = { 591 kAudioDevicePropertyDeviceUID, 592 kAudioObjectPropertyScopeGlobal, 593 kAudioObjectPropertyElementMaster 594 }; 595 CFStringRef device_uid = NULL; 596 UInt32 size = sizeof(device_uid); 597 OSStatus status = AudioObjectGetPropertyData(device_id, 598 &property_address, 599 0, 600 NULL, 601 &size, 602 &device_uid); 603 if (status != kAudioHardwareNoError || !device_uid) 604 return std::string(); 605 606 std::string ret(base::SysCFStringRefToUTF8(device_uid)); 607 CFRelease(device_uid); 608 609 return ret; 610 } 611 612 AudioInputStream* AudioManagerMac::MakeLinearInputStream( 613 const AudioParameters& params, const std::string& device_id) { 614 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format()); 615 return new PCMQueueInAudioInputStream(this, params); 616 } 617 618 AudioInputStream* AudioManagerMac::MakeLowLatencyInputStream( 619 const AudioParameters& params, const std::string& device_id) { 620 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); 621 // Gets the AudioDeviceID that refers to the AudioInputDevice with the device 622 // unique id. This AudioDeviceID is used to set the device for Audio Unit. 623 AudioDeviceID audio_device_id = GetAudioDeviceIdByUId(true, device_id); 624 AudioInputStream* stream = NULL; 625 if (audio_device_id != kAudioObjectUnknown) { 626 // AUAudioInputStream needs to be fed the preferred audio output parameters 627 // of the matching device so that the buffer size of both input and output 628 // can be matched. See constructor of AUAudioInputStream for more. 629 const std::string associated_output_device( 630 GetAssociatedOutputDeviceID(device_id)); 631 const AudioParameters output_params = 632 GetPreferredOutputStreamParameters( 633 associated_output_device.empty() ? 634 AudioManagerBase::kDefaultDeviceId : associated_output_device, 635 params); 636 stream = new AUAudioInputStream(this, params, output_params, 637 audio_device_id); 638 } 639 640 return stream; 641 } 642 643 AudioParameters AudioManagerMac::GetPreferredOutputStreamParameters( 644 const std::string& output_device_id, 645 const AudioParameters& input_params) { 646 AudioDeviceID device = GetAudioDeviceIdByUId(false, output_device_id); 647 if (device == kAudioObjectUnknown) { 648 DLOG(ERROR) << "Invalid output device " << output_device_id; 649 return AudioParameters(); 650 } 651 652 int hardware_channels = 2; 653 if (!GetDeviceChannels(device, kAudioDevicePropertyScopeOutput, 654 &hardware_channels)) { 655 // Fallback to stereo. 656 hardware_channels = 2; 657 } 658 659 ChannelLayout channel_layout = GuessChannelLayout(hardware_channels); 660 661 const int hardware_sample_rate = HardwareSampleRateForDevice(device); 662 const int buffer_size = ChooseBufferSize(hardware_sample_rate); 663 664 int input_channels = 0; 665 if (input_params.IsValid()) { 666 input_channels = input_params.input_channels(); 667 668 if (input_channels > 0) { 669 // TODO(xians): given the limitations of the AudioOutputStream 670 // back-ends used with synchronized I/O, we hard-code to stereo. 671 // Specifically, this is a limitation of AudioSynchronizedStream which 672 // can be removed as part of the work to consolidate these back-ends. 673 channel_layout = CHANNEL_LAYOUT_STEREO; 674 } 675 } 676 677 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) 678 channel_layout = CHANNEL_LAYOUT_DISCRETE; 679 else 680 hardware_channels = ChannelLayoutToChannelCount(channel_layout); 681 682 AudioParameters params( 683 AudioParameters::AUDIO_PCM_LOW_LATENCY, 684 channel_layout, 685 hardware_channels, 686 input_channels, 687 hardware_sample_rate, 688 16, 689 buffer_size, 690 AudioParameters::NO_EFFECTS); 691 692 return params; 693 } 694 695 void AudioManagerMac::CreateDeviceListener() { 696 DCHECK(GetMessageLoop()->BelongsToCurrentThread()); 697 698 // Get a baseline for the sample-rate and current device, 699 // so we can intelligently handle device notifications only when necessary. 700 current_sample_rate_ = HardwareSampleRate(); 701 if (!GetDefaultOutputDevice(¤t_output_device_)) 702 current_output_device_ = kAudioDeviceUnknown; 703 704 output_device_listener_.reset(new AudioDeviceListenerMac(base::Bind( 705 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this)))); 706 } 707 708 void AudioManagerMac::DestroyDeviceListener() { 709 DCHECK(GetMessageLoop()->BelongsToCurrentThread()); 710 output_device_listener_.reset(); 711 } 712 713 void AudioManagerMac::HandleDeviceChanges() { 714 if (!GetMessageLoop()->BelongsToCurrentThread()) { 715 GetMessageLoop()->PostTask(FROM_HERE, base::Bind( 716 &AudioManagerMac::HandleDeviceChanges, base::Unretained(this))); 717 return; 718 } 719 720 int new_sample_rate = HardwareSampleRate(); 721 AudioDeviceID new_output_device; 722 GetDefaultOutputDevice(&new_output_device); 723 724 if (current_sample_rate_ == new_sample_rate && 725 current_output_device_ == new_output_device) 726 return; 727 728 current_sample_rate_ = new_sample_rate; 729 current_output_device_ = new_output_device; 730 NotifyAllOutputDeviceChangeListeners(); 731 } 732 733 int AudioManagerMac::ChooseBufferSize(int output_sample_rate) { 734 int buffer_size = kDefaultLowLatencyBufferSize; 735 const int user_buffer_size = GetUserBufferSize(); 736 if (user_buffer_size) { 737 buffer_size = user_buffer_size; 738 } else if (output_sample_rate > 48000) { 739 // The default buffer size is too small for higher sample rates and may lead 740 // to glitching. Adjust upwards by multiples of the default size. 741 if (output_sample_rate <= 96000) 742 buffer_size = 2 * kDefaultLowLatencyBufferSize; 743 else if (output_sample_rate <= 192000) 744 buffer_size = 4 * kDefaultLowLatencyBufferSize; 745 } 746 747 return buffer_size; 748 } 749 750 AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) { 751 return new AudioManagerMac(audio_log_factory); 752 } 753 754 } // namespace media 755