• 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/interpolated_gain_curve.h"
12 
13 #include <array>
14 #include <type_traits>
15 #include <vector>
16 
17 #include "api/array_view.h"
18 #include "common_audio/include/audio_util.h"
19 #include "modules/audio_processing/agc2/agc2_common.h"
20 #include "modules/audio_processing/agc2/compute_interpolated_gain_curve.h"
21 #include "modules/audio_processing/agc2/limiter_db_gain_curve.h"
22 #include "modules/audio_processing/logging/apm_data_dumper.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/gunit.h"
25 
26 namespace webrtc {
27 namespace {
28 
29 constexpr double kLevelEpsilon = 1e-2 * kMaxAbsFloatS16Value;
30 constexpr float kInterpolatedGainCurveTolerance = 1.f / 32768.f;
31 ApmDataDumper apm_data_dumper(0);
32 static_assert(std::is_trivially_destructible<LimiterDbGainCurve>::value, "");
33 const LimiterDbGainCurve limiter;
34 
35 }  // namespace
36 
TEST(AutomaticGainController2InterpolatedGainCurve,CreateUse)37 TEST(AutomaticGainController2InterpolatedGainCurve, CreateUse) {
38   InterpolatedGainCurve igc(&apm_data_dumper, "");
39 
40   const auto levels = test::LinSpace(
41       kLevelEpsilon, DbfsToFloatS16(limiter.max_input_level_db() + 1), 500);
42   for (const auto level : levels) {
43     EXPECT_GE(igc.LookUpGainToApply(level), 0.0f);
44   }
45 }
46 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckValidOutput)47 TEST(AutomaticGainController2InterpolatedGainCurve, CheckValidOutput) {
48   InterpolatedGainCurve igc(&apm_data_dumper, "");
49 
50   const auto levels = test::LinSpace(
51       kLevelEpsilon, limiter.max_input_level_linear() * 2.0, 500);
52   for (const auto level : levels) {
53     SCOPED_TRACE(std::to_string(level));
54     const float gain = igc.LookUpGainToApply(level);
55     EXPECT_LE(0.0f, gain);
56     EXPECT_LE(gain, 1.0f);
57   }
58 }
59 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckMonotonicity)60 TEST(AutomaticGainController2InterpolatedGainCurve, CheckMonotonicity) {
61   InterpolatedGainCurve igc(&apm_data_dumper, "");
62 
63   const auto levels = test::LinSpace(
64       kLevelEpsilon, limiter.max_input_level_linear() + kLevelEpsilon + 0.5,
65       500);
66   float prev_gain = igc.LookUpGainToApply(0.0f);
67   for (const auto level : levels) {
68     const float gain = igc.LookUpGainToApply(level);
69     EXPECT_GE(prev_gain, gain);
70     prev_gain = gain;
71   }
72 }
73 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckApproximation)74 TEST(AutomaticGainController2InterpolatedGainCurve, CheckApproximation) {
75   InterpolatedGainCurve igc(&apm_data_dumper, "");
76 
77   const auto levels = test::LinSpace(
78       kLevelEpsilon, limiter.max_input_level_linear() - kLevelEpsilon, 500);
79   for (const auto level : levels) {
80     SCOPED_TRACE(std::to_string(level));
81     EXPECT_LT(
82         std::fabs(limiter.GetGainLinear(level) - igc.LookUpGainToApply(level)),
83         kInterpolatedGainCurveTolerance);
84   }
85 }
86 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckRegionBoundaries)87 TEST(AutomaticGainController2InterpolatedGainCurve, CheckRegionBoundaries) {
88   InterpolatedGainCurve igc(&apm_data_dumper, "");
89 
90   const std::vector<double> levels{
91       {kLevelEpsilon, limiter.knee_start_linear() + kLevelEpsilon,
92        limiter.limiter_start_linear() + kLevelEpsilon,
93        limiter.max_input_level_linear() + kLevelEpsilon}};
94   for (const auto level : levels) {
95     igc.LookUpGainToApply(level);
96   }
97 
98   const auto stats = igc.get_stats();
99   EXPECT_EQ(1ul, stats.look_ups_identity_region);
100   EXPECT_EQ(1ul, stats.look_ups_knee_region);
101   EXPECT_EQ(1ul, stats.look_ups_limiter_region);
102   EXPECT_EQ(1ul, stats.look_ups_saturation_region);
103 }
104 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckIdentityRegion)105 TEST(AutomaticGainController2InterpolatedGainCurve, CheckIdentityRegion) {
106   constexpr size_t kNumSteps = 10;
107   InterpolatedGainCurve igc(&apm_data_dumper, "");
108 
109   const auto levels =
110       test::LinSpace(kLevelEpsilon, limiter.knee_start_linear(), kNumSteps);
111   for (const auto level : levels) {
112     SCOPED_TRACE(std::to_string(level));
113     EXPECT_EQ(1.0f, igc.LookUpGainToApply(level));
114   }
115 
116   const auto stats = igc.get_stats();
117   EXPECT_EQ(kNumSteps - 1, stats.look_ups_identity_region);
118   EXPECT_EQ(1ul, stats.look_ups_knee_region);
119   EXPECT_EQ(0ul, stats.look_ups_limiter_region);
120   EXPECT_EQ(0ul, stats.look_ups_saturation_region);
121 }
122 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckNoOverApproximationKnee)123 TEST(AutomaticGainController2InterpolatedGainCurve,
124      CheckNoOverApproximationKnee) {
125   constexpr size_t kNumSteps = 10;
126   InterpolatedGainCurve igc(&apm_data_dumper, "");
127 
128   const auto levels =
129       test::LinSpace(limiter.knee_start_linear() + kLevelEpsilon,
130                      limiter.limiter_start_linear(), kNumSteps);
131   for (const auto level : levels) {
132     SCOPED_TRACE(std::to_string(level));
133     // Small tolerance added (needed because comparing a float with a double).
134     EXPECT_LE(igc.LookUpGainToApply(level),
135               limiter.GetGainLinear(level) + 1e-7);
136   }
137 
138   const auto stats = igc.get_stats();
139   EXPECT_EQ(0ul, stats.look_ups_identity_region);
140   EXPECT_EQ(kNumSteps - 1, stats.look_ups_knee_region);
141   EXPECT_EQ(1ul, stats.look_ups_limiter_region);
142   EXPECT_EQ(0ul, stats.look_ups_saturation_region);
143 }
144 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckNoOverApproximationBeyondKnee)145 TEST(AutomaticGainController2InterpolatedGainCurve,
146      CheckNoOverApproximationBeyondKnee) {
147   constexpr size_t kNumSteps = 10;
148   InterpolatedGainCurve igc(&apm_data_dumper, "");
149 
150   const auto levels = test::LinSpace(
151       limiter.limiter_start_linear() + kLevelEpsilon,
152       limiter.max_input_level_linear() - kLevelEpsilon, kNumSteps);
153   for (const auto level : levels) {
154     SCOPED_TRACE(std::to_string(level));
155     // Small tolerance added (needed because comparing a float with a double).
156     EXPECT_LE(igc.LookUpGainToApply(level),
157               limiter.GetGainLinear(level) + 1e-7);
158   }
159 
160   const auto stats = igc.get_stats();
161   EXPECT_EQ(0ul, stats.look_ups_identity_region);
162   EXPECT_EQ(0ul, stats.look_ups_knee_region);
163   EXPECT_EQ(kNumSteps, stats.look_ups_limiter_region);
164   EXPECT_EQ(0ul, stats.look_ups_saturation_region);
165 }
166 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckNoOverApproximationWithSaturation)167 TEST(AutomaticGainController2InterpolatedGainCurve,
168      CheckNoOverApproximationWithSaturation) {
169   constexpr size_t kNumSteps = 3;
170   InterpolatedGainCurve igc(&apm_data_dumper, "");
171 
172   const auto levels = test::LinSpace(
173       limiter.max_input_level_linear() + kLevelEpsilon,
174       limiter.max_input_level_linear() + kLevelEpsilon + 0.5, kNumSteps);
175   for (const auto level : levels) {
176     SCOPED_TRACE(std::to_string(level));
177     EXPECT_LE(igc.LookUpGainToApply(level), limiter.GetGainLinear(level));
178   }
179 
180   const auto stats = igc.get_stats();
181   EXPECT_EQ(0ul, stats.look_ups_identity_region);
182   EXPECT_EQ(0ul, stats.look_ups_knee_region);
183   EXPECT_EQ(0ul, stats.look_ups_limiter_region);
184   EXPECT_EQ(kNumSteps, stats.look_ups_saturation_region);
185 }
186 
TEST(AutomaticGainController2InterpolatedGainCurve,CheckApproximationParams)187 TEST(AutomaticGainController2InterpolatedGainCurve, CheckApproximationParams) {
188   test::InterpolatedParameters parameters =
189       test::ComputeInterpolatedGainCurveApproximationParams();
190 
191   InterpolatedGainCurve igc(&apm_data_dumper, "");
192 
193   for (size_t i = 0; i < kInterpolatedGainCurveTotalPoints; ++i) {
194     // The tolerance levels are chosen to account for deviations due
195     // to computing with single precision floating point numbers.
196     EXPECT_NEAR(igc.approximation_params_x_[i],
197                 parameters.computed_approximation_params_x[i], 0.9f);
198     EXPECT_NEAR(igc.approximation_params_m_[i],
199                 parameters.computed_approximation_params_m[i], 0.00001f);
200     EXPECT_NEAR(igc.approximation_params_q_[i],
201                 parameters.computed_approximation_params_q[i], 0.001f);
202   }
203 }
204 
205 }  // namespace webrtc
206