audio_converter.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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#include "media/base/audio_converter.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "media/base/audio_pull_fifo.h"
12#include "media/base/channel_mixer.h"
13#include "media/base/multi_channel_resampler.h"
14#include "media/base/vector_math.h"
15
16namespace media {
17
18AudioConverter::AudioConverter(const AudioParameters& input_params,
19                               const AudioParameters& output_params,
20                               bool disable_fifo)
21    : downmix_early_(false),
22      resampler_frame_delay_(0),
23      input_channel_count_(input_params.channels()) {
24  CHECK(input_params.IsValid());
25  CHECK(output_params.IsValid());
26
27  // Handle different input and output channel layouts.
28  if (input_params.channel_layout() != output_params.channel_layout()) {
29    DVLOG(1) << "Remixing channel layout from " << input_params.channel_layout()
30             << " to " << output_params.channel_layout() << "; from "
31             << input_params.channels() << " channels to "
32             << output_params.channels() << " channels.";
33    channel_mixer_.reset(new ChannelMixer(input_params, output_params));
34
35    // Pare off data as early as we can for efficiency.
36    downmix_early_ = input_params.channels() > output_params.channels();
37    if (downmix_early_) {
38      DVLOG(1) << "Remixing channel layout prior to resampling.";
39      // |unmixed_audio_| will be allocated on the fly.
40    } else {
41      // Instead, if we're not downmixing early we need a temporary AudioBus
42      // which matches the input channel count but uses the output frame size
43      // since we'll mix into the AudioBus from the output stream.
44      unmixed_audio_ = AudioBus::Create(
45          input_params.channels(), output_params.frames_per_buffer());
46    }
47  }
48
49  // Only resample if necessary since it's expensive.
50  if (input_params.sample_rate() != output_params.sample_rate()) {
51    DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to "
52             << output_params.sample_rate();
53    double io_sample_rate_ratio = input_params.sample_rate() /
54        static_cast<double>(output_params.sample_rate());
55    resampler_.reset(new MultiChannelResampler(
56        downmix_early_ ? output_params.channels() :
57            input_params.channels(),
58        io_sample_rate_ratio, 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  if (disable_fifo)
70    return;
71
72  // Since the resampler / output device may want a different buffer size than
73  // the caller asked for, we need to use a FIFO to ensure that both sides
74  // read in chunk sizes they're configured for.
75  if (resampler_.get() ||
76      input_params.frames_per_buffer() != output_params.frames_per_buffer()) {
77    DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer()
78             << " to " << output_params.frames_per_buffer();
79    audio_fifo_.reset(new AudioPullFifo(
80        downmix_early_ ? output_params.channels() :
81            input_params.channels(),
82        input_params.frames_per_buffer(), base::Bind(
83            &AudioConverter::SourceCallback,
84            base::Unretained(this))));
85  }
86}
87
88AudioConverter::~AudioConverter() {}
89
90void AudioConverter::AddInput(InputCallback* input) {
91  transform_inputs_.push_back(input);
92}
93
94void AudioConverter::RemoveInput(InputCallback* input) {
95  DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) !=
96         transform_inputs_.end());
97  transform_inputs_.remove(input);
98
99  if (transform_inputs_.empty())
100    Reset();
101}
102
103void AudioConverter::Reset() {
104  if (audio_fifo_)
105    audio_fifo_->Clear();
106  if (resampler_)
107    resampler_->Flush();
108}
109
110void AudioConverter::Convert(AudioBus* dest) {
111  if (transform_inputs_.empty()) {
112    dest->Zero();
113    return;
114  }
115
116  bool needs_mixing = channel_mixer_ && !downmix_early_;
117  AudioBus* temp_dest = needs_mixing ? unmixed_audio_.get() : dest;
118  DCHECK(temp_dest);
119
120  if (!resampler_ && !audio_fifo_) {
121    SourceCallback(0, temp_dest);
122  } else {
123    if (resampler_)
124      resampler_->Resample(temp_dest, temp_dest->frames());
125    else
126      ProvideInput(0, temp_dest);
127  }
128
129  if (needs_mixing) {
130    DCHECK_EQ(temp_dest->frames(), dest->frames());
131    channel_mixer_->Transform(temp_dest, dest);
132  }
133}
134
135void AudioConverter::SourceCallback(int fifo_frame_delay, AudioBus* dest) {
136  bool needs_downmix = channel_mixer_ && downmix_early_;
137
138  if (!mixer_input_audio_bus_ ||
139      mixer_input_audio_bus_->frames() != dest->frames()) {
140    mixer_input_audio_bus_ =
141        AudioBus::Create(input_channel_count_, dest->frames());
142  }
143
144  if (needs_downmix &&
145      (!unmixed_audio_ || unmixed_audio_->frames() != dest->frames())) {
146    // If we're downmixing early we need a temporary AudioBus which matches
147    // the the input channel count and input frame size since we're passing
148    // |unmixed_audio_| directly to the |source_callback_|.
149    unmixed_audio_ = AudioBus::Create(input_channel_count_, dest->frames());
150  }
151
152  AudioBus* temp_dest = needs_downmix ? unmixed_audio_.get() : dest;
153
154  // Sanity check our inputs.
155  DCHECK_EQ(temp_dest->frames(), mixer_input_audio_bus_->frames());
156  DCHECK_EQ(temp_dest->channels(), mixer_input_audio_bus_->channels());
157
158  // Calculate the buffer delay for this callback.
159  base::TimeDelta buffer_delay;
160  if (resampler_) {
161    buffer_delay += base::TimeDelta::FromMicroseconds(
162        resampler_frame_delay_ * output_frame_duration_.InMicroseconds());
163  }
164  if (audio_fifo_) {
165    buffer_delay += base::TimeDelta::FromMicroseconds(
166        fifo_frame_delay * input_frame_duration_.InMicroseconds());
167  }
168
169  // Have each mixer render its data into an output buffer then mix the result.
170  for (InputCallbackSet::iterator it = transform_inputs_.begin();
171       it != transform_inputs_.end(); ++it) {
172    InputCallback* input = *it;
173
174    float volume = input->ProvideInput(
175        mixer_input_audio_bus_.get(), buffer_delay);
176
177    // Optimize the most common single input, full volume case.
178    if (it == transform_inputs_.begin()) {
179      if (volume == 1.0f) {
180        mixer_input_audio_bus_->CopyTo(temp_dest);
181        continue;
182      }
183
184      // Zero |temp_dest| otherwise, so we're mixing into a clean buffer.
185      temp_dest->Zero();
186    }
187
188    // Volume adjust and mix each mixer input into |temp_dest| after rendering.
189    if (volume > 0) {
190      for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) {
191        vector_math::FMAC(
192            mixer_input_audio_bus_->channel(i), volume,
193            mixer_input_audio_bus_->frames(), temp_dest->channel(i));
194      }
195    }
196  }
197
198  if (needs_downmix) {
199    DCHECK_EQ(temp_dest->frames(), dest->frames());
200    channel_mixer_->Transform(temp_dest, dest);
201  }
202}
203
204void AudioConverter::ProvideInput(int resampler_frame_delay, AudioBus* dest) {
205  resampler_frame_delay_ = resampler_frame_delay;
206  if (audio_fifo_)
207    audio_fifo_->Consume(dest, dest->frames());
208  else
209    SourceCallback(0, dest);
210}
211
212}  // namespace media
213