AGC2 config: allow tuning of headroom, max gain and initial gain

This CL does *not* change the behavior of the AGC2 adaptive digital
controller - bitexactness verified with audioproc_f on a collection of
AEC dumps and Wav files (42 recordings in total).

Tested: compiled Chrome with this patch and made an appr.tc test call

Bug: webrtc:7494
Change-Id: Ia8a9f6fbc3a3459b888a2eed87e108f0d39cfe99
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/233520
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35140}
diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn
index 6dd8bab..ce70c5d 100644
--- a/modules/audio_processing/agc2/BUILD.gn
+++ b/modules/audio_processing/agc2/BUILD.gn
@@ -178,6 +178,7 @@
     ":common",
     ":gain_applier",
     ":test_utils",
+    "..:api",
     "..:apm_logging",
     "..:audio_frame_view",
     "../../../api:array_view",
diff --git a/modules/audio_processing/agc2/adaptive_agc.cc b/modules/audio_processing/agc2/adaptive_agc.cc
index 0e2535a..eafbcc2 100644
--- a/modules/audio_processing/agc2/adaptive_agc.cc
+++ b/modules/audio_processing/agc2/adaptive_agc.cc
@@ -43,14 +43,9 @@
 
 AdaptiveAgc::AdaptiveAgc(ApmDataDumper* apm_data_dumper,
                          const AdaptiveDigitalConfig& config)
-    : speech_level_estimator_(apm_data_dumper,
-                              config.adjacent_speech_frames_threshold),
+    : speech_level_estimator_(apm_data_dumper, config),
       vad_(config.vad_reset_period_ms, GetAllowedCpuFeatures(config)),
