Web Speech API: cancel outstanding infobar requests when aborting.
[chromium-blink-merge.git] / media / base / audio_converter.cc
blobac82e314ba32d50845cf2c8404a483c4cf398caf
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.
11 #include "media/base/audio_converter.h"
13 #include <algorithm>
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"
23 namespace media {
25 AudioConverter::AudioConverter(const AudioParameters& input_params,
26 const AudioParameters& output_params,
27 bool disable_fifo)
28 : downmix_early_(false),
29 resampler_frame_delay_(0),
30 input_channel_count_(input_params.channels()) {
31 CHECK(input_params.IsValid());
32 CHECK(output_params.IsValid());
34 // Handle different input and output channel layouts.
35 if (input_params.channel_layout() != output_params.channel_layout()) {
36 DVLOG(1) << "Remixing channel layout from " << input_params.channel_layout()
37 << " to " << output_params.channel_layout() << "; from "
38 << input_params.channels() << " channels to "
39 << output_params.channels() << " channels.";
40 channel_mixer_.reset(new ChannelMixer(input_params, output_params));
42 // Pare off data as early as we can for efficiency.
43 downmix_early_ = input_params.channels() > output_params.channels();
44 if (downmix_early_) {
45 DVLOG(1) << "Remixing channel layout prior to resampling.";
46 // |unmixed_audio_| will be allocated on the fly.
47 } else {
48 // Instead, if we're not downmixing early we need a temporary AudioBus
49 // which matches the input channel count but uses the output frame size
50 // since we'll mix into the AudioBus from the output stream.
51 unmixed_audio_ = AudioBus::Create(
52 input_params.channels(), output_params.frames_per_buffer());
56 // Only resample if necessary since it's expensive.
57 if (input_params.sample_rate() != output_params.sample_rate()) {
58 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to "
59 << output_params.sample_rate();
60 const double io_sample_rate_ratio = input_params.sample_rate() /
61 static_cast<double>(output_params.sample_rate());
62 const int request_size = disable_fifo ? SincResampler::kDefaultRequestSize :
63 input_params.frames_per_buffer();
64 resampler_.reset(new MultiChannelResampler(
65 downmix_early_ ? output_params.channels() :
66 input_params.channels(),
67 io_sample_rate_ratio, request_size, base::Bind(
68 &AudioConverter::ProvideInput, base::Unretained(this))));
71 input_frame_duration_ = base::TimeDelta::FromMicroseconds(
72 base::Time::kMicrosecondsPerSecond /
73 static_cast<double>(input_params.sample_rate()));
74 output_frame_duration_ = base::TimeDelta::FromMicroseconds(
75 base::Time::kMicrosecondsPerSecond /
76 static_cast<double>(output_params.sample_rate()));
78 // The resampler can be configured to work with a specific request size, so a
79 // FIFO is not necessary when resampling.
80 if (disable_fifo || resampler_)
81 return;
83 // Since the output device may want a different buffer size than the caller
84 // asked for, we need to use a FIFO to ensure that both sides read in chunk
85 // sizes they're configured for.
86 if (input_params.frames_per_buffer() != output_params.frames_per_buffer()) {
87 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer()
88 << " to " << output_params.frames_per_buffer();
89 audio_fifo_.reset(new AudioPullFifo(
90 downmix_early_ ? output_params.channels() :
91 input_params.channels(),
92 input_params.frames_per_buffer(), base::Bind(
93 &AudioConverter::SourceCallback,
94 base::Unretained(this))));
98 AudioConverter::~AudioConverter() {}
100 void AudioConverter::AddInput(InputCallback* input) {
101 // TODO(dalecurtis): Speculative CHECK for http://crbug.com/233026, should be
102 // converted to a DCHECK once resolved.
103 CHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) ==
104 transform_inputs_.end());
105 transform_inputs_.push_back(input);
108 void AudioConverter::RemoveInput(InputCallback* input) {
109 DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) !=
110 transform_inputs_.end());
111 transform_inputs_.remove(input);
113 if (transform_inputs_.empty())
114 Reset();
117 void AudioConverter::Reset() {
118 if (audio_fifo_)
119 audio_fifo_->Clear();
120 if (resampler_)
121 resampler_->Flush();
124 void AudioConverter::ConvertWithDelay(const base::TimeDelta& initial_delay,
125 AudioBus* dest) {
126 initial_delay_ = initial_delay;
128 if (transform_inputs_.empty()) {
129 dest->Zero();
130 return;
133 // Determine if channel mixing should be done and if it should be done before
134 // or after resampling. If it's possible to reduce the channel count prior to
135 // resampling we can save a lot of processing time. Vice versa, we don't want
136 // to increase the channel count prior to resampling for the same reason.
137 bool needs_mixing = channel_mixer_ && !downmix_early_;
138 AudioBus* temp_dest = needs_mixing ? unmixed_audio_.get() : dest;
139 DCHECK(temp_dest);
141 // Figure out which method to call based on whether we're resampling and
142 // rebuffering, just resampling, or just mixing. We want to avoid any extra
143 // steps when possible since we may be converting audio data in real time.
144 if (!resampler_ && !audio_fifo_) {
145 SourceCallback(0, temp_dest);
146 } else {
147 if (resampler_)
148 resampler_->Resample(temp_dest->frames(), temp_dest);
149 else
150 ProvideInput(0, temp_dest);
153 // Finally upmix the channels if we didn't do so earlier.
154 if (needs_mixing) {
155 DCHECK_EQ(temp_dest->frames(), dest->frames());
156 channel_mixer_->Transform(temp_dest, dest);
160 void AudioConverter::Convert(AudioBus* dest) {
161 ConvertWithDelay(base::TimeDelta::FromMilliseconds(0), dest);
164 void AudioConverter::SourceCallback(int fifo_frame_delay, AudioBus* dest) {
165 bool needs_downmix = channel_mixer_ && downmix_early_;
167 if (!mixer_input_audio_bus_ ||
168 mixer_input_audio_bus_->frames() != dest->frames()) {
169 mixer_input_audio_bus_ =
170 AudioBus::Create(input_channel_count_, dest->frames());
173 if (needs_downmix &&
174 (!unmixed_audio_ || unmixed_audio_->frames() != dest->frames())) {
175 // If we're downmixing early we need a temporary AudioBus which matches
176 // the the input channel count and input frame size since we're passing
177 // |unmixed_audio_| directly to the |source_callback_|.
178 unmixed_audio_ = AudioBus::Create(input_channel_count_, dest->frames());
181 AudioBus* temp_dest = needs_downmix ? unmixed_audio_.get() : dest;
183 // Sanity check our inputs.
184 DCHECK_EQ(temp_dest->frames(), mixer_input_audio_bus_->frames());
185 DCHECK_EQ(temp_dest->channels(), mixer_input_audio_bus_->channels());
187 // Calculate the buffer delay for this callback.
188 base::TimeDelta buffer_delay = initial_delay_;
189 if (resampler_) {
190 buffer_delay += base::TimeDelta::FromMicroseconds(
191 resampler_frame_delay_ * output_frame_duration_.InMicroseconds());
193 if (audio_fifo_) {
194 buffer_delay += base::TimeDelta::FromMicroseconds(
195 fifo_frame_delay * input_frame_duration_.InMicroseconds());
198 // Have each mixer render its data into an output buffer then mix the result.
199 for (InputCallbackSet::iterator it = transform_inputs_.begin();
200 it != transform_inputs_.end(); ++it) {
201 InputCallback* input = *it;
203 float volume = input->ProvideInput(
204 mixer_input_audio_bus_.get(), buffer_delay);
206 // Optimize the most common single input, full volume case.
207 if (it == transform_inputs_.begin()) {
208 if (volume == 1.0f) {
209 mixer_input_audio_bus_->CopyTo(temp_dest);
210 } else if (volume > 0) {
211 for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) {
212 vector_math::FMUL(
213 mixer_input_audio_bus_->channel(i), volume,
214 mixer_input_audio_bus_->frames(), temp_dest->channel(i));
216 } else {
217 // Zero |temp_dest| otherwise, so we're mixing into a clean buffer.
218 temp_dest->Zero();
221 continue;
224 // Volume adjust and mix each mixer input into |temp_dest| after rendering.
225 if (volume > 0) {
226 for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) {
227 vector_math::FMAC(
228 mixer_input_audio_bus_->channel(i), volume,
229 mixer_input_audio_bus_->frames(), temp_dest->channel(i));
234 if (needs_downmix) {
235 DCHECK_EQ(temp_dest->frames(), dest->frames());
236 channel_mixer_->Transform(temp_dest, dest);
240 void AudioConverter::ProvideInput(int resampler_frame_delay, AudioBus* dest) {
241 resampler_frame_delay_ = resampler_frame_delay;
242 if (audio_fifo_)
243 audio_fifo_->Consume(dest, dest->frames());
244 else
245 SourceCallback(0, dest);
248 } // namespace media