- add sources.
[platform/framework/web/crosswalk.git] / src / media / base / audio_converter.cc
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
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
98 AudioConverter::~AudioConverter() {}
99
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);
106 }
107
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);
112
113   if (transform_inputs_.empty())
114     Reset();
115 }
116
117 void AudioConverter::Reset() {
118   if (audio_fifo_)
119     audio_fifo_->Clear();
120   if (resampler_)
121     resampler_->Flush();
122 }
123
124 void AudioConverter::ConvertWithDelay(const base::TimeDelta& initial_delay,
125                                       AudioBus* dest) {
126   initial_delay_ = initial_delay;
127
128   if (transform_inputs_.empty()) {
129     dest->Zero();
130     return;
131   }
132
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);
140
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);
151   }
152
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);
157   }
158 }
159
160 void AudioConverter::Convert(AudioBus* dest) {
161   ConvertWithDelay(base::TimeDelta::FromMilliseconds(0), dest);
162 }
163
164 void AudioConverter::SourceCallback(int fifo_frame_delay, AudioBus* dest) {
165   bool needs_downmix = channel_mixer_ && downmix_early_;
166
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());
171   }
172
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());
179   }
180
181   AudioBus* temp_dest = needs_downmix ? unmixed_audio_.get() : dest;
182
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());
186
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());
192   }
193   if (audio_fifo_) {
194     buffer_delay += base::TimeDelta::FromMicroseconds(
195         fifo_frame_delay * input_frame_duration_.InMicroseconds());
196   }
197
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;
202
203     float volume = input->ProvideInput(
204         mixer_input_audio_bus_.get(), buffer_delay);
205
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));
215         }
216       } else {
217         // Zero |temp_dest| otherwise, so we're mixing into a clean buffer.
218         temp_dest->Zero();
219       }
220
221       continue;
222     }
223
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));
230       }
231     }
232   }
233
234   if (needs_downmix) {
235     DCHECK_EQ(temp_dest->frames(), dest->frames());
236     channel_mixer_->Transform(temp_dest, dest);
237   }
238 }
239
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);
246 }
247
248 }  // namespace media