-      gain_controller_(apm_data_dumper,
-                       config.adjacent_speech_frames_threshold,
-                       config.max_gain_change_db_per_second,
-                       config.max_output_noise_level_dbfs,
-                       config.dry_run),
+      gain_controller_(apm_data_dumper, config),
       apm_data_dumper_(apm_data_dumper),
       noise_level_estimator_(CreateNoiseFloorEstimator(apm_data_dumper)),
       saturation_protector_(
diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc
index e59b110..526ef06e 100644
--- a/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc
+++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.cc
@@ -23,31 +23,38 @@
 namespace webrtc {
 namespace {
 
+using AdaptiveDigitalConfig =
+    AudioProcessing::Config::GainController2::AdaptiveDigital;
+
 constexpr int kHeadroomHistogramMin = 0;
 constexpr int kHeadroomHistogramMax = 50;
+constexpr int kGainDbHistogramMax = 30;
 
-// This function maps input level to desired applied gain. We want to
-// boost the signal so that peaks are at -kHeadroomDbfs. We can't
-// apply more than kMaxGainDb gain.
-float ComputeGainDb(float input_level_dbfs) {
-  // If the level is very low, boost it as much as we can.
-  if (input_level_dbfs < -(kHeadroomDbfs + kMaxGainDb)) {
-    return kMaxGainDb;
+// Computes the gain for `input_level_dbfs` to reach `-config.headroom_db`.
+// Clamps the gain in [0, `config.max_gain_db`]. `config.headroom_db` is a
+// safety margin to allow transient peaks to exceed the target peak level
+// without clipping.
+float ComputeGainDb(float input_level_dbfs,
+                    const AdaptiveDigitalConfig& config) {
+  // If the level is very low, apply the maximum gain.
+  if (input_level_dbfs < -(config.headroom_db + config.max_gain_db)) {
+    return config.max_gain_db;
   }
   // We expect to end up here most of the time: the level is below
   // -headroom, but we can boost it to -headroom.
-  if (input_level_dbfs < -kHeadroomDbfs) {
-    return -kHeadroomDbfs - input_level_dbfs;
+  if (input_level_dbfs < -config.headroom_db) {
+    return -config.headroom_db - input_level_dbfs;
   }
-  // Otherwise, the level is too high and we can't boost.
-  RTC_DCHECK_GE(input_level_dbfs, -kHeadroomDbfs);
-  return 0.f;
+  // The level is too high and we can't boost.
+  RTC_DCHECK_GE(input_level_dbfs, -config.headroom_db);
+  return 0.0f;
 }
 
-// Returns `target_gain` if the output noise level is below
-// `max_output_noise_level_dbfs`; otherwise returns a capped gain so that the
-// output noise level equals `max_output_noise_level_dbfs`.
-float LimitGainByNoise(float target_gain,
+// Returns `target_gain_db` if applying such a gain to `input_noise_level_dbfs`
+// does not exceed `max_output_noise_level_dbfs`. Otherwise lowers and returns
+// `target_gain_db` so that the output noise level equals
+// `max_output_noise_level_dbfs`.
+float LimitGainByNoise(float target_gain_db,
                        float input_noise_level_dbfs,
                        float max_output_noise_level_dbfs,
                        ApmDataDumper& apm_data_dumper) {
@@ -55,24 +62,25 @@
       max_output_noise_level_dbfs - input_noise_level_dbfs;
   apm_data_dumper.DumpRaw("agc2_adaptive_gain_applier_max_allowed_gain_db",
                           max_allowed_gain_db);
-  return std::min(target_gain, std::max(max_allowed_gain_db, 0.f));
+  return std::min(target_gain_db, std::max(max_allowed_gain_db, 0.0f));
 }
 
-float LimitGainByLowConfidence(float target_gain,
-                               float last_gain,
+float LimitGainByLowConfidence(float target_gain_db,
+                               float last_gain_db,
                                float limiter_audio_level_dbfs,
                                bool estimate_is_confident) {
   if (estimate_is_confident ||
       limiter_audio_level_dbfs <= kLimiterThresholdForAgcGainDbfs) {
-    return target_gain;
+    return target_gain_db;
   }
-  const float limiter_level_before_gain = limiter_audio_level_dbfs - last_gain;
+  const float limiter_level_dbfs_before_gain =
+      limiter_audio_level_dbfs - last_gain_db;
 
-  // Compute a new gain so that `limiter_level_before_gain` + `new_target_gain`
-  // is not great than `kLimiterThresholdForAgcGainDbfs`.
-  const float new_target_gain = std::max(
-      kLimiterThresholdForAgcGainDbfs - limiter_level_before_gain, 0.f);
-  return std::min(new_target_gain, target_gain);
+  // Compute a new gain so that `limiter_level_dbfs_before_gain` +
+  // `new_target_gain_db` is not great than `kLimiterThresholdForAgcGainDbfs`.
+  const float new_target_gain_db = std::max(
+      kLimiterThresholdForAgcGainDbfs - limiter_level_dbfs_before_gain, 0.0f);
+  return std::min(new_target_gain_db, target_gain_db);
 }
 
 // Computes how the gain should change during this frame.
@@ -86,7 +94,7 @@
   RTC_DCHECK_GT(max_gain_increase_db, 0);
   float target_gain_difference_db = target_gain_db - last_gain_db;
   if (!gain_increase_allowed) {
-    target_gain_difference_db = std::min(target_gain_difference_db, 0.f);
+    target_gain_difference_db = std::min(target_gain_difference_db, 0.0f);
   }
   return rtc::SafeClamp(target_gain_difference_db, -max_gain_decrease_db,
                         max_gain_increase_db);
@@ -110,32 +118,28 @@
 
 AdaptiveDigitalGainApplier::AdaptiveDigitalGainApplier(
     ApmDataDumper* apm_data_dumper,
-    int adjacent_speech_frames_threshold,
-    float max_gain_change_db_per_second,
-    float max_output_noise_level_dbfs,
-    bool dry_run)
+    const AudioProcessing::Config::GainController2::AdaptiveDigital& config)
     : apm_data_dumper_(apm_data_dumper),
       gain_applier_(
           /*hard_clip_samples=*/false,
-          /*initial_gain_factor=*/DbToRatio(kInitialAdaptiveDigitalGainDb)),
-      adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold),
-      max_gain_change_db_per_10ms_(max_gain_change_db_per_second *
-                                   kFrameDurationMs / 1000.f),
-      max_output_noise_level_dbfs_(max_output_noise_level_dbfs),
-      dry_run_(dry_run),
+          /*initial_gain_factor=*/DbToRatio(config.initial_gain_db)),
+      config_(config),
+      max_gain_change_db_per_10ms_(config_.max_gain_change_db_per_second *
+                                   kFrameDurationMs / 1000.0f),
       calls_since_last_gain_log_(0),
-      frames_to_gain_increase_allowed_(adjacent_speech_frames_threshold_),
-      last_gain_db_(kInitialAdaptiveDigitalGainDb) {
-  RTC_DCHECK_GT(max_gain_change_db_per_second, 0.0f);
+      frames_to_gain_increase_allowed_(
+          config_.adjacent_speech_frames_threshold),
+      last_gain_db_(config_.initial_gain_db) {
+  RTC_DCHECK_GT(max_gain_change_db_per_10ms_, 0.0f);
   RTC_DCHECK_GE(frames_to_gain_increase_allowed_, 1);
-  RTC_DCHECK_GE(max_output_noise_level_dbfs_, -90.0f);
-  RTC_DCHECK_LE(max_output_noise_level_dbfs_, 0.0f);
+  RTC_DCHECK_GE(config_.max_output_noise_level_dbfs, -90.0f);
+  RTC_DCHECK_LE(config_.max_output_noise_level_dbfs, 0.0f);
   Initialize(/*sample_rate_hz=*/48000, /*num_channels=*/1);
 }
 
 void AdaptiveDigitalGainApplier::Initialize(int sample_rate_hz,
                                             int num_channels) {
-  if (!dry_run_) {
+  if (!config_.dry_run) {
     return;
   }
   RTC_DCHECK_GT(sample_rate_hz, 0);
@@ -159,7 +163,7 @@
 
 void AdaptiveDigitalGainApplier::Process(const FrameInfo& info,
                                          AudioFrameView<float> frame) {
-  RTC_DCHECK_GE(info.speech_level_dbfs, -150.f);
+  RTC_DCHECK_GE(info.speech_level_dbfs, -150.0f);
   RTC_DCHECK_GE(frame.num_channels(), 1);
   RTC_DCHECK(
       frame.samples_per_channel() == 80 || frame.samples_per_channel() == 160 ||
@@ -172,15 +176,16 @@
   const float input_level_dbfs = info.speech_level_dbfs + info.headroom_db;
 
   const float target_gain_db = LimitGainByLowConfidence(
-      LimitGainByNoise(ComputeGainDb(input_level_dbfs), info.noise_rms_dbfs,
-                       max_output_noise_level_dbfs_, *apm_data_dumper_),
+      LimitGainByNoise(ComputeGainDb(input_level_dbfs, config_),
+                       info.noise_rms_dbfs, config_.max_output_noise_level_dbfs,
+                       *apm_data_dumper_),
       last_gain_db_, info.limiter_envelope_dbfs, info.speech_level_reliable);
 
   // Forbid increasing the gain until enough adjacent speech frames are
   // observed.
   bool first_confident_speech_frame = false;
   if (info.speech_probability < kVadConfidenceThreshold) {
-    frames_to_gain_increase_allowed_ = adjacent_speech_frames_threshold_;
+    frames_to_gain_increase_allowed_ = config_.adjacent_speech_frames_threshold;
   } else if (frames_to_gain_increase_allowed_ > 0) {
     frames_to_gain_increase_allowed_--;
     first_confident_speech_frame = frames_to_gain_increase_allowed_ == 0;
@@ -196,7 +201,7 @@
     // No gain increase happened while waiting for a long enough speech
     // sequence. Therefore, temporarily allow a faster gain increase.
     RTC_DCHECK(gain_increase_allowed);
-    max_gain_increase_db *= adjacent_speech_frames_threshold_;
+    max_gain_increase_db *= config_.adjacent_speech_frames_threshold;
   }
 
   const float gain_change_this_frame_db = ComputeGainChangeThisFrameDb(
@@ -217,7 +222,7 @@
   }
 
   // Modify `frame` only if not running in "dry run" mode.
-  if (!dry_run_) {
+  if (!config_.dry_run) {
     gain_applier_.ApplyGain(frame);
   } else {
     // Copy `frame` so that `ApplyGain()` is called (on a copy).
@@ -247,7 +252,8 @@
         kHeadroomHistogramMax,
         kHeadroomHistogramMax - kHeadroomHistogramMin + 1);
     RTC_HISTOGRAM_COUNTS_LINEAR("WebRTC.Audio.Agc2.DigitalGainApplied",
-                                last_gain_db_, 0, kMaxGainDb, kMaxGainDb + 1);
+                                last_gain_db_, 0, kGainDbHistogramMax,
+                                kGainDbHistogramMax + 1);
     RTC_LOG(LS_INFO) << "AGC2 adaptive digital"
                      << " | speech_dbfs: " << info.speech_level_dbfs
                      << " | noise_dbfs: " << info.noise_rms_dbfs
diff --git a/modules/audio_processing/agc2/adaptive_digital_gain_applier.h b/modules/audio_processing/agc2/adaptive_digital_gain_applier.h
index 6fc8ac1..e254b51 100644
--- a/modules/audio_processing/agc2/adaptive_digital_gain_applier.h
+++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier.h
@@ -15,6 +15,7 @@
 
 #include "modules/audio_processing/agc2/gain_applier.h"
 #include "modules/audio_processing/include/audio_frame_view.h"
+#include "modules/audio_processing/include/audio_processing.h"
 
 namespace webrtc {
 
@@ -35,16 +36,9 @@
     float limiter_envelope_dbfs;  // Envelope level from the limiter (dBFS).
   };
 
-  // Ctor. `adjacent_speech_frames_threshold` indicates how many adjacent speech
-  // frames must be observed in order to consider the sequence as speech.
-  // `max_gain_change_db_per_second` limits the adaptation speed (uniformly
-  // operated across frames). `max_output_noise_level_dbfs` limits the output
-  // noise level. If `dry_run` is true, `Process()` will not modify the audio.
-  AdaptiveDigitalGainApplier(ApmDataDumper* apm_data_dumper,
-                             int adjacent_speech_frames_threshold,
-                             float max_gain_change_db_per_second,
-                             float max_output_noise_level_dbfs,
-                             bool dry_run);
+  AdaptiveDigitalGainApplier(
+      ApmDataDumper* apm_data_dumper,
+      const AudioProcessing::Config::GainController2::AdaptiveDigital& config);
   AdaptiveDigitalGainApplier(const AdaptiveDigitalGainApplier&) = delete;
   AdaptiveDigitalGainApplier& operator=(const AdaptiveDigitalGainApplier&) =
       delete;
@@ -59,10 +53,8 @@
   ApmDataDumper* const apm_data_dumper_;
   GainApplier gain_applier_;
 
-  const int adjacent_speech_frames_threshold_;
+  const AudioProcessing::Config::GainController2::AdaptiveDigital config_;
   const float max_gain_change_db_per_10ms_;
-  const float max_output_noise_level_dbfs_;
-  const bool dry_run_;
 
   int calls_since_last_gain_log_;
   int frames_to_gain_increase_allowed_;
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 3c5642b..efbc1e1 100644
--- a/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc
+++ b/modules/audio_processing/agc2/adaptive_digital_gain_applier_unittest.cc
@@ -16,6 +16,7 @@
 #include "common_audio/include/audio_util.h"
 #include "modules/audio_processing/agc2/agc2_common.h"
 #include "modules/audio_processing/agc2/vector_float_frame.h"
+#include "modules/audio_processing/include/audio_processing.h"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/gunit.h"
 
@@ -33,57 +34,68 @@
 constexpr float kNoNoiseDbfs = kMinLevelDbfs;
 constexpr float kWithNoiseDbfs = -20.0f;
 
-constexpr float kMaxGainChangePerSecondDb = 3.0f;
-constexpr float kMaxGainChangePerFrameDb =
-    kMaxGainChangePerSecondDb * kFrameDurationMs / 1000.0f;
-constexpr float kMaxOutputNoiseLevelDbfs = -50.0f;
+// Number of additional frames to process in the tests to ensure that the tested
+// adaptation processes have converged.
+constexpr int kNumExtraFrames = 10;
+
+constexpr float GetMaxGainChangePerFrameDb(
+    float max_gain_change_db_per_second) {
+  return max_gain_change_db_per_second * kFrameDurationMs / 1000.0f;
+}
+
+using AdaptiveDigitalConfig =
+    AudioProcessing::Config::GainController2::AdaptiveDigital;
+
+constexpr AdaptiveDigitalConfig kDefaultConfig{};
 
 // Helper to create initialized `AdaptiveDigitalGainApplier` objects.
 struct GainApplierHelper {
-  GainApplierHelper()
-      : GainApplierHelper(/*adjacent_speech_frames_threshold=*/1) {}
-  explicit GainApplierHelper(int adjacent_speech_frames_threshold)
+  explicit GainApplierHelper(const AdaptiveDigitalConfig& config)
       : apm_data_dumper(0),
-        gain_applier(std::make_unique<AdaptiveDigitalGainApplier>(
-            &apm_data_dumper,
-            adjacent_speech_frames_threshold,
-            kMaxGainChangePerSecondDb,
-            kMaxOutputNoiseLevelDbfs,
-            /*dry_run=*/false)) {}
+        gain_applier(
+            std::make_unique<AdaptiveDigitalGainApplier>(&apm_data_dumper,
+                                                         config)) {}
   ApmDataDumper apm_data_dumper;
   std::unique_ptr<AdaptiveDigitalGainApplier> gain_applier;
 };
 
-// Sample frame information for the tests mocking noiseless speech detected
-// with maximum probability and with level, headroom and limiter envelope chosen
-// so that the resulting gain equals `kInitialAdaptiveDigitalGainDb` - i.e., no
-// gain adaptation is expected.
-constexpr AdaptiveDigitalGainApplier::FrameInfo kFrameInfo{
-    /*speech_probability=*/kMaxSpeechProbability,
-    /*speech_level_dbfs=*/kInitialSpeechLevelEstimateDbfs,
-    /*speech_level_reliable=*/true,
-    /*noise_rms_dbfs=*/kNoNoiseDbfs,
-    /*headroom_db=*/kSaturationProtectorInitialHeadroomDb,
-    /*limiter_envelope_dbfs=*/-2.0f};
+// Returns a `FrameInfo` sample to simulate noiseless speech detected with
+// maximum probability and with level, headroom and limiter envelope chosen
+// so that the resulting gain equals the default initial adaptive digital gain
+// i.e., no gain adaptation is expected.
+AdaptiveDigitalGainApplier::FrameInfo GetFrameInfoToNotAdapt(
+    const AdaptiveDigitalConfig& config) {
+  AdaptiveDigitalGainApplier::FrameInfo info;
+  info.speech_probability = kMaxSpeechProbability;
+  info.speech_level_dbfs = -config.initial_gain_db - config.headroom_db;
+  info.speech_level_reliable = true;
+  info.noise_rms_dbfs = kNoNoiseDbfs;
+  info.headroom_db = config.headroom_db;
+  info.limiter_envelope_dbfs = -2.0f;
+  return info;
+}
 
 TEST(GainController2AdaptiveGainApplier, GainApplierShouldNotCrash) {
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kStereo);
   // Make one call with reasonable audio level values and settings.
   VectorFloatFrame fake_audio(kStereo, kFrameLen10ms48kHz, 10000.0f);
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
-  info.speech_level_dbfs = -5.0f;
-  helper.gain_applier->Process(kFrameInfo, fake_audio.float_frame_view());
+  helper.gain_applier->Process(GetFrameInfoToNotAdapt(kDefaultConfig),
+                               fake_audio.float_frame_view());
 }
 
 // Checks that the maximum allowed gain is applied.
 TEST(GainController2AdaptiveGainApplier, MaxGainApplied) {
   constexpr int kNumFramesToAdapt =
-      static_cast<int>(kMaxGainDb / kMaxGainChangePerFrameDb) + 10;
+      static_cast<int>(kDefaultConfig.max_gain_db /
+                       GetMaxGainChangePerFrameDb(
+                           kDefaultConfig.max_gain_change_db_per_second)) +
+      kNumExtraFrames;
 
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono);
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalGainApplier::FrameInfo info =
+      GetFrameInfoToNotAdapt(kDefaultConfig);
   info.speech_level_dbfs = -60.0f;
   float applied_gain;
   for (int i = 0; i < kNumFramesToAdapt; ++i) {
@@ -92,30 +104,33 @@
     applied_gain = fake_audio.float_frame_view().channel(0)[0];
   }
   const float applied_gain_db = 20.0f * std::log10f(applied_gain);
-  EXPECT_NEAR(applied_gain_db, kMaxGainDb, 0.1f);
+  EXPECT_NEAR(applied_gain_db, kDefaultConfig.max_gain_db, 0.1f);
 }
 
 TEST(GainController2AdaptiveGainApplier, GainDoesNotChangeFast) {
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono);
 
   constexpr float initial_level_dbfs = -25.0f;
-  // A few extra frames for safety.
+  constexpr float kMaxGainChangeDbPerFrame =
+      GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second);
   constexpr int kNumFramesToAdapt =
-      static_cast<int>(initial_level_dbfs / kMaxGainChangePerFrameDb) + 10;
+      static_cast<int>(initial_level_dbfs / kMaxGainChangeDbPerFrame) +
+      kNumExtraFrames;
 
-  const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb);
+  const float max_change_per_frame_linear = DbToRatio(kMaxGainChangeDbPerFrame);
 
   float last_gain_linear = 1.f;
   for (int i = 0; i < kNumFramesToAdapt; ++i) {
     SCOPED_TRACE(i);
     VectorFloatFrame fake_audio(kMono, kFrameLen10ms8kHz, 1.0f);
-    AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+    AdaptiveDigitalGainApplier::FrameInfo info =
+        GetFrameInfoToNotAdapt(kDefaultConfig);
     info.speech_level_dbfs = initial_level_dbfs;
     helper.gain_applier->Process(info, fake_audio.float_frame_view());
     float current_gain_linear = fake_audio.float_frame_view().channel(0)[0];
     EXPECT_LE(std::abs(current_gain_linear - last_gain_linear),
-              kMaxChangePerFrameLinear);
+              max_change_per_frame_linear);
     last_gain_linear = current_gain_linear;
   }
 
@@ -123,56 +138,61 @@
   for (int i = 0; i < kNumFramesToAdapt; ++i) {
     SCOPED_TRACE(i);
     VectorFloatFrame fake_audio(kMono, kFrameLen10ms8kHz, 1.0f);
-    AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+    AdaptiveDigitalGainApplier::FrameInfo info =
+        GetFrameInfoToNotAdapt(kDefaultConfig);
     info.speech_level_dbfs = 0.f;
     helper.gain_applier->Process(info, fake_audio.float_frame_view());
     float current_gain_linear = fake_audio.float_frame_view().channel(0)[0];
     EXPECT_LE(std::abs(current_gain_linear - last_gain_linear),
-              kMaxChangePerFrameLinear);
+              max_change_per_frame_linear);
     last_gain_linear = current_gain_linear;
   }
 }
 
 TEST(GainController2AdaptiveGainApplier, GainIsRampedInAFrame) {
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono);
 
   constexpr float initial_level_dbfs = -25.0f;
 
   VectorFloatFrame fake_audio(kMono, kFrameLen10ms48kHz, 1.0f);
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalGainApplier::FrameInfo info =
+      GetFrameInfoToNotAdapt(kDefaultConfig);
   info.speech_level_dbfs = initial_level_dbfs;
   helper.gain_applier->Process(info, fake_audio.float_frame_view());
   float maximal_difference = 0.0f;
-  float current_value = 1.0f * DbToRatio(kInitialAdaptiveDigitalGainDb);
+  float current_value = 1.0f * DbToRatio(kDefaultConfig.initial_gain_db);
   for (const auto& x : fake_audio.float_frame_view().channel(0)) {
     const float difference = std::abs(x - current_value);
     maximal_difference = std::max(maximal_difference, difference);
     current_value = x;
   }
 
-  const float kMaxChangePerFrameLinear = DbToRatio(kMaxGainChangePerFrameDb);
-  const float kMaxChangePerSample =
-      kMaxChangePerFrameLinear / kFrameLen10ms48kHz;
+  const float max_change_per_frame_linear = DbToRatio(
+      GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second));
+  const float max_change_per_sample =
+      max_change_per_frame_linear / kFrameLen10ms48kHz;
 
-  EXPECT_LE(maximal_difference, kMaxChangePerSample);
+  EXPECT_LE(maximal_difference, max_change_per_sample);
 }
 
 TEST(GainController2AdaptiveGainApplier, NoiseLimitsGain) {
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono);
 
   constexpr float initial_level_dbfs = -25.0f;
   constexpr int num_initial_frames =
-      kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb;
+      kDefaultConfig.initial_gain_db /
+      GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second);
   constexpr int num_frames = 50;
 
