blob: f469bc77758e13ef4296c3512d46ff3cc4f2f54d [file] [log] [blame]
alessiob3ec96df2017-05-22 06:57:06 -07001/*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Alessio Bazzica270f7b52017-10-13 11:05:17 +020011#include <algorithm>
alessiob3ec96df2017-05-22 06:57:06 -070012
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +010013#include "absl/memory/memory.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "api/array_view.h"
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +010015#include "modules/audio_processing/agc2/agc2_testing_common.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "modules/audio_processing/audio_buffer.h"
Alex Loikoe36e8bb2018-02-16 11:54:07 +010017#include "modules/audio_processing/gain_controller2.h"
Alex Loiko5e784612018-11-01 14:51:56 +010018#include "modules/audio_processing/test/audio_buffer_tools.h"
19#include "modules/audio_processing/test/bitexactness_tools.h"
Alessio Bazzica270f7b52017-10-13 11:05:17 +020020#include "rtc_base/checks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "test/gtest.h"
alessiob3ec96df2017-05-22 06:57:06 -070022
23namespace webrtc {
24namespace test {
alessiob3ec96df2017-05-22 06:57:06 -070025namespace {
26
alessiob3ec96df2017-05-22 06:57:06 -070027void SetAudioBufferSamples(float value, AudioBuffer* ab) {
Alessio Bazzica270f7b52017-10-13 11:05:17 +020028 // Sets all the samples in |ab| to |value|.
alessiob3ec96df2017-05-22 06:57:06 -070029 for (size_t k = 0; k < ab->num_channels(); ++k) {
Alessio Bazzica270f7b52017-10-13 11:05:17 +020030 std::fill(ab->channels_f()[k], ab->channels_f()[k] + ab->num_frames(),
31 value);
alessiob3ec96df2017-05-22 06:57:06 -070032 }
33}
34
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +010035float RunAgc2WithConstantInput(GainController2* agc2,
36 float input_level,
37 size_t num_frames,
38 int sample_rate) {
39 const int num_samples = rtc::CheckedDivExact(sample_rate, 100);
40 AudioBuffer ab(num_samples, 1, num_samples, 1, num_samples);
41
42 // Give time to the level estimator to converge.
43 for (size_t i = 0; i < num_frames + 1; ++i) {
44 SetAudioBufferSamples(input_level, &ab);
45 agc2->Process(&ab);
46 }
47
48 // Return the last sample from the last processed frame.
49 return ab.channels_f()[0][num_samples - 1];
50}
51
52AudioProcessing::Config::GainController2 CreateAgc2FixedDigitalModeConfig(
53 float fixed_gain_db) {
54 AudioProcessing::Config::GainController2 config;
55 config.adaptive_digital_mode = false;
56 config.fixed_gain_db = fixed_gain_db;
57 // TODO(alessiob): Check why ASSERT_TRUE() below does not compile.
58 EXPECT_TRUE(GainController2::Validate(config));
59 return config;
60}
61
62std::unique_ptr<GainController2> CreateAgc2FixedDigitalMode(
63 float fixed_gain_db,
64 size_t sample_rate_hz) {
65 auto agc2 = absl::make_unique<GainController2>();
66 agc2->ApplyConfig(CreateAgc2FixedDigitalModeConfig(fixed_gain_db));
67 agc2->Initialize(sample_rate_hz);
68 return agc2;
69}
70
71float GainAfterProcessingFile(GainController2* gain_controller) {
72 // Set up an AudioBuffer to be filled from the speech file.
73 constexpr size_t kStereo = 2u;
74 const StreamConfig capture_config(AudioProcessing::kSampleRate48kHz, kStereo,
75 false);
76 AudioBuffer ab(capture_config.num_frames(), capture_config.num_channels(),
77 capture_config.num_frames(), capture_config.num_channels(),
78 capture_config.num_frames());
79 test::InputAudioFile capture_file(
80 test::GetApmCaptureTestVectorFileName(AudioProcessing::kSampleRate48kHz));
81 std::vector<float> capture_input(capture_config.num_frames() *
82 capture_config.num_channels());
83
84 // The file should contain at least this many frames. Every iteration, we put
85 // a frame through the gain controller.
86 const int kNumFramesToProcess = 100;
87 for (int frame_no = 0; frame_no < kNumFramesToProcess; ++frame_no) {
88 ReadFloatSamplesFromStereoFile(capture_config.num_frames(),
89 capture_config.num_channels(), &capture_file,
90 capture_input);
91
92 test::CopyVectorToAudioBuffer(capture_config, capture_input, &ab);
93 gain_controller->Process(&ab);
94 }
95
96 // Send in a last frame with values constant 1 (It's low enough to detect high
97 // gain, and for ease of computation). The applied gain is the result.
98 constexpr float sample_value = 1.f;
99 SetAudioBufferSamples(sample_value, &ab);
100 gain_controller->Process(&ab);
101 return ab.channels_f()[0][0];
102}
103
alessiob3ec96df2017-05-22 06:57:06 -0700104} // namespace
105
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200106TEST(GainController2, CreateApplyConfig) {
107 // Instances GainController2 and applies different configurations.
108 std::unique_ptr<GainController2> gain_controller2(new GainController2());
109
110 // Check that the default config is valid.
111 AudioProcessing::Config::GainController2 config;
112 EXPECT_TRUE(GainController2::Validate(config));
113 gain_controller2->ApplyConfig(config);
114
115 // Check that attenuation is not allowed.
116 config.fixed_gain_db = -5.f;
117 EXPECT_FALSE(GainController2::Validate(config));
118
119 // Check that valid configurations are applied.
Alex Loiko20f60f02018-11-12 12:09:57 +0100120 for (const float& fixed_gain_db : {0.f, 5.f, 10.f, 40.f}) {
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200121 config.fixed_gain_db = fixed_gain_db;
122 EXPECT_TRUE(GainController2::Validate(config));
123 gain_controller2->ApplyConfig(config);
124 }
alessiob3ec96df2017-05-22 06:57:06 -0700125}
126
127TEST(GainController2, ToString) {
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200128 // Tests GainController2::ToString().
129 AudioProcessing::Config::GainController2 config;
130 config.fixed_gain_db = 5.f;
alessiob3ec96df2017-05-22 06:57:06 -0700131
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200132 config.enabled = false;
Alex Loiko9d2788f2018-03-29 11:02:43 +0200133 EXPECT_EQ("{enabled: false, fixed_gain_dB: 5}",
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200134 GainController2::ToString(config));
alessiob3ec96df2017-05-22 06:57:06 -0700135
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200136 config.enabled = true;
Alex Loiko9d2788f2018-03-29 11:02:43 +0200137 EXPECT_EQ("{enabled: true, fixed_gain_dB: 5}",
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200138 GainController2::ToString(config));
alessiob3ec96df2017-05-22 06:57:06 -0700139}
140
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100141TEST(GainController2FixedDigital, GainShouldChangeOnSetGain) {
142 constexpr float kInputLevel = 1000.f;
143 constexpr size_t kNumFrames = 5;
144 constexpr size_t kSampleRateHz = 8000;
145 constexpr float kGain0Db = 0.f;
146 constexpr float kGain20Db = 20.f;
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200147
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100148 auto agc2_fixed = CreateAgc2FixedDigitalMode(kGain0Db, kSampleRateHz);
149
150 // Signal level is unchanged with 0 db gain.
151 EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel,
152 kNumFrames, kSampleRateHz),
153 kInputLevel);
154
155 // +20 db should increase signal by a factor of 10.
156 agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGain20Db));
157 EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel,
158 kNumFrames, kSampleRateHz),
159 kInputLevel * 10);
alessiob3ec96df2017-05-22 06:57:06 -0700160}
161
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100162TEST(GainController2FixedDigital, ChangeFixedGainShouldBeFastAndTimeInvariant) {
163 // Number of frames required for the fixed gain controller to adapt on the
164 // input signal when the gain changes.
165 constexpr size_t kNumFrames = 5;
Alex Loiko5e784612018-11-01 14:51:56 +0100166
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100167 constexpr float kInputLevel = 1000.f;
168 constexpr size_t kSampleRateHz = 8000;
169 constexpr float kGainDbLow = 0.f;
170 constexpr float kGainDbHigh = 25.f;
171 static_assert(kGainDbLow < kGainDbHigh, "");
Alex Loiko5e784612018-11-01 14:51:56 +0100172
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100173 auto agc2_fixed = CreateAgc2FixedDigitalMode(kGainDbLow, kSampleRateHz);
174
175 // Start with a lower gain.
176 const float output_level_pre = RunAgc2WithConstantInput(
177 agc2_fixed.get(), kInputLevel, kNumFrames, kSampleRateHz);
178
179 // Increase gain.
180 agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGainDbHigh));
181 static_cast<void>(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel,
182 kNumFrames, kSampleRateHz));
183
184 // Back to the lower gain.
185 agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGainDbLow));
186 const float output_level_post = RunAgc2WithConstantInput(
187 agc2_fixed.get(), kInputLevel, kNumFrames, kSampleRateHz);
188
189 EXPECT_EQ(output_level_pre, output_level_post);
190}
191
192struct FixedDigitalTestParams {
193 FixedDigitalTestParams(float gain_db_min,
194 float gain_db_max,
195 size_t sample_rate,
196 bool saturation_expected)
197 : gain_db_min(gain_db_min),
198 gain_db_max(gain_db_max),
199 sample_rate(sample_rate),
200 saturation_expected(saturation_expected) {}
201 float gain_db_min;
202 float gain_db_max;
203 size_t sample_rate;
204 bool saturation_expected;
205};
206
207class FixedDigitalTest
208 : public testing::Test,
209 public testing::WithParamInterface<FixedDigitalTestParams> {};
210
211TEST_P(FixedDigitalTest, CheckSaturationBehaviorWithLimiter) {
212 const float kInputLevel = 32767.f;
213 const size_t kNumFrames = 5;
214
215 const auto params = GetParam();
216
217 const auto gains_db =
218 test::LinSpace(params.gain_db_min, params.gain_db_max, 10);
219 for (const auto gain_db : gains_db) {
220 SCOPED_TRACE(std::to_string(gain_db));
221 auto agc2_fixed = CreateAgc2FixedDigitalMode(gain_db, params.sample_rate);
222 const float processed_sample = RunAgc2WithConstantInput(
223 agc2_fixed.get(), kInputLevel, kNumFrames, params.sample_rate);
224 if (params.saturation_expected) {
225 EXPECT_FLOAT_EQ(processed_sample, 32767.f);
226 } else {
227 EXPECT_LT(processed_sample, 32767.f);
228 }
Alex Loiko5e784612018-11-01 14:51:56 +0100229 }
Alex Loiko5e784612018-11-01 14:51:56 +0100230}
231
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100232static_assert(test::kLimiterMaxInputLevelDbFs < 10, "");
233INSTANTIATE_TEST_CASE_P(
234 GainController2,
235 FixedDigitalTest,
236 ::testing::Values(
237 // When gain < |test::kLimiterMaxInputLevelDbFs|, the limiter will not
238 // saturate the signal (at any sample rate).
239 FixedDigitalTestParams(0.1f,
240 test::kLimiterMaxInputLevelDbFs - 0.01f,
241 8000,
242 false),
243 FixedDigitalTestParams(0.1,
244 test::kLimiterMaxInputLevelDbFs - 0.01f,
245 48000,
246 false),
247 // When gain > |test::kLimiterMaxInputLevelDbFs|, the limiter will
248 // saturate the signal (at any sample rate).
249 FixedDigitalTestParams(test::kLimiterMaxInputLevelDbFs + 0.01f,
250 10.f,
251 8000,
252 true),
253 FixedDigitalTestParams(test::kLimiterMaxInputLevelDbFs + 0.01f,
254 10.f,
255 48000,
256 true)));
257
Alex Loiko5e784612018-11-01 14:51:56 +0100258TEST(GainController2, UsageSaturationMargin) {
259 GainController2 gain_controller2;
260 gain_controller2.Initialize(AudioProcessing::kSampleRate48kHz);
261
262 AudioProcessing::Config::GainController2 config;
263 // Check that samples are not amplified as much when extra margin is
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100264 // high. They should not be amplified at all, but only after convergence. GC2
265 // starts with a gain, and it takes time until it's down to 0 dB.
Alex Loiko5e784612018-11-01 14:51:56 +0100266 config.extra_saturation_margin_db = 50.f;
267 config.fixed_gain_db = 0.f;
268 gain_controller2.ApplyConfig(config);
269
270 EXPECT_LT(GainAfterProcessingFile(&gain_controller2), 2.f);
271}
272
273TEST(GainController2, UsageNoSaturationMargin) {
274 GainController2 gain_controller2;
275 gain_controller2.Initialize(AudioProcessing::kSampleRate48kHz);
276
277 AudioProcessing::Config::GainController2 config;
278 // Check that some gain is applied if there is no margin.
279 config.extra_saturation_margin_db = 0.f;
280 config.fixed_gain_db = 0.f;
281 gain_controller2.ApplyConfig(config);
282
283 EXPECT_GT(GainAfterProcessingFile(&gain_controller2), 2.f);
284}
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100285
alessiob3ec96df2017-05-22 06:57:06 -0700286} // namespace test
287} // namespace webrtc