audio_converter.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4// 5// AudioConverter implementation. Uses MultiChannelSincResampler for resampling 6// audio, ChannelMixer for channel mixing, and AudioPullFifo for buffering. 7// 8// Delay estimates are provided to InputCallbacks based on the frame delay 9// information reported via the resampler and FIFO units. 10 11#include "media/base/audio_converter.h" 12 13#include <algorithm> 14 15#include "base/bind.h" 16#include "base/bind_helpers.h" 17#include "media/base/audio_bus.h" 18#include "media/base/audio_pull_fifo.h" 19#include "media/base/channel_mixer.h" 20#include "media/base/multi_channel_resampler.h" 21#include "media/base/vector_math.h" 22 23namespace media { 24 25AudioConverter::AudioConverter(const AudioParameters& input_params, 26 const AudioParameters& output_params, 27 bool disable_fifo) 28 : chunk_size_(output_params.frames_per_buffer()), 29 downmix_early_(false), 30 resampler_frame_delay_(0), 31 input_channel_count_(input_params.channels()) { 32 CHECK(input_params.IsValid()); 33 CHECK(output_params.IsValid()); 34 35 // Handle different input and output channel layouts. 36 if (input_params.channel_layout() != output_params.channel_layout()) { 37 DVLOG(1) << "Remixing channel layout from " << input_params.channel_layout() 38 << " to " << output_params.channel_layout() << "; from " 39 << input_params.channels() << " channels to " 40 << output_params.channels() << " channels."; 41 channel_mixer_.reset(new ChannelMixer(input_params, output_params)); 42 43 // Pare off data as early as we can for efficiency. 44 downmix_early_ = input_params.channels() > output_params.channels(); 45 } 46 47 // Only resample if necessary since it's expensive. 48 if (input_params.sample_rate() != output_params.sample_rate()) { 49 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " 50 << output_params.sample_rate(); 51 const double io_sample_rate_ratio = input_params.sample_rate() / 52 static_cast<double>(output_params.sample_rate()); 53 const int request_size = disable_fifo ? SincResampler::kDefaultRequestSize : 54 input_params.frames_per_buffer(); 55 resampler_.reset(new MultiChannelResampler( 56 downmix_early_ ? output_params.channels() : 57 input_params.channels(), 58 io_sample_rate_ratio, request_size, base::Bind( 59 &AudioConverter::ProvideInput, base::Unretained(this)))); 60 } 61 62 input_frame_duration_ = base::TimeDelta::FromMicroseconds( 63 base::Time::kMicrosecondsPerSecond / 64 static_cast<double>(input_params.sample_rate())); 65 output_frame_duration_ = base::TimeDelta::FromMicroseconds( 66 base::Time::kMicrosecondsPerSecond / 67 static_cast<double>(output_params.sample_rate())); 68 69 // The resampler can be configured to work with a specific request size, so a 70 // FIFO is not necessary when resampling. 71 if (disable_fifo || resampler_) 72 return; 73 74 // Since the output device may want a different buffer size than the caller 75 // asked for, we need to use a FIFO to ensure that both sides read in chunk 76 // sizes they're configured for. 77 if (input_params.frames_per_buffer() != output_params.frames_per_buffer()) { 78 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() 79 << " to " << output_params.frames_per_buffer(); 80 chunk_size_ = input_params.frames_per_buffer(); 81 audio_fifo_.reset(new AudioPullFifo( 82 downmix_early_ ? output_params.channels() : input_params.channels(), 83 chunk_size_, 84 base::Bind(&AudioConverter::SourceCallback, base::Unretained(this)))); 85 } 86} 87 88AudioConverter::~AudioConverter() {} 89 90void AudioConverter::AddInput(InputCallback* input) { 91 DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) == 92 transform_inputs_.end()); 93 transform_inputs_.push_back(input); 94} 95 96void AudioConverter::RemoveInput(InputCallback* input) { 97 DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) != 98 transform_inputs_.end()); 99 transform_inputs_.remove(input); 100 101 if (transform_inputs_.empty()) 102 Reset(); 103} 104 105void AudioConverter::Reset() { 106 if (audio_fifo_) 107 audio_fifo_->Clear(); 108 if (resampler_) 109 resampler_->Flush(); 110} 111 112int AudioConverter::ChunkSize() const { 113 if (!resampler_) 114 return chunk_size_; 115 return resampler_->ChunkSize(); 116} 117 118void AudioConverter::ConvertWithDelay(const base::TimeDelta& initial_delay, 119 AudioBus* dest) { 120 initial_delay_ = initial_delay; 121 122 if (transform_inputs_.empty()) { 123 dest->Zero(); 124 return; 125 } 126 127 // Determine if channel mixing should be done and if it should be done before 128 // or after resampling. If it's possible to reduce the channel count prior to 129 // resampling we can save a lot of processing time. Vice versa, we don't want 130 // to increase the channel count prior to resampling for the same reason. 131 bool needs_mixing = channel_mixer_ && !downmix_early_; 132 133 if (needs_mixing) 134 CreateUnmixedAudioIfNecessary(dest->frames()); 135 136 AudioBus* temp_dest = needs_mixing ? unmixed_audio_.get() : dest; 137 DCHECK(temp_dest); 138 139 // Figure out which method to call based on whether we're resampling and 140 // rebuffering, just resampling, or just mixing. We want to avoid any extra 141 // steps when possible since we may be converting audio data in real time. 142 if (!resampler_ && !audio_fifo_) { 143 SourceCallback(0, temp_dest); 144 } else { 145 if (resampler_) 146 resampler_->Resample(temp_dest->frames(), temp_dest); 147 else 148 ProvideInput(0, temp_dest); 149 } 150 151 // Finally upmix the channels if we didn't do so earlier. 152 if (needs_mixing) { 153 DCHECK_EQ(temp_dest->frames(), dest->frames()); 154 channel_mixer_->Transform(temp_dest, dest); 155 } 156} 157 158void AudioConverter::Convert(AudioBus* dest) { 159 ConvertWithDelay(base::TimeDelta::FromMilliseconds(0), dest); 160} 161 162void AudioConverter::SourceCallback(int fifo_frame_delay, AudioBus* dest) { 163 bool needs_downmix = channel_mixer_ && downmix_early_; 164 165 if (!mixer_input_audio_bus_ || 166 mixer_input_audio_bus_->frames() != dest->frames()) { 167 mixer_input_audio_bus_ = 168 AudioBus::Create(input_channel_count_, dest->frames()); 169 } 170 171 // If we're downmixing early we need a temporary AudioBus which matches 172 // the the input channel count and input frame size since we're passing 173 // |unmixed_audio_| directly to the |source_callback_|. 174 if (needs_downmix) 175 CreateUnmixedAudioIfNecessary(dest->frames()); 176 177 AudioBus* temp_dest = needs_downmix ? unmixed_audio_.get() : dest; 178 179 // Sanity check our inputs. 180 DCHECK_EQ(temp_dest->frames(), mixer_input_audio_bus_->frames()); 181 DCHECK_EQ(temp_dest->channels(), mixer_input_audio_bus_->channels()); 182 183 // Calculate the buffer delay for this callback. 184 base::TimeDelta buffer_delay = initial_delay_; 185 if (resampler_) { 186 buffer_delay += base::TimeDelta::FromMicroseconds( 187 resampler_frame_delay_ * output_frame_duration_.InMicroseconds()); 188 } 189 if (audio_fifo_) { 190 buffer_delay += base::TimeDelta::FromMicroseconds( 191 fifo_frame_delay * input_frame_duration_.InMicroseconds()); 192 } 193 194 // Have each mixer render its data into an output buffer then mix the result. 195 for (InputCallbackSet::iterator it = transform_inputs_.begin(); 196 it != transform_inputs_.end(); ++it) { 197 InputCallback* input = *it; 198 199 float volume = input->ProvideInput( 200 mixer_input_audio_bus_.get(), buffer_delay); 201 202 // Optimize the most common single input, full volume case. 203 if (it == transform_inputs_.begin()) { 204 if (volume == 1.0f) { 205 mixer_input_audio_bus_->CopyTo(temp_dest); 206 } else if (volume > 0) { 207 for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) { 208 vector_math::FMUL( 209 mixer_input_audio_bus_->channel(i), volume, 210 mixer_input_audio_bus_->frames(), temp_dest->channel(i)); 211 } 212 } else { 213 // Zero |temp_dest| otherwise, so we're mixing into a clean buffer. 214 temp_dest->Zero(); 215 } 216 217 continue; 218 } 219 220 // Volume adjust and mix each mixer input into |temp_dest| after rendering. 221 if (volume > 0) { 222 for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) { 223 vector_math::FMAC( 224 mixer_input_audio_bus_->channel(i), volume, 225 mixer_input_audio_bus_->frames(), temp_dest->channel(i)); 226 } 227 } 228 } 229 230 if (needs_downmix) { 231 DCHECK_EQ(temp_dest->frames(), dest->frames()); 232 channel_mixer_->Transform(temp_dest, dest); 233 } 234} 235 236void AudioConverter::ProvideInput(int resampler_frame_delay, AudioBus* dest) { 237 resampler_frame_delay_ = resampler_frame_delay; 238 if (audio_fifo_) 239 audio_fifo_->Consume(dest, dest->frames()); 240 else 241 SourceCallback(0, dest); 242} 243 244void AudioConverter::CreateUnmixedAudioIfNecessary(int frames) { 245 if (!unmixed_audio_ || unmixed_audio_->frames() != frames) 246 unmixed_audio_ = AudioBus::Create(input_channel_count_, frames); 247} 248 249} // namespace media 250