-  ASSERT_GT(kWithNoiseDbfs, kMaxOutputNoiseLevelDbfs)
+  ASSERT_GT(kWithNoiseDbfs, kDefaultConfig.max_output_noise_level_dbfs)
       << "kWithNoiseDbfs is too low";
 
   for (int i = 0; i < num_initial_frames + num_frames; ++i) {
     VectorFloatFrame fake_audio(kMono, kFrameLen10ms48kHz, 1.0f);
-    AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+    AdaptiveDigitalGainApplier::FrameInfo info =
+        GetFrameInfoToNotAdapt(kDefaultConfig);
     info.speech_level_dbfs = initial_level_dbfs;
     info.noise_rms_dbfs = kWithNoiseDbfs;
     helper.gain_applier->Process(info, fake_audio.float_frame_view());
@@ -189,31 +209,34 @@
 }
 
 TEST(GainController2GainApplier, CanHandlePositiveSpeechLevels) {
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kStereo);
 
   // Make one call with positive audio level values and settings.
   VectorFloatFrame fake_audio(kStereo, kFrameLen10ms48kHz, 10000.0f);
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalGainApplier::FrameInfo info =
+      GetFrameInfoToNotAdapt(kDefaultConfig);
   info.speech_level_dbfs = 5.0f;
   helper.gain_applier->Process(info, fake_audio.float_frame_view());
 }
 
 TEST(GainController2GainApplier, AudioLevelLimitsGain) {
-  GainApplierHelper helper;
+  GainApplierHelper helper(kDefaultConfig);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono);
 
   constexpr float initial_level_dbfs = -25.0f;
   constexpr int num_initial_frames =
