• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
23 namespace media {
24 
AudioConverter(const AudioParameters & input_params,const AudioParameters & output_params,bool disable_fifo)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());
33 
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));
41 
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());
53     }
54   }
55 
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))));
69   }
70 
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()));
77 
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;
82 
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))));
95   }
96 }
97 
~AudioConverter()98 AudioConverter::~AudioConverter() {}
99 
AddInput(InputCallback * input)100 void AudioConverter::AddInput(InputCallback* input) {
101   DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) ==
102          transform_inputs_.end());
103   transform_inputs_.push_back(input);
104 }
105 
RemoveInput(InputCallback * input)106 void AudioConverter::RemoveInput(InputCallback* input) {
107   DCHECK(std::find(transform_inputs_.begin(), transform_inputs_.end(), input) !=
108          transform_inputs_.end());
109   transform_inputs_.remove(input);
110 
111   if (transform_inputs_.empty())
112     Reset();
113 }
114 
Reset()115 void AudioConverter::Reset() {
116   if (audio_fifo_)
117     audio_fifo_->Clear();
118   if (resampler_)
119     resampler_->Flush();
120 }
121 
ConvertWithDelay(const base::TimeDelta & initial_delay,AudioBus * dest)122 void AudioConverter::ConvertWithDelay(const base::TimeDelta& initial_delay,
123                                       AudioBus* dest) {
124   initial_delay_ = initial_delay;
125 
126   if (transform_inputs_.empty()) {
127     dest->Zero();
128     return;
129   }
130 
131   // Determine if channel mixing should be done and if it should be done before
132   // or after resampling.  If it's possible to reduce the channel count prior to
133   // resampling we can save a lot of processing time.  Vice versa, we don't want
134   // to increase the channel count prior to resampling for the same reason.
135   bool needs_mixing = channel_mixer_ && !downmix_early_;
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 
Convert(AudioBus * dest)158 void AudioConverter::Convert(AudioBus* dest) {
159   ConvertWithDelay(base::TimeDelta::FromMilliseconds(0), dest);
160 }
161 
SourceCallback(int fifo_frame_delay,AudioBus * dest)162 void 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 (needs_downmix &&
172       (!unmixed_audio_ || unmixed_audio_->frames() != dest->frames())) {
173     // If we're downmixing early we need a temporary AudioBus which matches
174     // the the input channel count and input frame size since we're passing
175     // |unmixed_audio_| directly to the |source_callback_|.
176     unmixed_audio_ = AudioBus::Create(input_channel_count_, dest->frames());
177   }
178 
179   AudioBus* temp_dest = needs_downmix ? unmixed_audio_.get() : dest;
180 
181   // Sanity check our inputs.
182   DCHECK_EQ(temp_dest->frames(), mixer_input_audio_bus_->frames());
183   DCHECK_EQ(temp_dest->channels(), mixer_input_audio_bus_->channels());
184 
185   // Calculate the buffer delay for this callback.
186   base::TimeDelta buffer_delay = initial_delay_;
187   if (resampler_) {
188     buffer_delay += base::TimeDelta::FromMicroseconds(
189         resampler_frame_delay_ * output_frame_duration_.InMicroseconds());
190   }
191   if (audio_fifo_) {
192     buffer_delay += base::TimeDelta::FromMicroseconds(
193         fifo_frame_delay * input_frame_duration_.InMicroseconds());
194   }
195 
196   // Have each mixer render its data into an output buffer then mix the result.
197   for (InputCallbackSet::iterator it = transform_inputs_.begin();
198        it != transform_inputs_.end(); ++it) {
199     InputCallback* input = *it;
200 
201     float volume = input->ProvideInput(
202         mixer_input_audio_bus_.get(), buffer_delay);
203 
204     // Optimize the most common single input, full volume case.
205     if (it == transform_inputs_.begin()) {
206       if (volume == 1.0f) {
207         mixer_input_audio_bus_->CopyTo(temp_dest);
208       } else if (volume > 0) {
209         for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) {
210           vector_math::FMUL(
211               mixer_input_audio_bus_->channel(i), volume,
212               mixer_input_audio_bus_->frames(), temp_dest->channel(i));
213         }
214       } else {
215         // Zero |temp_dest| otherwise, so we're mixing into a clean buffer.
216         temp_dest->Zero();
217       }
218 
219       continue;
220     }
221 
222     // Volume adjust and mix each mixer input into |temp_dest| after rendering.
223     if (volume > 0) {
224       for (int i = 0; i < mixer_input_audio_bus_->channels(); ++i) {
225         vector_math::FMAC(
226             mixer_input_audio_bus_->channel(i), volume,
227             mixer_input_audio_bus_->frames(), temp_dest->channel(i));
228       }
229     }
230   }
231 
232   if (needs_downmix) {
233     DCHECK_EQ(temp_dest->frames(), dest->frames());
234     channel_mixer_->Transform(temp_dest, dest);
235   }
236 }
237 
ProvideInput(int resampler_frame_delay,AudioBus * dest)238 void AudioConverter::ProvideInput(int resampler_frame_delay, AudioBus* dest) {
239   resampler_frame_delay_ = resampler_frame_delay;
240   if (audio_fifo_)
241     audio_fifo_->Consume(dest, dest->frames());
242   else
243     SourceCallback(0, dest);
244 }
245 
246 }  // namespace media
247