saturation_protector_unittest.cc (6318B)
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 #include <cmath> 15 16 #include "modules/audio_processing/agc2/agc2_common.h" 17 #include "modules/audio_processing/logging/apm_data_dumper.h" 18 #include "test/gtest.h" 19 20 namespace webrtc { 21 namespace { 22 23 constexpr float kInitialHeadroomDb = 20.0f; 24 constexpr int kNoAdjacentSpeechFramesRequired = 1; 25 constexpr float kMaxSpeechProbability = 1.0f; 26 27 // Calls `Analyze(speech_probability, peak_dbfs, speech_level_dbfs)` 28 // `num_iterations` times on `saturation_protector` and return the largest 29 // headroom difference between two consecutive calls. 30 float RunOnConstantLevel(int num_iterations, 31 float speech_probability, 32 float peak_dbfs, 33 float speech_level_dbfs, 34 SaturationProtector& saturation_protector) { 35 float last_headroom = saturation_protector.HeadroomDb(); 36 float max_difference = 0.0f; 37 for (int i = 0; i < num_iterations; ++i) { 38 saturation_protector.Analyze(speech_probability, peak_dbfs, 39 speech_level_dbfs); 40 const float new_headroom = saturation_protector.HeadroomDb(); 41 max_difference = 42 std::max(max_difference, std::fabs(new_headroom - last_headroom)); 43 last_headroom = new_headroom; 44 } 45 return max_difference; 46 } 47 48 // Checks that the returned headroom value is correctly reset. 49 TEST(GainController2SaturationProtector, Reset) { 50 ApmDataDumper apm_data_dumper(0); 51 auto saturation_protector = CreateSaturationProtector( 52 kInitialHeadroomDb, kNoAdjacentSpeechFramesRequired, &apm_data_dumper); 53 const float initial_headroom_db = saturation_protector->HeadroomDb(); 54 RunOnConstantLevel(/*num_iterations=*/10, kMaxSpeechProbability, 55 /*peak_dbfs=*/0.0f, 56 /*speech_level_dbfs=*/-10.0f, *saturation_protector); 57 // Make sure that there are side-effects. 58 ASSERT_NE(initial_headroom_db, saturation_protector->HeadroomDb()); 59 saturation_protector->Reset(); 60 EXPECT_EQ(initial_headroom_db, saturation_protector->HeadroomDb()); 61 } 62 63 // Checks that the estimate converges to the ratio between peaks and level 64 // estimator values after a while. 65 TEST(GainController2SaturationProtector, EstimatesCrestRatio) { 66 constexpr int kNumIterations = 2000; 67 constexpr float kPeakLevelDbfs = -20.0f; 68 constexpr float kCrestFactorDb = kInitialHeadroomDb + 1.0f; 69 constexpr float kSpeechLevelDbfs = kPeakLevelDbfs - kCrestFactorDb; 70 const float kMaxDifferenceDb = 71 0.5f * std::fabs(kInitialHeadroomDb - kCrestFactorDb); 72 73 ApmDataDumper apm_data_dumper(0); 74 auto saturation_protector = CreateSaturationProtector( 75 kInitialHeadroomDb, kNoAdjacentSpeechFramesRequired, &apm_data_dumper); 76 RunOnConstantLevel(kNumIterations, kMaxSpeechProbability, kPeakLevelDbfs, 77 kSpeechLevelDbfs, *saturation_protector); 78 EXPECT_NEAR(saturation_protector->HeadroomDb(), kCrestFactorDb, 79 kMaxDifferenceDb); 80 } 81 82 // Checks that the headroom does not change too quickly. 83 TEST(GainController2SaturationProtector, ChangeSlowly) { 84 constexpr int kNumIterations = 1000; 85 constexpr float kPeakLevelDbfs = -20.f; 86 constexpr float kCrestFactorDb = kInitialHeadroomDb - 5.f; 87 constexpr float kOtherCrestFactorDb = kInitialHeadroomDb; 88 constexpr float kSpeechLevelDbfs = kPeakLevelDbfs - kCrestFactorDb; 89 constexpr float kOtherSpeechLevelDbfs = kPeakLevelDbfs - kOtherCrestFactorDb; 90 91 ApmDataDumper apm_data_dumper(0); 92 auto saturation_protector = CreateSaturationProtector( 93 kInitialHeadroomDb, kNoAdjacentSpeechFramesRequired, &apm_data_dumper); 94 float max_difference_db = 95 RunOnConstantLevel(kNumIterations, kMaxSpeechProbability, kPeakLevelDbfs, 96 kSpeechLevelDbfs, *saturation_protector); 97 max_difference_db = std::max( 98 RunOnConstantLevel(kNumIterations, kMaxSpeechProbability, kPeakLevelDbfs, 99 kOtherSpeechLevelDbfs, *saturation_protector), 100 max_difference_db); 101 constexpr float kMaxChangeSpeedDbPerSecond = 0.5f; // 1 db / 2 seconds. 102 EXPECT_LE(max_difference_db, 103 kMaxChangeSpeedDbPerSecond / 1000 * kFrameDurationMs); 104 } 105 106 class SaturationProtectorParametrization 107 : public ::testing::TestWithParam<int> { 108 protected: 109 int adjacent_speech_frames_threshold() const { return GetParam(); } 110 }; 111 112 TEST_P(SaturationProtectorParametrization, DoNotAdaptToShortSpeechSegments) { 113 ApmDataDumper apm_data_dumper(0); 114 auto saturation_protector = CreateSaturationProtector( 115 kInitialHeadroomDb, adjacent_speech_frames_threshold(), &apm_data_dumper); 116 const float initial_headroom_db = saturation_protector->HeadroomDb(); 117 RunOnConstantLevel(/*num_iterations=*/adjacent_speech_frames_threshold() - 1, 118 kMaxSpeechProbability, 119 /*peak_dbfs=*/0.0f, 120 /*speech_level_dbfs=*/-10.0f, *saturation_protector); 121 // No adaptation expected. 122 EXPECT_EQ(initial_headroom_db, saturation_protector->HeadroomDb()); 123 } 124 125 TEST_P(SaturationProtectorParametrization, AdaptToEnoughSpeechSegments) { 126 ApmDataDumper apm_data_dumper(0); 127 auto saturation_protector = CreateSaturationProtector( 128 kInitialHeadroomDb, adjacent_speech_frames_threshold(), &apm_data_dumper); 129 const float initial_headroom_db = saturation_protector->HeadroomDb(); 130 RunOnConstantLevel(/*num_iterations=*/adjacent_speech_frames_threshold() + 1, 131 kMaxSpeechProbability, 132 /*peak_dbfs=*/0.0f, 133 /*speech_level_dbfs=*/-10.0f, *saturation_protector); 134 // Adaptation expected. 135 EXPECT_NE(initial_headroom_db, saturation_protector->HeadroomDb()); 136 } 137 138 INSTANTIATE_TEST_SUITE_P(GainController2, 139 SaturationProtectorParametrization, 140 ::testing::Values(2, 9, 17)); 141 142 } // namespace 143 } // namespace webrtc