1 /*
2 * Copyright (c) 2018 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
11 #include "modules/audio_processing/agc2/adaptive_digital_gain_applier.h"
12
13 #include <algorithm>
14
15 #include "common_audio/include/audio_util.h"
16 #include "modules/audio_processing/agc2/agc2_common.h"
17 #include "modules/audio_processing/agc2/vector_float_frame.h"
18 #include "modules/audio_processing/logging/apm_data_dumper.h"
19 #include "rtc_base/gunit.h"
20
21 namespace webrtc {
22 namespace {
23 // Constants used in place of estimated noise levels.
24 constexpr float kNoNoiseDbfs = -90.f;
25 constexpr float kWithNoiseDbfs = -20.f;
26 constexpr VadWithLevel::LevelAndProbability kVadSpeech(1.f, -20.f, 0.f);
27
28 // Runs gain applier and returns the applied gain in linear scale.
RunOnConstantLevel(int num_iterations,VadWithLevel::LevelAndProbability vad_data,float input_level_dbfs,AdaptiveDigitalGainApplier * gain_applier)29 float RunOnConstantLevel(int num_iterations,
30 VadWithLevel::LevelAndProbability vad_data,
31 float input_level_dbfs,
32 AdaptiveDigitalGainApplier* gain_applier) {
33 float gain_linear = 0.f;
34
35 for (int i = 0; i < num_iterations; ++i) {
36 VectorFloatFrame fake_audio(1, 1, 1.f);
37 SignalWithLevels signal_with_levels(fake_audio.float_frame_view());
38 signal_with_levels.input_level_dbfs = input_level_dbfs;
39 signal_with_levels.input_noise_level_dbfs = kNoNoiseDbfs;
40 signal_with_levels.vad_result = vad_data;
41 signal_with_levels.limiter_audio_level_dbfs = -2.f;
42 signal_with_levels.estimate_is_confident = true;
43 gain_applier->Process(signal_with_levels);
44 gain_linear = fake_audio.float_frame_view().channel(0)[0];
45 }
46 return gain_linear;
47 }
48
49 // Returns 'SignalWithLevels' for typical GainApplier behavior. Voice on, no
50 // noise, low limiter, confident level.
TestSignalWithLevel(AudioFrameView<float> float_frame)51 SignalWithLevels TestSignalWithLevel(AudioFrameView<float> float_frame) {
52 SignalWithLevels result(float_frame);
53 result.input_level_dbfs = -1;
54 result.input_noise_level_dbfs = kNoNoiseDbfs;
55 result.vad_result = kVadSpeech;
56 result.estimate_is_confident = true;
57 result.limiter_audio_level_dbfs = -2.f;
58 return result;
59 }
60
61 } // namespace
62
TEST(AutomaticGainController2AdaptiveGainApplier,GainApplierShouldNotCrash)63 TEST(AutomaticGainController2AdaptiveGainApplier, GainApplierShouldNotCrash) {
64 static_assert(
65 std::is_trivially_destructible<VadWithLevel::LevelAndProbability>::value,
66 "");
67 ApmDataDumper apm_data_dumper(0);
68 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
69
70 // Make one call with reasonable audio level values and settings.
71 VectorFloatFrame fake_audio(2, 480, 10000.f);
72 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
73 signal_with_level.input_level_dbfs = -5.0;
74 gain_applier.Process(signal_with_level);
75 }
76
77 // Check that the output is -kHeadroom dBFS.
TEST(AutomaticGainController2AdaptiveGainApplier,TargetLevelIsReached)78 TEST(AutomaticGainController2AdaptiveGainApplier, TargetLevelIsReached) {
79 ApmDataDumper apm_data_dumper(0);
80 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
81
82 constexpr float initial_level_dbfs = -5.f;
83
84 const float applied_gain =
85 RunOnConstantLevel(200, kVadSpeech, initial_level_dbfs, &gain_applier);
86
87 EXPECT_NEAR(applied_gain, DbToRatio(-kHeadroomDbfs - initial_level_dbfs),
88 0.1f);
89 }
90
91 // Check that the output is -kHeadroom dBFS
TEST(AutomaticGainController2AdaptiveGainApplier,GainApproachesMaxGain)92 TEST(AutomaticGainController2AdaptiveGainApplier, GainApproachesMaxGain) {
93 ApmDataDumper apm_data_dumper(0);
94 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
95
96 constexpr float initial_level_dbfs = -kHeadroomDbfs - kMaxGainDb - 10.f;
97 // A few extra frames for safety.
98 constexpr int kNumFramesToAdapt =
99 static_cast<int>(kMaxGainDb / kMaxGainChangePerFrameDb) + 10;
100
101 const float applied_gain = RunOnConstantLevel(
102 kNumFramesToAdapt, kVadSpeech, initial_level_dbfs, &gain_applier);
103 EXPECT_NEAR(applied_gain, DbToRatio(kMaxGainDb), 0.1f);
104
105 const float applied_gain_db = 20.f * std::log10(applied_gain);
106 EXPECT_NEAR(applied_gain_db, kMaxGainDb, 0.1f);
107 }
108
TEST(AutomaticGainController2AdaptiveGainApplier,GainDoesNotChangeFast)109 TEST(AutomaticGainController2AdaptiveGainApplier, GainDoesNotChangeFast) {
110 ApmDataDumper apm_data_dumper(0);
111 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
112
113 constexpr float initial_level_dbfs = -25.f;
114 // A few extra frames for safety.
115 constexpr int kNumFramesToAdapt =
116 static_cast<int>(initial_level_dbfs / kMaxGainChangePerFrameDb) + 10;
117
118 const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb);
119
120 float last_gain_linear = 1.f;
121 for (int i = 0; i < kNumFramesToAdapt; ++i) {
122 SCOPED_TRACE(i);
123 VectorFloatFrame fake_audio(1, 1, 1.f);
124 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
125 signal_with_level.input_level_dbfs = initial_level_dbfs;
126 gain_applier.Process(signal_with_level);
127 float current_gain_linear = fake_audio.float_frame_view().channel(0)[0];
128 EXPECT_LE(std::abs(current_gain_linear - last_gain_linear),
129 kMaxChangePerFrameLinear);
130 last_gain_linear = current_gain_linear;
131 }
132
133 // Check that the same is true when gain decreases as well.
134 for (int i = 0; i < kNumFramesToAdapt; ++i) {
135 SCOPED_TRACE(i);
136 VectorFloatFrame fake_audio(1, 1, 1.f);
137 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
138 signal_with_level.input_level_dbfs = 0.f;
139 gain_applier.Process(signal_with_level);
140 float current_gain_linear = fake_audio.float_frame_view().channel(0)[0];
141 EXPECT_LE(std::abs(current_gain_linear - last_gain_linear),
142 kMaxChangePerFrameLinear);
143 last_gain_linear = current_gain_linear;
144 }
145 }
146
TEST(AutomaticGainController2AdaptiveGainApplier,GainIsRampedInAFrame)147 TEST(AutomaticGainController2AdaptiveGainApplier, GainIsRampedInAFrame) {
148 ApmDataDumper apm_data_dumper(0);
149 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
150
151 constexpr float initial_level_dbfs = -25.f;
152 constexpr int num_samples = 480;
153
154 VectorFloatFrame fake_audio(1, num_samples, 1.f);
155 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
156 signal_with_level.input_level_dbfs = initial_level_dbfs;
157 gain_applier.Process(signal_with_level);
158 float maximal_difference = 0.f;
159 float current_value = 1.f * DbToRatio(kInitialAdaptiveDigitalGainDb);
160 for (const auto& x : fake_audio.float_frame_view().channel(0)) {
161 const float difference = std::abs(x - current_value);
162 maximal_difference = std::max(maximal_difference, difference);
163 current_value = x;
164 }
165
166 const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb);
167 const float kMaxChangePerSample = kMaxChangePerFrameLinear / num_samples;
168
169 EXPECT_LE(maximal_difference, kMaxChangePerSample);
170 }
171
TEST(AutomaticGainController2AdaptiveGainApplier,NoiseLimitsGain)172 TEST(AutomaticGainController2AdaptiveGainApplier, NoiseLimitsGain) {
173 ApmDataDumper apm_data_dumper(0);
174 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
175
176 constexpr float initial_level_dbfs = -25.f;
177 constexpr int num_samples = 480;
178 constexpr int num_initial_frames =
179 kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb;
180 constexpr int num_frames = 50;
181
182 ASSERT_GT(kWithNoiseDbfs, kMaxNoiseLevelDbfs) << "kWithNoiseDbfs is too low";
183
184 for (int i = 0; i < num_initial_frames + num_frames; ++i) {
185 VectorFloatFrame fake_audio(1, num_samples, 1.f);
186 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
187 signal_with_level.input_level_dbfs = initial_level_dbfs;
188 signal_with_level.input_noise_level_dbfs = kWithNoiseDbfs;
189 gain_applier.Process(signal_with_level);
190
191 // Wait so that the adaptive gain applier has time to lower the gain.
192 if (i > num_initial_frames) {
193 const float maximal_ratio =
194 *std::max_element(fake_audio.float_frame_view().channel(0).begin(),
195 fake_audio.float_frame_view().channel(0).end());
196
197 EXPECT_NEAR(maximal_ratio, 1.f, 0.001f);
198 }
199 }
200 }
201
TEST(AutomaticGainController2GainApplier,CanHandlePositiveSpeechLevels)202 TEST(AutomaticGainController2GainApplier, CanHandlePositiveSpeechLevels) {
203 ApmDataDumper apm_data_dumper(0);
204 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
205
206 // Make one call with positive audio level values and settings.
207 VectorFloatFrame fake_audio(2, 480, 10000.f);
208 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
209 signal_with_level.input_level_dbfs = 5.0f;
210 gain_applier.Process(signal_with_level);
211 }
212
TEST(AutomaticGainController2GainApplier,AudioLevelLimitsGain)213 TEST(AutomaticGainController2GainApplier, AudioLevelLimitsGain) {
214 ApmDataDumper apm_data_dumper(0);
215 AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper);
216
217 constexpr float initial_level_dbfs = -25.f;
218 constexpr int num_samples = 480;
219 constexpr int num_initial_frames =
220 kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb;
221 constexpr int num_frames = 50;
222
223 ASSERT_GT(kWithNoiseDbfs, kMaxNoiseLevelDbfs) << "kWithNoiseDbfs is too low";
224
225 for (int i = 0; i < num_initial_frames + num_frames; ++i) {
226 VectorFloatFrame fake_audio(1, num_samples, 1.f);
227 auto signal_with_level = TestSignalWithLevel(fake_audio.float_frame_view());
228 signal_with_level.input_level_dbfs = initial_level_dbfs;
229 signal_with_level.limiter_audio_level_dbfs = 1.f;
230 signal_with_level.estimate_is_confident = false;
231 gain_applier.Process(signal_with_level);
232
233 // Wait so that the adaptive gain applier has time to lower the gain.
234 if (i > num_initial_frames) {
235 const float maximal_ratio =
236 *std::max_element(fake_audio.float_frame_view().channel(0).begin(),
237 fake_audio.float_frame_view().channel(0).end());
238
239 EXPECT_NEAR(maximal_ratio, 1.f, 0.001f);
240 }
241 }
242 }
243 } // namespace webrtc
244