| 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..1e2a38865a9a663c81b910d82c2467f1e0fa7dff
|
| --- /dev/null
|
| +++ b/content/renderer/media/audio_repetition_detector_unittest.cc
|
| @@ -0,0 +1,355 @@
|
| +// 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 look_back_ms) {
|
| + auto it = counters_.find(look_back_ms);
|
| + if (it == counters_.end())
|
| + return 0;
|
| + return it->second;
|
| + }
|
| +
|
| + void ResetCounters() {
|
| + counters_.clear();
|
| + }
|
| +
|
| + void ResetLookbackTime(const int* look_back_times, size_t num) {
|
| + states_.clear();
|
| + for (size_t i = 0; i < num; ++i) {
|
| + RegisterLookbackTime(look_back_times[i]);
|
| + }
|
| + }
|
| +
|
| + void set_max_frames(size_t max_frames) { max_frames_ = max_frames; }
|
| +
|
| + void set_min_length_ms(int min_length_ms) { min_length_ms_ = min_length_ms; }
|
| +
|
| + private:
|
| + void ReportRepetition(int look_back_ms) override {
|
| + 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 {
|
| + 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) {
|
| + 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 ResetLookbackTime(const int* look_back_times, size_t num) {
|
| + detector_.ResetLookbackTime(look_back_times, num);
|
| + }
|
| +
|
| + void SetMaxFrames(size_t max_frames) {
|
| + detector_.set_max_frames(max_frames);
|
| + }
|
| +
|
| + void SetMinLengthMs(int min_length_ms) {
|
| + detector_.set_min_length_ms(min_length_ms);
|
| + }
|
| +
|
| + 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;
|
| +
|
| + // 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}
|
| + };
|
| +
|
| + ResetLookbackTime(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) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + 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.
|
| + SetMinLengthMs(3);
|
| +
|
| + ResetLookbackTime(kLookbackTimes, arraysize(kLookbackTimes));
|
| + 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 int kLookbackTimes[] = {3};
|
| + const float kTestSignal[] = {1, 2, 1, 2, 3, 1, 2, 3};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + {3, 1},
|
| + };
|
| +
|
| + ResetLookbackTime(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},
|
| + };
|
| +
|
| + ResetLookbackTime(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) {
|
| + // 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}
|
| + };
|
| +
|
| + ResetLookbackTime(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.
|
| + SetMaxFrames(2);
|
| + ResetLookbackTime(kLookbackTimes, arraysize(kLookbackTimes));
|
| + 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 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}
|
| + };
|
| +
|
| + ResetLookbackTime(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) {
|
| + // To make the test signal most obvious, we choose a special sample rate.
|
| + const int kSampleRateHz = 1000;
|
| +
|
| + const int kLookbackTimes[] = {4};
|
| + const float kTestSignal[] = {1, 2, 3, -1, 1, 2, 3, -2};
|
| + const ExpectedCount kExpectedCounts[] = {
|
| + {4, 1},
|
| + };
|
| +
|
| + ResetLookbackTime(kLookbackTimes, arraysize(kLookbackTimes));
|
| + 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 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},
|
| + };
|
| +
|
| + ResetLookbackTime(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 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];
|
| +
|
| + // We currently track 20 look back times, 10, 20, 30, ..., 200.
|
| + ExpectedCount expect_counts[20];
|
| + for (int i = 0; i < 20; ++i) {
|
| + expect_counts[i].look_back_ms = (i + 1) * 10;
|
| + expect_counts[i].count = 0;
|
| + }
|
| +
|
| + // We only expect a repetition with 100 ms look back time.
|
| + expect_counts[9].count = 1;
|
| +
|
| + Verify(expect_counts, arraysize(expect_counts), test_signal, kSamples,
|
| + kSampleRateHz);
|
| +}
|
| +
|
| +} // namespace content
|
|
|