OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // The format of these tests are to enqueue a known amount of data and then | 5 // The format of these tests are to enqueue a known amount of data and then |
6 // request the exact amount we expect in order to dequeue the known amount of | 6 // request the exact amount we expect in order to dequeue the known amount of |
7 // data. This ensures that for any rate we are consuming input data at the | 7 // data. This ensures that for any rate we are consuming input data at the |
8 // correct rate. We always pass in a very large destination buffer with the | 8 // correct rate. We always pass in a very large destination buffer with the |
9 // expectation that FillBuffer() will fill as much as it can but no more. | 9 // expectation that FillBuffer() will fill as much as it can but no more. |
10 | 10 |
11 #include <cmath> | 11 #include <cmath> |
12 | 12 |
13 #include "base/bind.h" | 13 #include "base/bind.h" |
14 #include "base/callback.h" | 14 #include "base/callback.h" |
15 #include "base/memory/scoped_ptr.h" | |
15 #include "media/base/audio_buffer.h" | 16 #include "media/base/audio_buffer.h" |
16 #include "media/base/audio_bus.h" | 17 #include "media/base/audio_bus.h" |
17 #include "media/base/buffers.h" | 18 #include "media/base/buffers.h" |
18 #include "media/base/channel_layout.h" | 19 #include "media/base/channel_layout.h" |
19 #include "media/base/test_helpers.h" | 20 #include "media/base/test_helpers.h" |
20 #include "media/filters/audio_renderer_algorithm.h" | 21 #include "media/filters/audio_renderer_algorithm.h" |
22 #include "media/filters/audio_renderer_algorithm_util.h" | |
21 #include "testing/gtest/include/gtest/gtest.h" | 23 #include "testing/gtest/include/gtest/gtest.h" |
22 | |
23 namespace media { | 24 namespace media { |
24 | 25 |
25 static const int kFrameSize = 250; | 26 static const int kFrameSize = 250; |
26 static const int kSamplesPerSecond = 3000; | 27 static const int kSamplesPerSecond = 3000; |
27 static const SampleFormat kSampleFormat = kSampleFormatS16; | 28 static const SampleFormat kSampleFormat = kSampleFormatS16; |
29 static const int kOutputDurationInSec = 10; | |
30 | |
31 static void FillWithSquarePuleTrain( | |
ajm
2013/07/23 18:03:28
Pule -> Pulse
turaj
2013/07/29 22:09:57
Done.
| |
32 int pulse_width, int offset, int channel, AudioBus* audio_bus) { | |
33 float* ch = audio_bus->channel(channel); | |
34 | |
35 // Fill backward from |offset| - 1 toward zero, starting with -1, alternating | |
36 // between -1 and 1 every |pulse_width| samples. | |
37 int pulse = -1; | |
38 for (int n = offset - 1, k = 0; n >= 0; --n, ++k) { | |
39 if (k >= pulse_width) { | |
40 pulse = -pulse; | |
41 k = 0; | |
42 } | |
43 ch[n] = pulse; | |
44 } | |
45 | |
46 // Fill forward from |offset| towards the end, starting with 1, alternating | |
47 // between 1 and -1 every |pulse_width| samples. | |
48 pulse = 1; | |
49 for (int n = offset, k = 0; n < audio_bus->frames(); ++n, ++k) { | |
50 if (k >= pulse_width) { | |
51 pulse = -pulse; | |
52 k = 0; | |
53 } | |
54 ch[n] = pulse; | |
55 } | |
56 } | |
57 | |
28 | 58 |
29 class AudioRendererAlgorithmTest : public testing::Test { | 59 class AudioRendererAlgorithmTest : public testing::Test { |
30 public: | 60 public: |
31 AudioRendererAlgorithmTest() | 61 AudioRendererAlgorithmTest() |
32 : frames_enqueued_(0), | 62 : frames_enqueued_(0), |
33 channels_(0), | 63 channels_(0), |
34 sample_format_(kUnknownSampleFormat), | 64 sample_format_(kUnknownSampleFormat), |
35 bytes_per_sample_(0) { | 65 bytes_per_sample_(0) { |
36 } | 66 } |
37 | 67 |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
111 int initial_frames_buffered) { | 141 int initial_frames_buffered) { |
112 int frame_delta = frames_enqueued_ - initial_frames_enqueued; | 142 int frame_delta = frames_enqueued_ - initial_frames_enqueued; |
113 int buffered_delta = algorithm_.frames_buffered() - initial_frames_buffered; | 143 int buffered_delta = algorithm_.frames_buffered() - initial_frames_buffered; |
114 int consumed = frame_delta - buffered_delta; | 144 int consumed = frame_delta - buffered_delta; |
115 CHECK_GE(consumed, 0); | 145 CHECK_GE(consumed, 0); |
116 return consumed; | 146 return consumed; |
117 } | 147 } |
118 | 148 |
119 void TestPlaybackRate(double playback_rate) { | 149 void TestPlaybackRate(double playback_rate) { |
120 const int kDefaultBufferSize = algorithm_.samples_per_second() / 100; | 150 const int kDefaultBufferSize = algorithm_.samples_per_second() / 100; |
121 const int kDefaultFramesRequested = 2 * algorithm_.samples_per_second(); | 151 const int kDefaultFramesRequested = kOutputDurationInSec * |
152 algorithm_.samples_per_second(); | |
122 | 153 |
123 TestPlaybackRate( | 154 TestPlaybackRate( |
124 playback_rate, kDefaultBufferSize, kDefaultFramesRequested); | 155 playback_rate, kDefaultBufferSize, kDefaultFramesRequested); |
125 } | 156 } |
126 | 157 |
127 void TestPlaybackRate(double playback_rate, | 158 void TestPlaybackRate(double playback_rate, |
128 int buffer_size_in_frames, | 159 int buffer_size_in_frames, |
129 int total_frames_requested) { | 160 int total_frames_requested) { |
130 int initial_frames_enqueued = frames_enqueued_; | 161 int initial_frames_enqueued = frames_enqueued_; |
131 int initial_frames_buffered = algorithm_.frames_buffered(); | 162 int initial_frames_buffered = algorithm_.frames_buffered(); |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
263 TestPlaybackRate(2.1); | 294 TestPlaybackRate(2.1); |
264 TestPlaybackRate(0.9); | 295 TestPlaybackRate(0.9); |
265 TestPlaybackRate(0.6); | 296 TestPlaybackRate(0.6); |
266 TestPlaybackRate(1.4); | 297 TestPlaybackRate(1.4); |
267 TestPlaybackRate(0.3); | 298 TestPlaybackRate(0.3); |
268 } | 299 } |
269 | 300 |
270 TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) { | 301 TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) { |
271 Initialize(); | 302 Initialize(); |
272 static const int kBufferSizeInFrames = 1; | 303 static const int kBufferSizeInFrames = 1; |
273 static const int kFramesRequested = 2 * kSamplesPerSecond; | 304 static const int kFramesRequested = kOutputDurationInSec * kSamplesPerSecond; |
274 TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested); | 305 TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested); |
275 TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested); | 306 TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested); |
276 TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested); | 307 TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested); |
277 } | 308 } |
278 | 309 |
279 TEST_F(AudioRendererAlgorithmTest, FillBuffer_LargeBufferSize) { | 310 TEST_F(AudioRendererAlgorithmTest, FillBuffer_LargeBufferSize) { |
280 Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS16, 44100); | 311 Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS16, 44100); |
281 TestPlaybackRate(1.0); | 312 TestPlaybackRate(1.0); |
282 TestPlaybackRate(0.5); | 313 TestPlaybackRate(0.5); |
283 TestPlaybackRate(1.5); | 314 TestPlaybackRate(1.5); |
284 } | 315 } |
285 | 316 |
286 TEST_F(AudioRendererAlgorithmTest, FillBuffer_LowerQualityAudio) { | 317 TEST_F(AudioRendererAlgorithmTest, FillBuffer_LowerQualityAudio) { |
287 Initialize(CHANNEL_LAYOUT_MONO, kSampleFormatU8, kSamplesPerSecond); | 318 Initialize(CHANNEL_LAYOUT_MONO, kSampleFormatU8, kSamplesPerSecond); |
288 TestPlaybackRate(1.0); | 319 TestPlaybackRate(1.0); |
289 TestPlaybackRate(0.5); | 320 TestPlaybackRate(0.5); |
290 TestPlaybackRate(1.5); | 321 TestPlaybackRate(1.5); |
291 } | 322 } |
292 | 323 |
293 TEST_F(AudioRendererAlgorithmTest, FillBuffer_HigherQualityAudio) { | 324 TEST_F(AudioRendererAlgorithmTest, FillBuffer_HigherQualityAudio) { |
294 Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS32, kSamplesPerSecond); | 325 Initialize(CHANNEL_LAYOUT_STEREO, kSampleFormatS32, kSamplesPerSecond); |
295 TestPlaybackRate(1.0); | 326 TestPlaybackRate(1.0); |
296 TestPlaybackRate(0.5); | 327 TestPlaybackRate(0.5); |
297 TestPlaybackRate(1.5); | 328 TestPlaybackRate(1.5); |
298 } | 329 } |
299 | 330 |
331 TEST_F(AudioRendererAlgorithmTest, DotProduct) { | |
332 const int kChannels = 3; | |
333 const int kFrames = 20; | |
334 const int kPulseWidth = 2; | |
335 | |
336 scoped_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames); | |
337 scoped_ptr<AudioBus> b = AudioBus::Create(kChannels, kFrames); | |
338 | |
339 scoped_ptr<float[]> dot_prod(new float[kChannels]); | |
340 | |
341 FillWithSquarePuleTrain(kPulseWidth, 0, 0, a.get()); | |
342 FillWithSquarePuleTrain(kPulseWidth, 1, 1, a.get()); | |
343 FillWithSquarePuleTrain(kPulseWidth, 2, 2, a.get()); | |
344 | |
345 FillWithSquarePuleTrain(kPulseWidth, 0, 0, b.get()); | |
346 FillWithSquarePuleTrain(kPulseWidth, 0, 1, b.get()); | |
347 FillWithSquarePuleTrain(kPulseWidth, 0, 2, b.get()); | |
348 | |
349 MultiChannelDotProduct(a.get(), 0, b.get(), 0, kFrames, dot_prod.get()); | |
350 | |
351 EXPECT_FLOAT_EQ(kFrames, dot_prod[0]); | |
352 EXPECT_FLOAT_EQ(0, dot_prod[1]); | |
353 EXPECT_FLOAT_EQ(-kFrames, dot_prod[2]); | |
354 | |
355 MultiChannelDotProduct(a.get(), 4, b.get(), 8, kFrames / 2, dot_prod.get()); | |
356 | |
357 EXPECT_FLOAT_EQ(kFrames / 2, dot_prod[0]); | |
358 EXPECT_FLOAT_EQ(0, dot_prod[1]); | |
359 EXPECT_FLOAT_EQ(-kFrames / 2, dot_prod[2]); | |
360 } | |
361 | |
362 TEST_F(AudioRendererAlgorithmTest, MovingWindowEnergy) { | |
363 const int kChannels = 2; | |
364 const int kFrames = 20; | |
365 const int kFramesPerBlock = 3; | |
366 const int kNumBlocks = kFrames - (kFramesPerBlock - 1); | |
367 scoped_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames); | |
368 scoped_ptr<float[]> energies(new float[kChannels * kNumBlocks]); | |
369 float* ch_left = a->channel(0); | |
370 float* ch_right = a->channel(1); | |
371 | |
372 // Fill up both channels. | |
373 for (int n = 0; n < kFrames; ++n) { | |
374 ch_left[n] = n; | |
375 ch_right[n] = kFrames - 1 - n; | |
376 } | |
377 | |
378 MultiChannelMovingWindowEnergies(a.get(), kFramesPerBlock, energies.get()); | |
379 | |
380 for (int n = 0; n < kNumBlocks; ++n) { | |
381 float expected_energy = 0; | |
382 for (int k = 0; k < kFramesPerBlock; ++k) | |
383 expected_energy += ch_left[n + k] * ch_left[n + k]; | |
384 EXPECT_FLOAT_EQ(expected_energy, energies[2 * n]); | |
385 | |
386 expected_energy = 0; | |
387 for (int k = 0; k < kFramesPerBlock; ++k) | |
388 expected_energy += ch_right[n + k] * ch_right[n + k]; | |
389 EXPECT_FLOAT_EQ(expected_energy, energies[2 * n + 1]); | |
390 } | |
391 } | |
392 | |
393 TEST_F(AudioRendererAlgorithmTest, PartialAndDecimatedSearch) { | |
394 const int kFramesInSearchRegion = 12; | |
395 const int kChannels = 2; | |
396 float ch_0[] = {0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0}; | |
397 float ch_1[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 1.0, 0.1, 0.0, 0.0}; | |
398 ASSERT_EQ(sizeof(ch_0), sizeof(ch_1)); | |
399 ASSERT_EQ(static_cast<size_t>(kFramesInSearchRegion), | |
400 sizeof(ch_0) / sizeof(*ch_0)); | |
401 scoped_ptr<AudioBus> search_region = AudioBus::CreateWrapper(kChannels); | |
402 | |
403 search_region->SetChannelData(0, ch_0); | |
404 search_region->SetChannelData(1, ch_1); | |
405 search_region->set_frames(kFramesInSearchRegion); | |
406 ASSERT_EQ(kFramesInSearchRegion, search_region->frames()); | |
407 | |
408 const int kFramePerBlock = 4; | |
409 float target_0[] = {1.0, 1.0, 1.0, 0.0}; | |
410 float target_1[] = {0.0, 1.0, 0.1, 1.0}; | |
411 ASSERT_EQ(sizeof(target_0), sizeof(target_1)); | |
412 ASSERT_EQ(static_cast<size_t>(kFramePerBlock), | |
413 sizeof(target_0) / sizeof(*target_0)); | |
414 | |
415 scoped_ptr<AudioBus> target = AudioBus::CreateWrapper(2); | |
416 target->SetChannelData(0, target_0); | |
417 target->SetChannelData(1, target_1); | |
418 target->set_frames(kFramePerBlock); | |
419 ASSERT_EQ(kFramePerBlock, target->frames()); | |
420 | |
421 scoped_ptr<float[]> energy_target(new float[kChannels]); | |
422 | |
423 MultiChannelDotProduct(target.get(), 0, target.get(), 0, kFramePerBlock, | |
424 energy_target.get()); | |
425 | |
426 ASSERT_EQ(3.f, energy_target[0]); | |
427 ASSERT_EQ(2.01f, energy_target[1]); | |
428 | |
429 const int kNumCandidBlocks = kFramesInSearchRegion - (kFramePerBlock - 1); | |
430 scoped_ptr<float[]> energy_candid_blocks(new float[kNumCandidBlocks * | |
431 kChannels]); | |
432 | |
433 MultiChannelMovingWindowEnergies(search_region.get(), kFramePerBlock, | |
434 energy_candid_blocks.get()); | |
435 | |
436 ASSERT_FLOAT_EQ(0, energy_candid_blocks[0]); | |
437 ASSERT_FLOAT_EQ(0, energy_candid_blocks[2]); | |
438 ASSERT_FLOAT_EQ(1, energy_candid_blocks[4]); | |
439 ASSERT_FLOAT_EQ(2, energy_candid_blocks[6]); | |
440 ASSERT_FLOAT_EQ(3, energy_candid_blocks[8]); | |
441 ASSERT_FLOAT_EQ(3, energy_candid_blocks[10]); | |
442 ASSERT_FLOAT_EQ(2, energy_candid_blocks[12]); | |
443 ASSERT_FLOAT_EQ(1, energy_candid_blocks[14]); | |
444 ASSERT_FLOAT_EQ(0, energy_candid_blocks[16]); | |
445 | |
446 ASSERT_FLOAT_EQ(0, energy_candid_blocks[1]); | |
447 ASSERT_FLOAT_EQ(0, energy_candid_blocks[3]); | |
448 ASSERT_FLOAT_EQ(0, energy_candid_blocks[5]); | |
449 ASSERT_FLOAT_EQ(0, energy_candid_blocks[7]); | |
450 ASSERT_FLOAT_EQ(0.01, energy_candid_blocks[9]); | |
451 ASSERT_FLOAT_EQ(1.01, energy_candid_blocks[11]); | |
452 ASSERT_FLOAT_EQ(1.02, energy_candid_blocks[13]); | |
453 ASSERT_FLOAT_EQ(1.02, energy_candid_blocks[15]); | |
454 ASSERT_FLOAT_EQ(1.01, energy_candid_blocks[17]); | |
455 | |
456 // An interval which is of no effect. | |
457 interval exclude_interval = std::make_pair(-100, -10); | |
458 EXPECT_EQ(5, PartialSearch(0, kNumCandidBlocks - 1, exclude_interval, | |
459 target.get(), search_region.get(), | |
460 energy_target.get(), energy_candid_blocks.get())); | |
461 | |
462 // Exclude the the best match. | |
463 exclude_interval = std::make_pair(2, 5); | |
464 EXPECT_EQ(7, PartialSearch(0, kNumCandidBlocks - 1, exclude_interval, | |
465 target.get(), search_region.get(), | |
466 energy_target.get(), energy_candid_blocks.get())); | |
467 | |
468 // An interval which is of no effect. | |
469 exclude_interval = std::make_pair(-100, -10); | |
470 EXPECT_EQ(4, DecimatedSearch(4, exclude_interval, target.get(), | |
471 search_region.get(), energy_target.get(), | |
472 energy_candid_blocks.get())); | |
473 | |
474 EXPECT_EQ(5, OptimalIndex(search_region.get(), target.get(), | |
475 exclude_interval)); | |
476 } | |
477 | |
478 TEST_F(AudioRendererAlgorithmTest, CubicInterpolation) { | |
479 // Arbitrary coefficients. | |
480 const float kA = 0.7; | |
481 const float kB = 1.2; | |
482 const float kC = 0.8; | |
483 | |
484 float y_values[3]; | |
485 y_values[0] = kA - kB + kC; | |
486 y_values[1] = kC; | |
487 y_values[2] = kA + kB + kC; | |
488 | |
489 float extremum; | |
490 float extremum_value; | |
491 | |
492 CubicInterpol(y_values, &extremum, &extremum_value); | |
493 | |
494 float x_star = -kB / (2.f * kA); | |
495 float y_star = kA * x_star * x_star + kB * x_star + kC; | |
496 | |
497 EXPECT_FLOAT_EQ(x_star, extremum); | |
498 EXPECT_FLOAT_EQ(y_star, extremum_value); | |
499 } | |
500 | |
300 } // namespace media | 501 } // namespace media |
OLD | NEW |