blob: 258832acf5cacb49dfb081601e45b849d656d84b [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;
Alessio Bazzica1e2542f2018-11-13 14:44:15 +010055 config.adaptive_digital.enabled = false;
56 config.fixed_digital.gain_db = fixed_gain_db;
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +010057 // 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.
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100116 config.fixed_digital.gain_db = -5.f;
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200117 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 Bazzica1e2542f2018-11-13 14:44:15 +0100121 config.fixed_digital.gain_db = fixed_gain_db;
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200122 EXPECT_TRUE(GainController2::Validate(config));
123 gain_controller2->ApplyConfig(config);
124 }
alessiob3ec96df2017-05-22 06:57:06 -0700125}
126
127TEST(GainController2, ToString) {
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100128 // Tests GainController2::ToString(). Only test the enabled property.
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200129 AudioProcessing::Config::GainController2 config;
alessiob3ec96df2017-05-22 06:57:06 -0700130
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200131 config.enabled = false;
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100132 EXPECT_EQ("{enabled: false", GainController2::ToString(config).substr(0, 15));
alessiob3ec96df2017-05-22 06:57:06 -0700133
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200134 config.enabled = true;
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100135 EXPECT_EQ("{enabled: true", GainController2::ToString(config).substr(0, 14));
alessiob3ec96df2017-05-22 06:57:06 -0700136}
137
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100138TEST(GainController2FixedDigital, GainShouldChangeOnSetGain) {
139 constexpr float kInputLevel = 1000.f;
140 constexpr size_t kNumFrames = 5;
141 constexpr size_t kSampleRateHz = 8000;
142 constexpr float kGain0Db = 0.f;
143 constexpr float kGain20Db = 20.f;
Alessio Bazzica270f7b52017-10-13 11:05:17 +0200144
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100145 auto agc2_fixed = CreateAgc2FixedDigitalMode(kGain0Db, kSampleRateHz);
146
147 // Signal level is unchanged with 0 db gain.
148 EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel,
149 kNumFrames, kSampleRateHz),
150 kInputLevel);
151
152 // +20 db should increase signal by a factor of 10.
153 agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGain20Db));
154 EXPECT_FLOAT_EQ(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel,
155 kNumFrames, kSampleRateHz),
156 kInputLevel * 10);
alessiob3ec96df2017-05-22 06:57:06 -0700157}
158
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100159TEST(GainController2FixedDigital, ChangeFixedGainShouldBeFastAndTimeInvariant) {
160 // Number of frames required for the fixed gain controller to adapt on the
161 // input signal when the gain changes.
162 constexpr size_t kNumFrames = 5;
Alex Loiko5e784612018-11-01 14:51:56 +0100163
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100164 constexpr float kInputLevel = 1000.f;
165 constexpr size_t kSampleRateHz = 8000;
166 constexpr float kGainDbLow = 0.f;
167 constexpr float kGainDbHigh = 25.f;
168 static_assert(kGainDbLow < kGainDbHigh, "");
Alex Loiko5e784612018-11-01 14:51:56 +0100169
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100170 auto agc2_fixed = CreateAgc2FixedDigitalMode(kGainDbLow, kSampleRateHz);
171
172 // Start with a lower gain.
173 const float output_level_pre = RunAgc2WithConstantInput(
174 agc2_fixed.get(), kInputLevel, kNumFrames, kSampleRateHz);
175
176 // Increase gain.
177 agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGainDbHigh));
178 static_cast<void>(RunAgc2WithConstantInput(agc2_fixed.get(), kInputLevel,
179 kNumFrames, kSampleRateHz));
180
181 // Back to the lower gain.
182 agc2_fixed->ApplyConfig(CreateAgc2FixedDigitalModeConfig(kGainDbLow));
183 const float output_level_post = RunAgc2WithConstantInput(
184 agc2_fixed.get(), kInputLevel, kNumFrames, kSampleRateHz);
185
186 EXPECT_EQ(output_level_pre, output_level_post);
187}
188
189struct FixedDigitalTestParams {
190 FixedDigitalTestParams(float gain_db_min,
191 float gain_db_max,
192 size_t sample_rate,
193 bool saturation_expected)
194 : gain_db_min(gain_db_min),
195 gain_db_max(gain_db_max),
196 sample_rate(sample_rate),
197 saturation_expected(saturation_expected) {}
198 float gain_db_min;
199 float gain_db_max;
200 size_t sample_rate;
201 bool saturation_expected;
202};
203
204class FixedDigitalTest
205 : public testing::Test,
206 public testing::WithParamInterface<FixedDigitalTestParams> {};
207
208TEST_P(FixedDigitalTest, CheckSaturationBehaviorWithLimiter) {
209 const float kInputLevel = 32767.f;
210 const size_t kNumFrames = 5;
211
212 const auto params = GetParam();
213
214 const auto gains_db =
215 test::LinSpace(params.gain_db_min, params.gain_db_max, 10);
216 for (const auto gain_db : gains_db) {
217 SCOPED_TRACE(std::to_string(gain_db));
218 auto agc2_fixed = CreateAgc2FixedDigitalMode(gain_db, params.sample_rate);
219 const float processed_sample = RunAgc2WithConstantInput(
220 agc2_fixed.get(), kInputLevel, kNumFrames, params.sample_rate);
221 if (params.saturation_expected) {
222 EXPECT_FLOAT_EQ(processed_sample, 32767.f);
223 } else {
224 EXPECT_LT(processed_sample, 32767.f);
225 }
Alex Loiko5e784612018-11-01 14:51:56 +0100226 }
Alex Loiko5e784612018-11-01 14:51:56 +0100227}
228
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100229static_assert(test::kLimiterMaxInputLevelDbFs < 10, "");
Mirko Bonadeic84f6612019-01-31 12:20:57 +0100230INSTANTIATE_TEST_SUITE_P(
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100231 GainController2,
232 FixedDigitalTest,
233 ::testing::Values(
234 // When gain < |test::kLimiterMaxInputLevelDbFs|, the limiter will not
235 // saturate the signal (at any sample rate).
236 FixedDigitalTestParams(0.1f,
237 test::kLimiterMaxInputLevelDbFs - 0.01f,
238 8000,
239 false),
240 FixedDigitalTestParams(0.1,
241 test::kLimiterMaxInputLevelDbFs - 0.01f,
242 48000,
243 false),
244 // When gain > |test::kLimiterMaxInputLevelDbFs|, the limiter will
245 // saturate the signal (at any sample rate).
246 FixedDigitalTestParams(test::kLimiterMaxInputLevelDbFs + 0.01f,
247 10.f,
248 8000,
249 true),
250 FixedDigitalTestParams(test::kLimiterMaxInputLevelDbFs + 0.01f,
251 10.f,
252 48000,
253 true)));
254
Alex Loiko5e784612018-11-01 14:51:56 +0100255TEST(GainController2, UsageSaturationMargin) {
256 GainController2 gain_controller2;
257 gain_controller2.Initialize(AudioProcessing::kSampleRate48kHz);
258
259 AudioProcessing::Config::GainController2 config;
260 // Check that samples are not amplified as much when extra margin is
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100261 // high. They should not be amplified at all, but only after convergence. GC2
262 // starts with a gain, and it takes time until it's down to 0 dB.
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100263 config.fixed_digital.gain_db = 0.f;
Alessio Bazzica8da7b352018-11-21 10:50:58 +0100264 config.adaptive_digital.enabled = true;
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100265 config.adaptive_digital.extra_saturation_margin_db = 50.f;
Alex Loiko5e784612018-11-01 14:51:56 +0100266 gain_controller2.ApplyConfig(config);
267
268 EXPECT_LT(GainAfterProcessingFile(&gain_controller2), 2.f);
269}
270
271TEST(GainController2, UsageNoSaturationMargin) {
272 GainController2 gain_controller2;
273 gain_controller2.Initialize(AudioProcessing::kSampleRate48kHz);
274
275 AudioProcessing::Config::GainController2 config;
276 // Check that some gain is applied if there is no margin.
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100277 config.fixed_digital.gain_db = 0.f;
Alessio Bazzica8da7b352018-11-21 10:50:58 +0100278 config.adaptive_digital.enabled = true;
Alessio Bazzica1e2542f2018-11-13 14:44:15 +0100279 config.adaptive_digital.extra_saturation_margin_db = 0.f;
Alex Loiko5e784612018-11-01 14:51:56 +0100280 gain_controller2.ApplyConfig(config);
281
282 EXPECT_GT(GainAfterProcessingFile(&gain_controller2), 2.f);
283}
Alessio Bazzica3e4c77f2018-11-01 21:31:38 +0100284
alessiob3ec96df2017-05-22 06:57:06 -0700285} // namespace test
286} // namespace webrtc