-      kInitialAdaptiveDigitalGainDb / kMaxGainChangePerFrameDb;
+      kDefaultConfig.initial_gain_db /
+      GetMaxGainChangePerFrameDb(kDefaultConfig.max_gain_change_db_per_second);
   constexpr int num_frames = 50;
 
-  ASSERT_GT(kWithNoiseDbfs, kMaxOutputNoiseLevelDbfs)
+  ASSERT_GT(kWithNoiseDbfs, kDefaultConfig.max_output_noise_level_dbfs)
       << "kWithNoiseDbfs is too low";
 
   for (int i = 0; i < num_initial_frames + num_frames; ++i) {
     VectorFloatFrame fake_audio(kMono, kFrameLen10ms48kHz, 1.0f);
-    AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+    AdaptiveDigitalGainApplier::FrameInfo info =
+        GetFrameInfoToNotAdapt(kDefaultConfig);
     info.speech_level_dbfs = initial_level_dbfs;
     info.limiter_envelope_dbfs = 1.0f;
     info.speech_level_reliable = false;
@@ -232,21 +255,22 @@
 
 class AdaptiveDigitalGainApplierTest : public ::testing::TestWithParam<int> {
  protected:
-  int AdjacentSpeechFramesThreshold() const { return GetParam(); }
+  int adjacent_speech_frames_threshold() const { return GetParam(); }
 };
 
 TEST_P(AdaptiveDigitalGainApplierTest,
        DoNotIncreaseGainWithTooFewSpeechFrames) {
-  const int adjacent_speech_frames_threshold = AdjacentSpeechFramesThreshold();
-  GainApplierHelper helper(adjacent_speech_frames_threshold);
+  AdaptiveDigitalConfig config;
+  config.adjacent_speech_frames_threshold = adjacent_speech_frames_threshold();
+  GainApplierHelper helper(config);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono);
 
   // Lower the speech level so that the target gain will be increased.
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config);
   info.speech_level_dbfs -= 12.0f;
 
   float prev_gain = 0.0f;
-  for (int i = 0; i < adjacent_speech_frames_threshold; ++i) {
+  for (int i = 0; i < config.adjacent_speech_frames_threshold; ++i) {
     SCOPED_TRACE(i);
     VectorFloatFrame audio(kMono, kFrameLen10ms48kHz, 1.0f);
     helper.gain_applier->Process(info, audio.float_frame_view());
@@ -259,16 +283,17 @@
 }
 
 TEST_P(AdaptiveDigitalGainApplierTest, IncreaseGainWithEnoughSpeechFrames) {
-  const int adjacent_speech_frames_threshold = AdjacentSpeechFramesThreshold();
-  GainApplierHelper helper(adjacent_speech_frames_threshold);
+  AdaptiveDigitalConfig config;
+  config.adjacent_speech_frames_threshold = adjacent_speech_frames_threshold();
+  GainApplierHelper helper(config);
   helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono);
 
   // Lower the speech level so that the target gain will be increased.
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config);
   info.speech_level_dbfs -= 12.0f;
 
   float prev_gain = 0.0f;
