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/fixed_digital_level_estimator.h"
12
13 #include <limits>
14
15 #include "common_audio/include/audio_util.h"
16 #include "modules/audio_processing/agc2/agc2_common.h"
17 #include "modules/audio_processing/agc2/agc2_testing_common.h"
18 #include "modules/audio_processing/agc2/vector_float_frame.h"
19 #include "modules/audio_processing/logging/apm_data_dumper.h"
20 #include "rtc_base/gunit.h"
21
22 namespace webrtc {
23 namespace {
24
25 constexpr float kInputLevel = 10000.f;
26
27 // Run audio at specified settings through the level estimator, and
28 // verify that the output level falls within the bounds.
TestLevelEstimator(int sample_rate_hz,int num_channels,float input_level_linear_scale,float expected_min,float expected_max)29 void TestLevelEstimator(int sample_rate_hz,
30 int num_channels,
31 float input_level_linear_scale,
32 float expected_min,
33 float expected_max) {
34 ApmDataDumper apm_data_dumper(0);
35 FixedDigitalLevelEstimator level_estimator(sample_rate_hz, &apm_data_dumper);
36
37 const VectorFloatFrame vectors_with_float_frame(
38 num_channels, rtc::CheckedDivExact(sample_rate_hz, 100),
39 input_level_linear_scale);
40
41 for (int i = 0; i < 500; ++i) {
42 const auto level = level_estimator.ComputeLevel(
43 vectors_with_float_frame.float_frame_view());
44
45 // Give the estimator some time to ramp up.
46 if (i < 50) {
47 continue;
48 }
49
50 for (const auto& x : level) {
51 EXPECT_LE(expected_min, x);
52 EXPECT_LE(x, expected_max);
53 }
54 }
55 }
56
57 // Returns time it takes for the level estimator to decrease its level
58 // estimate by 'level_reduction_db'.
TimeMsToDecreaseLevel(int sample_rate_hz,int num_channels,float input_level_db,float level_reduction_db)59 float TimeMsToDecreaseLevel(int sample_rate_hz,
60 int num_channels,
61 float input_level_db,
62 float level_reduction_db) {
63 const float input_level = DbfsToFloatS16(input_level_db);
64 RTC_DCHECK_GT(level_reduction_db, 0);
65
66 const VectorFloatFrame vectors_with_float_frame(
67 num_channels, rtc::CheckedDivExact(sample_rate_hz, 100), input_level);
68
69 ApmDataDumper apm_data_dumper(0);
70 FixedDigitalLevelEstimator level_estimator(sample_rate_hz, &apm_data_dumper);
71
72 // Give the LevelEstimator plenty of time to ramp up and stabilize
73 float last_level = 0.f;
74 for (int i = 0; i < 500; ++i) {
75 const auto level_envelope = level_estimator.ComputeLevel(
76 vectors_with_float_frame.float_frame_view());
77 last_level = *level_envelope.rbegin();
78 }
79
80 // Set input to 0.
81 VectorFloatFrame vectors_with_zero_float_frame(
82 num_channels, rtc::CheckedDivExact(sample_rate_hz, 100), 0);
83
84 const float reduced_level_linear =
85 DbfsToFloatS16(input_level_db - level_reduction_db);
86 int sub_frames_until_level_reduction = 0;
87 while (last_level > reduced_level_linear) {
88 const auto level_envelope = level_estimator.ComputeLevel(
89 vectors_with_zero_float_frame.float_frame_view());
90 for (const auto& v : level_envelope) {
91 EXPECT_LT(v, last_level);
92 sub_frames_until_level_reduction++;
93 last_level = v;
94 if (last_level <= reduced_level_linear) {
95 break;
96 }
97 }
98 }
99 return static_cast<float>(sub_frames_until_level_reduction) *
100 kFrameDurationMs / kSubFramesInFrame;
101 }
102 } // namespace
103
TEST(GainController2FixedDigitalLevelEstimator,EstimatorShouldNotCrash)104 TEST(GainController2FixedDigitalLevelEstimator, EstimatorShouldNotCrash) {
105 TestLevelEstimator(8000, 1, 0, std::numeric_limits<float>::lowest(),
106 std::numeric_limits<float>::max());
107 }
108
TEST(GainController2FixedDigitalLevelEstimator,EstimatorShouldEstimateConstantLevel)109 TEST(GainController2FixedDigitalLevelEstimator,
110 EstimatorShouldEstimateConstantLevel) {
111 TestLevelEstimator(10000, 1, kInputLevel, kInputLevel * 0.99,
112 kInputLevel * 1.01);
113 }
114
TEST(GainController2FixedDigitalLevelEstimator,EstimatorShouldEstimateConstantLevelForManyChannels)115 TEST(GainController2FixedDigitalLevelEstimator,
116 EstimatorShouldEstimateConstantLevelForManyChannels) {
117 constexpr size_t num_channels = 10;
118 TestLevelEstimator(20000, num_channels, kInputLevel, kInputLevel * 0.99,
119 kInputLevel * 1.01);
120 }
121
TEST(GainController2FixedDigitalLevelEstimator,TimeToDecreaseForLowLevel)122 TEST(GainController2FixedDigitalLevelEstimator, TimeToDecreaseForLowLevel) {
123 constexpr float kLevelReductionDb = 25;
124 constexpr float kInitialLowLevel = -40;
125 constexpr float kExpectedTime = kLevelReductionDb * test::kDecayMs;
126
127 const float time_to_decrease =
128 TimeMsToDecreaseLevel(22000, 1, kInitialLowLevel, kLevelReductionDb);
129
130 EXPECT_LE(kExpectedTime * 0.9, time_to_decrease);
131 EXPECT_LE(time_to_decrease, kExpectedTime * 1.1);
132 }
133
TEST(GainController2FixedDigitalLevelEstimator,TimeToDecreaseForFullScaleLevel)134 TEST(GainController2FixedDigitalLevelEstimator,
135 TimeToDecreaseForFullScaleLevel) {
136 constexpr float kLevelReductionDb = 25;
137 constexpr float kExpectedTime = kLevelReductionDb * test::kDecayMs;
138
139 const float time_to_decrease =
140 TimeMsToDecreaseLevel(26000, 1, 0, kLevelReductionDb);
141
142 EXPECT_LE(kExpectedTime * 0.9, time_to_decrease);
143 EXPECT_LE(time_to_decrease, kExpectedTime * 1.1);
144 }
145
TEST(GainController2FixedDigitalLevelEstimator,TimeToDecreaseForMultipleChannels)146 TEST(GainController2FixedDigitalLevelEstimator,
147 TimeToDecreaseForMultipleChannels) {
148 constexpr float kLevelReductionDb = 25;
149 constexpr float kExpectedTime = kLevelReductionDb * test::kDecayMs;
150 constexpr size_t kNumChannels = 10;
151
152 const float time_to_decrease =
153 TimeMsToDecreaseLevel(28000, kNumChannels, 0, kLevelReductionDb);
154
155 EXPECT_LE(kExpectedTime * 0.9, time_to_decrease);
156 EXPECT_LE(time_to_decrease, kExpectedTime * 1.1);
157 }
158
159 } // namespace webrtc
160