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