Chromium Code Reviews| 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 |