1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
/*
* Copyright (c) 2018 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 "modules/audio_processing/agc2/vad_wrapper.h"
#include <limits>
#include <memory>
#include <tuple>
#include <utility>
#include <vector>
#include "api/audio/audio_view.h"
#include "modules/audio_processing/agc2/agc2_common.h"
#include "rtc_base/checks.h"
#include "rtc_base/gunit.h"
#include "rtc_base/numerics/safe_compare.h"
#include "test/gmock.h"
namespace webrtc {
namespace {
using ::testing::AnyNumber;
using ::testing::Return;
using ::testing::ReturnRoundRobin;
using ::testing::Truly;
constexpr int kNumFramesPerSecond = 100;
constexpr int kNoVadPeriodicReset =
kFrameDurationMs * (std::numeric_limits<int>::max() / kFrameDurationMs);
constexpr int kSampleRate8kHz = 8000;
class MockVad : public VoiceActivityDetectorWrapper::MonoVad {
public:
MOCK_METHOD(int, SampleRateHz, (), (const, override));
MOCK_METHOD(void, Reset, (), (override));
MOCK_METHOD(float, Analyze, (ArrayView<const float> frame), (override));
};
// Checks that the ctor and `Initialize()` read the sample rate of the wrapped
// VAD.
TEST(GainController2VoiceActivityDetectorWrapper, CtorAndInitReadSampleRate) {
auto vad = std::make_unique<MockVad>();
EXPECT_CALL(*vad, SampleRateHz)
.Times(1)
.WillRepeatedly(Return(kSampleRate8kHz));
EXPECT_CALL(*vad, Reset).Times(AnyNumber());
auto vad_wrapper = std::make_unique<VoiceActivityDetectorWrapper>(
kNoVadPeriodicReset, std::move(vad), kSampleRate8kHz);
}
// Creates a `VoiceActivityDetectorWrapper` injecting a mock VAD that
// repeatedly returns the next value from `speech_probabilities` and that
// restarts from the beginning when after the last element is returned.
std::unique_ptr<VoiceActivityDetectorWrapper> CreateMockVadWrapper(
int vad_reset_period_ms,
int sample_rate_hz,
const std::vector<float>& speech_probabilities,
int expected_vad_reset_calls) {
auto vad = std::make_unique<MockVad>();
EXPECT_CALL(*vad, SampleRateHz)
.Times(AnyNumber())
.WillRepeatedly(Return(sample_rate_hz));
if (expected_vad_reset_calls >= 0) {
EXPECT_CALL(*vad, Reset).Times(expected_vad_reset_calls);
}
EXPECT_CALL(*vad, Analyze)
.Times(AnyNumber())
.WillRepeatedly(ReturnRoundRobin(speech_probabilities));
return std::make_unique<VoiceActivityDetectorWrapper>(
vad_reset_period_ms, std::move(vad), kSampleRate8kHz);
}
// 10 ms mono frame.
struct FrameWithView {
// Ctor. Initializes the frame samples with `value`.
explicit FrameWithView(int sample_rate_hz)
: samples(CheckedDivExact(sample_rate_hz, kNumFramesPerSecond), 0.0f),
view(samples.data(), samples.size(), /*num_channels=*/1) {}
std::vector<float> samples;
const DeinterleavedView<const float> view;
};
// Checks that the expected speech probabilities are returned.
TEST(GainController2VoiceActivityDetectorWrapper, CheckSpeechProbabilities) {
const std::vector<float> speech_probabilities{0.709f, 0.484f, 0.882f, 0.167f,
0.44f, 0.525f, 0.858f, 0.314f,
0.653f, 0.965f, 0.413f, 0.0f};
auto vad_wrapper = CreateMockVadWrapper(kNoVadPeriodicReset, kSampleRate8kHz,
speech_probabilities,
/*expected_vad_reset_calls=*/1);
FrameWithView frame(kSampleRate8kHz);
for (int i = 0; SafeLt(i, speech_probabilities.size()); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(speech_probabilities[i], vad_wrapper->Analyze(frame.view));
}
}
// Checks that the VAD is not periodically reset.
TEST(GainController2VoiceActivityDetectorWrapper, VadNoPeriodicReset) {
constexpr int kNumFrames = 19;
auto vad_wrapper = CreateMockVadWrapper(kNoVadPeriodicReset, kSampleRate8kHz,
/*speech_probabilities=*/{1.0f},
/*expected_vad_reset_calls=*/1);
FrameWithView frame(kSampleRate8kHz);
for (int i = 0; i < kNumFrames; ++i) {
vad_wrapper->Analyze(frame.view);
}
}
class VadPeriodResetParametrization
: public ::testing::TestWithParam<std::tuple<int, int>> {
protected:
int num_frames() const { return std::get<0>(GetParam()); }
int vad_reset_period_frames() const { return std::get<1>(GetParam()); }
};
// Checks that the VAD is periodically reset with the expected period.
TEST_P(VadPeriodResetParametrization, VadPeriodicReset) {
auto vad_wrapper = CreateMockVadWrapper(
/*vad_reset_period_ms=*/vad_reset_period_frames() * kFrameDurationMs,
kSampleRate8kHz,
/*speech_probabilities=*/{1.0f},
/*expected_vad_reset_calls=*/1 +
num_frames() / vad_reset_period_frames());
FrameWithView frame(kSampleRate8kHz);
for (int i = 0; i < num_frames(); ++i) {
vad_wrapper->Analyze(frame.view);
}
}
INSTANTIATE_TEST_SUITE_P(GainController2VoiceActivityDetectorWrapper,
VadPeriodResetParametrization,
::testing::Combine(::testing::Values(1, 19, 123),
::testing::Values(2, 5, 20, 53)));
class VadResamplingParametrization
: public ::testing::TestWithParam<std::tuple<int, int>> {
protected:
int input_sample_rate_hz() const { return std::get<0>(GetParam()); }
int vad_sample_rate_hz() const { return std::get<1>(GetParam()); }
};
// Checks that regardless of the input audio sample rate, the wrapped VAD
// analyzes frames having the expected size, that is according to its internal
// sample rate.
TEST_P(VadResamplingParametrization, CheckResampledFrameSize) {
auto vad = std::make_unique<MockVad>();
EXPECT_CALL(*vad, SampleRateHz)
.Times(AnyNumber())
.WillRepeatedly(Return(vad_sample_rate_hz()));
EXPECT_CALL(*vad, Reset).Times(1);
EXPECT_CALL(*vad, Analyze(Truly([this](ArrayView<const float> frame) {
return SafeEq(frame.size(),
CheckedDivExact(vad_sample_rate_hz(), kNumFramesPerSecond));
}))).Times(1);
auto vad_wrapper = std::make_unique<VoiceActivityDetectorWrapper>(
kNoVadPeriodicReset, std::move(vad), input_sample_rate_hz());
FrameWithView frame(input_sample_rate_hz());
vad_wrapper->Analyze(frame.view);
}
INSTANTIATE_TEST_SUITE_P(
GainController2VoiceActivityDetectorWrapper,
VadResamplingParametrization,
::testing::Combine(::testing::Values(8000, 16000, 44100, 48000),
::testing::Values(6000, 8000, 12000, 16000, 24000)));
} // namespace
} // namespace webrtc
|