• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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