| Index: media/audio/mac/audio_synchronized_mac.cc
|
| diff --git a/media/audio/mac/audio_synchronized_mac.cc b/media/audio/mac/audio_synchronized_mac.cc
|
| deleted file mode 100644
|
| index a9bc88e2bd3be7205dab6062ed63fa44d2db0b7c..0000000000000000000000000000000000000000
|
| --- a/media/audio/mac/audio_synchronized_mac.cc
|
| +++ /dev/null
|
| @@ -1,976 +0,0 @@
|
| -// Copyright (c) 2012 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.
|
| -
|
| -#include "media/audio/mac/audio_synchronized_mac.h"
|
| -
|
| -#include <CoreServices/CoreServices.h>
|
| -#include <algorithm>
|
| -
|
| -#include "base/basictypes.h"
|
| -#include "base/debug/trace_event.h"
|
| -#include "base/logging.h"
|
| -#include "base/mac/mac_logging.h"
|
| -#include "media/audio/mac/audio_manager_mac.h"
|
| -#include "media/base/channel_mixer.h"
|
| -
|
| -namespace media {
|
| -
|
| -static const int kHardwareBufferSize = 128;
|
| -static const int kFifoSize = 16384;
|
| -
|
| -// TODO(crogers): handle the non-stereo case.
|
| -static const int kChannels = 2;
|
| -
|
| -// This value was determined empirically for minimum latency while still
|
| -// guarding against FIFO under-runs.
|
| -static const int kBaseTargetFifoFrames = 256 + 64;
|
| -
|
| -// If the input and output sample-rate don't match, then we need to maintain
|
| -// an additional safety margin due to the callback timing jitter and the
|
| -// varispeed buffering. This value was empirically tuned.
|
| -static const int kAdditionalTargetFifoFrames = 128;
|
| -
|
| -static void ZeroBufferList(AudioBufferList* buffer_list) {
|
| - for (size_t i = 0; i < buffer_list->mNumberBuffers; ++i)
|
| - memset(buffer_list->mBuffers[i].mData,
|
| - 0,
|
| - buffer_list->mBuffers[i].mDataByteSize);
|
| -}
|
| -
|
| -static void WrapBufferList(AudioBufferList* buffer_list,
|
| - AudioBus* bus,
|
| - int frames) {
|
| - DCHECK(buffer_list);
|
| - DCHECK(bus);
|
| - int channels = bus->channels();
|
| - int buffer_list_channels = buffer_list->mNumberBuffers;
|
| -
|
| - // Copy pointers from AudioBufferList.
|
| - int source_idx = 0;
|
| - for (int i = 0; i < channels; ++i) {
|
| - bus->SetChannelData(
|
| - i, static_cast<float*>(buffer_list->mBuffers[source_idx].mData));
|
| -
|
| - // It's ok to pass in a |buffer_list| with fewer channels, in which
|
| - // case we just duplicate the last channel.
|
| - if (source_idx < buffer_list_channels - 1)
|
| - ++source_idx;
|
| - }
|
| -
|
| - // Finally set the actual length.
|
| - bus->set_frames(frames);
|
| -}
|
| -
|
| -AudioSynchronizedStream::AudioSynchronizedStream(
|
| - AudioManagerMac* manager,
|
| - const AudioParameters& params,
|
| - AudioDeviceID input_id,
|
| - AudioDeviceID output_id)
|
| - : manager_(manager),
|
| - params_(params),
|
| - input_sample_rate_(0),
|
| - output_sample_rate_(0),
|
| - input_id_(input_id),
|
| - output_id_(output_id),
|
| - input_buffer_list_(NULL),
|
| - fifo_(kChannels, kFifoSize),
|
| - target_fifo_frames_(kBaseTargetFifoFrames),
|
| - average_delta_(0.0),
|
| - fifo_rate_compensation_(1.0),
|
| - input_unit_(0),
|
| - varispeed_unit_(0),
|
| - output_unit_(0),
|
| - first_input_time_(-1),
|
| - is_running_(false),
|
| - hardware_buffer_size_(kHardwareBufferSize),
|
| - channels_(kChannels) {
|
| - VLOG(1) << "AudioSynchronizedStream::AudioSynchronizedStream()";
|
| -}
|
| -
|
| -AudioSynchronizedStream::~AudioSynchronizedStream() {
|
| - DCHECK(!input_unit_);
|
| - DCHECK(!output_unit_);
|
| - DCHECK(!varispeed_unit_);
|
| -}
|
| -
|
| -bool AudioSynchronizedStream::Open() {
|
| - if (params_.channels() != kChannels) {
|
| - LOG(ERROR) << "Only stereo output is currently supported.";
|
| - return false;
|
| - }
|
| -
|
| - // Create the input, output, and varispeed AudioUnits.
|
| - OSStatus result = CreateAudioUnits();
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Cannot create AudioUnits.";
|
| - return false;
|
| - }
|
| -
|
| - result = SetupInput(input_id_);
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error configuring input AudioUnit.";
|
| - return false;
|
| - }
|
| -
|
| - result = SetupOutput(output_id_);
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error configuring output AudioUnit.";
|
| - return false;
|
| - }
|
| -
|
| - result = SetupCallbacks();
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error setting up callbacks on AudioUnits.";
|
| - return false;
|
| - }
|
| -
|
| - result = SetupStreamFormats();
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error configuring stream formats on AudioUnits.";
|
| - return false;
|
| - }
|
| -
|
| - AllocateInputData();
|
| -
|
| - // Final initialization of the AudioUnits.
|
| - result = AudioUnitInitialize(input_unit_);
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error initializing input AudioUnit.";
|
| - return false;
|
| - }
|
| -
|
| - result = AudioUnitInitialize(output_unit_);
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error initializing output AudioUnit.";
|
| - return false;
|
| - }
|
| -
|
| - result = AudioUnitInitialize(varispeed_unit_);
|
| - if (result != noErr) {
|
| - LOG(ERROR) << "Error initializing varispeed AudioUnit.";
|
| - return false;
|
| - }
|
| -
|
| - if (input_sample_rate_ != output_sample_rate_) {
|
| - // Add extra safety margin.
|
| - target_fifo_frames_ += kAdditionalTargetFifoFrames;
|
| - }
|
| -
|
| - // Buffer initial silence corresponding to target I/O buffering.
|
| - fifo_.Clear();
|
| - scoped_ptr<AudioBus> silence =
|
| - AudioBus::Create(channels_, target_fifo_frames_);
|
| - silence->Zero();
|
| - fifo_.Push(silence.get());
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void AudioSynchronizedStream::Close() {
|
| - DCHECK(!is_running_);
|
| -
|
| - if (input_buffer_list_) {
|
| - free(input_buffer_list_);
|
| - input_buffer_list_ = 0;
|
| - input_bus_.reset(NULL);
|
| - wrapper_bus_.reset(NULL);
|
| - }
|
| -
|
| - if (input_unit_) {
|
| - AudioUnitUninitialize(input_unit_);
|
| - CloseComponent(input_unit_);
|
| - }
|
| -
|
| - if (output_unit_) {
|
| - AudioUnitUninitialize(output_unit_);
|
| - CloseComponent(output_unit_);
|
| - }
|
| -
|
| - if (varispeed_unit_) {
|
| - AudioUnitUninitialize(varispeed_unit_);
|
| - CloseComponent(varispeed_unit_);
|
| - }
|
| -
|
| - input_unit_ = NULL;
|
| - output_unit_ = NULL;
|
| - varispeed_unit_ = NULL;
|
| -
|
| - // Inform the audio manager that we have been closed. This can cause our
|
| - // destruction.
|
| - manager_->ReleaseOutputStream(this);
|
| -}
|
| -
|
| -void AudioSynchronizedStream::Start(AudioSourceCallback* callback) {
|
| - DCHECK(callback);
|
| - DCHECK(input_unit_);
|
| - DCHECK(output_unit_);
|
| - DCHECK(varispeed_unit_);
|
| -
|
| - if (is_running_ || !input_unit_ || !output_unit_ || !varispeed_unit_)
|
| - return;
|
| -
|
| - source_ = callback;
|
| -
|
| - // Reset state variables each time we Start().
|
| - fifo_rate_compensation_ = 1.0;
|
| - average_delta_ = 0.0;
|
| -
|
| - OSStatus result = noErr;
|
| -
|
| - if (!is_running_) {
|
| - first_input_time_ = -1;
|
| -
|
| - result = AudioOutputUnitStart(input_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| -
|
| - if (result == noErr) {
|
| - result = AudioOutputUnitStart(output_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - }
|
| - }
|
| -
|
| - is_running_ = true;
|
| -}
|
| -
|
| -void AudioSynchronizedStream::Stop() {
|
| - OSStatus result = noErr;
|
| - if (is_running_) {
|
| - result = AudioOutputUnitStop(input_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| -
|
| - if (result == noErr) {
|
| - result = AudioOutputUnitStop(output_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - }
|
| - }
|
| -
|
| - if (result == noErr)
|
| - is_running_ = false;
|
| -}
|
| -
|
| -bool AudioSynchronizedStream::IsRunning() {
|
| - return is_running_;
|
| -}
|
| -
|
| -// TODO(crogers): implement - or remove from AudioOutputStream.
|
| -void AudioSynchronizedStream::SetVolume(double volume) {}
|
| -void AudioSynchronizedStream::GetVolume(double* volume) {}
|
| -
|
| -OSStatus AudioSynchronizedStream::SetOutputDeviceAsCurrent(
|
| - AudioDeviceID output_id) {
|
| - OSStatus result = noErr;
|
| -
|
| - // Get the default output device if device is unknown.
|
| - if (output_id == kAudioDeviceUnknown) {
|
| - AudioObjectPropertyAddress pa;
|
| - pa.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
| - pa.mScope = kAudioObjectPropertyScopeGlobal;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - UInt32 size = sizeof(output_id);
|
| -
|
| - result = AudioObjectGetPropertyData(
|
| - kAudioObjectSystemObject,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - &size,
|
| - &output_id);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| - }
|
| -
|
| - // Set the render frame size.
|
| - UInt32 frame_size = hardware_buffer_size_;
|
| - AudioObjectPropertyAddress pa;
|
| - pa.mSelector = kAudioDevicePropertyBufferFrameSize;
|
| - pa.mScope = kAudioDevicePropertyScopeInput;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - result = AudioObjectSetPropertyData(
|
| - output_id,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - sizeof(frame_size),
|
| - &frame_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - output_info_.Initialize(output_id, false);
|
| -
|
| - // Set the Current Device to the Default Output Unit.
|
| - result = AudioUnitSetProperty(
|
| - output_unit_,
|
| - kAudioOutputUnitProperty_CurrentDevice,
|
| - kAudioUnitScope_Global,
|
| - 0,
|
| - &output_info_.id_,
|
| - sizeof(output_info_.id_));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::SetInputDeviceAsCurrent(
|
| - AudioDeviceID input_id) {
|
| - OSStatus result = noErr;
|
| -
|
| - // Get the default input device if device is unknown.
|
| - if (input_id == kAudioDeviceUnknown) {
|
| - AudioObjectPropertyAddress pa;
|
| - pa.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
| - pa.mScope = kAudioObjectPropertyScopeGlobal;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - UInt32 size = sizeof(input_id);
|
| -
|
| - result = AudioObjectGetPropertyData(
|
| - kAudioObjectSystemObject,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - &size,
|
| - &input_id);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| - }
|
| -
|
| - // Set the render frame size.
|
| - UInt32 frame_size = hardware_buffer_size_;
|
| - AudioObjectPropertyAddress pa;
|
| - pa.mSelector = kAudioDevicePropertyBufferFrameSize;
|
| - pa.mScope = kAudioDevicePropertyScopeInput;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - result = AudioObjectSetPropertyData(
|
| - input_id,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - sizeof(frame_size),
|
| - &frame_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - input_info_.Initialize(input_id, true);
|
| -
|
| - // Set the Current Device to the AUHAL.
|
| - // This should be done only after I/O has been enabled on the AUHAL.
|
| - result = AudioUnitSetProperty(
|
| - input_unit_,
|
| - kAudioOutputUnitProperty_CurrentDevice,
|
| - kAudioUnitScope_Global,
|
| - 0,
|
| - &input_info_.id_,
|
| - sizeof(input_info_.id_));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::CreateAudioUnits() {
|
| - // Q: Why do we need a varispeed unit?
|
| - // A: If the input device and the output device are running at
|
| - // different sample rates and/or on different clocks, we will need
|
| - // to compensate to avoid a pitch change and
|
| - // to avoid buffer under and over runs.
|
| - ComponentDescription varispeed_desc;
|
| - varispeed_desc.componentType = kAudioUnitType_FormatConverter;
|
| - varispeed_desc.componentSubType = kAudioUnitSubType_Varispeed;
|
| - varispeed_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
| - varispeed_desc.componentFlags = 0;
|
| - varispeed_desc.componentFlagsMask = 0;
|
| -
|
| - Component varispeed_comp = FindNextComponent(NULL, &varispeed_desc);
|
| - if (varispeed_comp == NULL)
|
| - return -1;
|
| -
|
| - OSStatus result = OpenAComponent(varispeed_comp, &varispeed_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Open input AudioUnit.
|
| - ComponentDescription input_desc;
|
| - input_desc.componentType = kAudioUnitType_Output;
|
| - input_desc.componentSubType = kAudioUnitSubType_HALOutput;
|
| - input_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
| - input_desc.componentFlags = 0;
|
| - input_desc.componentFlagsMask = 0;
|
| -
|
| - Component input_comp = FindNextComponent(NULL, &input_desc);
|
| - if (input_comp == NULL)
|
| - return -1;
|
| -
|
| - result = OpenAComponent(input_comp, &input_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Open output AudioUnit.
|
| - ComponentDescription output_desc;
|
| - output_desc.componentType = kAudioUnitType_Output;
|
| - output_desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
| - output_desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
| - output_desc.componentFlags = 0;
|
| - output_desc.componentFlagsMask = 0;
|
| -
|
| - Component output_comp = FindNextComponent(NULL, &output_desc);
|
| - if (output_comp == NULL)
|
| - return -1;
|
| -
|
| - result = OpenAComponent(output_comp, &output_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - return noErr;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::SetupInput(AudioDeviceID input_id) {
|
| - // The AUHAL used for input needs to be initialized
|
| - // before anything is done to it.
|
| - OSStatus result = AudioUnitInitialize(input_unit_);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // We must enable the Audio Unit (AUHAL) for input and disable output
|
| - // BEFORE setting the AUHAL's current device.
|
| - result = EnableIO();
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - result = SetInputDeviceAsCurrent(input_id);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| -
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::EnableIO() {
|
| - // Enable input on the AUHAL.
|
| - UInt32 enable_io = 1;
|
| - OSStatus result = AudioUnitSetProperty(
|
| - input_unit_,
|
| - kAudioOutputUnitProperty_EnableIO,
|
| - kAudioUnitScope_Input,
|
| - 1, // input element
|
| - &enable_io,
|
| - sizeof(enable_io));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Disable Output on the AUHAL.
|
| - enable_io = 0;
|
| - result = AudioUnitSetProperty(
|
| - input_unit_,
|
| - kAudioOutputUnitProperty_EnableIO,
|
| - kAudioUnitScope_Output,
|
| - 0, // output element
|
| - &enable_io,
|
| - sizeof(enable_io));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::SetupOutput(AudioDeviceID output_id) {
|
| - OSStatus result = noErr;
|
| -
|
| - result = SetOutputDeviceAsCurrent(output_id);
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Tell the output unit not to reset timestamps.
|
| - // Otherwise sample rate changes will cause sync loss.
|
| - UInt32 start_at_zero = 0;
|
| - result = AudioUnitSetProperty(
|
| - output_unit_,
|
| - kAudioOutputUnitProperty_StartTimestampsAtZero,
|
| - kAudioUnitScope_Global,
|
| - 0,
|
| - &start_at_zero,
|
| - sizeof(start_at_zero));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| -
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::SetupCallbacks() {
|
| - // Set the input callback.
|
| - AURenderCallbackStruct callback;
|
| - callback.inputProc = InputProc;
|
| - callback.inputProcRefCon = this;
|
| - OSStatus result = AudioUnitSetProperty(
|
| - input_unit_,
|
| - kAudioOutputUnitProperty_SetInputCallback,
|
| - kAudioUnitScope_Global,
|
| - 0,
|
| - &callback,
|
| - sizeof(callback));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Set the output callback.
|
| - callback.inputProc = OutputProc;
|
| - callback.inputProcRefCon = this;
|
| - result = AudioUnitSetProperty(
|
| - output_unit_,
|
| - kAudioUnitProperty_SetRenderCallback,
|
| - kAudioUnitScope_Input,
|
| - 0,
|
| - &callback,
|
| - sizeof(callback));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Set the varispeed callback.
|
| - callback.inputProc = VarispeedProc;
|
| - callback.inputProcRefCon = this;
|
| - result = AudioUnitSetProperty(
|
| - varispeed_unit_,
|
| - kAudioUnitProperty_SetRenderCallback,
|
| - kAudioUnitScope_Input,
|
| - 0,
|
| - &callback,
|
| - sizeof(callback));
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| -
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::SetupStreamFormats() {
|
| - AudioStreamBasicDescription asbd, asbd_dev1_in, asbd_dev2_out;
|
| -
|
| - // Get the Stream Format (Output client side).
|
| - UInt32 property_size = sizeof(asbd_dev1_in);
|
| - OSStatus result = AudioUnitGetProperty(
|
| - input_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Input,
|
| - 1,
|
| - &asbd_dev1_in,
|
| - &property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Get the Stream Format (client side).
|
| - property_size = sizeof(asbd);
|
| - result = AudioUnitGetProperty(
|
| - input_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Output,
|
| - 1,
|
| - &asbd,
|
| - &property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Get the Stream Format (Output client side).
|
| - property_size = sizeof(asbd_dev2_out);
|
| - result = AudioUnitGetProperty(
|
| - output_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Output,
|
| - 0,
|
| - &asbd_dev2_out,
|
| - &property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Set the format of all the AUs to the input/output devices channel count.
|
| - // For a simple case, you want to set this to
|
| - // the lower of count of the channels in the input device vs output device.
|
| - asbd.mChannelsPerFrame = std::min(asbd_dev1_in.mChannelsPerFrame,
|
| - asbd_dev2_out.mChannelsPerFrame);
|
| -
|
| - // We must get the sample rate of the input device and set it to the
|
| - // stream format of AUHAL.
|
| - Float64 rate = 0;
|
| - property_size = sizeof(rate);
|
| -
|
| - AudioObjectPropertyAddress pa;
|
| - pa.mSelector = kAudioDevicePropertyNominalSampleRate;
|
| - pa.mScope = kAudioObjectPropertyScopeWildcard;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - result = AudioObjectGetPropertyData(
|
| - input_info_.id_,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - &property_size,
|
| - &rate);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - input_sample_rate_ = rate;
|
| -
|
| - asbd.mSampleRate = rate;
|
| - property_size = sizeof(asbd);
|
| -
|
| - // Set the new formats to the AUs...
|
| - result = AudioUnitSetProperty(
|
| - input_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Output,
|
| - 1,
|
| - &asbd,
|
| - property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - result = AudioUnitSetProperty(
|
| - varispeed_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Input,
|
| - 0,
|
| - &asbd,
|
| - property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Set the correct sample rate for the output device,
|
| - // but keep the channel count the same.
|
| - property_size = sizeof(rate);
|
| -
|
| - pa.mSelector = kAudioDevicePropertyNominalSampleRate;
|
| - pa.mScope = kAudioObjectPropertyScopeWildcard;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - result = AudioObjectGetPropertyData(
|
| - output_info_.id_,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - &property_size,
|
| - &rate);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - output_sample_rate_ = rate;
|
| -
|
| - // The requested sample-rate must match the hardware sample-rate.
|
| - if (output_sample_rate_ != params_.sample_rate()) {
|
| - LOG(ERROR) << "Requested sample-rate: " << params_.sample_rate()
|
| - << " must match the hardware sample-rate: " << output_sample_rate_;
|
| - return kAudioDeviceUnsupportedFormatError;
|
| - }
|
| -
|
| - asbd.mSampleRate = rate;
|
| - property_size = sizeof(asbd);
|
| -
|
| - // Set the new audio stream formats for the rest of the AUs...
|
| - result = AudioUnitSetProperty(
|
| - varispeed_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Output,
|
| - 0,
|
| - &asbd,
|
| - property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - result = AudioUnitSetProperty(
|
| - output_unit_,
|
| - kAudioUnitProperty_StreamFormat,
|
| - kAudioUnitScope_Input,
|
| - 0,
|
| - &asbd,
|
| - property_size);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - return result;
|
| -}
|
| -
|
| -void AudioSynchronizedStream::AllocateInputData() {
|
| - // Get the native number of input channels that the hardware supports.
|
| - int hardware_channels = 0;
|
| - bool got_hardware_channels = AudioManagerMac::GetDeviceChannels(
|
| - input_id_, kAudioDevicePropertyScopeInput, &hardware_channels);
|
| - if (!got_hardware_channels || hardware_channels > 2) {
|
| - // Only mono and stereo are supported on the input side. When it fails to
|
| - // get the native channel number or the native channel number is bigger
|
| - // than 2, we open the device in stereo mode.
|
| - hardware_channels = 2;
|
| - }
|
| -
|
| - // Allocate storage for the AudioBufferList used for the
|
| - // input data from the input AudioUnit.
|
| - // We allocate enough space for with one AudioBuffer per channel.
|
| - size_t malloc_size = offsetof(AudioBufferList, mBuffers[0]) +
|
| - (sizeof(AudioBuffer) * hardware_channels);
|
| -
|
| - input_buffer_list_ = static_cast<AudioBufferList*>(malloc(malloc_size));
|
| - input_buffer_list_->mNumberBuffers = hardware_channels;
|
| -
|
| - input_bus_ = AudioBus::Create(hardware_channels, hardware_buffer_size_);
|
| - wrapper_bus_ = AudioBus::CreateWrapper(channels_);
|
| - if (hardware_channels != params_.input_channels()) {
|
| - ChannelLayout hardware_channel_layout =
|
| - GuessChannelLayout(hardware_channels);
|
| - ChannelLayout requested_channel_layout =
|
| - GuessChannelLayout(params_.input_channels());
|
| - channel_mixer_.reset(new ChannelMixer(hardware_channel_layout,
|
| - requested_channel_layout));
|
| - mixer_bus_ = AudioBus::Create(params_.input_channels(),
|
| - hardware_buffer_size_);
|
| - }
|
| -
|
| - // Allocate buffers for AudioBufferList.
|
| - UInt32 buffer_size_bytes = input_bus_->frames() * sizeof(Float32);
|
| - for (size_t i = 0; i < input_buffer_list_->mNumberBuffers; ++i) {
|
| - input_buffer_list_->mBuffers[i].mNumberChannels = 1;
|
| - input_buffer_list_->mBuffers[i].mDataByteSize = buffer_size_bytes;
|
| - input_buffer_list_->mBuffers[i].mData = input_bus_->channel(i);
|
| - }
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::HandleInputCallback(
|
| - AudioUnitRenderActionFlags* io_action_flags,
|
| - const AudioTimeStamp* time_stamp,
|
| - UInt32 bus_number,
|
| - UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| - TRACE_EVENT0("audio", "AudioSynchronizedStream::HandleInputCallback");
|
| -
|
| - if (first_input_time_ < 0.0)
|
| - first_input_time_ = time_stamp->mSampleTime;
|
| -
|
| - // Get the new audio input data.
|
| - OSStatus result = AudioUnitRender(
|
| - input_unit_,
|
| - io_action_flags,
|
| - time_stamp,
|
| - bus_number,
|
| - number_of_frames,
|
| - input_buffer_list_);
|
| -
|
| - // TODO(xians): Add back the DCHECK after synchronize IO supports all
|
| - // combination of input and output params. See http://issue/246521.
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Buffer input into FIFO.
|
| - int available_frames = fifo_.max_frames() - fifo_.frames();
|
| - if (input_bus_->frames() <= available_frames) {
|
| - if (channel_mixer_) {
|
| - channel_mixer_->Transform(input_bus_.get(), mixer_bus_.get());
|
| - fifo_.Push(mixer_bus_.get());
|
| - } else {
|
| - fifo_.Push(input_bus_.get());
|
| - }
|
| - }
|
| -
|
| - return result;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::HandleVarispeedCallback(
|
| - AudioUnitRenderActionFlags* io_action_flags,
|
| - const AudioTimeStamp* time_stamp,
|
| - UInt32 bus_number,
|
| - UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| - // Create a wrapper bus on the AudioBufferList.
|
| - WrapBufferList(io_data, wrapper_bus_.get(), number_of_frames);
|
| -
|
| - if (fifo_.frames() < static_cast<int>(number_of_frames)) {
|
| - // We don't DCHECK here, since this is a possible run-time condition
|
| - // if the machine is bogged down.
|
| - wrapper_bus_->Zero();
|
| - return noErr;
|
| - }
|
| -
|
| - // Read from the FIFO to feed the varispeed.
|
| - fifo_.Consume(wrapper_bus_.get(), 0, number_of_frames);
|
| -
|
| - return noErr;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::HandleOutputCallback(
|
| - AudioUnitRenderActionFlags* io_action_flags,
|
| - const AudioTimeStamp* time_stamp,
|
| - UInt32 bus_number,
|
| - UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| - // Input callback hasn't run yet or we've suddenly changed sample-rates
|
| - // -> silence.
|
| - if (first_input_time_ < 0.0 ||
|
| - static_cast<int>(number_of_frames) != params_.frames_per_buffer()) {
|
| - ZeroBufferList(io_data);
|
| - return noErr;
|
| - }
|
| -
|
| - // Use the varispeed playback rate to offset small discrepancies
|
| - // in hardware clocks, and also any differences in sample-rate
|
| - // between input and output devices.
|
| -
|
| - // Calculate a varispeed rate scalar factor to compensate for drift between
|
| - // input and output. We use the actual number of frames still in the FIFO
|
| - // compared with the ideal value of |target_fifo_frames_|.
|
| - int delta = fifo_.frames() - target_fifo_frames_;
|
| -
|
| - // Average |delta| because it can jitter back/forth quite frequently
|
| - // by +/- the hardware buffer-size *if* the input and output callbacks are
|
| - // happening at almost exactly the same time. Also, if the input and output
|
| - // sample-rates are different then |delta| will jitter quite a bit due to
|
| - // the rate conversion happening in the varispeed, plus the jittering of
|
| - // the callbacks. The average value is what's important here.
|
| - average_delta_ += (delta - average_delta_) * 0.1;
|
| -
|
| - // Compute a rate compensation which always attracts us back to the
|
| - // |target_fifo_frames_| over a period of kCorrectionTimeSeconds.
|
| - const double kCorrectionTimeSeconds = 0.1;
|
| - double correction_time_frames = kCorrectionTimeSeconds * output_sample_rate_;
|
| - fifo_rate_compensation_ =
|
| - (correction_time_frames + average_delta_) / correction_time_frames;
|
| -
|
| - // Adjust for FIFO drift.
|
| - OSStatus result = AudioUnitSetParameter(
|
| - varispeed_unit_,
|
| - kVarispeedParam_PlaybackRate,
|
| - kAudioUnitScope_Global,
|
| - 0,
|
| - fifo_rate_compensation_,
|
| - 0);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Render to the output using the varispeed.
|
| - result = AudioUnitRender(
|
| - varispeed_unit_,
|
| - io_action_flags,
|
| - time_stamp,
|
| - 0,
|
| - number_of_frames,
|
| - io_data);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| - if (result != noErr)
|
| - return result;
|
| -
|
| - // Create a wrapper bus on the AudioBufferList.
|
| - WrapBufferList(io_data, wrapper_bus_.get(), number_of_frames);
|
| -
|
| - // Process in-place!
|
| - source_->OnMoreIOData(wrapper_bus_.get(),
|
| - wrapper_bus_.get(),
|
| - AudioBuffersState(0, 0));
|
| -
|
| - return noErr;
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::InputProc(
|
| - void* user_data,
|
| - AudioUnitRenderActionFlags* io_action_flags,
|
| - const AudioTimeStamp* time_stamp,
|
| - UInt32 bus_number,
|
| - UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| - AudioSynchronizedStream* stream =
|
| - static_cast<AudioSynchronizedStream*>(user_data);
|
| - DCHECK(stream);
|
| -
|
| - return stream->HandleInputCallback(
|
| - io_action_flags,
|
| - time_stamp,
|
| - bus_number,
|
| - number_of_frames,
|
| - io_data);
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::VarispeedProc(
|
| - void* user_data,
|
| - AudioUnitRenderActionFlags* io_action_flags,
|
| - const AudioTimeStamp* time_stamp,
|
| - UInt32 bus_number,
|
| - UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| - AudioSynchronizedStream* stream =
|
| - static_cast<AudioSynchronizedStream*>(user_data);
|
| - DCHECK(stream);
|
| -
|
| - return stream->HandleVarispeedCallback(
|
| - io_action_flags,
|
| - time_stamp,
|
| - bus_number,
|
| - number_of_frames,
|
| - io_data);
|
| -}
|
| -
|
| -OSStatus AudioSynchronizedStream::OutputProc(
|
| - void* user_data,
|
| - AudioUnitRenderActionFlags* io_action_flags,
|
| - const AudioTimeStamp* time_stamp,
|
| - UInt32 bus_number,
|
| - UInt32 number_of_frames,
|
| - AudioBufferList* io_data) {
|
| - AudioSynchronizedStream* stream =
|
| - static_cast<AudioSynchronizedStream*>(user_data);
|
| - DCHECK(stream);
|
| -
|
| - return stream->HandleOutputCallback(
|
| - io_action_flags,
|
| - time_stamp,
|
| - bus_number,
|
| - number_of_frames,
|
| - io_data);
|
| -}
|
| -
|
| -void AudioSynchronizedStream::AudioDeviceInfo::Initialize(
|
| - AudioDeviceID id, bool is_input) {
|
| - id_ = id;
|
| - is_input_ = is_input;
|
| - if (id_ == kAudioDeviceUnknown)
|
| - return;
|
| -
|
| - UInt32 property_size = sizeof(buffer_size_frames_);
|
| -
|
| - AudioObjectPropertyAddress pa;
|
| - pa.mSelector = kAudioDevicePropertyBufferFrameSize;
|
| - pa.mScope = kAudioObjectPropertyScopeWildcard;
|
| - pa.mElement = kAudioObjectPropertyElementMaster;
|
| - OSStatus result = AudioObjectGetPropertyData(
|
| - id_,
|
| - &pa,
|
| - 0,
|
| - 0,
|
| - &property_size,
|
| - &buffer_size_frames_);
|
| -
|
| - OSSTATUS_DCHECK(result == noErr, result);
|
| -}
|
| -
|
| -} // namespace media
|
|
|