| Index: webrtc/modules/audio_processing/repetition_detector_unittest.cc
|
| diff --git a/webrtc/modules/audio_processing/repetition_detector_unittest.cc b/webrtc/modules/audio_processing/repetition_detector_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9b247558330391a60c5f251275f56a756c78a6b7
|
| --- /dev/null
|
| +++ b/webrtc/modules/audio_processing/repetition_detector_unittest.cc
|
| @@ -0,0 +1,379 @@
|
| +/*
|
| + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license
|
| + * that can be found in the LICENSE file in the root of the source
|
| + * tree. An additional intellectual property rights grant can be found
|
| + * in the file PATENTS. All contributing project authors may
|
| + * be found in the AUTHORS file in the root of the source tree.
|
| + */
|
| +
|
| +#include <map>
|
| +
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "webrtc/base/arraysize.h"
|
| +#include "webrtc/base/scoped_ptr.h"
|
| +#include "webrtc/modules/audio_processing/repetition_detector.h"
|
| +#include "webrtc/modules/remote_bitrate_estimator/test/random.h"
|
| +
|
| +namespace webrtc {
|
| +
|
| +class RepetitionDetectorForTest : public RepetitionDetector {
|
| + public:
|
| + int GetCount(int id) {
|
| + auto it = counters_.find(id);
|
| + if (it == counters_.end()) {
|
| + return 0;
|
| + }
|
| + return counters_[id];
|
| + }
|
| +
|
| + void ResetCounters() {
|
| + for (auto& item : counters_) {
|
| + item.second = 0;
|
| + }
|
| + }
|
| +
|
| + void ResetRepetitionPattern(const RepetitionDetector::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]++;
|
| + }
|
| +
|
| + std::map<int, size_t> counters_;
|
| +};
|
| +
|
| +class RepetitionDetectorTest : 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.
|
| + rtc::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 RepetitionDetector::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:
|
| + RepetitionDetectorForTest detector_;
|
| +};
|
| +
|
| +TEST_F(RepetitionDetectorTest, Basic) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, StereoOutOfSync) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, IncompletePattern) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, PatternLongerThanFrame) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, TwoPatterns) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, MaxFramesShorterThanInput) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, NestedPatterns) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, NotFullLengthPattern) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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(RepetitionDetectorTest, ZerosCountOrNot) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const RepetitionDetector::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 RepetitionDetector.
|
| +TEST_F(RepetitionDetectorTest, 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];
|
| + Random random(0x12345678);
|
| + size_t idx = 0;
|
| + for (; idx < kPreSamples + kRepSamples + kSkipSamples; ++idx) {
|
| + test_signal[idx] = random.Rand();
|
| + }
|
| + for (; idx < kSamples; ++idx) {
|
| + test_signal[idx] = test_signal[idx - kSkipSamples];
|
| + }
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + // Partial zero will count.
|
| + {0, 0}, // 10 ms look back
|
| + {1, 1} // 100 ms look back
|
| + };
|
| + Verify(kExpectedCounts, arraysize(kExpectedCounts), test_signal, kSamples,
|
| + kSampleRateHz);
|
| +}
|
| +
|
| +} // namespace webrtc
|
|
|