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 <aleloi@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23805}
diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc
index f5b6b91..ca6ec5d 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 @@
     float input_noise_level_dbfs,
     const VadWithLevel::LevelAndProbability vad_result,
     AudioFrameView<float> 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 860da00..ea9e5c7 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(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 6aa2e91..b670f4b 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 @@
 float AdaptiveModeLevelEstimator::LatestLevelEstimate() const {
   return rtc::SafeClamp<float>(
       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 186c59b..5ca7c55 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 @@
   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 71909d0..1915ce2 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 @@
   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 @@
   // 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 @@
           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 216e1b6..dc9be47 100644
--- a/modules/audio_processing/agc2/saturation_protector.cc
+++ b/modules/audio_processing/agc2/saturation_protector.cc
@@ -80,6 +80,10 @@
   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 3a796fa..3f207da 100644
--- a/modules/audio_processing/agc2/saturation_protector.h
+++ b/modules/audio_processing/agc2/saturation_protector.h
@@ -35,6 +35,9 @@
   // detected.
   float LastMargin() const;
 
+  // Resets the internal memory.
+  void Reset();
+
   void DebugDumpEstimate() const;
 
  private: