Chromium Code Reviews| Index: content/renderer/media/audio_repetition_detector_unittest.cc |
| diff --git a/content/renderer/media/audio_repetition_detector_unittest.cc b/content/renderer/media/audio_repetition_detector_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e270e63c4fe27f98cce399436b19cb0c4b2deabc |
| --- /dev/null |
| +++ b/content/renderer/media/audio_repetition_detector_unittest.cc |
| @@ -0,0 +1,377 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include <map> |
| + |
| +#include "base/macros.h" |
| +#include "base/rand_util.h" |
| +#include "content/renderer/media/audio_repetition_detector.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +namespace content { |
| + |
| +class AudioRepetitionDetectorForTest : public AudioRepetitionDetector { |
| + public: |
| + int GetCount(int id) { |
| + auto it = counters_.find(id); |
| + if (it == counters_.end()) |
| + return 0; |
| + return counters_[id]; |
|
tommi (sloooow) - chröme
2015/10/01 17:37:47
nit: this is the second time the function looks up
minyue
2015/10/01 21:25:08
good point
|
| + } |
| + |
| + void ResetCounters() { |
| + for (auto& item : counters_) |
| + item.second = 0; |
| + } |
| + |
| + void ResetRepetitionPattern(const AudioRepetitionDetector::Pattern* patterns, |
| + size_t num_patterns) { |
| + states_.clear(); |
| + RegisterRepetitionPatterns(patterns, num_patterns); |
| + } |
| + |
| + void set_max_frames(size_t max_frames) { max_frames_ = max_frames; } |
| + |
| + private: |
| + void ReportRepetition(int id) override { |
| + auto it = counters_.find(id); |
| + if (it == counters_.end()) |
| + counters_[id] = 0; |
| + counters_[id]++; |
|
tommi (sloooow) - chröme
2015/10/01 17:37:47
same thing here. three lookups in the same functio
minyue
2015/10/01 21:25:08
Done.
|
| + } |
| + |
| + std::map<int, size_t> counters_; |
| +}; |
| + |
| +class AudioRepetitionDetectorTest : public ::testing::Test { |
| + protected: |
| + struct ExpectedCount { |
| + int id_; |
| + int count_; |
| + }; |
| + |
| + // Verify if the counts on the repetition patterns match expectation after |
| + // injecting a signal. No reset on the counters |
| + void Verify(const ExpectedCount* expected_counts, size_t num_patterns, |
| + const float* tester, size_t num_frames, |
| + int sample_rate_hz, size_t channels = 1) { |
| + detector_.Detect(tester, num_frames, channels, sample_rate_hz); |
| + int id; |
| + for (size_t idx = 0; idx < num_patterns; idx++) { |
| + id = expected_counts[idx].id_; |
| + EXPECT_EQ(expected_counts[idx].count_, detector_.GetCount(id)) << |
| + "Repetition #" << id << " counted wrong."; |
| + } |
| + } |
| + |
| + void VerifyStereo(const ExpectedCount* expected_counts, size_t num_patterns, |
| + const float* tester, size_t num_frames, |
| + int sample_rate_hz) { |
| + const size_t kNumChannels = 2; |
| + |
| + // Get memory to store interleaved stereo. |
| + scoped_ptr<float[]> tester_stereo( |
| + new float[num_frames * kNumChannels]); |
| + |
| + for (size_t idx = 0; idx < num_frames; ++idx, ++tester) { |
| + for (size_t channel = 0; channel < kNumChannels; ++channel) |
| + tester_stereo[idx * kNumChannels + channel] = *tester; |
| + } |
| + |
| + Verify(expected_counts, num_patterns, tester_stereo.get(), |
| + num_frames, sample_rate_hz, kNumChannels); |
| + } |
| + |
| + void ResetRepetitionPattern(const AudioRepetitionDetector::Pattern* patterns, |
| + size_t num_patterns) { |
| + detector_.ResetRepetitionPattern(patterns, num_patterns); |
| + } |
| + |
| + void SetMaxFrames(size_t max_frames) { |
| + detector_.set_max_frames(max_frames); |
| + } |
| + |
| + void ResetCounters() { |
| + detector_.ResetCounters(); |
| + } |
| + |
| + private: |
| + AudioRepetitionDetectorForTest detector_; |
| +}; |
| + |
| +TEST_F(AudioRepetitionDetectorTest, Basic) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3} |
| + }; |
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3}; |
| + const ExpectedCount kExpectedCounts_1[] = { |
| + {0, 1} |
| + }; |
| + const ExpectedCount kExpectedCounts_2[] = { |
| + {0, 1} |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + ResetCounters(); |
| + |
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, StereoOutOfSync) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3} |
| + }; |
| + const float kTestSignal[] = { |
| + 1, 1, |
| + 2, 2, |
| + 3, 3, |
| + 1, 1, |
| + 2, 2, |
| + 3, 1}; |
| + const ExpectedCount kExpectedCounts[] = { |
| + {0, 0} |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal) / 2, kSampleRateHz, 2); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, IncompletePattern) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3}, |
| + }; |
| + const float kTestSignal[] = {1, 2, 1, 2, 3, 1, 2, 3}; |
| + const ExpectedCount kExpectedCounts[] = { |
| + {0, 1}, |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, PatternLongerThanFrame) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 6, 6}, |
| + }; |
| + const float kTestSignal_1[] = {1, 2, 3, 4, 5}; |
| + const float kTestSignal_2[] = {6, 1, 2, 3, 4, 5, 6}; |
| + const ExpectedCount kExpectedCounts_1[] = { |
| + {0, 0}, |
| + }; |
| + const ExpectedCount kExpectedCounts_2[] = { |
| + {0, 1}, |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1, |
| + arraysize(kTestSignal_1), kSampleRateHz); |
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2, |
| + arraysize(kTestSignal_2), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1, |
| + arraysize(kTestSignal_1), kSampleRateHz); |
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2, |
| + arraysize(kTestSignal_2), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, TwoPatterns) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3}, |
| + {1, 4, 4}, |
| + }; |
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4}; |
| + const ExpectedCount kExpectedCounts[] = { |
| + // 1,2,3 belongs to both patterns. |
| + {0, 1}, |
| + {1, 1} |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, MaxFramesShorterThanInput) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3}, |
| + {1, 4, 4}, |
| + }; |
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4}; |
| + const ExpectedCount kExpectedCounts[] = { |
| + // 1,2,3 belongs to both patterns. |
| + {0, 1}, |
| + {1, 1} |
| + }; |
| + |
| + // length of kTestSignal is 11 but I set maximum frames to be 2. The detection |
| + // should still work. |
| + SetMaxFrames(2); |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, NestedPatterns) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3}, |
| + {1, 6, 6}, // When a triplet repeated 3 times, this is triggered. |
| + }; |
| + const float kTestSignal[] = {1, 2, 3, 1, 2, 3}; |
| + const ExpectedCount kExpectedCounts_1[] = { |
| + {0, 1}, |
| + {1, 0} |
| + }; |
| + const ExpectedCount kExpectedCounts_2[] = { |
| + {0, 1}, |
| + {1, 1} |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, NotFullLengthPattern) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 4, 3}, |
| + }; |
| + const float kTestSignal[] = {1, 2, 3, -1, 1, 2, 3, -2}; |
| + const ExpectedCount kExpectedCounts[] = { |
| + {0, 1}, |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
| + arraysize(kTestSignal), kSampleRateHz); |
| +} |
| + |
| +TEST_F(AudioRepetitionDetectorTest, ZerosCountOrNot) { |
| + // To make the test signal most obvious, we choose a special sample rate. |
| + const int kSampleRateHz = 1000; |
| + |
| + const AudioRepetitionDetector::Pattern kRepetitionPatterns[] = { |
| + // id, look_back_ms, min_length_ms |
| + {0, 3, 3}, |
| + }; |
| + const float kTestSignal_1[] = {0, 0, 0, 0, 0, 0}; |
| + const float kTestSignal_2[] = {0, 1, 2, 0, 1, 2}; |
| + const ExpectedCount kExpectedCounts_1[] = { |
| + // Full zeros won't count. |
| + {0, 0}, |
| + }; |
| + const ExpectedCount kExpectedCounts_2[] = { |
| + // Partial zero will count. |
| + {0, 1}, |
| + }; |
| + |
| + ResetRepetitionPattern(kRepetitionPatterns, arraysize(kRepetitionPatterns)); |
| + Verify(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1, |
| + arraysize(kTestSignal_1), kSampleRateHz); |
| + Verify(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2, |
| + arraysize(kTestSignal_2), kSampleRateHz); |
| + ResetCounters(); |
| + VerifyStereo(kExpectedCounts_1, arraysize(kExpectedCounts_1), kTestSignal_1, |
| + arraysize(kTestSignal_1), kSampleRateHz); |
| + VerifyStereo(kExpectedCounts_2, arraysize(kExpectedCounts_2), kTestSignal_2, |
| + arraysize(kTestSignal_2), kSampleRateHz); |
| +} |
| + |
| +// Previous tests use short signal to test the detection algorithm, this one |
| +// tests the built-in pattern in AudioRepetitionDetector. |
| +TEST_F(AudioRepetitionDetectorTest, BuiltInPattern) { |
| + const int kSampleRateHz = 44100; |
| + // Let the signal be "*(4ms)-A(13ms)-*(100ms)-A", where * denotes random |
| + // samples. |
| + const size_t kPreSamples = kSampleRateHz * 4 / 1000; |
| + const size_t kRepSamples = kSampleRateHz * 13 / 1000; |
| + const size_t kSkipSamples = kSampleRateHz * 100 / 1000; |
| + const size_t kSamples = kPreSamples + kRepSamples * 2 + kSkipSamples; |
| + |
| + float test_signal[kSamples]; |
| + size_t idx = 0; |
| + for (; idx < kPreSamples + kRepSamples + kSkipSamples; ++idx) |
| + test_signal[idx] = static_cast<float>(base::RandDouble()); |
| + |
| + for (; idx < kSamples; ++idx) |
| + test_signal[idx] = test_signal[idx - kSkipSamples]; |
| + |
| + const ExpectedCount kExpectedCounts[] = { |
| + // Partial zero will count. |
| + {1, 0}, // 10 ms look back |
| + {2, 0}, // 20 ms look back |
| + {3, 0}, // 30 ms look back |
| + {4, 0}, // 40 ms look back |
| + {5, 0}, // 50 ms look back |
| + {6, 0}, // 60 ms look back |
| + {7, 0}, // 70 ms look back |
| + {8, 0}, // 80 ms look back |
| + {9, 0}, // 90 ms look back |
| + {10, 1}, // 100 ms look back |
| + {20, 0} // 200 ms look back |
| + }; |
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), test_signal, kSamples, |
| + kSampleRateHz); |
| +} |
| + |
| +} // namespace content |