-  for (int i = 0; i < adjacent_speech_frames_threshold; ++i) {
+  for (int i = 0; i < config.adjacent_speech_frames_threshold; ++i) {
     SCOPED_TRACE(i);
     VectorFloatFrame audio(kMono, kFrameLen10ms48kHz, 1.0f);
     helper.gain_applier->Process(info, audio.float_frame_view());
@@ -289,63 +314,65 @@
 
 // Checks that the input is never modified when running in dry run mode.
 TEST(GainController2GainApplier, DryRunDoesNotChangeInput) {
-  ApmDataDumper apm_data_dumper(0);
-  AdaptiveDigitalGainApplier gain_applier(
-      &apm_data_dumper, /*adjacent_speech_frames_threshold=*/1,
-      kMaxGainChangePerSecondDb, kMaxOutputNoiseLevelDbfs, /*dry_run=*/true);
+  AdaptiveDigitalConfig config;
+  config.dry_run = true;
+  GainApplierHelper helper(config);
+
   // Simulate an input signal with log speech level.
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config);
   info.speech_level_dbfs = -60.0f;
-  // Allow enough time to reach the maximum gain.
-  constexpr int kNumFramesToAdapt =
-      static_cast<int>(kMaxGainDb / kMaxGainChangePerFrameDb) + 10;
+  const int num_frames_to_adapt =
+      static_cast<int>(
+          config.max_gain_db /
+          GetMaxGainChangePerFrameDb(config.max_gain_change_db_per_second)) +
+      kNumExtraFrames;
   constexpr float kPcmSamples = 123.456f;
   // Run the gain applier and check that the PCM samples are not modified.
-  gain_applier.Initialize(/*sample_rate_hz=*/8000, kMono);
-  for (int i = 0; i < kNumFramesToAdapt; ++i) {
+  helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono);
+  for (int i = 0; i < num_frames_to_adapt; ++i) {
     SCOPED_TRACE(i);
     VectorFloatFrame fake_audio(kMono, kFrameLen10ms8kHz, kPcmSamples);
-    gain_applier.Process(info, fake_audio.float_frame_view());
+    helper.gain_applier->Process(info, fake_audio.float_frame_view());
     EXPECT_FLOAT_EQ(fake_audio.float_frame_view().channel(0)[0], kPcmSamples);
   }
 }
 
 // Checks that no sample is modified before and after the sample rate changes.
 TEST(GainController2GainApplier, DryRunHandlesSampleRateChange) {
-  ApmDataDumper apm_data_dumper(0);
-  AdaptiveDigitalGainApplier gain_applier(
-      &apm_data_dumper, /*adjacent_speech_frames_threshold=*/1,
-      kMaxGainChangePerSecondDb, kMaxOutputNoiseLevelDbfs, /*dry_run=*/true);
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalConfig config;
+  config.dry_run = true;
+  GainApplierHelper helper(config);
+
+  AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config);
   info.speech_level_dbfs = -60.0f;
   constexpr float kPcmSamples = 123.456f;
   VectorFloatFrame fake_audio_8k(kMono, kFrameLen10ms8kHz, kPcmSamples);
-  gain_applier.Initialize(/*sample_rate_hz=*/8000, kMono);
-  gain_applier.Process(info, fake_audio_8k.float_frame_view());
+  helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono);
+  helper.gain_applier->Process(info, fake_audio_8k.float_frame_view());
   EXPECT_FLOAT_EQ(fake_audio_8k.float_frame_view().channel(0)[0], kPcmSamples);
-  gain_applier.Initialize(/*sample_rate_hz=*/48000, kMono);
+  helper.gain_applier->Initialize(/*sample_rate_hz=*/48000, kMono);
   VectorFloatFrame fake_audio_48k(kMono, kFrameLen10ms48kHz, kPcmSamples);
-  gain_applier.Process(info, fake_audio_48k.float_frame_view());
+  helper.gain_applier->Process(info, fake_audio_48k.float_frame_view());
   EXPECT_FLOAT_EQ(fake_audio_48k.float_frame_view().channel(0)[0], kPcmSamples);
 }
 
 // Checks that no sample is modified before and after the number of channels
 // changes.
 TEST(GainController2GainApplier, DryRunHandlesNumChannelsChange) {
-  ApmDataDumper apm_data_dumper(0);
-  AdaptiveDigitalGainApplier gain_applier(
-      &apm_data_dumper, /*adjacent_speech_frames_threshold=*/1,
-      kMaxGainChangePerSecondDb, kMaxOutputNoiseLevelDbfs, /*dry_run=*/true);
-  AdaptiveDigitalGainApplier::FrameInfo info = kFrameInfo;
+  AdaptiveDigitalConfig config;
+  config.dry_run = true;
+  GainApplierHelper helper(config);
+
+  AdaptiveDigitalGainApplier::FrameInfo info = GetFrameInfoToNotAdapt(config);
   info.speech_level_dbfs = -60.0f;
   constexpr float kPcmSamples = 123.456f;
   VectorFloatFrame fake_audio_8k(kMono, kFrameLen10ms8kHz, kPcmSamples);
-  gain_applier.Initialize(/*sample_rate_hz=*/8000, kMono);
-  gain_applier.Process(info, fake_audio_8k.float_frame_view());
+  helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kMono);
+  helper.gain_applier->Process(info, fake_audio_8k.float_frame_view());
   EXPECT_FLOAT_EQ(fake_audio_8k.float_frame_view().channel(0)[0], kPcmSamples);
   VectorFloatFrame fake_audio_48k(kStereo, kFrameLen10ms8kHz, kPcmSamples);
-  gain_applier.Initialize(/*sample_rate_hz=*/8000, kStereo);
-  gain_applier.Process(info, fake_audio_48k.float_frame_view());
+  helper.gain_applier->Initialize(/*sample_rate_hz=*/8000, kStereo);
+  helper.gain_applier->Process(info, fake_audio_48k.float_frame_view());
   EXPECT_FLOAT_EQ(fake_audio_48k.float_frame_view().channel(0)[0], kPcmSamples);
   EXPECT_FLOAT_EQ(fake_audio_48k.float_frame_view().channel(1)[0], kPcmSamples);
 }
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc
index ca3279e..81e7d29 100644
--- a/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc
+++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.cc
@@ -20,7 +20,14 @@
 namespace {
 
 float ClampLevelEstimateDbfs(float level_estimate_dbfs) {
-  return rtc::SafeClamp<float>(level_estimate_dbfs, -90.f, 30.f);
+  return rtc::SafeClamp<float>(level_estimate_dbfs, -90.0f, 30.0f);
+}
+
+// Returns the initial speech level estimate needed to apply the initial gain.
+float GetInitialSpeechLevelEstimateDbfs(
+    const AudioProcessing::Config::GainController2::AdaptiveDigital& config) {
+  return ClampLevelEstimateDbfs(-kSaturationProtectorInitialHeadroomDb -
+                                config.initial_gain_db - config.headroom_db);
 }
 
 }  // namespace
@@ -38,17 +45,13 @@
 }
 
 AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator(
-    ApmDataDumper* apm_data_dumper)
-    : AdaptiveModeLevelEstimator(
-          apm_data_dumper,
-          kDefaultLevelEstimatorAdjacentSpeechFramesThreshold) {}
-
-AdaptiveModeLevelEstimator::AdaptiveModeLevelEstimator(
     ApmDataDumper* apm_data_dumper,
-    int adjacent_speech_frames_threshold)
+    const AudioProcessing::Config::GainController2::AdaptiveDigital& config)
     : apm_data_dumper_(apm_data_dumper),
