From 4d01146f1679dd90bba45adb60d24ad11fe1155e Mon Sep 17 00:00:00 2001 From: Alex Loiko Date: Mon, 2 Jul 2018 17:00:31 +0200 Subject: [PATCH] Prepare AGC2 for analog gain changes. 1. Adds support for Reset calls in AGC2. The AGC will be reset during analog gain changes. 2. Allows AdaptiveModeLevelEstimator to return estimates > 0. This can happen if the signal gain is too high. It's needed for letting the analog AGC know that the gain is too high. Bug: webrtc:7494 Change-Id: I38def17c21cc01c36aaea79a2401d8c2f289407b Reviewed-on: https://webrtc-review.googlesource.com/79360 Commit-Queue: Alex Loiko Reviewed-by: Ivo Creusen Cr-Commit-Position: refs/heads/master@{#23805} --- .../agc2/adaptive_digital_gain_applier.cc | 3 +- .../adaptive_digital_gain_applier_unittest.cc | 10 +++++ .../agc2/adaptive_mode_level_estimator.cc | 10 ++++- .../agc2/adaptive_mode_level_estimator.h | 1 + .../adaptive_mode_level_estimator_unittest.cc | 40 +++++++++++++++++-- .../agc2/saturation_protector.cc | 4 ++ .../agc2/saturation_protector.h | 3 ++ 7 files changed, 66 insertions(+), 5 deletions(-) diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc index f5b6b91adf..ca6ec5d4ec 100644 --- a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc +++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc @@ -76,8 +76,9 @@ void AdaptiveDigitalGainApplier::Process( float input_noise_level_dbfs, const VadWithLevel::LevelAndProbability vad_result, AudioFrameView float_frame) { + input_level_dbfs = std::min(input_level_dbfs, 0.f); + RTC_DCHECK_GE(input_level_dbfs, -150.f); - RTC_DCHECK_LE(input_level_dbfs, 0.f); RTC_DCHECK_GE(float_frame.num_channels(), 1); RTC_DCHECK_GE(float_frame.samples_per_channel(), 1); diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc index 860da0034c..ea9e5c74dd 100644 --- a/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc +++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc @@ -175,4 +175,14 @@ TEST(AutomaticGainController2AdaptiveGainApplier, NoiseLimitsGain) { } } } + +TEST(AutomaticGainController2GainApplier, CanHandlePositiveSpeechLevels) { + ApmDataDumper apm_data_dumper(0); + AdaptiveDigitalGainApplier gain_applier(&apm_data_dumper); + + // Make one call with positive audio level values and settings. + VectorFloatFrame fake_audio(2, 480, 10000.f); + gain_applier.Process(5.0f, kNoNoiseDbfs, kVadSpeech, + fake_audio.float_frame_view()); +} } // namespace webrtc diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc index 6aa2e910ea..b670f4b469 100644 --- a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc +++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc @@ -56,7 +56,15 @@ void AdaptiveModeLevelEstimator::UpdateEstimation( float AdaptiveModeLevelEstimator::LatestLevelEstimate() const { return rtc::SafeClamp( last_estimate_with_offset_dbfs_ + saturation_protector_.LastMargin(), - -90.f, 0.f); + -90.f, 30.f); +} + +void AdaptiveModeLevelEstimator::Reset() { + buffer_size_ms_ = 0; + last_estimate_with_offset_dbfs_ = kInitialSpeechLevelEstimateDbfs; + estimate_numerator_ = 0.f; + estimate_denominator_ = 0.f; + saturation_protector_.Reset(); } void AdaptiveModeLevelEstimator::DebugDumpEstimate() { diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h index 186c59bf97..5ca7c550be 100644 --- a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h +++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h @@ -22,6 +22,7 @@ class AdaptiveModeLevelEstimator { explicit AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper); void UpdateEstimation(const VadWithLevel::LevelAndProbability& vad_data); float LatestLevelEstimate() const; + void Reset(); private: void DebugDumpEstimate(); diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc index 71909d0626..1915ce23a4 100644 --- a/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc +++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc @@ -76,7 +76,7 @@ TEST(AutomaticGainController2AdaptiveModeLevelEstimator, TimeToAdapt) { ApmDataDumper apm_data_dumper(0); AdaptiveModeLevelEstimator level_estimator(&apm_data_dumper); - // Run for one 'window size' interval + // Run for one 'window size' interval. constexpr float kInitialSpeechRmsDbfs = -30.f; RunOnConstantLevel( kFullBufferSizeMs / kFrameDurationMs, @@ -88,7 +88,7 @@ TEST(AutomaticGainController2AdaptiveModeLevelEstimator, TimeToAdapt) { // 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 'window size' interval. + // It should at most differ by 25% after one half 'window size' interval. const float kMaxDifferenceDb = 0.25 * std::abs(kDifferentSpeechRmsDbfs - kInitialSpeechRmsDbfs); RunOnConstantLevel( @@ -109,7 +109,41 @@ TEST(AutomaticGainController2AdaptiveModeLevelEstimator, TimeToAdapt) { kDifferentSpeechRmsDbfs), &level_estimator); EXPECT_NEAR(level_estimator.LatestLevelEstimate(), kDifferentSpeechRmsDbfs, - kMaxDifferenceDb); + 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 - kInitialSaturationMarginDb, + 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 - kInitialSaturationMarginDb, + 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()), + kMaxDifferenceDb); } } // namespace webrtc diff --git a/modules/audio_processing/agc2/saturation_protector.cc b/modules/audio_processing/agc2/saturation_protector.cc index 216e1b6238..dc9be47404 100644 --- a/modules/audio_processing/agc2/saturation_protector.cc +++ b/modules/audio_processing/agc2/saturation_protector.cc @@ -80,6 +80,10 @@ float SaturationProtector::LastMargin() const { return last_margin_; } +void SaturationProtector::Reset() { + peak_enveloper_ = PeakEnveloper(); +} + void SaturationProtector::DebugDumpEstimate() const { apm_data_dumper_->DumpRaw( "agc2_adaptive_saturation_protector_delayed_peak_dbfs", diff --git a/modules/audio_processing/agc2/saturation_protector.h b/modules/audio_processing/agc2/saturation_protector.h index 3a796fa65f..3f207da830 100644 --- a/modules/audio_processing/agc2/saturation_protector.h +++ b/modules/audio_processing/agc2/saturation_protector.h @@ -35,6 +35,9 @@ class SaturationProtector { // detected. float LastMargin() const; + // Resets the internal memory. + void Reset(); + void DebugDumpEstimate() const; private: