• 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/saturation_protector.h"
12 
13 #include <algorithm>
14 
15 #include "modules/audio_processing/agc2/agc2_common.h"
16 #include "modules/audio_processing/logging/apm_data_dumper.h"
17 #include "rtc_base/gunit.h"
18 
19 namespace webrtc {
20 namespace {
RunOnConstantLevel(int num_iterations,VadWithLevel::LevelAndProbability vad_data,float estimated_level_dbfs,SaturationProtector * saturation_protector)21 float RunOnConstantLevel(int num_iterations,
22                          VadWithLevel::LevelAndProbability vad_data,
23                          float estimated_level_dbfs,
24                          SaturationProtector* saturation_protector) {
25   float last_margin = saturation_protector->LastMargin();
26   float max_difference = 0.f;
27   for (int i = 0; i < num_iterations; ++i) {
28     saturation_protector->UpdateMargin(vad_data, estimated_level_dbfs);
29     const float new_margin = saturation_protector->LastMargin();
30     max_difference =
31         std::max(max_difference, std::abs(new_margin - last_margin));
32     last_margin = new_margin;
33     saturation_protector->DebugDumpEstimate();
34   }
35   return max_difference;
36 }
37 }  // namespace
38 
TEST(AutomaticGainController2SaturationProtector,ProtectorShouldNotCrash)39 TEST(AutomaticGainController2SaturationProtector, ProtectorShouldNotCrash) {
40   ApmDataDumper apm_data_dumper(0);
41   SaturationProtector saturation_protector(&apm_data_dumper);
42   VadWithLevel::LevelAndProbability vad_data(1.f, -20.f, -10.f);
43 
44   saturation_protector.UpdateMargin(vad_data, -20.f);
45   static_cast<void>(saturation_protector.LastMargin());
46   saturation_protector.DebugDumpEstimate();
47 }
48 
49 // Check that the estimate converges to the ratio between peaks and
50 // level estimator values after a while.
TEST(AutomaticGainController2SaturationProtector,ProtectorEstimatesCrestRatio)51 TEST(AutomaticGainController2SaturationProtector,
52      ProtectorEstimatesCrestRatio) {
53   ApmDataDumper apm_data_dumper(0);
54   SaturationProtector saturation_protector(&apm_data_dumper);
55 
56   constexpr float kPeakLevel = -20.f;
57   const float kCrestFactor = GetInitialSaturationMarginDb() + 1.f;
58   const float kSpeechLevel = kPeakLevel - kCrestFactor;
59   const float kMaxDifference =
60       0.5 * std::abs(GetInitialSaturationMarginDb() - kCrestFactor);
61 
62   static_cast<void>(RunOnConstantLevel(
63       2000, VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
64       kSpeechLevel, &saturation_protector));
65 
66   EXPECT_NEAR(
67       saturation_protector.LastMargin() - GetExtraSaturationMarginOffsetDb(),
68       kCrestFactor, kMaxDifference);
69 }
70 
TEST(AutomaticGainController2SaturationProtector,ProtectorChangesSlowly)71 TEST(AutomaticGainController2SaturationProtector, ProtectorChangesSlowly) {
72   ApmDataDumper apm_data_dumper(0);
73   SaturationProtector saturation_protector(&apm_data_dumper);
74 
75   constexpr float kPeakLevel = -20.f;
76   const float kCrestFactor = GetInitialSaturationMarginDb() - 5.f;
77   const float kOtherCrestFactor = GetInitialSaturationMarginDb();
78   const float kSpeechLevel = kPeakLevel - kCrestFactor;
79   const float kOtherSpeechLevel = kPeakLevel - kOtherCrestFactor;
80 
81   constexpr int kNumIterations = 1000;
82   float max_difference = RunOnConstantLevel(
83       kNumIterations, VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
84       kSpeechLevel, &saturation_protector);
85 
86   max_difference =
87       std::max(RunOnConstantLevel(
88                    kNumIterations,
89                    VadWithLevel::LevelAndProbability(1.f, -90.f, kPeakLevel),
90                    kOtherSpeechLevel, &saturation_protector),
91                max_difference);
92 
93   constexpr float kMaxChangeSpeedDbPerSecond = 0.5;  // 1 db / 2 seconds.
94 
95   EXPECT_LE(max_difference,
96             kMaxChangeSpeedDbPerSecond / 1000 * kFrameDurationMs);
97 }
98 
TEST(AutomaticGainController2SaturationProtector,ProtectorAdaptsToDelayedChanges)99 TEST(AutomaticGainController2SaturationProtector,
100      ProtectorAdaptsToDelayedChanges) {
101   ApmDataDumper apm_data_dumper(0);
102   SaturationProtector saturation_protector(&apm_data_dumper);
103 
104   constexpr int kDelayIterations = kFullBufferSizeMs / kFrameDurationMs;
105   constexpr float kInitialSpeechLevelDbfs = -30;
106   constexpr float kLaterSpeechLevelDbfs = -15;
107 
108   // First run on initial level.
109   float max_difference = RunOnConstantLevel(
110       kDelayIterations,
111       VadWithLevel::LevelAndProbability(
112           1.f, -90.f, kInitialSpeechLevelDbfs + GetInitialSaturationMarginDb()),
113       kInitialSpeechLevelDbfs, &saturation_protector);
114 
115   // Then peak changes, but not RMS.
116   max_difference =
117       std::max(RunOnConstantLevel(
118                    kDelayIterations,
119                    VadWithLevel::LevelAndProbability(
120                        1.f, -90.f,
121                        kLaterSpeechLevelDbfs + GetInitialSaturationMarginDb()),
122                    kInitialSpeechLevelDbfs, &saturation_protector),
123                max_difference);
124 
125   // Then both change.
126   max_difference =
127       std::max(RunOnConstantLevel(
128                    kDelayIterations,
129                    VadWithLevel::LevelAndProbability(
130                        1.f, -90.f,
131                        kLaterSpeechLevelDbfs + GetInitialSaturationMarginDb()),
132                    kLaterSpeechLevelDbfs, &saturation_protector),
133                max_difference);
134 
135   // The saturation protector expects that the RMS changes roughly
136   // 'kFullBufferSizeMs' after peaks change. This is to account for
137   // delay introduces by the level estimator. Therefore, the input
138   // above is 'normal' and 'expected', and shouldn't influence the
139   // margin by much.
140 
141   const float total_difference = std::abs(saturation_protector.LastMargin() -
142                                           GetExtraSaturationMarginOffsetDb() -
143                                           GetInitialSaturationMarginDb());
144 
145   EXPECT_LE(total_difference, 0.05f);
146   EXPECT_LE(max_difference, 0.01f);
147 }
148 
149 }  // namespace webrtc
150