-      adjacent_speech_frames_threshold_(adjacent_speech_frames_threshold),
-      level_dbfs_(ClampLevelEstimateDbfs(kInitialSpeechLevelEstimateDbfs)) {
+      initial_speech_level_dbfs_(GetInitialSpeechLevelEstimateDbfs(config)),
+      adjacent_speech_frames_threshold_(
+          config.adjacent_speech_frames_threshold),
+      level_dbfs_(initial_speech_level_dbfs_) {
   RTC_DCHECK(apm_data_dumper_);
   RTC_DCHECK_GE(adjacent_speech_frames_threshold_, 1);
   Reset();
@@ -128,14 +131,14 @@
 void AdaptiveModeLevelEstimator::Reset() {
   ResetLevelEstimatorState(preliminary_state_);
   ResetLevelEstimatorState(reliable_state_);
-  level_dbfs_ = ClampLevelEstimateDbfs(kInitialSpeechLevelEstimateDbfs);
+  level_dbfs_ = initial_speech_level_dbfs_;
   num_adjacent_speech_frames_ = 0;
 }
 
 void AdaptiveModeLevelEstimator::ResetLevelEstimatorState(
     LevelEstimatorState& state) const {
   state.time_to_confidence_ms = kLevelEstimatorTimeToConfidenceMs;
-  state.level_dbfs.numerator = kInitialSpeechLevelEstimateDbfs;
+  state.level_dbfs.numerator = initial_speech_level_dbfs_;
   state.level_dbfs.denominator = 1.0f;
 }
 
diff --git a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h
index e39b6ce..e15c6af 100644
--- a/modules/audio_processing/agc2/adaptive_mode_level_estimator.h
+++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator.h
@@ -24,12 +24,12 @@
 // Level estimator for the digital adaptive gain controller.
 class AdaptiveModeLevelEstimator {
  public:
-  explicit AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper);
+  AdaptiveModeLevelEstimator(
+      ApmDataDumper* apm_data_dumper,
+      const AudioProcessing::Config::GainController2::AdaptiveDigital& config);
   AdaptiveModeLevelEstimator(const AdaptiveModeLevelEstimator&) = delete;
   AdaptiveModeLevelEstimator& operator=(const AdaptiveModeLevelEstimator&) =
       delete;
-  AdaptiveModeLevelEstimator(ApmDataDumper* apm_data_dumper,
-                             int adjacent_speech_frames_threshold);
 
   // Updates the level estimation.
   void Update(const VadLevelAnalyzer::Result& vad_data);
@@ -63,6 +63,7 @@
 
   ApmDataDumper* const apm_data_dumper_;
 
+  const float initial_speech_level_dbfs_;
   const int adjacent_speech_frames_threshold_;
   LevelEstimatorState preliminary_state_;
   LevelEstimatorState reliable_state_;
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 c55950a..1cdd91d 100644
--- a/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc
+++ b/modules/audio_processing/agc2/adaptive_mode_level_estimator_unittest.cc
@@ -13,37 +13,22 @@
 #include <memory>
 
 #include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/include/audio_processing.h"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/gunit.h"
 
 namespace webrtc {
 namespace {
 
+using AdaptiveDigitalConfig =
+    AudioProcessing::Config::GainController2::AdaptiveDigital;
+
 // Number of speech frames that the level estimator must observe in order to
 // become confident about the estimated level.
 constexpr int kNumFramesToConfidence =
     kLevelEstimatorTimeToConfidenceMs / kFrameDurationMs;
 static_assert(kNumFramesToConfidence > 0, "");
 
-// Fake levels and speech probabilities used in the tests.
-static_assert(kInitialSpeechLevelEstimateDbfs < 0.0f, "");
-constexpr float kVadLevelRms = kInitialSpeechLevelEstimateDbfs / 2.0f;
-constexpr float kVadLevelPeak = kInitialSpeechLevelEstimateDbfs / 3.0f;
-static_assert(kVadLevelRms < kVadLevelPeak, "");
-static_assert(kVadLevelRms > kInitialSpeechLevelEstimateDbfs, "");
-static_assert(kVadLevelRms - kInitialSpeechLevelEstimateDbfs > 5.0f,
-              "Adjust `kVadLevelRms` so that the difference from the initial "
-              "level is wide enough for the tests.");
-
-constexpr VadLevelAnalyzer::Result kVadDataSpeech{/*speech_probability=*/1.0f,
-                                                  kVadLevelRms, kVadLevelPeak};
-constexpr VadLevelAnalyzer::Result kVadDataNonSpeech{
-    /*speech_probability=*/kVadConfidenceThreshold / 2.0f, kVadLevelRms,
-    kVadLevelPeak};
-
-constexpr float kMinSpeechProbability = 0.0f;
-constexpr float kMaxSpeechProbability = 1.0f;
-
 constexpr float kConvergenceSpeedTestsLevelTolerance = 0.5f;
 
 // Provides the `vad_level` value `num_iterations` times to `level_estimator`.
@@ -55,31 +40,51 @@
   }
 }
 
+constexpr AdaptiveDigitalConfig GetAdaptiveDigitalConfig(
+    int adjacent_speech_frames_threshold) {
+  AdaptiveDigitalConfig config;
+  config.adjacent_speech_frames_threshold = adjacent_speech_frames_threshold;
+  return config;
+}
+
 // Level estimator with data dumper.
 struct TestLevelEstimator {
-  TestLevelEstimator()
+  explicit TestLevelEstimator(int adjacent_speech_frames_threshold)
       : data_dumper(0),
         estimator(std::make_unique<AdaptiveModeLevelEstimator>(
             &data_dumper,
-            /*adjacent_speech_frames_threshold=*/1)) {}
+            GetAdaptiveDigitalConfig(adjacent_speech_frames_threshold))),
+        initial_speech_level_dbfs(estimator->level_dbfs()),
+        vad_level_rms(initial_speech_level_dbfs / 2.0f),
+        vad_level_peak(initial_speech_level_dbfs / 3.0f),
+        vad_data_speech(
+            {/*speech_probability=*/1.0f, vad_level_rms, vad_level_peak}),
+        vad_data_non_speech(
+            {/*speech_probability=*/kVadConfidenceThreshold / 2.0f,
+             vad_level_rms, vad_level_peak}) {
+    RTC_DCHECK_LT(vad_level_rms, vad_level_peak);
+    RTC_DCHECK_LT(initial_speech_level_dbfs, vad_level_rms);
+    RTC_DCHECK_GT(vad_level_rms - initial_speech_level_dbfs, 5.0f)
+        << "Adjust `vad_level_rms` so that the difference from the initial "
+           "level is wide enough for the tests";
+  }
   ApmDataDumper data_dumper;
   std::unique_ptr<AdaptiveModeLevelEstimator> estimator;
+  const float initial_speech_level_dbfs;
+  const float vad_level_rms;
+  const float vad_level_peak;
+  const VadLevelAnalyzer::Result vad_data_speech;
+  const VadLevelAnalyzer::Result vad_data_non_speech;
 };
 
-// Checks the initially estimated level.
-TEST(GainController2AdaptiveModeLevelEstimator, CheckInitialEstimate) {
-  TestLevelEstimator level_estimator;
-  EXPECT_FLOAT_EQ(level_estimator.estimator->level_dbfs(),
-                  kInitialSpeechLevelEstimateDbfs);
-}
-
 // Checks that the level estimator converges to a constant input speech level.
 TEST(GainController2AdaptiveModeLevelEstimator, LevelStabilizes) {
-  TestLevelEstimator level_estimator;
-  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech,
+  TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1);
+  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence,
+                     level_estimator.vad_data_speech,
                      *level_estimator.estimator);
   const float estimated_level_dbfs = level_estimator.estimator->level_dbfs();
-  RunOnConstantLevel(/*num_iterations=*/1, kVadDataSpeech,
+  RunOnConstantLevel(/*num_iterations=*/1, level_estimator.vad_data_speech,
                      *level_estimator.estimator);
   EXPECT_NEAR(level_estimator.estimator->level_dbfs(), estimated_level_dbfs,
               0.1f);
@@ -88,17 +93,19 @@
 // Checks that the level controller does not become confident when too few
 // speech frames are observed.
 TEST(GainController2AdaptiveModeLevelEstimator, IsNotConfident) {
-  TestLevelEstimator level_estimator;
+  TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1);
   RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence / 2,
-                     kVadDataSpeech, *level_estimator.estimator);
+                     level_estimator.vad_data_speech,
+                     *level_estimator.estimator);
   EXPECT_FALSE(level_estimator.estimator->IsConfident());
 }
 
 // Checks that the level controller becomes confident when enough speech frames
 // are observed.
 TEST(GainController2AdaptiveModeLevelEstimator, IsConfident) {
-  TestLevelEstimator level_estimator;
-  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech,
+  TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1);
+  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence,
+                     level_estimator.vad_data_speech,
                      *level_estimator.estimator);
   EXPECT_TRUE(level_estimator.estimator->IsConfident());
 }
@@ -107,14 +114,15 @@
 // frames.
 TEST(GainController2AdaptiveModeLevelEstimator,
      EstimatorIgnoresNonSpeechFrames) {
-  TestLevelEstimator level_estimator;
+  TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1);
   // Simulate speech.
