| /* |
| * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "modules/audio_processing/agc2/adaptive_mode_level_estimator.h" |
| |
| #include "modules/audio_processing/agc2/agc2_common.h" |
| #include "modules/audio_processing/logging/apm_data_dumper.h" |
| #include "rtc_base/gunit.h" |
| |
| namespace webrtc { |
| namespace { |
| void RunOnConstantLevel(int num_iterations, |
| VadWithLevel::LevelAndProbability vad_data, |
| AdaptiveModeLevelEstimator* level_estimator) { |
| for (int i = 0; i < num_iterations; ++i) { |
| level_estimator->UpdateEstimation(vad_data); // By copy |
| } |
| } |
| } // namespace |
| |
| TEST(AutomaticGainController2AdaptiveModeLevelEstimator, |
| EstimatorShouldNotCrash) { |
| ApmDataDumper apm_data_dumper(0); |
| AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper); |
| |
| VadWithLevel::LevelAndProbability vad_data(1.f, -20.f, -10.f); |
| level_estimator.UpdateEstimation(vad_data); |
| static_cast<void>(level_estimator.LatestLevelEstimate()); |
| } |
| |
| TEST(AutomaticGainController2AdaptiveModeLevelEstimator, LevelShouldStabilize) { |
| ApmDataDumper apm_data_dumper(0); |
| AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper); |
| |
| constexpr float kSpeechPeakDbfs = -15.f; |
| RunOnConstantLevel(100, |
| VadWithLevel::LevelAndProbability( |
| 1.f, kSpeechPeakDbfs - GetInitialSaturationMarginDb(), |
| kSpeechPeakDbfs), |
| &level_estimator); |
| |
| EXPECT_NEAR(level_estimator.LatestLevelEstimate() - |
| GetExtraSaturationMarginOffsetDb(), |
| kSpeechPeakDbfs, 0.1f); |
| } |
| |
| TEST(AutomaticGainController2AdaptiveModeLevelEstimator, |
| EstimatorIgnoresZeroProbabilityFrames) { |
| ApmDataDumper apm_data_dumper(0); |
| AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper); |
| |
| // Run for one second of fake audio. |
| constexpr float kSpeechRmsDbfs = -25.f; |
| RunOnConstantLevel( |
| 100, |
| VadWithLevel::LevelAndProbability( |
| 1.f, kSpeechRmsDbfs - GetInitialSaturationMarginDb(), kSpeechRmsDbfs), |
| &level_estimator); |
| |
| // Run for one more second, but mark as not speech. |
| constexpr float kNoiseRmsDbfs = 0.f; |
| RunOnConstantLevel( |
| 100, VadWithLevel::LevelAndProbability(0.f, kNoiseRmsDbfs, kNoiseRmsDbfs), |
| &level_estimator); |
| |
| // Level should not have changed. |
| EXPECT_NEAR(level_estimator.LatestLevelEstimate() - |
| GetExtraSaturationMarginOffsetDb(), |
| kSpeechRmsDbfs, 0.1f); |
| } |
| |
| TEST(AutomaticGainController2AdaptiveModeLevelEstimator, TimeToAdapt) { |
| ApmDataDumper apm_data_dumper(0); |
| AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper); |
| |
| // Run for one 'window size' interval. |
| constexpr float kInitialSpeechRmsDbfs = -30.f; |
| RunOnConstantLevel( |
| kFullBufferSizeMs / kFrameDurationMs, |
| VadWithLevel::LevelAndProbability( |
| 1.f, kInitialSpeechRmsDbfs - GetInitialSaturationMarginDb(), |
| kInitialSpeechRmsDbfs), |
| &level_estimator); |
| |
| // Run for one half 'window size' interval. This should not be enough to |
| // adapt. |
| constexpr float kDifferentSpeechRmsDbfs = -10.f; |
| // It should at most differ by 25% after one half 'window size' interval. |
| const float kMaxDifferenceDb = |
| 0.25 * std::abs(kDifferentSpeechRmsDbfs - kInitialSpeechRmsDbfs); |
| RunOnConstantLevel( |
| static_cast<int>(kFullBufferSizeMs / kFrameDurationMs / 2), |
| VadWithLevel::LevelAndProbability( |
| 1.f, kDifferentSpeechRmsDbfs - GetInitialSaturationMarginDb(), |
| kDifferentSpeechRmsDbfs), |
| &level_estimator); |
| EXPECT_GT( |
| std::abs(kDifferentSpeechRmsDbfs - level_estimator.LatestLevelEstimate()), |
| kMaxDifferenceDb); |
| |
| // Run for some more time. Afterwards, we should have adapted. |
| RunOnConstantLevel( |
| static_cast<int>(3 * kFullBufferSizeMs / kFrameDurationMs), |
| VadWithLevel::LevelAndProbability( |
| 1.f, kDifferentSpeechRmsDbfs - GetInitialSaturationMarginDb(), |
| kDifferentSpeechRmsDbfs), |
| &level_estimator); |
| EXPECT_NEAR(level_estimator.LatestLevelEstimate() - |
| GetExtraSaturationMarginOffsetDb(), |
| kDifferentSpeechRmsDbfs, kMaxDifferenceDb * 0.5f); |
| } |
| |
| TEST(AutomaticGainController2AdaptiveModeLevelEstimator, |
| ResetGivesFastAdaptation) { |
| ApmDataDumper apm_data_dumper(0); |
| AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper); |
| |
| // Run the level estimator for one window size interval. This gives time to |
| // adapt. |
| constexpr float kInitialSpeechRmsDbfs = -30.f; |
| RunOnConstantLevel( |
| kFullBufferSizeMs / kFrameDurationMs, |
| VadWithLevel::LevelAndProbability( |
| 1.f, kInitialSpeechRmsDbfs - GetInitialSaturationMarginDb(), |
| kInitialSpeechRmsDbfs), |
| &level_estimator); |
| |
| constexpr float kDifferentSpeechRmsDbfs = -10.f; |
| // Reset and run one half window size interval. |
| level_estimator.Reset(); |
| |
| RunOnConstantLevel( |
| kFullBufferSizeMs / kFrameDurationMs / 2, |
| VadWithLevel::LevelAndProbability( |
| 1.f, kDifferentSpeechRmsDbfs - GetInitialSaturationMarginDb(), |
| kDifferentSpeechRmsDbfs), |
| &level_estimator); |
| |
| // The level should be close to 'kDifferentSpeechRmsDbfs'. |
| const float kMaxDifferenceDb = |
| 0.1f * std::abs(kDifferentSpeechRmsDbfs - kInitialSpeechRmsDbfs); |
| EXPECT_LT(std::abs(kDifferentSpeechRmsDbfs - |
| (level_estimator.LatestLevelEstimate() - |
| GetExtraSaturationMarginOffsetDb())), |
| kMaxDifferenceDb); |
| } |
| |
| } // namespace webrtc |