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..cdb49b2f1a4143b34c4712434c724359b24c9e53 |
--- /dev/null |
+++ b/content/renderer/media/audio_repetition_detector_unittest.cc |
@@ -0,0 +1,353 @@ |
+// 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/bind.h" |
+#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 { |
+ |
+namespace { |
+const int kDefaultMinLengthMs = 1; |
+const size_t kDefaultMaxFrames = 480; // 10 ms * 48 kHz |
+ |
+// Sample rate used in many tests. We choose a special sample rate in order to |
+// make the test signal obvious. |
+const int kSampleRateHz = 1000; |
+ |
+} |
+ |
+class AudioRepetitionDetectorForTest : public AudioRepetitionDetector { |
+ public: |
+ AudioRepetitionDetectorForTest(int min_length_ms, size_t max_frames, |
+ const int* look_back_times, |
+ size_t num_look_back) |
+ : AudioRepetitionDetector( |
+ min_length_ms, max_frames, |
+ std::vector<int>(look_back_times, look_back_times + num_look_back), |
+ base::Bind(&AudioRepetitionDetectorForTest::OnRepetitionDetected, |
+ base::Unretained(this))) { |
+ } |
+ |
+ int GetCount(int look_back_ms) const { |
+ auto it = counters_.find(look_back_ms); |
+ return it == counters_.end() ? 0 : it->second; |
+ } |
+ |
+ void ResetCounters() { |
+ counters_.clear(); |
+ } |
+ |
+ private: |
+ void OnRepetitionDetected(int look_back_ms) { |
+ auto it = counters_.find(look_back_ms); |
+ if (it == counters_.end()) { |
+ counters_.insert(std::pair<int, size_t>(look_back_ms, 1)); |
+ return; |
+ } |
+ it->second++; |
+ } |
+ |
+ std::map<int, size_t> counters_; |
+}; |
+ |
+class AudioRepetitionDetectorTest : public ::testing::Test { |
+ public: |
+ AudioRepetitionDetectorTest() |
+ : detector_(nullptr) { |
+ } |
+ |
+ protected: |
+ struct ExpectedCount { |
+ int look_back_ms; |
+ 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); |
+ for (size_t idx = 0; idx < num_patterns; ++idx) { |
+ const int look_back_ms = expected_counts[idx].look_back_ms; |
+ EXPECT_EQ(expected_counts[idx].count, detector_->GetCount(look_back_ms)) |
+ << "Repetition with look back " |
+ << look_back_ms |
+ << " ms counted wrong."; |
+ } |
+ } |
+ |
+ void VerifyStereo(const ExpectedCount* expected_counts, size_t num_patterns, |
+ const float* tester, size_t num_frames, |
+ int sample_rate_hz) { |
+ static 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 SetDetector(int min_length_ms, size_t max_frames, |
+ const int* look_back_times, size_t num_look_back) { |
+ detector_.reset(new AudioRepetitionDetectorForTest(min_length_ms, |
+ max_frames, |
+ look_back_times, |
+ num_look_back)); |
+ } |
+ |
+ void ResetCounters() { |
+ detector_->ResetCounters(); |
+ } |
+ |
+ private: |
+ scoped_ptr<AudioRepetitionDetectorForTest> detector_; |
+}; |
+ |
+TEST_F(AudioRepetitionDetectorTest, Basic) { |
+ // Check that one look back time will registered only once. |
+ const int kLookbackTimes[] = {3, 3, 3, 3}; |
+ |
+ const float kTestSignal[] = {1, 2, 3, 1, 2, 3}; |
+ const ExpectedCount kExpectedCounts_1[] = { |
+ {3, 1} |
+ }; |
+ const ExpectedCount kExpectedCounts_2[] = { |
+ {3, 1} |
+ }; |
+ |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ 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) { |
+ const int kLookbackTimes[] = {3}; |
+ const float kTestSignal[] = { |
+ 1, 1, |
+ 2, 2, |
+ 3, 3, |
+ 1, 1, |
+ 2, 2, |
+ 3, 1}; |
+ const ExpectedCount kExpectedCounts[] = { |
+ {3, 0} |
+ }; |
+ |
+ // By default, any repetition longer than 1 ms (1 sample at 1000 Hz) will be |
+ // counted as repetition. This test needs to make it longer. |
+ SetDetector(3, kDefaultMaxFrames, kLookbackTimes, arraysize(kLookbackTimes)); |
+ Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
+ arraysize(kTestSignal) / 2, kSampleRateHz, 2); |
+} |
+ |
+TEST_F(AudioRepetitionDetectorTest, IncompletePattern) { |
+ const int kLookbackTimes[] = {3}; |
+ const float kTestSignal[] = {1, 2, 1, 2, 3, 1, 2, 3}; |
+ const ExpectedCount kExpectedCounts[] = { |
+ {3, 1}, |
+ }; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ 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 int kLookbackTimes[] = {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[] = { |
+ {6, 0}, |
+ }; |
+ const ExpectedCount kExpectedCounts_2[] = { |
+ {6, 1}, |
+ }; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ 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) { |
+ const int kLookbackTimes[] = {3, 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. |
+ {3, 1}, |
+ {4, 1} |
+ }; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ 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 int kLookbackTimes[] = {3, 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. |
+ {3, 1}, |
+ {4, 1} |
+ }; |
+ |
+ // length of kTestSignal is 11 but I set maximum frames to be 2. The detection |
+ // should still work. |
+ SetDetector(kDefaultMinLengthMs, 2, kLookbackTimes,arraysize(kLookbackTimes)); |
+ Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
+ arraysize(kTestSignal), kSampleRateHz); |
+ ResetCounters(); |
+ VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
+ arraysize(kTestSignal), kSampleRateHz); |
+} |
+ |
+TEST_F(AudioRepetitionDetectorTest, NestedPatterns) { |
+ const int kLookbackTimes[] = {6, 3}; |
+ const float kTestSignal[] = {1, 2, 3, 1, 2, 3}; |
+ const ExpectedCount kExpectedCounts_1[] = { |
+ {3, 1}, |
+ {6, 0} |
+ }; |
+ const ExpectedCount kExpectedCounts_2[] = { |
+ {3, 1}, |
+ {6, 1} |
+ }; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ 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) { |
+ const int kLookbackTimes[] = {4}; |
+ const float kTestSignal[] = {1, 2, 3, -1, 1, 2, 3, -2}; |
+ const ExpectedCount kExpectedCounts[] = { |
+ {4, 1}, |
+ }; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ Verify(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
+ arraysize(kTestSignal), kSampleRateHz); |
+ ResetCounters(); |
+ VerifyStereo(kExpectedCounts, arraysize(kExpectedCounts), kTestSignal, |
+ arraysize(kTestSignal), kSampleRateHz); |
+} |
+ |
+TEST_F(AudioRepetitionDetectorTest, ZerosCountOrNot) { |
+ const int kLookbackTimes[] = {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. |
+ {3, 0}, |
+ }; |
+ const ExpectedCount kExpectedCounts_2[] = { |
+ // Partial zero will count. |
+ {3, 1}, |
+ }; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ 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 a normal frame size |
+TEST_F(AudioRepetitionDetectorTest, NormalSignal) { |
+ const int kNormalSampleRateHz = 44100; |
+ // Let the signal be "*(4ms)-A(13ms)-*(100ms)-A", where * denotes random |
+ // samples. |
+ const size_t kPreSamples = kNormalSampleRateHz * 4 / 1000; |
+ const size_t kRepSamples = kNormalSampleRateHz * 13 / 1000; |
+ const size_t kSkipSamples = kNormalSampleRateHz * 100 / 1000; |
+ const size_t kSamples = kPreSamples + kRepSamples * 2 + kSkipSamples; |
+ |
+ const int kLookbackTimes[] = {80, 90, 100, 110, 120}; |
+ |
+ 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]; |
+ |
+ ExpectedCount expect_counts[arraysize(kLookbackTimes)]; |
+ for (size_t i = 0; i < arraysize(kLookbackTimes); ++i) { |
+ expect_counts[i].look_back_ms = kLookbackTimes[i]; |
+ expect_counts[i].count = 0; |
+ } |
+ |
+ // We only expect a repetition with 100 ms look back time. |
+ expect_counts[2].count = 1; |
+ |
+ SetDetector(kDefaultMinLengthMs, kDefaultMaxFrames, kLookbackTimes, |
+ arraysize(kLookbackTimes)); |
+ Verify(expect_counts, arraysize(expect_counts), test_signal, kSamples, |
+ kNormalSampleRateHz); |
+} |
+ |
+} // namespace content |