audio_converter.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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_pull_fifo.h" 18#include "media/base/channel_mixer.h" 19#include "media/base/multi_channel_resampler.h" 20#include "media/base/vector_math.h" 21 22namespace media { 23 24AudioConverter::AudioConverter(const AudioParameters& input_params, 25 const AudioParameters& output_params, 26 bool disable_fifo) 27 : downmix_early_(false), 28 resampler_frame_delay_(0), 29 input_channel_count_(input_params.channels()) { 30 CHECK(input_params.IsValid()); 31 CHECK(output_params.IsValid()); 32 33 // Handle different input and output channel layouts. 34 if (input_params.channel_layout() != output_params.channel_layout()) { 35 DVLOG(1) << "Remixing channel layout from " << input_params.channel_layout() 36 << " to " << output_params.channel_layout() << "; from " 37 << input_params.channels() << " channels to " 38 << output_params.channels() << " channels."; 39 channel_mixer_.reset(new ChannelMixer(input_params, output_params)); 40 41 // Pare off data as early as we can for efficiency. 42 downmix_early_ = input_params.channels() > output_params.channels(); 43 if (downmix_early_) { 44 DVLOG(1) << "Remixing channel layout prior to resampling."; 45 // |unmixed_audio_| will be allocated on the fly. 46 } else { 47 // Instead, if we're not downmixing early we need a temporary AudioBus 48 // which matches the input channel count but uses the output frame size 49 // since we'll mix into the AudioBus from the output stream. 50 unmixed_audio_ = AudioBus::Create( 51 input_params.channels(), output_params.frames_per_buffer()); 52 } 53 } 54 55 // Only resample if necessary since it's expensive. 56 if (input_params.sample_rate() != output_params.sample_rate()) { 57 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " 58 << output_params.sample_rate(); 59 double io_sample_rate_ratio = input_params.sample_rate() / 60 static_cast<double>(output_params.sample_rate()); 61 resampler_.reset(new MultiChannelResampler( 62 downmix_early_ ? output_params.channels() : 63 input_params.channels(), 64 io_sample_rate_ratio, base::Bind( 65 &AudioConverter::ProvideInput, base::Unretained(this)))); 66 } 67 68 input_frame_duration_ = base::TimeDelta::FromMicroseconds( 69 base::Time::kMicrosecondsPerSecond / 70 static_cast<double>(input_params.sample_rate())); 71 output_frame_duration_ = base::TimeDelta::FromMicroseconds( 72 base::Time::kMicrosecondsPerSecond / 73 static_cast<double>(output_params.sample_rate())); 74 75 if (disable_fifo) 76 return; 77 78 // Since the resampler / output device may want a different buffer size than 79 // the caller asked for, we need to use a FIFO to ensure that both sides 80 // read in chunk sizes they're configured for. 81 if (resampler_.get() || 82 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { 83 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() 84 << " to " << output_params.frames_per_buffer(); 85 audio_fifo_.reset(new AudioPullFifo( 86 downmix_early_ ? output_params.channels() : 87 input_params.channels(), 88 input_params.frames_per_buffer(), base::Bind( 89 &AudioConverter::SourceCallback, 90 base::Unretained(this)))); 91 } 92} 93 94AudioConverter::~AudioConverter() {} 95 96void AudioConverter::AddInput(InputCallback* input) { 97 // TODO(dalecurtis): Speculative CHECK for http://crbug.com/233026, should be 98 // converted to a DCHECK once resolved. 99 CHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) == 100 transform_inputs_.end()); 101 transform_inputs_.push_back(input); 102} 103 104void AudioConverter::RemoveInput(InputCallback* input) { 105 DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) != 106 transform_inputs_.end()); 107 transform_inputs_.remove(input); 108 109 if (transform_inputs_.empty()) 110 Reset(); 111} 112 113void AudioConverter::Reset() { 114 if (audio_fifo_) 115 audio_fifo_->Clear(); 116 if (resampler_) 117 resampler_->Flush(); 118} 119 120void AudioConverter::ConvertWithDelay(const base::TimeDelta& initial_delay, 121 AudioBus* dest) { 122 initial_delay_ = initial_delay; 123 124 if (transform_inputs_.empty()) { 125 dest->Zero(); 126 return; 127 } 128 129 // Determine if channel mixing should be done and if it should be done before 130 // or after resampling. If it's possible to reduce the channel count prior to 131 // resampling we can save a lot of processing time. Vice versa, we don't want 132 // to increase the channel count prior to resampling for the same reason. 133 bool needs_mixing = channel_mixer_ && !downmix_early_; 134 AudioBus* temp_dest = needs_mixing ? unmixed_audio_.get() : dest; 135 DCHECK(temp_dest); 136 137 // Figure out which method to call based on whether we're resampling and 138 // rebuffering, just resampling, or just mixing. We want to avoid any extra 139 // steps when possible since we may be converting audio data in real time. 140 if (!resampler_ && !audio_fifo_) { 141 SourceCallback(0, temp_dest); 142 } else { 143 if (resampler_) 144 resampler_->Resample(temp_dest, temp_dest->frames()); 145 else 146 ProvideInput(0, temp_dest); 147 } 148 149 // Finally upmix the channels if we didn't do so earlier. 150 if (needs_mixing) { 151 DCHECK_EQ(temp_dest->frames(), dest->frames()); 152 channel_mixer_->Transform(temp_dest, dest); 153 } 154} 155 156void AudioConverter::Convert(AudioBus* dest) { 157 ConvertWithDelay(base::TimeDelta::FromMilliseconds(0), dest); 158} 159 160void AudioConverter::SourceCallback(int fifo_frame_delay, AudioBus* dest) { 161 bool needs_downmix = channel_mixer_ && downmix_early_; 162 163 if (!mixer_input_audio_bus_ || 164 mixer_input_audio_bus_->frames() != dest->frames()) { 165 mixer_input_audio_bus_ = 166 AudioBus::Create(input_channel_count_, dest->frames()); 167 } 168 169 if (needs_downmix && 170 (!unmixed_audio_ || unmixed_audio_->frames() != dest->frames())) { 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 unmixed_audio_ = AudioBus::Create(input_channel_count_, dest->frames()); 175 } 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 244} // namespace media 245