-  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech,
+  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence,
+                     level_estimator.vad_data_speech,
                      *level_estimator.estimator);
   const float estimated_level_dbfs = level_estimator.estimator->level_dbfs();
   // Simulate full-scale non-speech.
   RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence,
-                     VadLevelAnalyzer::Result{kMinSpeechProbability,
+                     VadLevelAnalyzer::Result{/*speech_probability=*/0.0f,
                                               /*rms_dbfs=*/0.0f,
                                               /*peak_dbfs=*/0.0f},
                      *level_estimator.estimator);
@@ -126,28 +134,30 @@
 // Checks the convergence speed of the estimator before it becomes confident.
 TEST(GainController2AdaptiveModeLevelEstimator,
      ConvergenceSpeedBeforeConfidence) {
-  TestLevelEstimator level_estimator;
-  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence, kVadDataSpeech,
+  TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1);
+  RunOnConstantLevel(/*num_iterations=*/kNumFramesToConfidence,
+                     level_estimator.vad_data_speech,
                      *level_estimator.estimator);
-  EXPECT_NEAR(level_estimator.estimator->level_dbfs(), kVadDataSpeech.rms_dbfs,
+  EXPECT_NEAR(level_estimator.estimator->level_dbfs(),
+              level_estimator.vad_data_speech.rms_dbfs,
               kConvergenceSpeedTestsLevelTolerance);
 }
 
 // Checks the convergence speed of the estimator after it becomes confident.
 TEST(GainController2AdaptiveModeLevelEstimator,
      ConvergenceSpeedAfterConfidence) {
-  TestLevelEstimator level_estimator;
+  TestLevelEstimator level_estimator(/*adjacent_speech_frames_threshold=*/1);
   // Reach confidence using the initial level estimate.
   RunOnConstantLevel(
       /*num_iterations=*/kNumFramesToConfidence,
       VadLevelAnalyzer::Result{
-          kMaxSpeechProbability,
-          /*rms_dbfs=*/kInitialSpeechLevelEstimateDbfs,
-          /*peak_dbfs=*/kInitialSpeechLevelEstimateDbfs + 6.0f},
+          /*speech_probability=*/1.0f,
+          /*rms_dbfs=*/level_estimator.initial_speech_level_dbfs,
+          /*peak_dbfs=*/level_estimator.initial_speech_level_dbfs + 6.0f},
       *level_estimator.estimator);
   // No estimate change should occur, but confidence is achieved.
   ASSERT_FLOAT_EQ(level_estimator.estimator->level_dbfs(),
-                  kInitialSpeechLevelEstimateDbfs);
+                  level_estimator.initial_speech_level_dbfs);
   ASSERT_TRUE(level_estimator.estimator->IsConfident());
   // After confidence.
   constexpr float kConvergenceTimeAfterConfidenceNumFrames = 600;  // 6 seconds.
@@ -155,8 +165,9 @@
       kConvergenceTimeAfterConfidenceNumFrames > kNumFramesToConfidence, "");
   RunOnConstantLevel(
       /*num_iterations=*/kConvergenceTimeAfterConfidenceNumFrames,
-      kVadDataSpeech, *level_estimator.estimator);
-  EXPECT_NEAR(level_estimator.estimator->level_dbfs(), kVadDataSpeech.rms_dbfs,
+      level_estimator.vad_data_speech, *level_estimator.estimator);
+  EXPECT_NEAR(level_estimator.estimator->level_dbfs(),
+              level_estimator.vad_data_speech.rms_dbfs,
               kConvergenceSpeedTestsLevelTolerance);
 }
 
@@ -168,30 +179,26 @@
 
 TEST_P(AdaptiveModeLevelEstimatorParametrization,
        DoNotAdaptToShortSpeechSegments) {
-  ApmDataDumper apm_data_dumper(0);
-  AdaptiveModeLevelEstimator level_estimator(
-      &apm_data_dumper, adjacent_speech_frames_threshold());
-  const float initial_level = level_estimator.level_dbfs();
-  ASSERT_LT(initial_level, kVadDataSpeech.peak_dbfs);
+  TestLevelEstimator level_estimator(adjacent_speech_frames_threshold());
+  const float initial_level = level_estimator.estimator->level_dbfs();
+  ASSERT_LT(initial_level, level_estimator.vad_data_speech.peak_dbfs);
   for (int i = 0; i < adjacent_speech_frames_threshold() - 1; ++i) {
     SCOPED_TRACE(i);
-    level_estimator.Update(kVadDataSpeech);
-    EXPECT_EQ(initial_level, level_estimator.level_dbfs());
+    level_estimator.estimator->Update(level_estimator.vad_data_speech);
+    EXPECT_EQ(initial_level, level_estimator.estimator->level_dbfs());
   }
-  level_estimator.Update(kVadDataNonSpeech);
-  EXPECT_EQ(initial_level, level_estimator.level_dbfs());
+  level_estimator.estimator->Update(level_estimator.vad_data_non_speech);
+  EXPECT_EQ(initial_level, level_estimator.estimator->level_dbfs());
 }
 
 TEST_P(AdaptiveModeLevelEstimatorParametrization, AdaptToEnoughSpeechSegments) {
-  ApmDataDumper apm_data_dumper(0);
-  AdaptiveModeLevelEstimator level_estimator(
-      &apm_data_dumper, adjacent_speech_frames_threshold());
-  const float initial_level = level_estimator.level_dbfs();
-  ASSERT_LT(initial_level, kVadDataSpeech.peak_dbfs);
+  TestLevelEstimator level_estimator(adjacent_speech_frames_threshold());
+  const float initial_level = level_estimator.estimator->level_dbfs();
+  ASSERT_LT(initial_level, level_estimator.vad_data_speech.peak_dbfs);
   for (int i = 0; i < adjacent_speech_frames_threshold(); ++i) {
-    level_estimator.Update(kVadDataSpeech);
+    level_estimator.estimator->Update(level_estimator.vad_data_speech);
   }
-  EXPECT_LT(initial_level, level_estimator.level_dbfs());
+  EXPECT_LT(initial_level, level_estimator.estimator->level_dbfs());
 }
 
 INSTANTIATE_TEST_SUITE_P(GainController2,
diff --git a/modules/audio_processing/agc2/agc2_common.h b/modules/audio_processing/agc2/agc2_common.h
index da28d8d..4af8552 100644
--- a/modules/audio_processing/agc2/agc2_common.h
+++ b/modules/audio_processing/agc2/agc2_common.h
@@ -24,38 +24,26 @@
 constexpr int kSubFramesInFrame = 20;
 constexpr int kMaximalNumberOfSamplesPerChannel = 480;
 
-// Adaptive digital gain applier settings below.
-constexpr float kHeadroomDbfs = 6.0f;
-constexpr float kMaxGainDb = 30.0f;
-constexpr float kInitialAdaptiveDigitalGainDb = 8.0f;
+// Adaptive digital gain applier settings.
+
 // At what limiter levels should we start decreasing the adaptive digital gain.
 constexpr float kLimiterThresholdForAgcGainDbfs = -1.0f;
 
 // This is the threshold for speech. Speech frames are used for updating the
 // speech level, measuring the amount of speech, and decide when to allow target
-// gain reduction.
+// gain changes.
 constexpr float kVadConfidenceThreshold = 0.95f;
 
-// Adaptive digital level estimator parameters.
 // Number of milliseconds of speech frames to observe to make the estimator
 // confident.
 constexpr float kLevelEstimatorTimeToConfidenceMs = 400;
 constexpr float kLevelEstimatorLeakFactor =
     1.0f - 1.0f / kLevelEstimatorTimeToConfidenceMs;
 
-// Robust VAD probability and speech decisions.
-constexpr int kDefaultLevelEstimatorAdjacentSpeechFramesThreshold = 12;
-
 // Saturation Protector settings.
 constexpr float kSaturationProtectorInitialHeadroomDb = 20.0f;
 constexpr int kSaturationProtectorBufferSize = 4;
 
-// Set the initial speech level estimate so that `kInitialAdaptiveDigitalGainDb`
-// is applied at the beginning of the call.
-constexpr float kInitialSpeechLevelEstimateDbfs =
-    -kSaturationProtectorInitialHeadroomDb - kInitialAdaptiveDigitalGainDb -
-    kHeadroomDbfs;
-
 // Number of interpolation points for each region of the limiter.
 // These values have been tuned to limit the interpolated gain curve error given
 // the limiter parameters and allowing a maximum error of +/- 32768^-1.
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index 100a3c0..436effd 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -3107,6 +3107,18 @@
   b_adaptive.dry_run = a_adaptive.dry_run;
   EXPECT_EQ(a, b);
 
+  a_adaptive.headroom_db += 1.0f;
+  b_adaptive.headroom_db = a_adaptive.headroom_db;
+  EXPECT_EQ(a, b);
+
+  a_adaptive.max_gain_db += 1.0f;
+  b_adaptive.max_gain_db = a_adaptive.max_gain_db;
+  EXPECT_EQ(a, b);
+
+  a_adaptive.initial_gain_db += 1.0f;
+  b_adaptive.initial_gain_db = a_adaptive.initial_gain_db;
+  EXPECT_EQ(a, b);
+
   a_adaptive.vad_reset_period_ms++;
   b_adaptive.vad_reset_period_ms = a_adaptive.vad_reset_period_ms;
   EXPECT_EQ(a, b);
@@ -3164,6 +3176,18 @@
   EXPECT_NE(a, b);
   a_adaptive = b_adaptive;
 
+  a_adaptive.headroom_db += 1.0f;
+  EXPECT_NE(a, b);
+  a_adaptive = b_adaptive;
+
+  a_adaptive.max_gain_db += 1.0f;
+  EXPECT_NE(a, b);
+  a_adaptive = b_adaptive;
+
+  a_adaptive.initial_gain_db += 1.0f;
+  EXPECT_NE(a, b);
+  a_adaptive = b_adaptive;
+
   a_adaptive.vad_reset_period_ms++;
   EXPECT_NE(a, b);
   a_adaptive = b_adaptive;
diff --git a/modules/audio_processing/gain_controller2.cc b/modules/audio_processing/gain_controller2.cc
index 195044a..8a22fed 100644
--- a/modules/audio_processing/gain_controller2.cc
+++ b/modules/audio_processing/gain_controller2.cc
@@ -105,7 +105,9 @@
     const AudioProcessing::Config::GainController2& config) {
   const auto& fixed = config.fixed_digital;
   const auto& adaptive = config.adaptive_digital;
-  return fixed.gain_db >= 0.f && fixed.gain_db < 50.f &&
+  return fixed.gain_db >= 0.0f && fixed.gain_db < 50.f &&
+         adaptive.headroom_db >= 0.0f && adaptive.max_gain_db > 0.0f &&
+         adaptive.initial_gain_db >= 0.0f &&
          adaptive.max_gain_change_db_per_second > 0.0f &&
          adaptive.max_output_noise_level_dbfs <= 0.0f;
 }
