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