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 |