Chromium Code Reviews| Index: media/filters/audio_renderer_algorithm_unittest.cc |
| diff --git a/media/filters/audio_renderer_algorithm_unittest.cc b/media/filters/audio_renderer_algorithm_unittest.cc |
| index d5119c00c2b116022f095c868bd059b7f5d67a5b..5cd3eeb01ec7e56efb6f127954e259312dbb05f3 100644 |
| --- a/media/filters/audio_renderer_algorithm_unittest.cc |
| +++ b/media/filters/audio_renderer_algorithm_unittest.cc |
| @@ -12,19 +12,49 @@ |
| #include "base/bind.h" |
| #include "base/callback.h" |
| +#include "base/memory/scoped_ptr.h" |
| #include "media/base/audio_buffer.h" |
| #include "media/base/audio_bus.h" |
| #include "media/base/buffers.h" |
| #include "media/base/channel_layout.h" |
| #include "media/base/test_helpers.h" |
| #include "media/filters/audio_renderer_algorithm.h" |
| +#include "media/filters/audio_renderer_algorithm_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| - |
| namespace media { |
| static const int kFrameSize = 250; |
| static const int kSamplesPerSecond = 3000; |
| static const SampleFormat kSampleFormat = kSampleFormatS16; |
| +static const int kOutputDurationInSec = 10; |
| + |
| +static void FillWithSquarePuleTrain( |
|
ajm
2013/07/23 18:03:28
Pule -> Pulse
turaj
2013/07/29 22:09:57
Done.
|
| + int pulse_width, int offset, int channel, AudioBus* audio_bus) { |
| + float* ch = audio_bus->channel(channel); |
| + |
| + // Fill backward from |offset| - 1 toward zero, starting with -1, alternating |
| + // between -1 and 1 every |pulse_width| samples. |
| + int pulse = -1; |
| + for (int n = offset - 1, k = 0; n >= 0; --n, ++k) { |
| + if (k >= pulse_width) { |
| + pulse = -pulse; |
| + k = 0; |
| + } |
| + ch[n] = pulse; |
| + } |
| + |
| + // Fill forward from |offset| towards the end, starting with 1, alternating |
| + // between 1 and -1 every |pulse_width| samples. |
| + pulse = 1; |
| + for (int n = offset, k = 0; n < audio_bus->frames(); ++n, ++k) { |
| + if (k >= pulse_width) { |
| + pulse = -pulse; |
| + k = 0; |
| + } |
| + ch[n] = pulse; |
| + } |
| +} |
| + |
| class AudioRendererAlgorithmTest : public testing::Test { |
| public: |
| @@ -118,7 +148,8 @@ class AudioRendererAlgorithmTest : public testing::Test { |
| void TestPlaybackRate(double playback_rate) { |
| const int kDefaultBufferSize = algorithm_.samples_per_second() / 100; |
| - const int kDefaultFramesRequested = 2 * algorithm_.samples_per_second(); |
| + const int kDefaultFramesRequested = kOutputDurationInSec * |
| + algorithm_.samples_per_second(); |
| TestPlaybackRate( |
| playback_rate, kDefaultBufferSize, kDefaultFramesRequested); |
| @@ -270,7 +301,7 @@ TEST_F(AudioRendererAlgorithmTest, FillBuffer_JumpAroundSpeeds) { |
| TEST_F(AudioRendererAlgorithmTest, FillBuffer_SmallBufferSize) { |
| Initialize(); |
| static const int kBufferSizeInFrames = 1; |
| - static const int kFramesRequested = 2 * kSamplesPerSecond; |
| + static const int kFramesRequested = kOutputDurationInSec * kSamplesPerSecond; |
| TestPlaybackRate(1.0, kBufferSizeInFrames, kFramesRequested); |
| TestPlaybackRate(0.5, kBufferSizeInFrames, kFramesRequested); |
| TestPlaybackRate(1.5, kBufferSizeInFrames, kFramesRequested); |
| @@ -297,4 +328,174 @@ TEST_F(AudioRendererAlgorithmTest, FillBuffer_HigherQualityAudio) { |
| TestPlaybackRate(1.5); |
| } |
| +TEST_F(AudioRendererAlgorithmTest, DotProduct) { |
| + const int kChannels = 3; |
| + const int kFrames = 20; |
| + const int kPulseWidth = 2; |
| + |
| + scoped_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames); |
| + scoped_ptr<AudioBus> b = AudioBus::Create(kChannels, kFrames); |
| + |
| + scoped_ptr<float[]> dot_prod(new float[kChannels]); |
| + |
| + FillWithSquarePuleTrain(kPulseWidth, 0, 0, a.get()); |
| + FillWithSquarePuleTrain(kPulseWidth, 1, 1, a.get()); |
| + FillWithSquarePuleTrain(kPulseWidth, 2, 2, a.get()); |
| + |
| + FillWithSquarePuleTrain(kPulseWidth, 0, 0, b.get()); |
| + FillWithSquarePuleTrain(kPulseWidth, 0, 1, b.get()); |
| + FillWithSquarePuleTrain(kPulseWidth, 0, 2, b.get()); |
| + |
| + MultiChannelDotProduct(a.get(), 0, b.get(), 0, kFrames, dot_prod.get()); |
| + |
| + EXPECT_FLOAT_EQ(kFrames, dot_prod[0]); |
| + EXPECT_FLOAT_EQ(0, dot_prod[1]); |
| + EXPECT_FLOAT_EQ(-kFrames, dot_prod[2]); |
| + |
| + MultiChannelDotProduct(a.get(), 4, b.get(), 8, kFrames / 2, dot_prod.get()); |
| + |
| + EXPECT_FLOAT_EQ(kFrames / 2, dot_prod[0]); |
| + EXPECT_FLOAT_EQ(0, dot_prod[1]); |
| + EXPECT_FLOAT_EQ(-kFrames / 2, dot_prod[2]); |
| +} |
| + |
| +TEST_F(AudioRendererAlgorithmTest, MovingWindowEnergy) { |
| + const int kChannels = 2; |
| + const int kFrames = 20; |
| + const int kFramesPerBlock = 3; |
| + const int kNumBlocks = kFrames - (kFramesPerBlock - 1); |
| + scoped_ptr<AudioBus> a = AudioBus::Create(kChannels, kFrames); |
| + scoped_ptr<float[]> energies(new float[kChannels * kNumBlocks]); |
| + float* ch_left = a->channel(0); |
| + float* ch_right = a->channel(1); |
| + |
| + // Fill up both channels. |
| + for (int n = 0; n < kFrames; ++n) { |
| + ch_left[n] = n; |
| + ch_right[n] = kFrames - 1 - n; |
| + } |
| + |
| + MultiChannelMovingWindowEnergies(a.get(), kFramesPerBlock, energies.get()); |
| + |
| + for (int n = 0; n < kNumBlocks; ++n) { |
| + float expected_energy = 0; |
| + for (int k = 0; k < kFramesPerBlock; ++k) |
| + expected_energy += ch_left[n + k] * ch_left[n + k]; |
| + EXPECT_FLOAT_EQ(expected_energy, energies[2 * n]); |
| + |
| + expected_energy = 0; |
| + for (int k = 0; k < kFramesPerBlock; ++k) |
| + expected_energy += ch_right[n + k] * ch_right[n + k]; |
| + EXPECT_FLOAT_EQ(expected_energy, energies[2 * n + 1]); |
| + } |
| +} |
| + |
| +TEST_F(AudioRendererAlgorithmTest, PartialAndDecimatedSearch) { |
| + const int kFramesInSearchRegion = 12; |
| + const int kChannels = 2; |
| + 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}; |
| + 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}; |
| + ASSERT_EQ(sizeof(ch_0), sizeof(ch_1)); |
| + ASSERT_EQ(static_cast<size_t>(kFramesInSearchRegion), |
| + sizeof(ch_0) / sizeof(*ch_0)); |
| + scoped_ptr<AudioBus> search_region = AudioBus::CreateWrapper(kChannels); |
| + |
| + search_region->SetChannelData(0, ch_0); |
| + search_region->SetChannelData(1, ch_1); |
| + search_region->set_frames(kFramesInSearchRegion); |
| + ASSERT_EQ(kFramesInSearchRegion, search_region->frames()); |
| + |
| + const int kFramePerBlock = 4; |
| + float target_0[] = {1.0, 1.0, 1.0, 0.0}; |
| + float target_1[] = {0.0, 1.0, 0.1, 1.0}; |
| + ASSERT_EQ(sizeof(target_0), sizeof(target_1)); |
| + ASSERT_EQ(static_cast<size_t>(kFramePerBlock), |
| + sizeof(target_0) / sizeof(*target_0)); |
| + |
| + scoped_ptr<AudioBus> target = AudioBus::CreateWrapper(2); |
| + target->SetChannelData(0, target_0); |
| + target->SetChannelData(1, target_1); |
| + target->set_frames(kFramePerBlock); |
| + ASSERT_EQ(kFramePerBlock, target->frames()); |
| + |
| + scoped_ptr<float[]> energy_target(new float[kChannels]); |
| + |
| + MultiChannelDotProduct(target.get(), 0, target.get(), 0, kFramePerBlock, |
| + energy_target.get()); |
| + |
| + ASSERT_EQ(3.f, energy_target[0]); |
| + ASSERT_EQ(2.01f, energy_target[1]); |
| + |
| + const int kNumCandidBlocks = kFramesInSearchRegion - (kFramePerBlock - 1); |
| + scoped_ptr<float[]> energy_candid_blocks(new float[kNumCandidBlocks * |
| + kChannels]); |
| + |
| + MultiChannelMovingWindowEnergies(search_region.get(), kFramePerBlock, |
| + energy_candid_blocks.get()); |
| + |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[0]); |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[2]); |
| + ASSERT_FLOAT_EQ(1, energy_candid_blocks[4]); |
| + ASSERT_FLOAT_EQ(2, energy_candid_blocks[6]); |
| + ASSERT_FLOAT_EQ(3, energy_candid_blocks[8]); |
| + ASSERT_FLOAT_EQ(3, energy_candid_blocks[10]); |
| + ASSERT_FLOAT_EQ(2, energy_candid_blocks[12]); |
| + ASSERT_FLOAT_EQ(1, energy_candid_blocks[14]); |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[16]); |
| + |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[1]); |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[3]); |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[5]); |
| + ASSERT_FLOAT_EQ(0, energy_candid_blocks[7]); |
| + ASSERT_FLOAT_EQ(0.01, energy_candid_blocks[9]); |
| + ASSERT_FLOAT_EQ(1.01, energy_candid_blocks[11]); |
| + ASSERT_FLOAT_EQ(1.02, energy_candid_blocks[13]); |
| + ASSERT_FLOAT_EQ(1.02, energy_candid_blocks[15]); |
| + ASSERT_FLOAT_EQ(1.01, energy_candid_blocks[17]); |
| + |
| + // An interval which is of no effect. |
| + interval exclude_interval = std::make_pair(-100, -10); |
| + EXPECT_EQ(5, PartialSearch(0, kNumCandidBlocks - 1, exclude_interval, |
| + target.get(), search_region.get(), |
| + energy_target.get(), energy_candid_blocks.get())); |
| + |
| + // Exclude the the best match. |
| + exclude_interval = std::make_pair(2, 5); |
| + EXPECT_EQ(7, PartialSearch(0, kNumCandidBlocks - 1, exclude_interval, |
| + target.get(), search_region.get(), |
| + energy_target.get(), energy_candid_blocks.get())); |
| + |
| + // An interval which is of no effect. |
| + exclude_interval = std::make_pair(-100, -10); |
| + EXPECT_EQ(4, DecimatedSearch(4, exclude_interval, target.get(), |
| + search_region.get(), energy_target.get(), |
| + energy_candid_blocks.get())); |
| + |
| + EXPECT_EQ(5, OptimalIndex(search_region.get(), target.get(), |
| + exclude_interval)); |
| +} |
| + |
| +TEST_F(AudioRendererAlgorithmTest, CubicInterpolation) { |
| + // Arbitrary coefficients. |
| + const float kA = 0.7; |
| + const float kB = 1.2; |
| + const float kC = 0.8; |
| + |
| + float y_values[3]; |
| + y_values[0] = kA - kB + kC; |
| + y_values[1] = kC; |
| + y_values[2] = kA + kB + kC; |
| + |
| + float extremum; |
| + float extremum_value; |
| + |
| + CubicInterpol(y_values, &extremum, &extremum_value); |
| + |
| + float x_star = -kB / (2.f * kA); |
| + float y_star = kA * x_star * x_star + kB * x_star + kC; |
| + |
| + EXPECT_FLOAT_EQ(x_star, extremum); |
| + EXPECT_FLOAT_EQ(y_star, extremum_value); |
| +} |
| + |
| } // namespace media |