xref: /aosp_15_r20/external/webrtc/modules/audio_processing/agc2/interpolated_gain_curve_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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(GainController2InterpolatedGainCurve,CreateUse)37 TEST(GainController2InterpolatedGainCurve, 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(GainController2InterpolatedGainCurve,CheckValidOutput)47 TEST(GainController2InterpolatedGainCurve, 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(GainController2InterpolatedGainCurve,CheckMonotonicity)60 TEST(GainController2InterpolatedGainCurve, 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(GainController2InterpolatedGainCurve,CheckApproximation)74 TEST(GainController2InterpolatedGainCurve, 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(GainController2InterpolatedGainCurve,CheckRegionBoundaries)87 TEST(GainController2InterpolatedGainCurve, 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(GainController2InterpolatedGainCurve,CheckIdentityRegion)105 TEST(GainController2InterpolatedGainCurve, 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(GainController2InterpolatedGainCurve,CheckNoOverApproximationKnee)123 TEST(GainController2InterpolatedGainCurve, CheckNoOverApproximationKnee) {
124   constexpr size_t kNumSteps = 10;
125   InterpolatedGainCurve igc(&apm_data_dumper, "");
126 
127   const auto levels =
128       test::LinSpace(limiter.knee_start_linear() + kLevelEpsilon,
129                      limiter.limiter_start_linear(), kNumSteps);
130   for (const auto level : levels) {
131     SCOPED_TRACE(std::to_string(level));
132     // Small tolerance added (needed because comparing a float with a double).
133     EXPECT_LE(igc.LookUpGainToApply(level),
134               limiter.GetGainLinear(level) + 1e-7);
135   }
136 
137   const auto stats = igc.get_stats();
138   EXPECT_EQ(0ul, stats.look_ups_identity_region);
139   EXPECT_EQ(kNumSteps - 1, stats.look_ups_knee_region);
140   EXPECT_EQ(1ul, stats.look_ups_limiter_region);
141   EXPECT_EQ(0ul, stats.look_ups_saturation_region);
142 }
143 
TEST(GainController2InterpolatedGainCurve,CheckNoOverApproximationBeyondKnee)144 TEST(GainController2InterpolatedGainCurve, CheckNoOverApproximationBeyondKnee) {
145   constexpr size_t kNumSteps = 10;
146   InterpolatedGainCurve igc(&apm_data_dumper, "");
147 
148   const auto levels = test::LinSpace(
149       limiter.limiter_start_linear() + kLevelEpsilon,
150       limiter.max_input_level_linear() - kLevelEpsilon, kNumSteps);
151   for (const auto level : levels) {
152     SCOPED_TRACE(std::to_string(level));
153     // Small tolerance added (needed because comparing a float with a double).
154     EXPECT_LE(igc.LookUpGainToApply(level),
155               limiter.GetGainLinear(level) + 1e-7);
156   }
157 
158   const auto stats = igc.get_stats();
159   EXPECT_EQ(0ul, stats.look_ups_identity_region);
160   EXPECT_EQ(0ul, stats.look_ups_knee_region);
161   EXPECT_EQ(kNumSteps, stats.look_ups_limiter_region);
162   EXPECT_EQ(0ul, stats.look_ups_saturation_region);
163 }
164 
TEST(GainController2InterpolatedGainCurve,CheckNoOverApproximationWithSaturation)165 TEST(GainController2InterpolatedGainCurve,
166      CheckNoOverApproximationWithSaturation) {
167   constexpr size_t kNumSteps = 3;
168   InterpolatedGainCurve igc(&apm_data_dumper, "");
169 
170   const auto levels = test::LinSpace(
171       limiter.max_input_level_linear() + kLevelEpsilon,
172       limiter.max_input_level_linear() + kLevelEpsilon + 0.5, kNumSteps);
173   for (const auto level : levels) {
174     SCOPED_TRACE(std::to_string(level));
175     EXPECT_LE(igc.LookUpGainToApply(level), limiter.GetGainLinear(level));
176   }
177 
178   const auto stats = igc.get_stats();
179   EXPECT_EQ(0ul, stats.look_ups_identity_region);
180   EXPECT_EQ(0ul, stats.look_ups_knee_region);
181   EXPECT_EQ(0ul, stats.look_ups_limiter_region);
182   EXPECT_EQ(kNumSteps, stats.look_ups_saturation_region);
183 }
184 
TEST(GainController2InterpolatedGainCurve,CheckApproximationParams)185 TEST(GainController2InterpolatedGainCurve, CheckApproximationParams) {
186   test::InterpolatedParameters parameters =
187       test::ComputeInterpolatedGainCurveApproximationParams();
188 
189   InterpolatedGainCurve igc(&apm_data_dumper, "");
190 
191   for (size_t i = 0; i < kInterpolatedGainCurveTotalPoints; ++i) {
192     // The tolerance levels are chosen to account for deviations due
193     // to computing with single precision floating point numbers.
194     EXPECT_NEAR(igc.approximation_params_x_[i],
195                 parameters.computed_approximation_params_x[i], 0.9f);
196     EXPECT_NEAR(igc.approximation_params_m_[i],
197                 parameters.computed_approximation_params_m[i], 0.00001f);
198     EXPECT_NEAR(igc.approximation_params_q_[i],
199                 parameters.computed_approximation_params_q[i], 0.001f);
200   }
201 }
202 
203 }  // namespace webrtc
204