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
27namespace media {
28
29// Maximum number of output streams that can be open simultaneously.
30static const int kMaxOutputStreams = 50;
31
32// Default buffer size in samples for low-latency input and output streams.
33static const int kDefaultLowLatencyBufferSize = 128;
34
35// Default sample-rate on most Apple hardware.
36static const int kFallbackSampleRate = 44100;
37
38static 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.
58bool 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.
68static 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
172static 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
223AudioManagerMac::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
237AudioManagerMac::~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
250bool AudioManagerMac::HasAudioOutputDevices() {
251  return HasAudioHardware(kAudioHardwarePropertyDefaultOutputDevice);
252}
253
254bool 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.
260bool AudioManagerMac::GetDefaultInputDevice(
261    AudioDeviceID* device) {
262  return GetDefaultDevice(device, true);
263}
264
265bool AudioManagerMac::GetDefaultOutputDevice(
266    AudioDeviceID* device) {
267  return GetDefaultDevice(device, false);
268}
269
270bool 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
299bool 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
310bool 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
356int 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
381int 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
390void AudioManagerMac::GetAudioInputDeviceNames(
391    media::AudioDeviceNames* device_names) {
392  DCHECK(device_names->empty());
393  GetAudioDeviceInfo(true, device_names);
394}
395
396void AudioManagerMac::GetAudioOutputDeviceNames(
397    media::AudioDeviceNames* device_names) {
398  DCHECK(device_names->empty());
399  GetAudioDeviceInfo(false, device_names);
400}
401
402AudioParameters 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
436std::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
515AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
516    const AudioParameters& params) {
517  return MakeLowLatencyOutputStream(params, std::string(), std::string());
518}
519
520AudioOutputStream* 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
585std::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
612AudioInputStream* 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
618AudioInputStream* 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
643AudioParameters 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
695void 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(&current_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
708void AudioManagerMac::DestroyDeviceListener() {
709  DCHECK(GetMessageLoop()->BelongsToCurrentThread());
710  output_device_listener_.reset();
711}
712
713void 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
733int 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
750AudioManager* CreateAudioManager(AudioLogFactory* audio_log_factory) {
751  return new AudioManagerMac(audio_log_factory);
752}
753
754}  // namespace media
755