Commit ebae1d3f authored by xians's avatar xians Committed by Commit bot

Used native deinterleaved and float point format for the input streams.

If we call GetProperty of kAudioUnitProperty_StreamFormat before setting the format, the device will report kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved as the native format of the device, which is the same as the output.

This patch changes the format to use kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved to open the device, so that we will avoid format flipping back and forth. Hope this optimization will help increase the stability of the input audio on Mac.

TBR=DaleCurtis@chromium.org

BUG=404884
TEST=media_unittests && https://webrtc.googlecode.com/svn-history/r5497/trunk/samples/js/demos/html/pc1.html, https://www.google.com/intl/en/chrome/demos/speech.html

Review URL: https://codereview.chromium.org/510073002

Cr-Commit-Position: refs/heads/master@{#292636}
parent 22aa58e6
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/mac/mac_logging.h" #include "base/mac/mac_logging.h"
#include "media/audio/mac/audio_manager_mac.h" #include "media/audio/mac/audio_manager_mac.h"
#include "media/base/audio_block_fifo.h"
#include "media/base/audio_bus.h" #include "media/base/audio_bus.h"
#include "media/base/data_buffer.h" #include "media/base/data_buffer.h"
...@@ -31,6 +32,23 @@ static std::ostream& operator<<(std::ostream& os, ...@@ -31,6 +32,23 @@ static std::ostream& operator<<(std::ostream& os,
return os; return os;
} }
static void WrapBufferList(AudioBufferList* buffer_list,
AudioBus* bus,
int frames) {
DCHECK(buffer_list);
DCHECK(bus);
const int channels = bus->channels();
const int buffer_list_channels = buffer_list->mNumberBuffers;
CHECK_EQ(channels, buffer_list_channels);
// Copy pointers from AudioBufferList.
for (int i = 0; i < channels; ++i)
bus->SetChannelData(i, static_cast<float*>(buffer_list->mBuffers[i].mData));
// Finally set the actual length.
bus->set_frames(frames);
}
// See "Technical Note TN2091 - Device input using the HAL Output Audio Unit" // See "Technical Note TN2091 - Device input using the HAL Output Audio Unit"
// http://developer.apple.com/library/mac/#technotes/tn2091/_index.html // http://developer.apple.com/library/mac/#technotes/tn2091/_index.html
// for more details and background regarding this implementation. // for more details and background regarding this implementation.
...@@ -46,43 +64,46 @@ AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager, ...@@ -46,43 +64,46 @@ AUAudioInputStream::AUAudioInputStream(AudioManagerMac* manager,
started_(false), started_(false),
hardware_latency_frames_(0), hardware_latency_frames_(0),
number_of_channels_in_frame_(0), number_of_channels_in_frame_(0),
fifo_(input_params.channels(), audio_wrapper_(AudioBus::CreateWrapper(input_params.channels())) {
number_of_frames_,
kNumberOfBlocksBufferInFifo) {
DCHECK(manager_); DCHECK(manager_);
// Set up the desired (output) format specified by the client. // Set up the desired (output) format specified by the client.
format_.mSampleRate = input_params.sample_rate(); format_.mSampleRate = input_params.sample_rate();
format_.mFormatID = kAudioFormatLinearPCM; format_.mFormatID = kAudioFormatLinearPCM;
format_.mFormatFlags = kLinearPCMFormatFlagIsPacked | format_.mFormatFlags =
kLinearPCMFormatFlagIsSignedInteger; kAudioFormatFlagsNativeFloatPacked | kLinearPCMFormatFlagIsNonInterleaved;
format_.mBitsPerChannel = input_params.bits_per_sample(); size_t bytes_per_sample = sizeof(Float32);
format_.mBitsPerChannel = bytes_per_sample * 8;
format_.mChannelsPerFrame = input_params.channels(); format_.mChannelsPerFrame = input_params.channels();
format_.mFramesPerPacket = 1; // uncompressed audio format_.mFramesPerPacket = 1;
format_.mBytesPerPacket = (format_.mBitsPerChannel * format_.mBytesPerFrame = bytes_per_sample;
input_params.channels()) / 8; format_.mBytesPerPacket = format_.mBytesPerFrame * format_.mFramesPerPacket;
format_.mBytesPerFrame = format_.mBytesPerPacket;
format_.mReserved = 0; format_.mReserved = 0;
DVLOG(1) << "Desired ouput format: " << format_; DVLOG(1) << "Desired ouput format: " << format_;
// Derive size (in bytes) of the buffers that we will render to. // Allocate AudioBufferList based on the number of channels.
UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame; audio_buffer_list_.reset(static_cast<AudioBufferList*>(
DVLOG(1) << "Size of data buffer in bytes : " << data_byte_size; malloc(sizeof(AudioBufferList) * input_params.channels())));
audio_buffer_list_->mNumberBuffers = input_params.channels();
// Allocate AudioBuffers to be used as storage for the received audio. // Allocate AudioBuffers to be used as storage for the received audio.
// The AudioBufferList structure works as a placeholder for the // The AudioBufferList structure works as a placeholder for the
// AudioBuffer structure, which holds a pointer to the actual data buffer. // AudioBuffer structure, which holds a pointer to the actual data buffer.
audio_data_buffer_.reset(new uint8[data_byte_size]); UInt32 data_byte_size = number_of_frames_ * format_.mBytesPerFrame;
audio_buffer_list_.mNumberBuffers = 1; audio_data_buffer_.reset(static_cast<float*>(base::AlignedAlloc(
data_byte_size * audio_buffer_list_->mNumberBuffers,
AudioBuffer* audio_buffer = audio_buffer_list_.mBuffers; AudioBus::kChannelAlignment)));
audio_buffer->mNumberChannels = input_params.channels(); AudioBuffer* audio_buffer = audio_buffer_list_->mBuffers;
audio_buffer->mDataByteSize = data_byte_size; for (UInt32 i = 0; i < audio_buffer_list_->mNumberBuffers; ++i) {
audio_buffer->mData = audio_data_buffer_.get(); audio_buffer[i].mNumberChannels = 1;
audio_buffer[i].mDataByteSize = data_byte_size;
audio_buffer[i].mData = audio_data_buffer_.get() + i * data_byte_size;
}
} }
AUAudioInputStream::~AUAudioInputStream() {} AUAudioInputStream::~AUAudioInputStream() {
}
// Obtain and open the AUHAL AudioOutputUnit for recording. // Obtain and open the AUHAL AudioOutputUnit for recording.
bool AUAudioInputStream::Open() { bool AUAudioInputStream::Open() {
...@@ -165,23 +186,6 @@ bool AUAudioInputStream::Open() { ...@@ -165,23 +186,6 @@ bool AUAudioInputStream::Open() {
return false; return false;
} }
// Register the input procedure for the AUHAL.
// This procedure will be called when the AUHAL has received new data
// from the input device.
AURenderCallbackStruct callback;
callback.inputProc = InputProc;
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(audio_unit_,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callback,
sizeof(callback));
if (result) {
HandleError(result);
return false;
}
// Set up the the desired (output) format. // Set up the the desired (output) format.
// For obtaining input from a device, the device format is always expressed // For obtaining input from a device, the device format is always expressed
// on the output scope of the AUHAL's Element 1. // on the output scope of the AUHAL's Element 1.
...@@ -229,6 +233,23 @@ bool AUAudioInputStream::Open() { ...@@ -229,6 +233,23 @@ bool AUAudioInputStream::Open() {
} }
} }
// Register the input procedure for the AUHAL.
// This procedure will be called when the AUHAL has received new data
// from the input device.
AURenderCallbackStruct callback;
callback.inputProc = InputProc;
callback.inputProcRefCon = this;
result = AudioUnitSetProperty(audio_unit_,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callback,
sizeof(callback));
if (result) {
HandleError(result);
return false;
}
// Finally, initialize the audio unit and ensure that it is ready to render. // Finally, initialize the audio unit and ensure that it is ready to render.
// Allocates memory according to the maximum number of audio frames // Allocates memory according to the maximum number of audio frames
// it can produce in response to a single render call. // it can produce in response to a single render call.
...@@ -342,9 +363,9 @@ void AUAudioInputStream::SetVolume(double volume) { ...@@ -342,9 +363,9 @@ void AUAudioInputStream::SetVolume(double volume) {
Float32 volume_float32 = static_cast<Float32>(volume); Float32 volume_float32 = static_cast<Float32>(volume);
AudioObjectPropertyAddress property_address = { AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeInput, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster kAudioObjectPropertyElementMaster
}; };
// Try to set the volume for master volume channel. // Try to set the volume for master volume channel.
...@@ -390,15 +411,15 @@ void AUAudioInputStream::SetVolume(double volume) { ...@@ -390,15 +411,15 @@ void AUAudioInputStream::SetVolume(double volume) {
double AUAudioInputStream::GetVolume() { double AUAudioInputStream::GetVolume() {
// Verify that we have a valid device. // Verify that we have a valid device.
if (input_device_id_ == kAudioObjectUnknown){ if (input_device_id_ == kAudioObjectUnknown) {
NOTREACHED() << "Device ID is unknown"; NOTREACHED() << "Device ID is unknown";
return 0.0; return 0.0;
} }
AudioObjectPropertyAddress property_address = { AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeInput, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster kAudioObjectPropertyElementMaster
}; };
if (AudioObjectHasProperty(input_device_id_, &property_address)) { if (AudioObjectHasProperty(input_device_id_, &property_address)) {
...@@ -406,12 +427,8 @@ double AUAudioInputStream::GetVolume() { ...@@ -406,12 +427,8 @@ double AUAudioInputStream::GetVolume() {
// master channel. // master channel.
Float32 volume_float32 = 0.0; Float32 volume_float32 = 0.0;
UInt32 size = sizeof(volume_float32); UInt32 size = sizeof(volume_float32);
OSStatus result = AudioObjectGetPropertyData(input_device_id_, OSStatus result = AudioObjectGetPropertyData(
&property_address, input_device_id_, &property_address, 0, NULL, &size, &volume_float32);
0,
NULL,
&size,
&volume_float32);
if (result == noErr) if (result == noErr)
return static_cast<double>(volume_float32); return static_cast<double>(volume_float32);
} else { } else {
...@@ -472,9 +489,8 @@ OSStatus AUAudioInputStream::InputProc(void* user_data, ...@@ -472,9 +489,8 @@ OSStatus AUAudioInputStream::InputProc(void* user_data,
return result; return result;
// Deliver recorded data to the consumer as a callback. // Deliver recorded data to the consumer as a callback.
return audio_input->Provide(number_of_frames, return audio_input->Provide(
audio_input->audio_buffer_list(), number_of_frames, audio_input->audio_buffer_list(), time_stamp);
time_stamp);
} }
OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
...@@ -491,22 +507,42 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames, ...@@ -491,22 +507,42 @@ OSStatus AUAudioInputStream::Provide(UInt32 number_of_frames,
AudioBuffer& buffer = io_data->mBuffers[0]; AudioBuffer& buffer = io_data->mBuffers[0];
uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData); uint8* audio_data = reinterpret_cast<uint8*>(buffer.mData);
uint32 capture_delay_bytes = static_cast<uint32> uint32 capture_delay_bytes = static_cast<uint32>(
((capture_latency_frames + 0.5) * format_.mBytesPerFrame); (capture_latency_frames + 0.5) * format_.mBytesPerFrame);
DCHECK(audio_data); DCHECK(audio_data);
if (!audio_data) if (!audio_data)
return kAudioUnitErr_InvalidElement; return kAudioUnitErr_InvalidElement;
// Copy captured (and interleaved) data into FIFO. // Wrap the output AudioBufferList to |audio_wrapper_|.
fifo_.Push(audio_data, number_of_frames, format_.mBitsPerChannel / 8); WrapBufferList(io_data, audio_wrapper_.get(), number_of_frames);
// If the stream parameters change for any reason, we need to insert a FIFO
// since the OnMoreData() pipeline can't handle frame size changes.
if (number_of_frames != number_of_frames_) {
// Create a FIFO on the fly to handle any discrepancies in callback rates.
if (!fifo_) {
fifo_.reset(new AudioBlockFifo(audio_wrapper_->channels(),
number_of_frames_,
kNumberOfBlocksBufferInFifo));
}
}
// When FIFO does not kick in, data will be directly passed to the callback.
if (!fifo_) {
CHECK_EQ(audio_wrapper_->frames(), static_cast<int>(number_of_frames_));
sink_->OnData(
this, audio_wrapper_.get(), capture_delay_bytes, normalized_volume);
return noErr;
}
// Compensate the audio delay caused by the FIFO.
capture_delay_bytes += fifo_->GetAvailableFrames() * format_.mBytesPerFrame;
fifo_->Push(audio_wrapper_.get());
// Consume and deliver the data when the FIFO has a block of available data. // Consume and deliver the data when the FIFO has a block of available data.
while (fifo_.available_blocks()) { while (fifo_->available_blocks()) {
const AudioBus* audio_bus = fifo_.Consume(); const AudioBus* audio_bus = fifo_->Consume();
DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_)); DCHECK_EQ(audio_bus->frames(), static_cast<int>(number_of_frames_));
// Compensate the audio delay caused by the FIFO.
capture_delay_bytes += fifo_.GetAvailableFrames() * format_.mBytesPerFrame;
sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume); sink_->OnData(this, audio_bus, capture_delay_bytes, normalized_volume);
} }
...@@ -519,9 +555,9 @@ int AUAudioInputStream::HardwareSampleRate() { ...@@ -519,9 +555,9 @@ int AUAudioInputStream::HardwareSampleRate() {
UInt32 info_size = sizeof(device_id); UInt32 info_size = sizeof(device_id);
AudioObjectPropertyAddress default_input_device_address = { AudioObjectPropertyAddress default_input_device_address = {
kAudioHardwarePropertyDefaultInputDevice, kAudioHardwarePropertyDefaultInputDevice,
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster kAudioObjectPropertyElementMaster
}; };
OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject, OSStatus result = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&default_input_device_address, &default_input_device_address,
...@@ -536,10 +572,8 @@ int AUAudioInputStream::HardwareSampleRate() { ...@@ -536,10 +572,8 @@ int AUAudioInputStream::HardwareSampleRate() {
info_size = sizeof(nominal_sample_rate); info_size = sizeof(nominal_sample_rate);
AudioObjectPropertyAddress nominal_sample_rate_address = { AudioObjectPropertyAddress nominal_sample_rate_address = {
kAudioDevicePropertyNominalSampleRate, kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
kAudioObjectPropertyElementMaster
};
result = AudioObjectGetPropertyData(device_id, result = AudioObjectGetPropertyData(device_id,
&nominal_sample_rate_address, &nominal_sample_rate_address,
0, 0,
...@@ -572,9 +606,9 @@ double AUAudioInputStream::GetHardwareLatency() { ...@@ -572,9 +606,9 @@ double AUAudioInputStream::GetHardwareLatency() {
// Get input audio device latency. // Get input audio device latency.
AudioObjectPropertyAddress property_address = { AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyLatency, kAudioDevicePropertyLatency,
kAudioDevicePropertyScopeInput, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster kAudioObjectPropertyElementMaster
}; };
UInt32 device_latency_frames = 0; UInt32 device_latency_frames = 0;
size = sizeof(device_latency_frames); size = sizeof(device_latency_frames);
...@@ -586,19 +620,19 @@ double AUAudioInputStream::GetHardwareLatency() { ...@@ -586,19 +620,19 @@ double AUAudioInputStream::GetHardwareLatency() {
&device_latency_frames); &device_latency_frames);
DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency."; DLOG_IF(WARNING, result != noErr) << "Could not get audio device latency.";
return static_cast<double>((audio_unit_latency_sec * return static_cast<double>((audio_unit_latency_sec * format_.mSampleRate) +
format_.mSampleRate) + device_latency_frames); device_latency_frames);
} }
double AUAudioInputStream::GetCaptureLatency( double AUAudioInputStream::GetCaptureLatency(
const AudioTimeStamp* input_time_stamp) { const AudioTimeStamp* input_time_stamp) {
// Get the delay between between the actual recording instant and the time // Get the delay between between the actual recording instant and the time
// when the data packet is provided as a callback. // when the data packet is provided as a callback.
UInt64 capture_time_ns = AudioConvertHostTimeToNanos( UInt64 capture_time_ns =
input_time_stamp->mHostTime); AudioConvertHostTimeToNanos(input_time_stamp->mHostTime);
UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); UInt64 now_ns = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
double delay_frames = static_cast<double> double delay_frames = static_cast<double>(1e-9 * (now_ns - capture_time_ns) *
(1e-9 * (now_ns - capture_time_ns) * format_.mSampleRate); format_.mSampleRate);
// Total latency is composed by the dynamic latency and the fixed // Total latency is composed by the dynamic latency and the fixed
// hardware latency. // hardware latency.
...@@ -608,18 +642,14 @@ double AUAudioInputStream::GetCaptureLatency( ...@@ -608,18 +642,14 @@ double AUAudioInputStream::GetCaptureLatency(
int AUAudioInputStream::GetNumberOfChannelsFromStream() { int AUAudioInputStream::GetNumberOfChannelsFromStream() {
// Get the stream format, to be able to read the number of channels. // Get the stream format, to be able to read the number of channels.
AudioObjectPropertyAddress property_address = { AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyStreamFormat, kAudioDevicePropertyStreamFormat,
kAudioDevicePropertyScopeInput, kAudioDevicePropertyScopeInput,
kAudioObjectPropertyElementMaster kAudioObjectPropertyElementMaster
}; };
AudioStreamBasicDescription stream_format; AudioStreamBasicDescription stream_format;
UInt32 size = sizeof(stream_format); UInt32 size = sizeof(stream_format);
OSStatus result = AudioObjectGetPropertyData(input_device_id_, OSStatus result = AudioObjectGetPropertyData(
&property_address, input_device_id_, &property_address, 0, NULL, &size, &stream_format);
0,
NULL,
&size,
&stream_format);
if (result != noErr) { if (result != noErr) {
DLOG(WARNING) << "Could not get stream format"; DLOG(WARNING) << "Could not get stream format";
return 0; return 0;
...@@ -629,8 +659,8 @@ int AUAudioInputStream::GetNumberOfChannelsFromStream() { ...@@ -629,8 +659,8 @@ int AUAudioInputStream::GetNumberOfChannelsFromStream() {
} }
void AUAudioInputStream::HandleError(OSStatus err) { void AUAudioInputStream::HandleError(OSStatus err) {
NOTREACHED() << "error " << GetMacOSStatusErrorString(err) NOTREACHED() << "error " << GetMacOSStatusErrorString(err) << " (" << err
<< " (" << err << ")"; << ")";
if (sink_) if (sink_)
sink_->OnError(this); sink_->OnError(this);
} }
...@@ -638,13 +668,12 @@ void AUAudioInputStream::HandleError(OSStatus err) { ...@@ -638,13 +668,12 @@ void AUAudioInputStream::HandleError(OSStatus err) {
bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) { bool AUAudioInputStream::IsVolumeSettableOnChannel(int channel) {
Boolean is_settable = false; Boolean is_settable = false;
AudioObjectPropertyAddress property_address = { AudioObjectPropertyAddress property_address = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyVolumeScalar,
kAudioDevicePropertyScopeInput, kAudioDevicePropertyScopeInput,
static_cast<UInt32>(channel) static_cast<UInt32>(channel)
}; };
OSStatus result = AudioObjectIsPropertySettable(input_device_id_, OSStatus result = AudioObjectIsPropertySettable(
&property_address, input_device_id_, &property_address, &is_settable);
&is_settable);
return (result == noErr) ? is_settable : false; return (result == noErr) ? is_settable : false;
} }
......
...@@ -45,10 +45,10 @@ ...@@ -45,10 +45,10 @@
#include "media/audio/agc_audio_stream.h" #include "media/audio/agc_audio_stream.h"
#include "media/audio/audio_io.h" #include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h" #include "media/audio/audio_parameters.h"
#include "media/base/audio_block_fifo.h"
namespace media { namespace media {
class AudioBlockFifo;
class AudioBus; class AudioBus;
class AudioManagerMac; class AudioManagerMac;
class DataBuffer; class DataBuffer;
...@@ -78,7 +78,7 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { ...@@ -78,7 +78,7 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> {
bool started() const { return started_; } bool started() const { return started_; }
AudioUnit audio_unit() { return audio_unit_; } AudioUnit audio_unit() { return audio_unit_; }
AudioBufferList* audio_buffer_list() { return &audio_buffer_list_; } AudioBufferList* audio_buffer_list() { return audio_buffer_list_.get(); }
private: private:
// AudioOutputUnit callback. // AudioOutputUnit callback.
...@@ -90,7 +90,8 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { ...@@ -90,7 +90,8 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> {
AudioBufferList* io_data); AudioBufferList* io_data);
// Pushes recorded data to consumer of the input audio stream. // Pushes recorded data to consumer of the input audio stream.
OSStatus Provide(UInt32 number_of_frames, AudioBufferList* io_data, OSStatus Provide(UInt32 number_of_frames,
AudioBufferList* io_data,
const AudioTimeStamp* time_stamp); const AudioTimeStamp* time_stamp);
// Gets the fixed capture hardware latency and store it during initialization. // Gets the fixed capture hardware latency and store it during initialization.
...@@ -132,11 +133,11 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { ...@@ -132,11 +133,11 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> {
AudioDeviceID input_device_id_; AudioDeviceID input_device_id_;
// Provides a mechanism for encapsulating one or more buffers of audio data. // Provides a mechanism for encapsulating one or more buffers of audio data.
AudioBufferList audio_buffer_list_; scoped_ptr<AudioBufferList, base::FreeDeleter> audio_buffer_list_;
// Temporary storage for recorded data. The InputProc() renders into this // Temporary storage for recorded data. The InputProc() renders into this
// array as soon as a frame of the desired buffer size has been recorded. // array as soon as a frame of the desired buffer size has been recorded.
scoped_ptr<uint8[]> audio_data_buffer_; scoped_ptr<float, base::AlignedFreeDeleter> audio_data_buffer_;
// True after successfull Start(), false after successful Stop(). // True after successfull Start(), false after successful Stop().
bool started_; bool started_;
...@@ -148,8 +149,12 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> { ...@@ -148,8 +149,12 @@ class AUAudioInputStream : public AgcAudioStream<AudioInputStream> {
// when querying the volume of each channel. // when querying the volume of each channel.
int number_of_channels_in_frame_; int number_of_channels_in_frame_;
// FIFO used to accumulates recorded data. // Dynamically allocated FIFO used when CoreAudio asks for unexpected frame
media::AudioBlockFifo fifo_; // sizes.
scoped_ptr<AudioBlockFifo> fifo_;
// AudioBus wrapper for delievering data via AudioSourceCallback::OnData().
scoped_ptr<AudioBus> audio_wrapper_;
// Used to defer Start() to workaround http://crbug.com/160920. // Used to defer Start() to workaround http://crbug.com/160920.
base::CancelableClosure deferred_start_cb_; base::CancelableClosure deferred_start_cb_;
......
...@@ -22,7 +22,8 @@ AudioBlockFifo::AudioBlockFifo(int channels, int frames, int blocks) ...@@ -22,7 +22,8 @@ AudioBlockFifo::AudioBlockFifo(int channels, int frames, int blocks)
} }
} }
AudioBlockFifo::~AudioBlockFifo() {} AudioBlockFifo::~AudioBlockFifo() {
}
void AudioBlockFifo::Push(const void* source, void AudioBlockFifo::Push(const void* source,
int frames, int frames,
...@@ -46,20 +47,39 @@ void AudioBlockFifo::Push(const void* source, ...@@ -46,20 +47,39 @@ void AudioBlockFifo::Push(const void* source,
// Deinterleave the content to the FIFO and update the |write_pos_|. // Deinterleave the content to the FIFO and update the |write_pos_|.
current_block->FromInterleavedPartial( current_block->FromInterleavedPartial(
source_ptr, write_pos_, push_frames, bytes_per_sample); source_ptr, write_pos_, push_frames, bytes_per_sample);
write_pos_ = (write_pos_ + push_frames) % block_frames_;
if (!write_pos_) {
// The current block is completely filled, increment |write_block_| and
// |available_blocks_|.
write_block_ = (write_block_ + 1) % audio_blocks_.size();
++available_blocks_;
}
UpdatePosition(push_frames);
source_ptr += push_frames * bytes_per_sample * current_block->channels(); source_ptr += push_frames * bytes_per_sample * current_block->channels();
frames_to_push -= push_frames; frames_to_push -= push_frames;
DCHECK_GE(frames_to_push, 0); DCHECK_GE(frames_to_push, 0);
} }
} }
void AudioBlockFifo::Push(const AudioBus* source) {
DCHECK(source);
DCHECK_LT(available_blocks_, static_cast<int>(audio_blocks_.size()));
int source_start_frame = 0;
while (source_start_frame < source->frames()) {
// Get the current write block.
AudioBus* current_block = audio_blocks_[write_block_];
DCHECK_EQ(source->channels(), current_block->channels());
// Figure out what segment sizes we need when adding the new content to
// the FIFO.
const int push_frames = std::min(block_frames_ - write_pos_,
source->frames() - source_start_frame);
// Copy the data to FIFO.
source->CopyPartialFramesTo(
source_start_frame, push_frames, write_pos_, current_block);
UpdatePosition(push_frames);
source_start_frame += push_frames;
DCHECK_LE(source_start_frame, source->frames());
}
}
const AudioBus* AudioBlockFifo::Consume() { const AudioBus* AudioBlockFifo::Consume() {
DCHECK(available_blocks_); DCHECK(available_blocks_);
AudioBus* audio_bus = audio_blocks_[read_block_]; AudioBus* audio_bus = audio_blocks_[read_block_];
...@@ -86,4 +106,14 @@ int AudioBlockFifo::GetUnfilledFrames() const { ...@@ -86,4 +106,14 @@ int AudioBlockFifo::GetUnfilledFrames() const {
return unfilled_frames; return unfilled_frames;
} }
void AudioBlockFifo::UpdatePosition(int push_frames) {
write_pos_ = (write_pos_ + push_frames) % block_frames_;
if (!write_pos_) {
// The current block is completely filled, increment |write_block_| and
// |available_blocks_|.
write_block_ = (write_block_ + 1) % audio_blocks_.size();
++available_blocks_;
}
}
} // namespace media } // namespace media
...@@ -28,6 +28,10 @@ class MEDIA_EXPORT AudioBlockFifo { ...@@ -28,6 +28,10 @@ class MEDIA_EXPORT AudioBlockFifo {
// Push() will crash if the allocated space is insufficient. // Push() will crash if the allocated space is insufficient.
void Push(const void* source, int frames, int bytes_per_sample); void Push(const void* source, int frames, int bytes_per_sample);
// Pushes the audio data from |source| to the FIFO.
// Push() will crash if the allocated space is insufficient.
void Push(const AudioBus* source);
// Consumes a block of audio from the FIFO. Returns an AudioBus which // Consumes a block of audio from the FIFO. Returns an AudioBus which
// contains the consumed audio data to avoid copying. // contains the consumed audio data to avoid copying.
// Consume() will crash if the FIFO does not contain a block of data. // Consume() will crash if the FIFO does not contain a block of data.
...@@ -46,6 +50,9 @@ class MEDIA_EXPORT AudioBlockFifo { ...@@ -46,6 +50,9 @@ class MEDIA_EXPORT AudioBlockFifo {
int GetUnfilledFrames() const; int GetUnfilledFrames() const;
private: private:
// Helper method to update the indexes in Push methods.
void UpdatePosition(int push_frames);
// The actual FIFO is a vector of audio buses. // The actual FIFO is a vector of audio buses.
ScopedVector<AudioBus> audio_blocks_; ScopedVector<AudioBus> audio_blocks_;
......
...@@ -8,29 +8,48 @@ ...@@ -8,29 +8,48 @@
namespace media { namespace media {
class AudioBlockFifoTest : public testing::Test { class AudioBlockFifoTest : public testing::Test {
public: protected:
AudioBlockFifoTest() {} AudioBlockFifoTest() {}
virtual ~AudioBlockFifoTest() {} virtual ~AudioBlockFifoTest() {}
void PushAndVerify(AudioBlockFifo* fifo, int frames_to_push, private:
int channels, int block_frames, int max_frames) { DISALLOW_COPY_AND_ASSIGN(AudioBlockFifoTest);
};
class AudioBlockFifoFormatTest : public AudioBlockFifoTest,
public testing::WithParamInterface<bool> {
protected:
void PushAndVerify(AudioBlockFifo* fifo,
int frames_to_push,
int channels,
int block_frames,
int max_frames) {
const int bytes_per_sample = 2; const int bytes_per_sample = 2;
const int data_byte_size = bytes_per_sample * channels * frames_to_push; const int data_byte_size = bytes_per_sample * channels * frames_to_push;
scoped_ptr<uint8[]> data(new uint8[data_byte_size]); if (GetParam()) {
memset(data.get(), 0, data_byte_size); scoped_ptr<media::AudioBus> data =
AudioBus::Create(channels, frames_to_push);
for (int filled_frames = max_frames - fifo->GetUnfilledFrames(); for (int filled_frames = max_frames - fifo->GetUnfilledFrames();
filled_frames + frames_to_push <= max_frames;) { filled_frames + frames_to_push <= max_frames;) {
fifo->Push(data.get(), frames_to_push, bytes_per_sample); fifo->Push(data.get());
filled_frames += frames_to_push; filled_frames += frames_to_push;
EXPECT_EQ(max_frames - filled_frames, fifo->GetUnfilledFrames()); EXPECT_EQ(max_frames - filled_frames, fifo->GetUnfilledFrames());
EXPECT_EQ(static_cast<int>(filled_frames / block_frames), EXPECT_EQ(static_cast<int>(filled_frames / block_frames),
fifo->available_blocks()); fifo->available_blocks());
}
} else {
scoped_ptr<uint8[]> data(new uint8[data_byte_size]);
memset(data.get(), 0, data_byte_size);
for (int filled_frames = max_frames - fifo->GetUnfilledFrames();
filled_frames + frames_to_push <= max_frames;) {
fifo->Push(data.get(), frames_to_push, bytes_per_sample);
filled_frames += frames_to_push;
EXPECT_EQ(max_frames - filled_frames, fifo->GetUnfilledFrames());
EXPECT_EQ(static_cast<int>(filled_frames / block_frames),
fifo->available_blocks());
}
} }
} }
private:
DISALLOW_COPY_AND_ASSIGN(AudioBlockFifoTest);
}; };
// Verify that construction works as intended. // Verify that construction works as intended.
...@@ -44,7 +63,7 @@ TEST_F(AudioBlockFifoTest, Construct) { ...@@ -44,7 +63,7 @@ TEST_F(AudioBlockFifoTest, Construct) {
} }
// Pushes audio bus objects to/from a FIFO up to different degrees. // Pushes audio bus objects to/from a FIFO up to different degrees.
TEST_F(AudioBlockFifoTest, Push) { TEST_P(AudioBlockFifoFormatTest, Push) {
const int channels = 2; const int channels = 2;
const int frames = 128; const int frames = 128;
const int blocks = 2; const int blocks = 2;
...@@ -65,7 +84,7 @@ TEST_F(AudioBlockFifoTest, Push) { ...@@ -65,7 +84,7 @@ TEST_F(AudioBlockFifoTest, Push) {
// Perform a sequence of Push/Consume calls to different degrees, and verify // Perform a sequence of Push/Consume calls to different degrees, and verify
// things are correct. // things are correct.
TEST_F(AudioBlockFifoTest, PushAndConsume) { TEST_P(AudioBlockFifoFormatTest, PushAndConsume) {
const int channels = 2; const int channels = 2;
const int frames = 441; const int frames = 441;
const int blocks = 4; const int blocks = 4;
...@@ -100,10 +119,9 @@ TEST_F(AudioBlockFifoTest, PushAndConsume) { ...@@ -100,10 +119,9 @@ TEST_F(AudioBlockFifoTest, PushAndConsume) {
fifo.Clear(); fifo.Clear();
int new_push_frames = 128; int new_push_frames = 128;
// Change the input frame and try to fill up the FIFO. // Change the input frame and try to fill up the FIFO.
PushAndVerify(&fifo, new_push_frames, channels, frames, PushAndVerify(&fifo, new_push_frames, channels, frames, frames * blocks);
frames * blocks);
EXPECT_TRUE(fifo.GetUnfilledFrames() != 0); EXPECT_TRUE(fifo.GetUnfilledFrames() != 0);
EXPECT_TRUE(fifo.available_blocks() == blocks -1); EXPECT_TRUE(fifo.available_blocks() == blocks - 1);
// Consume all the existing filled blocks of data. // Consume all the existing filled blocks of data.
while (fifo.available_blocks()) { while (fifo.available_blocks()) {
...@@ -122,14 +140,13 @@ TEST_F(AudioBlockFifoTest, PushAndConsume) { ...@@ -122,14 +140,13 @@ TEST_F(AudioBlockFifoTest, PushAndConsume) {
// Completely fill up the buffer again. // Completely fill up the buffer again.
new_push_frames = frames * blocks - remain_frames; new_push_frames = frames * blocks - remain_frames;
PushAndVerify(&fifo, new_push_frames, channels, frames, PushAndVerify(&fifo, new_push_frames, channels, frames, frames * blocks);
frames * blocks);
EXPECT_TRUE(fifo.GetUnfilledFrames() == 0); EXPECT_TRUE(fifo.GetUnfilledFrames() == 0);
EXPECT_TRUE(fifo.available_blocks() == blocks); EXPECT_TRUE(fifo.available_blocks() == blocks);
} }
// Perform a sequence of Push/Consume calls to a 1 block FIFO. // Perform a sequence of Push/Consume calls to a 1 block FIFO.
TEST_F(AudioBlockFifoTest, PushAndConsumeOneBlockFifo) { TEST_P(AudioBlockFifoFormatTest, PushAndConsumeOneBlockFifo) {
static const int channels = 2; static const int channels = 2;
static const int frames = 441; static const int frames = 441;
static const int blocks = 1; static const int blocks = 1;
...@@ -146,4 +163,8 @@ TEST_F(AudioBlockFifoTest, PushAndConsumeOneBlockFifo) { ...@@ -146,4 +163,8 @@ TEST_F(AudioBlockFifoTest, PushAndConsumeOneBlockFifo) {
EXPECT_TRUE(fifo.GetUnfilledFrames() == frames); EXPECT_TRUE(fifo.GetUnfilledFrames() == frames);
} }
INSTANTIATE_TEST_CASE_P(AudioBlockFifoTests,
AudioBlockFifoFormatTest,
::testing::Values(false, true));
} // namespace media } // namespace media
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