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
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"
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
> {
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.
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
;
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
);
147 for (int i
= 0; i
< kConvertCycles
; ++i
)
148 ASSERT_TRUE(RenderAndValidateAudioData(0));
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
));
164 // Remove every other input.
165 for (size_t i
= 1; i
< fake_callbacks_
.size(); i
+= 2)
166 converter_
->RemoveInput(fake_callbacks_
[i
]);
169 float scale
= inputs
> 1 ? inputs
/ 2.0f
: inputs
;
170 for (int i
= 0; i
< kConvertCycles
; ++i
)
171 ASSERT_TRUE(RenderAndValidateAudioData(scale
));
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_|.
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();
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
{
240 NullInputProvider() {}
241 virtual ~NullInputProvider() {}
243 virtual double ProvideInput(AudioBus
* audio_bus
,
244 base::TimeDelta buffer_delay
) OVERRIDE
{
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
,
272 AudioParameters
output_params(
273 AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_STEREO
,
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
,
298 AudioParameters
output_params(
299 AudioParameters::AUDIO_PCM_LINEAR
, CHANNEL_LAYOUT_STEREO
,
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
) {
340 EXPECT_TRUE(RenderAndValidateAudioData(0.0f
));
343 TEST_P(AudioConverterTest
, OneInput
) {
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)));