| 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
|
|
|