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.
5 #include "media/audio/mac/audio_manager_mac.h"
7 #include <CoreAudio/AudioHardware.h>
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/audio_util.h"
17 #include "media/audio/mac/audio_auhal_mac.h"
18 #include "media/audio/mac/audio_input_mac.h"
19 #include "media/audio/mac/audio_low_latency_input_mac.h"
20 #include "media/audio/mac/audio_low_latency_output_mac.h"
21 #include "media/audio/mac/audio_synchronized_mac.h"
22 #include "media/audio/mac/audio_unified_mac.h"
23 #include "media/base/bind_to_loop.h"
24 #include "media/base/channel_layout.h"
25 #include "media/base/limits.h"
26 #include "media/base/media_switches.h"
30 // Maximum number of output streams that can be open simultaneously.
31 static const int kMaxOutputStreams
= 50;
33 // Default buffer size in samples for low-latency input and output streams.
34 static const int kDefaultLowLatencyBufferSize
= 128;
36 // Default sample-rate on most Apple hardware.
37 static const int kFallbackSampleRate
= 44100;
39 static int ChooseBufferSize(int output_sample_rate
) {
40 int buffer_size
= kDefaultLowLatencyBufferSize
;
41 const int user_buffer_size
= GetUserBufferSize();
42 if (user_buffer_size
) {
43 buffer_size
= user_buffer_size
;
44 } else if (output_sample_rate
> 48000) {
45 // The default buffer size is too small for higher sample rates and may lead
46 // to glitching. Adjust upwards by multiples of the default size.
47 if (output_sample_rate
<= 96000)
48 buffer_size
= 2 * kDefaultLowLatencyBufferSize
;
49 else if (output_sample_rate
<= 192000)
50 buffer_size
= 4 * kDefaultLowLatencyBufferSize
;
56 static bool HasAudioHardware(AudioObjectPropertySelector selector
) {
57 AudioDeviceID output_device_id
= kAudioObjectUnknown
;
58 const AudioObjectPropertyAddress property_address
= {
60 kAudioObjectPropertyScopeGlobal
, // mScope
61 kAudioObjectPropertyElementMaster
// mElement
63 UInt32 output_device_id_size
= static_cast<UInt32
>(sizeof(output_device_id
));
64 OSStatus err
= AudioObjectGetPropertyData(kAudioObjectSystemObject
,
66 0, // inQualifierDataSize
67 NULL
, // inQualifierData
68 &output_device_id_size
,
70 return err
== kAudioHardwareNoError
&&
71 output_device_id
!= kAudioObjectUnknown
;
74 // Returns true if the default input device is the same as
75 // the default output device.
76 bool AudioManagerMac::HasUnifiedDefaultIO() {
77 AudioDeviceID input_id
, output_id
;
78 if (!GetDefaultInputDevice(&input_id
) || !GetDefaultOutputDevice(&output_id
))
81 return input_id
== output_id
;
84 static void GetAudioDeviceInfo(bool is_input
,
85 media::AudioDeviceNames
* device_names
) {
87 device_names
->clear();
89 // Query the number of total devices.
90 AudioObjectPropertyAddress property_address
= {
91 kAudioHardwarePropertyDevices
,
92 kAudioObjectPropertyScopeGlobal
,
93 kAudioObjectPropertyElementMaster
96 OSStatus result
= AudioObjectGetPropertyDataSize(kAudioObjectSystemObject
,
104 int device_count
= size
/ sizeof(AudioDeviceID
);
106 // Get the array of device ids for all the devices, which includes both
107 // input devices and output devices.
108 scoped_ptr_malloc
<AudioDeviceID
>
109 devices(reinterpret_cast<AudioDeviceID
*>(malloc(size
)));
110 AudioDeviceID
* device_ids
= devices
.get();
111 result
= AudioObjectGetPropertyData(kAudioObjectSystemObject
,
120 // Iterate over all available devices to gather information.
121 for (int i
= 0; i
< device_count
; ++i
) {
122 // Get the number of input or output channels of the device.
123 property_address
.mScope
= is_input
?
124 kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
125 property_address
.mSelector
= kAudioDevicePropertyStreams
;
127 result
= AudioObjectGetPropertyDataSize(device_ids
[i
],
136 CFStringRef uid
= NULL
;
138 property_address
.mSelector
= kAudioDevicePropertyDeviceUID
;
139 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
140 result
= AudioObjectGetPropertyData(device_ids
[i
],
150 CFStringRef name
= NULL
;
151 property_address
.mSelector
= kAudioObjectPropertyName
;
152 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
153 result
= AudioObjectGetPropertyData(device_ids
[i
],
165 // Store the device name and UID.
166 media::AudioDeviceName device_name
;
167 device_name
.device_name
= base::SysCFStringRefToUTF8(name
);
168 device_name
.unique_id
= base::SysCFStringRefToUTF8(uid
);
169 device_names
->push_back(device_name
);
171 // We are responsible for releasing the returned CFObject. See the
172 // comment in the AudioHardware.h for constant
173 // kAudioDevicePropertyDeviceUID.
181 static AudioDeviceID
GetAudioDeviceIdByUId(bool is_input
,
182 const std::string
& device_id
) {
183 AudioObjectPropertyAddress property_address
= {
184 kAudioHardwarePropertyDevices
,
185 kAudioObjectPropertyScopeGlobal
,
186 kAudioObjectPropertyElementMaster
188 AudioDeviceID audio_device_id
= kAudioObjectUnknown
;
189 UInt32 device_size
= sizeof(audio_device_id
);
190 OSStatus result
= -1;
192 if (device_id
== AudioManagerBase::kDefaultDeviceId
) {
194 property_address
.mSelector
= is_input
?
195 kAudioHardwarePropertyDefaultInputDevice
:
196 kAudioHardwarePropertyDefaultOutputDevice
;
198 result
= AudioObjectGetPropertyData(kAudioObjectSystemObject
,
205 // Non-default device.
206 base::ScopedCFTypeRef
<CFStringRef
> uid(
207 base::SysUTF8ToCFStringRef(device_id
));
208 AudioValueTranslation value
;
209 value
.mInputData
= &uid
;
210 value
.mInputDataSize
= sizeof(CFStringRef
);
211 value
.mOutputData
= &audio_device_id
;
212 value
.mOutputDataSize
= device_size
;
213 UInt32 translation_size
= sizeof(AudioValueTranslation
);
215 property_address
.mSelector
= kAudioHardwarePropertyDeviceForUID
;
216 result
= AudioObjectGetPropertyData(kAudioObjectSystemObject
,
225 OSSTATUS_DLOG(WARNING
, result
) << "Unable to query device " << device_id
226 << " for AudioDeviceID";
229 return audio_device_id
;
232 AudioManagerMac::AudioManagerMac()
233 : current_sample_rate_(0) {
234 current_output_device_
= kAudioDeviceUnknown
;
236 SetMaxOutputStreamsAllowed(kMaxOutputStreams
);
238 // Task must be posted last to avoid races from handing out "this" to the
239 // audio thread. Always PostTask even if we're on the right thread since
240 // AudioManager creation is on the startup path and this may be slow.
241 GetMessageLoop()->PostTask(FROM_HERE
, base::Bind(
242 &AudioManagerMac::CreateDeviceListener
, base::Unretained(this)));
245 AudioManagerMac::~AudioManagerMac() {
246 if (GetMessageLoop()->BelongsToCurrentThread()) {
247 DestroyDeviceListener();
249 // It's safe to post a task here since Shutdown() will wait for all tasks to
250 // complete before returning.
251 GetMessageLoop()->PostTask(FROM_HERE
, base::Bind(
252 &AudioManagerMac::DestroyDeviceListener
, base::Unretained(this)));
258 bool AudioManagerMac::HasAudioOutputDevices() {
259 return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice
);
262 bool AudioManagerMac::HasAudioInputDevices() {
263 return HasAudioHardware(kAudioHardwarePropertyDefaultInputDevice
);
266 // TODO(crogers): There are several places on the OSX specific code which
267 // could benefit from these helper functions.
268 bool AudioManagerMac::GetDefaultInputDevice(
269 AudioDeviceID
* device
) {
270 return GetDefaultDevice(device
, true);
273 bool AudioManagerMac::GetDefaultOutputDevice(
274 AudioDeviceID
* device
) {
275 return GetDefaultDevice(device
, false);
278 bool AudioManagerMac::GetDefaultDevice(
279 AudioDeviceID
* device
, bool input
) {
282 // Obtain the current output device selected by the user.
283 AudioObjectPropertyAddress pa
;
284 pa
.mSelector
= input
? kAudioHardwarePropertyDefaultInputDevice
:
285 kAudioHardwarePropertyDefaultOutputDevice
;
286 pa
.mScope
= kAudioObjectPropertyScopeGlobal
;
287 pa
.mElement
= kAudioObjectPropertyElementMaster
;
289 UInt32 size
= sizeof(*device
);
291 OSStatus result
= AudioObjectGetPropertyData(
292 kAudioObjectSystemObject
,
299 if ((result
!= kAudioHardwareNoError
) || (*device
== kAudioDeviceUnknown
)) {
300 DLOG(ERROR
) << "Error getting default AudioDevice.";
307 bool AudioManagerMac::GetDefaultOutputChannels(
309 AudioDeviceID device
;
310 if (!GetDefaultOutputDevice(&device
))
313 return GetDeviceChannels(device
,
314 kAudioDevicePropertyScopeOutput
,
318 bool AudioManagerMac::GetDeviceChannels(
319 AudioDeviceID device
,
320 AudioObjectPropertyScope scope
,
324 // Get stream configuration.
325 AudioObjectPropertyAddress pa
;
326 pa
.mSelector
= kAudioDevicePropertyStreamConfiguration
;
328 pa
.mElement
= kAudioObjectPropertyElementMaster
;
331 OSStatus result
= AudioObjectGetPropertyDataSize(device
, &pa
, 0, 0, &size
);
332 if (result
!= noErr
|| !size
)
336 scoped_ptr
<uint8
[]> list_storage(new uint8
[size
]);
337 AudioBufferList
& buffer_list
=
338 *reinterpret_cast<AudioBufferList
*>(list_storage
.get());
340 result
= AudioObjectGetPropertyData(
350 // Determine number of input channels.
351 int channels_per_frame
= buffer_list
.mNumberBuffers
> 0 ?
352 buffer_list
.mBuffers
[0].mNumberChannels
: 0;
353 if (channels_per_frame
== 1 && buffer_list
.mNumberBuffers
> 1) {
355 *channels
= buffer_list
.mNumberBuffers
;
358 *channels
= channels_per_frame
;
364 int AudioManagerMac::HardwareSampleRateForDevice(AudioDeviceID device_id
) {
365 Float64 nominal_sample_rate
;
366 UInt32 info_size
= sizeof(nominal_sample_rate
);
368 static const AudioObjectPropertyAddress kNominalSampleRateAddress
= {
369 kAudioDevicePropertyNominalSampleRate
,
370 kAudioObjectPropertyScopeGlobal
,
371 kAudioObjectPropertyElementMaster
373 OSStatus result
= AudioObjectGetPropertyData(
375 &kNominalSampleRateAddress
,
379 &nominal_sample_rate
);
380 if (result
!= noErr
) {
381 OSSTATUS_DLOG(WARNING
, result
)
382 << "Could not get default sample rate for device: " << device_id
;
386 return static_cast<int>(nominal_sample_rate
);
389 int AudioManagerMac::HardwareSampleRate() {
390 // Determine the default output device's sample-rate.
391 AudioDeviceID device_id
= kAudioObjectUnknown
;
392 if (!GetDefaultOutputDevice(&device_id
))
393 return kFallbackSampleRate
;
395 return HardwareSampleRateForDevice(device_id
);
398 void AudioManagerMac::GetAudioInputDeviceNames(
399 media::AudioDeviceNames
* device_names
) {
400 GetAudioDeviceInfo(true, device_names
);
401 if (!device_names
->empty()) {
402 // Prepend the default device to the list since we always want it to be
403 // on the top of the list for all platforms. There is no duplicate
404 // counting here since the default device has been abstracted out before.
405 media::AudioDeviceName name
;
406 name
.device_name
= AudioManagerBase::kDefaultDeviceName
;
407 name
.unique_id
= AudioManagerBase::kDefaultDeviceId
;
408 device_names
->push_front(name
);
412 AudioParameters
AudioManagerMac::GetInputStreamParameters(
413 const std::string
& device_id
) {
414 // Due to the sharing of the input and output buffer sizes, we need to choose
415 // the input buffer size based on the output sample rate. See
416 // http://crbug.com/154352.
417 const int buffer_size
= ChooseBufferSize(
418 AUAudioOutputStream::HardwareSampleRate());
420 AudioDeviceID device
= GetAudioDeviceIdByUId(true, device_id
);
421 if (device
== kAudioObjectUnknown
) {
422 DLOG(ERROR
) << "Invalid device " << device_id
;
423 return AudioParameters();
427 ChannelLayout channel_layout
= CHANNEL_LAYOUT_STEREO
;
428 if (GetDeviceChannels(device
, kAudioDevicePropertyScopeInput
, &channels
) &&
430 channel_layout
= GuessChannelLayout(channels
);
432 DLOG(ERROR
) << "Failed to get the device channels, use stereo as default "
433 << "for device " << device_id
;
436 int sample_rate
= HardwareSampleRateForDevice(device
);
438 sample_rate
= kFallbackSampleRate
;
440 // TODO(xians): query the native channel layout for the specific device.
441 return AudioParameters(
442 AudioParameters::AUDIO_PCM_LOW_LATENCY
, channel_layout
,
443 sample_rate
, 16, buffer_size
);
446 AudioOutputStream
* AudioManagerMac::MakeLinearOutputStream(
447 const AudioParameters
& params
) {
448 return MakeLowLatencyOutputStream(params
, std::string());
451 AudioOutputStream
* AudioManagerMac::MakeLowLatencyOutputStream(
452 const AudioParameters
& params
, const std::string
& input_device_id
) {
453 // Handle basic output with no input channels.
454 if (params
.input_channels() == 0) {
455 AudioDeviceID device
= kAudioObjectUnknown
;
456 GetDefaultOutputDevice(&device
);
457 return new AUHALStream(this, params
, device
);
460 // TODO(crogers): support more than stereo input.
461 if (params
.input_channels() != 2) {
462 // WebAudio is currently hard-coded to 2 channels so we should not
464 NOTREACHED() << "Only stereo input is currently supported!";
468 AudioDeviceID device
= kAudioObjectUnknown
;
469 if (HasUnifiedDefaultIO()) {
470 // For I/O, the simplest case is when the default input and output
471 // devices are the same.
472 GetDefaultOutputDevice(&device
);
473 LOG(INFO
) << "UNIFIED: default input and output devices are identical";
475 // Some audio hardware is presented as separate input and output devices
476 // even though they are really the same physical hardware and
477 // share the same "clock domain" at the lowest levels of the driver.
478 // A common of example of this is the "built-in" audio hardware:
479 // "Built-in Line Input"
481 // We would like to use an "aggregate" device for these situations, since
482 // CoreAudio will make the most efficient use of the shared "clock domain"
483 // so we get the lowest latency and use fewer threads.
484 device
= aggregate_device_manager_
.GetDefaultAggregateDevice();
485 if (device
!= kAudioObjectUnknown
)
486 LOG(INFO
) << "Using AGGREGATE audio device";
489 if (device
!= kAudioObjectUnknown
&&
490 input_device_id
== AudioManagerBase::kDefaultDeviceId
)
491 return new AUHALStream(this, params
, device
);
493 // Fallback to AudioSynchronizedStream which will handle completely
494 // different and arbitrary combinations of input and output devices
495 // even running at different sample-rates.
496 // kAudioDeviceUnknown translates to "use default" here.
497 // TODO(crogers): consider tracking UMA stats on AUHALStream
498 // versus AudioSynchronizedStream.
499 AudioDeviceID audio_device_id
= GetAudioDeviceIdByUId(true, input_device_id
);
500 if (audio_device_id
== kAudioObjectUnknown
)
503 return new AudioSynchronizedStream(this,
506 kAudioDeviceUnknown
);
509 AudioInputStream
* AudioManagerMac::MakeLinearInputStream(
510 const AudioParameters
& params
, const std::string
& device_id
) {
511 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR
, params
.format());
512 return new PCMQueueInAudioInputStream(this, params
);
515 AudioInputStream
* AudioManagerMac::MakeLowLatencyInputStream(
516 const AudioParameters
& params
, const std::string
& device_id
) {
517 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY
, params
.format());
518 // Gets the AudioDeviceID that refers to the AudioOutputDevice with the device
519 // unique id. This AudioDeviceID is used to set the device for Audio Unit.
520 AudioDeviceID audio_device_id
= GetAudioDeviceIdByUId(true, device_id
);
521 AudioInputStream
* stream
= NULL
;
522 if (audio_device_id
!= kAudioObjectUnknown
)
523 stream
= new AUAudioInputStream(this, params
, audio_device_id
);
528 AudioParameters
AudioManagerMac::GetPreferredOutputStreamParameters(
529 const AudioParameters
& input_params
) {
530 int hardware_channels
= 2;
531 if (!GetDefaultOutputChannels(&hardware_channels
)) {
532 // Fallback to stereo.
533 hardware_channels
= 2;
536 ChannelLayout channel_layout
= GuessChannelLayout(hardware_channels
);
538 const int hardware_sample_rate
= AUAudioOutputStream::HardwareSampleRate();
539 const int buffer_size
= ChooseBufferSize(hardware_sample_rate
);
541 int input_channels
= 0;
542 if (input_params
.IsValid()) {
543 input_channels
= input_params
.input_channels();
545 if (input_channels
> 0) {
546 // TODO(crogers): given the limitations of the AudioOutputStream
547 // back-ends used with synchronized I/O, we hard-code to stereo.
548 // Specifically, this is a limitation of AudioSynchronizedStream which
549 // can be removed as part of the work to consolidate these back-ends.
550 channel_layout
= CHANNEL_LAYOUT_STEREO
;
554 AudioParameters
params(
555 AudioParameters::AUDIO_PCM_LOW_LATENCY
,
558 hardware_sample_rate
,
562 if (channel_layout
== CHANNEL_LAYOUT_UNSUPPORTED
)
563 params
.SetDiscreteChannels(hardware_channels
);
568 void AudioManagerMac::CreateDeviceListener() {
569 DCHECK(GetMessageLoop()->BelongsToCurrentThread());
571 // Get a baseline for the sample-rate and current device,
572 // so we can intelligently handle device notifications only when necessary.
573 current_sample_rate_
= HardwareSampleRate();
574 if (!GetDefaultOutputDevice(¤t_output_device_
))
575 current_output_device_
= kAudioDeviceUnknown
;
577 output_device_listener_
.reset(new AudioDeviceListenerMac(base::Bind(
578 &AudioManagerMac::HandleDeviceChanges
, base::Unretained(this))));
581 void AudioManagerMac::DestroyDeviceListener() {
582 DCHECK(GetMessageLoop()->BelongsToCurrentThread());
583 output_device_listener_
.reset();
586 void AudioManagerMac::HandleDeviceChanges() {
587 if (!GetMessageLoop()->BelongsToCurrentThread()) {
588 GetMessageLoop()->PostTask(FROM_HERE
, base::Bind(
589 &AudioManagerMac::HandleDeviceChanges
, base::Unretained(this)));
593 int new_sample_rate
= HardwareSampleRate();
594 AudioDeviceID new_output_device
;
595 GetDefaultOutputDevice(&new_output_device
);
597 if (current_sample_rate_
== new_sample_rate
&&
598 current_output_device_
== new_output_device
)
601 current_sample_rate_
= new_sample_rate
;
602 current_output_device_
= new_output_device
;
603 NotifyAllOutputDeviceChangeListeners();
606 AudioManager
* CreateAudioManager() {
607 return new AudioManagerMac();