Removed unused VideoCaptureCapability parameters.
[chromium-blink-merge.git] / media / base / audio_converter_unittest.cc
blobd218ac882f4e689d42c4b8865744d0f5aaa2cdbe
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.
5 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
8 #include <cmath>
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "media/base/audio_converter.h"
17 #include "media/base/fake_audio_render_callback.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
21 namespace media {
23 // Command line switch for runtime adjustment of benchmark iterations.
24 static const char kBenchmarkIterations[] = "audio-converter-iterations";
25 static const int kDefaultIterations = 10;
27 // Parameters which control the many input case tests.
28 static const int kConvertInputs = 8;
29 static const int kConvertCycles = 3;
31 // Parameters used for testing.
32 static const int kBitsPerChannel = 32;
33 static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO;
34 static const int kHighLatencyBufferSize = 2048;
35 static const int kLowLatencyBufferSize = 256;
36 static const int kSampleRate = 48000;
38 // Number of full sine wave cycles for each Render() call.
39 static const int kSineCycles = 4;
41 // Tuple of <input rate, output rate, output channel layout, epsilon>.
42 typedef std::tr1::tuple<int, int, ChannelLayout, double> AudioConverterTestData;
43 class AudioConverterTest
44 : public testing::TestWithParam<AudioConverterTestData> {
45 public:
46 AudioConverterTest()
47 : epsilon_(std::tr1::get<3>(GetParam())) {
48 // Create input and output parameters based on test parameters.
49 input_parameters_ = AudioParameters(
50 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
51 std::tr1::get<0>(GetParam()), kBitsPerChannel, kHighLatencyBufferSize);
52 output_parameters_ = AudioParameters(
53 AudioParameters::AUDIO_PCM_LOW_LATENCY, std::tr1::get<2>(GetParam()),
54 std::tr1::get<1>(GetParam()), 16, kLowLatencyBufferSize);
56 converter_.reset(new AudioConverter(
57 input_parameters_, output_parameters_, false));
59 audio_bus_ = AudioBus::Create(output_parameters_);
60 expected_audio_bus_ = AudioBus::Create(output_parameters_);
62 // Allocate one callback for generating expected results.
63 double step = kSineCycles / static_cast<double>(
64 output_parameters_.frames_per_buffer());
65 expected_callback_.reset(new FakeAudioRenderCallback(step));
68 // Creates |count| input callbacks to be used for conversion testing.
69 void InitializeInputs(int count) {
70 // Setup FakeAudioRenderCallback step to compensate for resampling.
71 double scale_factor = input_parameters_.sample_rate() /
72 static_cast<double>(output_parameters_.sample_rate());
73 double step = kSineCycles / (scale_factor *
74 static_cast<double>(output_parameters_.frames_per_buffer()));
76 for (int i = 0; i < count; ++i) {
77 fake_callbacks_.push_back(new FakeAudioRenderCallback(step));
78 converter_->AddInput(fake_callbacks_[i]);
82 // Resets all input callbacks to a pristine state.
83 void Reset() {
84 converter_->Reset();
85 for (size_t i = 0; i < fake_callbacks_.size(); ++i)
86 fake_callbacks_[i]->reset();
87 expected_callback_->reset();
90 // Sets the volume on all input callbacks to |volume|.
91 void SetVolume(float volume) {
92 for (size_t i = 0; i < fake_callbacks_.size(); ++i)
93 fake_callbacks_[i]->set_volume(volume);
96 // Validates audio data between |audio_bus_| and |expected_audio_bus_| from
97 // |index|..|frames| after |scale| is applied to the expected audio data.
98 bool ValidateAudioData(int index, int frames, float scale) {
99 for (int i = 0; i < audio_bus_->channels(); ++i) {
100 for (int j = index; j < frames; ++j) {
101 double error = fabs(audio_bus_->channel(i)[j] -
102 expected_audio_bus_->channel(i)[j] * scale);
103 if (error > epsilon_) {
104 EXPECT_NEAR(expected_audio_bus_->channel(i)[j] * scale,
105 audio_bus_->channel(i)[j], epsilon_)
106 << " i=" << i << ", j=" << j;
107 return false;
111 return true;
114 // Runs a single Convert() stage, fills |expected_audio_bus_| appropriately,
115 // and validates equality with |audio_bus_| after |scale| is applied.
116 bool RenderAndValidateAudioData(float scale) {
117 // Render actual audio data.
118 converter_->Convert(audio_bus_.get());
120 // Render expected audio data.
121 expected_callback_->Render(expected_audio_bus_.get(), 0);
123 // Zero out unused channels in the expected AudioBus just as AudioConverter
124 // would during channel mixing.
125 for (int i = input_parameters_.channels();
126 i < output_parameters_.channels(); ++i) {
127 memset(expected_audio_bus_->channel(i), 0,
128 audio_bus_->frames() * sizeof(*audio_bus_->channel(i)));
131 return ValidateAudioData(0, audio_bus_->frames(), scale);
134 // Fills |audio_bus_| fully with |value|.
135 void FillAudioData(float value) {
136 for (int i = 0; i < audio_bus_->channels(); ++i) {
137 std::fill(audio_bus_->channel(i),
138 audio_bus_->channel(i) + audio_bus_->frames(), value);
142 // Verifies converter output with a |inputs| number of transform inputs.
143 void RunTest(int inputs) {
144 InitializeInputs(inputs);
146 SetVolume(0);
147 for (int i = 0; i < kConvertCycles; ++i)
148 ASSERT_TRUE(RenderAndValidateAudioData(0));
150 Reset();
152 // Set a different volume for each input and verify the results.
153 float total_scale = 0;
154 for (size_t i = 0; i < fake_callbacks_.size(); ++i) {
155 float volume = static_cast<float>(i) / fake_callbacks_.size();
156 total_scale += volume;
157 fake_callbacks_[i]->set_volume(volume);
159 for (int i = 0; i < kConvertCycles; ++i)
160 ASSERT_TRUE(RenderAndValidateAudioData(total_scale));
162 Reset();
164 // Remove every other input.
165 for (size_t i = 1; i < fake_callbacks_.size(); i += 2)
166 converter_->RemoveInput(fake_callbacks_[i]);
168 SetVolume(1);
169 float scale = inputs > 1 ? inputs / 2.0f : inputs;
170 for (int i = 0; i < kConvertCycles; ++i)
171 ASSERT_TRUE(RenderAndValidateAudioData(scale));
174 protected:
175 virtual ~AudioConverterTest() {}
177 // Converter under test.
178 scoped_ptr<AudioConverter> converter_;
180 // Input and output parameters used for AudioConverter construction.
181 AudioParameters input_parameters_;
182 AudioParameters output_parameters_;
184 // Destination AudioBus for AudioConverter output.
185 scoped_ptr<AudioBus> audio_bus_;
187 // AudioBus containing expected results for comparison with |audio_bus_|.
188 scoped_ptr<AudioBus> expected_audio_bus_;
190 // Vector of all input callbacks used to drive AudioConverter::Convert().
191 ScopedVector<FakeAudioRenderCallback> fake_callbacks_;
193 // Parallel input callback which generates the expected output.
194 scoped_ptr<FakeAudioRenderCallback> expected_callback_;
196 // Epsilon value with which to perform comparisons between |audio_bus_| and
197 // |expected_audio_bus_|.
198 double epsilon_;
200 DISALLOW_COPY_AND_ASSIGN(AudioConverterTest);
203 // Ensure the buffer delay provided by AudioConverter is accurate.
204 TEST(AudioConverterTest, AudioDelay) {
205 // Choose input and output parameters such that the transform must make
206 // multiple calls to fill the buffer.
207 AudioParameters input_parameters = AudioParameters(
208 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate,
209 kBitsPerChannel, kLowLatencyBufferSize);
210 AudioParameters output_parameters = AudioParameters(
211 AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout, kSampleRate * 2,
212 kBitsPerChannel, kHighLatencyBufferSize);
214 AudioConverter converter(input_parameters, output_parameters, false);
215 FakeAudioRenderCallback callback(0.2);
216 scoped_ptr<AudioBus> audio_bus = AudioBus::Create(output_parameters);
217 converter.AddInput(&callback);
218 converter.Convert(audio_bus.get());
220 // Calculate the expected buffer delay for given AudioParameters.
221 double input_sample_rate = input_parameters.sample_rate();
222 int fill_count =
223 (output_parameters.frames_per_buffer() * input_sample_rate /
224 output_parameters.sample_rate()) / input_parameters.frames_per_buffer();
226 base::TimeDelta input_frame_duration = base::TimeDelta::FromMicroseconds(
227 base::Time::kMicrosecondsPerSecond / input_sample_rate);
229 int expected_last_delay_milliseconds =
230 fill_count * input_parameters.frames_per_buffer() *
231 input_frame_duration.InMillisecondsF();
233 EXPECT_EQ(expected_last_delay_milliseconds,
234 callback.last_audio_delay_milliseconds());
237 // InputCallback that zero's out the provided AudioBus. Used for benchmarking.
238 class NullInputProvider : public AudioConverter::InputCallback {
239 public:
240 NullInputProvider() {}
241 virtual ~NullInputProvider() {}
243 virtual double ProvideInput(AudioBus* audio_bus,
244 base::TimeDelta buffer_delay) OVERRIDE {
245 audio_bus->Zero();
246 return 1;
250 // Benchmark for audio conversion. Original benchmarks were run with
251 // --audio-converter-iterations=50000.
252 TEST(AudioConverterTest, ConvertBenchmark) {
253 int benchmark_iterations = kDefaultIterations;
254 std::string iterations(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
255 kBenchmarkIterations));
256 base::StringToInt(iterations, &benchmark_iterations);
257 if (benchmark_iterations < kDefaultIterations)
258 benchmark_iterations = kDefaultIterations;
260 NullInputProvider fake_input1;
261 NullInputProvider fake_input2;
262 NullInputProvider fake_input3;
264 printf("Benchmarking %d iterations:\n", benchmark_iterations);
267 // Create input and output parameters to convert between the two most common
268 // sets of parameters (as indicated via UMA data).
269 AudioParameters input_params(
270 AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
271 48000, 16, 2048);
272 AudioParameters output_params(
273 AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
274 44100, 16, 440);
275 scoped_ptr<AudioBus> output_bus = AudioBus::Create(output_params);
277 scoped_ptr<AudioConverter> converter(
278 new AudioConverter(input_params, output_params, true));
279 converter->AddInput(&fake_input1);
280 converter->AddInput(&fake_input2);
281 converter->AddInput(&fake_input3);
283 // Benchmark Convert() w/ FIFO.
284 base::TimeTicks start = base::TimeTicks::HighResNow();
285 for (int i = 0; i < benchmark_iterations; ++i) {
286 converter->Convert(output_bus.get());
288 double total_time_ms =
289 (base::TimeTicks::HighResNow() - start).InMillisecondsF();
290 printf("Convert() w/ Resampling took %.2fms.\n", total_time_ms);
293 // Create input and output parameters to convert between common buffer sizes
294 // without any resampling for the FIFO vs no FIFO benchmarks.
295 AudioParameters input_params(
296 AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
297 44100, 16, 2048);
298 AudioParameters output_params(
299 AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_STEREO,
300 44100, 16, 440);
301 scoped_ptr<AudioBus> output_bus = AudioBus::Create(output_params);
304 scoped_ptr<AudioConverter> converter(
305 new AudioConverter(input_params, output_params, false));
306 converter->AddInput(&fake_input1);
307 converter->AddInput(&fake_input2);
308 converter->AddInput(&fake_input3);
310 // Benchmark Convert() w/ FIFO.
311 base::TimeTicks start = base::TimeTicks::HighResNow();
312 for (int i = 0; i < benchmark_iterations; ++i) {
313 converter->Convert(output_bus.get());
315 double total_time_ms =
316 (base::TimeTicks::HighResNow() - start).InMillisecondsF();
317 printf("Convert() w/ FIFO took %.2fms.\n", total_time_ms);
321 scoped_ptr<AudioConverter> converter(
322 new AudioConverter(input_params, output_params, true));
323 converter->AddInput(&fake_input1);
324 converter->AddInput(&fake_input2);
325 converter->AddInput(&fake_input3);
327 // Benchmark Convert() w/o FIFO.
328 base::TimeTicks start = base::TimeTicks::HighResNow();
329 for (int i = 0; i < benchmark_iterations; ++i) {
330 converter->Convert(output_bus.get());
332 double total_time_ms =
333 (base::TimeTicks::HighResNow() - start).InMillisecondsF();
334 printf("Convert() w/o FIFO took %.2fms.\n", total_time_ms);
338 TEST_P(AudioConverterTest, NoInputs) {
339 FillAudioData(1.0f);
340 EXPECT_TRUE(RenderAndValidateAudioData(0.0f));
343 TEST_P(AudioConverterTest, OneInput) {
344 RunTest(1);
347 TEST_P(AudioConverterTest, ManyInputs) {
348 RunTest(kConvertInputs);
351 INSTANTIATE_TEST_CASE_P(
352 AudioConverterTest, AudioConverterTest, testing::Values(
353 // No resampling. No channel mixing.
354 std::tr1::make_tuple(44100, 44100, CHANNEL_LAYOUT_STEREO, 0.00000048),
356 // Upsampling. Channel upmixing.
357 std::tr1::make_tuple(44100, 48000, CHANNEL_LAYOUT_QUAD, 0.033),
359 // Downsampling. Channel downmixing.
360 std::tr1::make_tuple(48000, 41000, CHANNEL_LAYOUT_MONO, 0.042)));
362 } // namespace media