diff --git a/modules/audio_processing/gain_controller2_unittest.cc b/modules/audio_processing/gain_controller2_unittest.cc
index c8ee113..d1c1f5b 100644
--- a/modules/audio_processing/gain_controller2_unittest.cc
+++ b/modules/audio_processing/gain_controller2_unittest.cc
@@ -89,6 +89,36 @@
   EXPECT_TRUE(GainController2::Validate(config));
 }
 
+TEST(GainController2, CheckHeadroomDb) {
+  AudioProcessing::Config::GainController2 config;
+  config.adaptive_digital.headroom_db = -1.0f;
+  EXPECT_FALSE(GainController2::Validate(config));
+  config.adaptive_digital.headroom_db = 0.0f;
+  EXPECT_TRUE(GainController2::Validate(config));
+  config.adaptive_digital.headroom_db = 5.0f;
+  EXPECT_TRUE(GainController2::Validate(config));
+}
+
+TEST(GainController2, CheckMaxGainDb) {
+  AudioProcessing::Config::GainController2 config;
+  config.adaptive_digital.max_gain_db = -1.0f;
+  EXPECT_FALSE(GainController2::Validate(config));
+  config.adaptive_digital.max_gain_db = 0.0f;
+  EXPECT_FALSE(GainController2::Validate(config));
+  config.adaptive_digital.max_gain_db = 5.0f;
+  EXPECT_TRUE(GainController2::Validate(config));
+}
+
+TEST(GainController2, CheckInitialGainDb) {
+  AudioProcessing::Config::GainController2 config;
+  config.adaptive_digital.initial_gain_db = -1.0f;
+  EXPECT_FALSE(GainController2::Validate(config));
+  config.adaptive_digital.initial_gain_db = 0.0f;
+  EXPECT_TRUE(GainController2::Validate(config));
+  config.adaptive_digital.initial_gain_db = 5.0f;
+  EXPECT_TRUE(GainController2::Validate(config));
+}
+
 TEST(GainController2, CheckAdaptiveDigitalMaxGainChangeSpeedConfig) {
   AudioProcessing::Config::GainController2 config;
   config.adaptive_digital.max_gain_change_db_per_second = -1.0f;
diff --git a/modules/audio_processing/include/audio_processing.cc b/modules/audio_processing/include/audio_processing.cc
index 2286196..ddd8078 100644
--- a/modules/audio_processing/include/audio_processing.cc
+++ b/modules/audio_processing/include/audio_processing.cc
@@ -90,6 +90,8 @@
 bool Agc2Config::AdaptiveDigital::operator==(
     const Agc2Config::AdaptiveDigital& rhs) const {
   return enabled == rhs.enabled && dry_run == rhs.dry_run &&
+         headroom_db == rhs.headroom_db && max_gain_db == rhs.max_gain_db &&
+         initial_gain_db == rhs.initial_gain_db &&
          vad_reset_period_ms == rhs.vad_reset_period_ms &&
          adjacent_speech_frames_threshold ==
              rhs.adjacent_speech_frames_threshold &&
@@ -197,6 +199,10 @@
       << " }, adaptive_digital: { enabled: "
       << gain_controller2.adaptive_digital.enabled
       << ", dry_run: " << gain_controller2.adaptive_digital.dry_run
+      << ", headroom_db: " << gain_controller2.adaptive_digital.headroom_db
+      << ", max_gain_db: " << gain_controller2.adaptive_digital.max_gain_db
+      << ", initial_gain_db: "
+      << gain_controller2.adaptive_digital.initial_gain_db
       << ", vad_reset_period_ms: "
       << gain_controller2.adaptive_digital.vad_reset_period_ms
       << ", adjacent_speech_frames_threshold: "
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 8f07c6e..121e430 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -367,12 +367,19 @@
         }
 
         bool enabled = false;
-        // Run the adaptive digital controller but the signal is not modified.
+        // When true, the adaptive digital controller runs but the signal is not
+        // modified.
         bool dry_run = false;
+        float headroom_db = 6.0f;
+        // TODO(bugs.webrtc.org/7494): Consider removing and inferring from
+        // `max_output_noise_level_dbfs`.
+        float max_gain_db = 30.0f;
+        float initial_gain_db = 8.0f;
         int vad_reset_period_ms = 1500;
         int adjacent_speech_frames_threshold = 12;
         float max_gain_change_db_per_second = 3.0f;
         float max_output_noise_level_dbfs = -50.0f;
+        // TODO(bugs.webrtc.org/7494): Replace with field trials.
         bool sse2_allowed = true;
         bool avx2_allowed = true;
         bool neon_allowed = true;