blob: eca9b367263402e843f57682eb8f1e9180c9c6de [file] [log] [blame]
// 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 "content/renderer/media/audio_repetition_detector.h"
#include <stddef.h>
#include <map>
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/rand_util.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.
std::unique_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:
std::unique_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, DcCountOrNot) {
const int kLookbackTimes[] = {3};
const float kDc = 1.2345f;
const float kTestSignal_1[] = {kDc, kDc, kDc, kDc, kDc, kDc};
const float kTestSignal_2[] = {kDc, 1, 2, kDc, 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