Commit 9df2d09c authored by crogers@google.com's avatar crogers@google.com

Add OSX aggregate audio device support for best performance.

Some audio hardware is presented as separate input and output devices
even though they are really the same physical hardware and
share the same "clock domain" at the lowest levels of the driver.
A common of example of this is the "built-in" audio hardware:
    "Built-in Line Input"
    "Built-in Output"
We would like to use an "aggregate" device for these situations, since
CoreAudio will make the most efficient use of the shared "clock domain"
so we get the lowest latency and use fewer threads.

BUG=none
TEST=extensive manual testing with built-in Mac hardware
http://chromium.googlecode.com/svn/trunk/samples/audio/visualizer-live.html

Review URL: https://chromiumcodereview.appspot.com/13403002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@194765 0039d316-1c4b-4281-b951-d872f2087c98
parent 3d69f33e
This diff is collapsed.
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_AUDIO_MAC_AGGREGATE_DEVICE_MANAGER_H_
#define MEDIA_AUDIO_MAC_AGGREGATE_DEVICE_MANAGER_H_
#include <CoreAudio/CoreAudio.h>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "media/base/media_export.h"
namespace media {
class MEDIA_EXPORT AggregateDeviceManager {
public:
AggregateDeviceManager();
~AggregateDeviceManager();
// Lazily creates an aggregate device based on the default
// input and output devices.
// It will either return a valid device or kAudioDeviceUnknown
// if the default devices are not suitable for aggregate devices.
AudioDeviceID GetDefaultAggregateDevice();
private:
// The caller is responsible for releasing the CFStringRef.
static CFStringRef GetDeviceUID(AudioDeviceID id);
static void GetDeviceName(AudioDeviceID id, char* name, UInt32 size);
static UInt32 GetClockDomain(AudioDeviceID device_id);
static OSStatus GetPluginID(AudioObjectID* id);
CFMutableDictionaryRef CreateAggregateDeviceDictionary(
AudioDeviceID input_id,
AudioDeviceID output_id);
CFMutableArrayRef CreateSubDeviceArray(CFStringRef input_device_UID,
CFStringRef output_device_UID);
OSStatus CreateAggregateDevice(AudioDeviceID input_id,
AudioDeviceID output_id,
AudioDeviceID* aggregate_device);
void DestroyAggregateDevice();
AudioObjectID plugin_id_;
AudioDeviceID input_device_;
AudioDeviceID output_device_;
AudioDeviceID aggregate_device_;
DISALLOW_COPY_AND_ASSIGN(AggregateDeviceManager);
};
} // namespace media
#endif // MEDIA_AUDIO_MAC_AGGREGATE_DEVICE_MANAGER_H_
...@@ -76,10 +76,12 @@ AUHALStream::AUHALStream( ...@@ -76,10 +76,12 @@ AUHALStream::AUHALStream(
// We must have a manager. // We must have a manager.
DCHECK(manager_); DCHECK(manager_);
DVLOG(1) << "Input channels: " << input_channels_; VLOG(1) << "AUHALStream::AUHALStream()";
DVLOG(1) << "Output channels: " << output_channels_; VLOG(1) << "Device: " << device;
DVLOG(1) << "Sample rate: " << params_.sample_rate(); VLOG(1) << "Input channels: " << input_channels_;
DVLOG(1) << "Buffer size: " << number_of_frames_; VLOG(1) << "Output channels: " << output_channels_;
VLOG(1) << "Sample rate: " << params_.sample_rate();
VLOG(1) << "Buffer size: " << number_of_frames_;
} }
AUHALStream::~AUHALStream() { AUHALStream::~AUHALStream() {
......
...@@ -426,24 +426,55 @@ AudioOutputStream* AudioManagerMac::MakeLinearOutputStream( ...@@ -426,24 +426,55 @@ AudioOutputStream* AudioManagerMac::MakeLinearOutputStream(
AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream( AudioOutputStream* AudioManagerMac::MakeLowLatencyOutputStream(
const AudioParameters& params) { const AudioParameters& params) {
// Handle basic output with no input channels.
if (params.input_channels() == 0) {
AudioDeviceID device = kAudioObjectUnknown;
GetDefaultOutputDevice(&device);
return new AUHALStream(this, params, device);
}
// TODO(crogers): support more than stereo input. // TODO(crogers): support more than stereo input.
if (params.input_channels() == 2) { if (params.input_channels() != 2) {
if (HasUnifiedDefaultIO()) // WebAudio is currently hard-coded to 2 channels so we should not
return new AudioHardwareUnifiedStream(this, params); // see this case.
NOTREACHED() << "Only stereo input is currently supported!";
// TODO(crogers): use aggregate devices along with AUHALStream return NULL;
// to get better performance for built-in hardware.
// kAudioDeviceUnknown translates to "use default" here.
return new AudioSynchronizedStream(this,
params,
kAudioDeviceUnknown,
kAudioDeviceUnknown);
} }
AudioDeviceID device = kAudioObjectUnknown; AudioDeviceID device = kAudioObjectUnknown;
GetDefaultOutputDevice(&device); if (HasUnifiedDefaultIO()) {
return new AUHALStream(this, params, device); // For I/O, the simplest case is when the default input and output
// devices are the same.
GetDefaultOutputDevice(&device);
LOG(INFO) << "UNIFIED: default input and output devices are identical";
} else {
// Some audio hardware is presented as separate input and output devices
// even though they are really the same physical hardware and
// share the same "clock domain" at the lowest levels of the driver.
// A common of example of this is the "built-in" audio hardware:
// "Built-in Line Input"
// "Built-in Output"
// We would like to use an "aggregate" device for these situations, since
// CoreAudio will make the most efficient use of the shared "clock domain"
// so we get the lowest latency and use fewer threads.
device = aggregate_device_manager_.GetDefaultAggregateDevice();
if (device != kAudioObjectUnknown)
LOG(INFO) << "Using AGGREGATE audio device";
}
if (device != kAudioObjectUnknown)
return new AUHALStream(this, params, device);
// Fallback to AudioSynchronizedStream which will handle completely
// different and arbitrary combinations of input and output devices
// even running at different sample-rates.
// kAudioDeviceUnknown translates to "use default" here.
// TODO(crogers): consider tracking UMA stats on AUHALStream
// versus AudioSynchronizedStream.
return new AudioSynchronizedStream(this,
params,
kAudioDeviceUnknown,
kAudioDeviceUnknown);
} }
AudioInputStream* AudioManagerMac::MakeLinearInputStream( AudioInputStream* AudioManagerMac::MakeLinearInputStream(
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "base/message_loop_proxy.h" #include "base/message_loop_proxy.h"
#include "media/audio/audio_manager_base.h" #include "media/audio/audio_manager_base.h"
#include "media/audio/mac/aggregate_device_manager.h"
#include "media/audio/mac/audio_device_listener_mac.h" #include "media/audio/mac/audio_device_listener_mac.h"
namespace media { namespace media {
...@@ -75,6 +76,8 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase { ...@@ -75,6 +76,8 @@ class MEDIA_EXPORT AudioManagerMac : public AudioManagerBase {
int current_sample_rate_; int current_sample_rate_;
AudioDeviceID current_output_device_; AudioDeviceID current_output_device_;
AggregateDeviceManager aggregate_device_manager_;
DISALLOW_COPY_AND_ASSIGN(AudioManagerMac); DISALLOW_COPY_AND_ASSIGN(AudioManagerMac);
}; };
......
...@@ -85,6 +85,7 @@ AudioSynchronizedStream::AudioSynchronizedStream( ...@@ -85,6 +85,7 @@ AudioSynchronizedStream::AudioSynchronizedStream(
is_running_(false), is_running_(false),
hardware_buffer_size_(kHardwareBufferSize), hardware_buffer_size_(kHardwareBufferSize),
channels_(kChannels) { channels_(kChannels) {
VLOG(1) << "AudioSynchronizedStream::AudioSynchronizedStream()";
} }
AudioSynchronizedStream::~AudioSynchronizedStream() { AudioSynchronizedStream::~AudioSynchronizedStream() {
......
...@@ -136,6 +136,8 @@ ...@@ -136,6 +136,8 @@
'audio/linux/alsa_wrapper.h', 'audio/linux/alsa_wrapper.h',
'audio/linux/audio_manager_linux.cc', 'audio/linux/audio_manager_linux.cc',
'audio/linux/audio_manager_linux.h', 'audio/linux/audio_manager_linux.h',
'audio/mac/aggregate_device_manager.cc',
'audio/mac/aggregate_device_manager.h',
'audio/mac/audio_auhal_mac.cc', 'audio/mac/audio_auhal_mac.cc',
'audio/mac/audio_auhal_mac.h', 'audio/mac/audio_auhal_mac.h',
'audio/mac/audio_device_listener_mac.cc', 'audio/mac/audio_device_listener_mac.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment