Unify AGC2 experiment field trials into one

In order to experiment with AGC2 and TS at the same time, 3 field
trials are removed and merged into `WebRTC-Audio-GainController2`,
which is existing.

New parameters for the `WebRTC-Audio-GainController2` field trial:
- `switch_to_agc2`: true by default; when true, the gain control
  switches to AGC2 (both for the input volume and for the adaptive
  digital gain);
- `min_input_volume`: minimum input volume enforced by the input
  volume controller when the applied input volume is not zero;
- `disallow_transient_suppressor_usage`: when true, TS is never
  created.

Removed field trials:
- `WebRTC-Audio-Agc2-MinInputVolume`: now a parameter of
  `WebRTC-Audio-GainController2`;
- `WebRTC-ApmTransientSuppressorKillSwitch`: now a parameter of
  `WebRTC-Audio-GainController2`;
- `WebRTC-Audio-TransientSuppressorVadMode`: automatically inferred
  from `WebRTC-Audio-GainController2`.

Bug: webrtc:7494
Change-Id: I452798c0862d71f9adae6d163fe841df05ca44d6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/287861
Commit-Queue: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Hanna Silen <silen@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38890}
diff --git a/modules/audio_processing/agc2/input_volume_controller.cc b/modules/audio_processing/agc2/input_volume_controller.cc
index fe1b3d9..bcc650f 100644
--- a/modules/audio_processing/agc2/input_volume_controller.cc
+++ b/modules/audio_processing/agc2/input_volume_controller.cc
@@ -50,29 +50,6 @@
   return config;
 }
 
-// Returns the minimum input volume to recommend.
-// If the "WebRTC-Audio-Agc2-MinInputVolume" field trial is specified, parses it
-// and returns the value specified after "Enabled-" if valid - i.e., in the
-// range 0-255. Otherwise returns the default value.
-// Example:
-// "WebRTC-Audio-Agc2-MinInputVolume/Enabled-80" => returns 80.
-int GetMinInputVolume() {
-  constexpr int kDefaultMinInputVolume = 12;
-  constexpr char kFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume";
-  if (!webrtc::field_trial::IsEnabled(kFieldTrial)) {
-    return kDefaultMinInputVolume;
-  }
-  std::string field_trial_str = webrtc::field_trial::FindFullName(kFieldTrial);
-  int min_input_volume = -1;
-  sscanf(field_trial_str.c_str(), "Enabled-%d", &min_input_volume);
-  if (min_input_volume >= 0 && min_input_volume <= 255) {
-    return min_input_volume;
-  }
-  RTC_LOG(LS_WARNING) << "[AGC2] Invalid volume for " << kFieldTrial
-                      << ", ignored.";
-  return kDefaultMinInputVolume;
-}
-
 // Returns an input volume in the [`min_input_volume`, `kMaxInputVolume`] range
 // that reduces `gain_error_db`, which is a gain error estimated when
 // `input_volume` was applied, according to a fixed gain map.
@@ -377,7 +354,7 @@
 InputVolumeController::InputVolumeController(int num_capture_channels,
                                              const Config& config)
     : num_capture_channels_(num_capture_channels),
-      min_input_volume_(GetMinInputVolume()),
+      min_input_volume_(config.min_input_volume),
       capture_output_used_(true),
       clipped_level_step_(config.clipped_level_step),
       clipped_ratio_threshold_(config.clipped_ratio_threshold),
diff --git a/modules/audio_processing/agc2/input_volume_controller.h b/modules/audio_processing/agc2/input_volume_controller.h
index 95ed160..40eae88 100644
--- a/modules/audio_processing/agc2/input_volume_controller.h
+++ b/modules/audio_processing/agc2/input_volume_controller.h
@@ -35,6 +35,9 @@
  public:
   // Config for the constructor.
   struct Config {
+    // Minimum input volume that can be recommended. Not enforced when the
+    // applied input volume is zero outside startup.
+    int min_input_volume = 20;
     // Lowest input volume level that will be applied in response to clipping.
     int clipped_level_min = 70;
     // Amount input volume level is lowered with every clipping event. Limited
@@ -52,13 +55,9 @@
     // [`target_range_min_dbfs`, `target_range_max_dbfs`], no input volume
     // adjustments are done based on the speech level. For speech levels below
     // and above the range, the targets `target_range_min_dbfs` and
-    // `target_range_max_dbfs` are used, respectively. The example values
-    // `target_range_max_dbfs` -18 and `target_range_min_dbfs` -48 refer to a
-    // configuration where the zero-digital-gain target is -18 dBFS and the
-    // digital gain control is expected to compensate for speech level errors
-    // up to -30 dB.
-    int target_range_max_dbfs = -18;
-    int target_range_min_dbfs = -48;
+    // `target_range_max_dbfs` are used, respectively.
+    int target_range_max_dbfs = -30;
+    int target_range_min_dbfs = -50;
     // Number of wait frames between the recommended input volume updates.
     int update_input_volume_wait_frames = 100;
     // Speech probability threshold: speech probabilities below the threshold
diff --git a/modules/audio_processing/agc2/input_volume_controller_unittest.cc b/modules/audio_processing/agc2/input_volume_controller_unittest.cc
index f1ce5c4..3979b2d 100644
--- a/modules/audio_processing/agc2/input_volume_controller_unittest.cc
+++ b/modules/audio_processing/agc2/input_volume_controller_unittest.cc
@@ -38,7 +38,7 @@
 constexpr int kInitialInputVolume = 128;
 constexpr int kClippedMin = 165;  // Arbitrary, but different from the default.
 constexpr float kAboveClippedThreshold = 0.2f;
-constexpr int kMinMicLevel = 12;
+constexpr int kMinMicLevel = 20;
 constexpr int kClippedLevelStep = 15;
 constexpr float kClippedRatioThreshold = 0.1f;
 constexpr int kClippedWaitFrames = 300;
@@ -56,7 +56,6 @@
 
 using InputVolumeControllerConfig = InputVolumeController::Config;
 
-constexpr InputVolumeControllerConfig kDefaultInputVolumeControllerConfig{};
 constexpr ClippingPredictorConfig kDefaultClippingPredictorConfig{};
 
 std::unique_ptr<InputVolumeController> CreateInputVolumeController(
@@ -66,6 +65,7 @@
     bool enable_clipping_predictor = false,
     int update_input_volume_wait_frames = 0) {
   InputVolumeControllerConfig config{
+      .min_input_volume = kMinMicLevel,
       .clipped_level_min = kClippedMin,
       .clipped_level_step = clipped_level_step,
       .clipped_ratio_threshold = clipped_ratio_threshold,
@@ -82,34 +82,6 @@
                                                  config);
 }
 
-constexpr char kMinInputVolumeFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume";
-
-std::string GetAgcMinInputVolumeFieldTrial(const std::string& value) {
-  char field_trial_buffer[64];
-  rtc::SimpleStringBuilder builder(field_trial_buffer);
-  builder << kMinInputVolumeFieldTrial << "/" << value << "/";
-  return builder.str();
-}
-
-std::string GetAgcMinInputVolumeFieldTrialEnabled(
-    int enabled_value,
-    const std::string& suffix = "") {
-  RTC_DCHECK_GE(enabled_value, 0);
-  RTC_DCHECK_LE(enabled_value, 255);
-  char field_trial_buffer[64];
-  rtc::SimpleStringBuilder builder(field_trial_buffer);
-  builder << kMinInputVolumeFieldTrial << "/Enabled-" << enabled_value << suffix
-          << "/";
-  return builder.str();
-}
-
-std::string GetAgcMinInputVolumeFieldTrial(absl::optional<int> volume) {
-  if (volume.has_value()) {
-    return GetAgcMinInputVolumeFieldTrialEnabled(*volume);
-  }
-  return GetAgcMinInputVolumeFieldTrial("Disabled");
-}
-
 // (Over)writes `samples_value` for the samples in `audio_buffer`.
 // When `clipped_ratio`, a value in [0, 1], is greater than 0, the corresponding
 // fraction of the frame is set to a full scale value to simulate clipping.
@@ -150,31 +122,6 @@
   }
 }
 
-// Deprecated.
-// TODO(bugs.webrtc.org/7494): Delete this helper, use
-// `InputVolumeControllerTestHelper::CallAgcSequence()` instead.
-int CallAnalyzeAndRecommend(int num_calls,
-                            int initial_volume,
-                            const AudioBuffer& audio_buffer,
-                            float speech_probability,
-                            absl::optional<float> speech_level_dbfs,
-                            InputVolumeController& controller) {
-  RTC_DCHECK(controller.capture_output_used());
-  int volume = initial_volume;
-  for (int n = 0; n < num_calls; ++n) {
-    controller.AnalyzeInputAudio(volume, audio_buffer);
-    const auto recommended_input_volume =
-        controller.RecommendInputVolume(speech_probability, speech_level_dbfs);
-
-    // Expect no errors: Applied volume set for every frame;
-    // `RecommendInputVolume()` returns a non-empty value.
-    EXPECT_TRUE(recommended_input_volume.has_value());
-
-    volume = *recommended_input_volume;
-  }
-  return volume;
-}
-
 // Reads a given number of 10 ms chunks from a PCM file and feeds them to
 // `InputVolumeController`.
 class SpeechSamplesReader {
@@ -379,24 +326,12 @@
 };
 
 class InputVolumeControllerParametrizedTest
-    : public ::testing::TestWithParam<absl::optional<int>> {
- protected:
-  InputVolumeControllerParametrizedTest()
-      : field_trials_(GetAgcMinInputVolumeFieldTrial(GetParam())) {}
-
-  int GetMinInputVolume() const { return GetParam().value_or(kMinMicLevel); }
-
- private:
-  test::ScopedFieldTrials field_trials_;
-};
-
-INSTANTIATE_TEST_SUITE_P(,
-                         InputVolumeControllerParametrizedTest,
-                         ::testing::Values(absl::nullopt, 12, 20));
+    : public ::testing::TestWithParam<int> {};
 
 TEST_P(InputVolumeControllerParametrizedTest,
        StartupMinVolumeConfigurationIsRespected) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
 
   EXPECT_EQ(*helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
                                     kSpeechLevel),
@@ -404,7 +339,9 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, MicVolumeResponseToRmsError) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   int volume = *helper.CallAgcSequence(kInitialInputVolume,
                                        kHighSpeechProbability, kSpeechLevel);
 
@@ -446,7 +383,10 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, MicVolumeIsLimited) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  const int min_input_volume = GetParam();
+  config.min_input_volume = min_input_volume;
+  InputVolumeControllerTestHelper helper(config);
   int volume = *helper.CallAgcSequence(kInitialInputVolume,
                                        kHighSpeechProbability, kSpeechLevel);
 
@@ -492,16 +432,18 @@
   // Won't go lower than the minimum.
   volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
                                            kHighSpeechProbability, 22.0f);
-  EXPECT_EQ(volume, std::max(18, GetMinInputVolume()));
+  EXPECT_EQ(volume, std::max(18, min_input_volume));
 
   volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
                                            kHighSpeechProbability, 22.0f);
-  EXPECT_EQ(volume, std::max(12, GetMinInputVolume()));
+  EXPECT_EQ(volume, std::max(12, min_input_volume));
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, NoActionWhileMuted) {
-  InputVolumeControllerTestHelper helper_1;
-  InputVolumeControllerTestHelper helper_2;
+  InputVolumeControllerTestHelper helper_1(
+      /*config=*/{.min_input_volume = GetParam()});
+  InputVolumeControllerTestHelper helper_2(
+      /*config=*/{.min_input_volume = GetParam()});
 
   int volume_1 = *helper_1.CallAgcSequence(/*applied_input_volume=*/255,
                                            kHighSpeechProbability, kSpeechLevel,
@@ -531,7 +473,8 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        UnmutingChecksVolumeWithoutRaising) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -548,7 +491,9 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, UnmutingRaisesTooLowVolume) {
-  InputVolumeControllerTestHelper helper;
+  const int min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = min_input_volume});
   helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -560,12 +505,14 @@
   EXPECT_EQ(
       helper.CallRecommendInputVolume(/*num_calls=*/1, kInputVolume,
                                       kHighSpeechProbability, kSpeechLevel),
-      GetMinInputVolume());
+      min_input_volume);
 }
 
 TEST_P(InputVolumeControllerParametrizedTest,
        ManualLevelChangeResultsInNoSetMicCall) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   int volume = *helper.CallAgcSequence(kInitialInputVolume,
                                        kHighSpeechProbability, kSpeechLevel);
 
@@ -589,7 +536,9 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        RecoveryAfterManualLevelChangeFromMax) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   int volume = *helper.CallAgcSequence(kInitialInputVolume,
                                        kHighSpeechProbability, kSpeechLevel);
 
@@ -621,7 +570,10 @@
 // of the input volume.
 TEST_P(InputVolumeControllerParametrizedTest,
        EnforceMinInputVolumeDuringUpwardsAdjustment) {
-  InputVolumeControllerTestHelper helper;
+  const int min_input_volume = GetParam();
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = min_input_volume;
+  InputVolumeControllerTestHelper helper(config);
   int volume = *helper.CallAgcSequence(kInitialInputVolume,
                                        kHighSpeechProbability, kSpeechLevel);
 
@@ -631,19 +583,19 @@
       /*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f);
 
   // Trigger an upward adjustment of the input volume.
-  EXPECT_EQ(volume, GetMinInputVolume());
+  EXPECT_EQ(volume, min_input_volume);
   volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
                                            kHighSpeechProbability, -29.0f);
-  EXPECT_EQ(volume, GetMinInputVolume());
+  EXPECT_EQ(volume, min_input_volume);
   volume = helper.CallRecommendInputVolume(/*num_calls=*/1, volume,
                                            kHighSpeechProbability, -30.0f);
-  EXPECT_EQ(volume, GetMinInputVolume());
+  EXPECT_EQ(volume, min_input_volume);
 
   // After a number of consistently low speech level observations, the input
   // volume is eventually raised above the minimum.
   volume = helper.CallRecommendInputVolume(/*num_calls=*/10, volume,
                                            kHighSpeechProbability, -38.0f);
-  EXPECT_GT(volume, GetMinInputVolume());
+  EXPECT_GT(volume, min_input_volume);
 }
 
 // Checks that, when the min mic level override is specified, AGC immediately
@@ -651,7 +603,9 @@
 // minimum gain to enforce.
 TEST_P(InputVolumeControllerParametrizedTest,
        RecoveryAfterManualLevelChangeBelowMin) {
-  InputVolumeControllerTestHelper helper;
+  const int min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = min_input_volume});
   int volume = *helper.CallAgcSequence(kInitialInputVolume,
                                        kHighSpeechProbability, kSpeechLevel);
 
@@ -659,11 +613,12 @@
   // AGC won't take any action.
   volume = helper.CallRecommendInputVolume(
       /*num_calls=*/1, /*initial_volume=*/1, kHighSpeechProbability, -17.0f);
-  EXPECT_EQ(volume, GetMinInputVolume());
+  EXPECT_EQ(volume, min_input_volume);
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, NoClippingHasNoImpact) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -673,7 +628,8 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        ClippingUnderThresholdHasNoImpact) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -682,7 +638,8 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, ClippingLowersVolume) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -692,7 +649,8 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        WaitingPeriodBetweenClippingChecks) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -710,7 +668,9 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, ClippingLoweringIsLimited) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   helper.CallAgcSequence(/*applied_input_volume=*/180, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -725,7 +685,8 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        ClippingMaxIsRespectedWhenEqualToLevel) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(/*applied_input_volume=*/255, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -740,7 +701,9 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        ClippingMaxIsRespectedWhenHigherThanLevel) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   helper.CallAgcSequence(/*applied_input_volume=*/200, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -758,7 +721,9 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, UserCanRaiseVolumeAfterClipping) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   helper.CallAgcSequence(/*applied_input_volume=*/225, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -787,7 +752,9 @@
 
 TEST_P(InputVolumeControllerParametrizedTest,
        ClippingDoesNotPullLowVolumeBackUp) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   helper.CallAgcSequence(/*applied_input_volume=*/80, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -798,7 +765,8 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, TakesNoActionOnZeroMicVolume) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = GetParam()});
   helper.CallAgcSequence(kInitialInputVolume, kHighSpeechProbability,
                          kSpeechLevel);
 
@@ -809,7 +777,9 @@
 }
 
 TEST_P(InputVolumeControllerParametrizedTest, ClippingDetectionLowersVolume) {
-  InputVolumeControllerTestHelper helper;
+  InputVolumeControllerConfig config = GetInputVolumeControllerTestConfig();
+  config.min_input_volume = GetParam();
+  InputVolumeControllerTestHelper helper(config);
   int volume = *helper.CallAgcSequence(/*applied_input_volume=*/255,
                                        kHighSpeechProbability, kSpeechLevel,
                                        /*num_calls=*/1);
@@ -829,298 +799,6 @@
   EXPECT_EQ(volume, 240);
 }
 
-TEST(InputVolumeControllerTest, MinInputVolumeDefault) {
-  std::unique_ptr<InputVolumeController> controller =
-      CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                  kClippedWaitFrames);
-  EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
-            kMinMicLevel);
-}
-
-TEST(InputVolumeControllerTest, MinInputVolumeDisabled) {
-  for (const std::string& field_trial_suffix : {"", "_20220210"}) {
-    test::ScopedFieldTrials field_trial(
-        GetAgcMinInputVolumeFieldTrial("Disabled" + field_trial_suffix));
-    std::unique_ptr<InputVolumeController> controller =
-        CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                    kClippedWaitFrames);
-
-    EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
-              kMinMicLevel);
-  }
-}
-
-// Checks that a field-trial parameter outside of the valid range [0,255] is
-// ignored.
-TEST(InputVolumeControllerTest, MinInputVolumeOutOfRangeAbove) {
-  test::ScopedFieldTrials field_trial(
-      GetAgcMinInputVolumeFieldTrial("Enabled-256"));
-  std::unique_ptr<InputVolumeController> controller =
-      CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                  kClippedWaitFrames);
-  EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
-            kMinMicLevel);
-}
-
-// Checks that a field-trial parameter outside of the valid range [0,255] is
-// ignored.
-TEST(InputVolumeControllerTest, MinInputVolumeOutOfRangeBelow) {
-  test::ScopedFieldTrials field_trial(
-      GetAgcMinInputVolumeFieldTrial("Enabled--1"));
-  std::unique_ptr<InputVolumeController> controller =
-      CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                  kClippedWaitFrames);
-  EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
-            kMinMicLevel);
-}
-
-// Verifies that a valid experiment changes the minimum microphone level. The
-// start volume is larger than the min level and should therefore not be
-// changed.
-TEST(InputVolumeControllerTest, MinInputVolumeEnabled50) {
-  constexpr int kMinInputVolume = 50;
-  for (const std::string& field_trial_suffix : {"", "_20220210"}) {
-    SCOPED_TRACE(field_trial_suffix);
-    test::ScopedFieldTrials field_trial(GetAgcMinInputVolumeFieldTrialEnabled(
-        kMinInputVolume, field_trial_suffix));
-    std::unique_ptr<InputVolumeController> controller =
-        CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                    kClippedWaitFrames);
-
-    EXPECT_EQ(controller->channel_controllers_[0]->min_input_volume(),
-              kMinInputVolume);
-  }
-}
-
-// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
-// specified with a valid value, the mic level never gets lowered beyond the
-// override value in the presence of clipping.
-TEST(InputVolumeControllerTest, MinInputVolumeCheckMinLevelWithClipping) {
-  constexpr int kMinInputVolume = 250;
-
-  // Create and initialize two AGCs by specifying and leaving unspecified the
-  // relevant field trial.
-  const auto factory = []() {
-    std::unique_ptr<InputVolumeController> controller =
-        CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                    kClippedWaitFrames);
-    controller->Initialize();
-    return controller;
-  };
-  std::unique_ptr<InputVolumeController> controller = factory();
-  std::unique_ptr<InputVolumeController> controller_with_override;
-  {
-    test::ScopedFieldTrials field_trial(
-        GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
-    controller_with_override = factory();
-  }
-
-  // Create a test input signal which containts 80% of clipped samples.
-  AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
-                           1);
-  WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
-                          audio_buffer);
-
-  // Simulate 4 seconds of clipping; it is expected to trigger a downward
-  // adjustment of the analog gain. Use low speech probability to limit the
-  // volume changes to clipping handling.
-  const int volume = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      kLowSpeechProbability, /*speech_level_dbfs=*/-42.0f, *controller);
-  const int volume_with_override = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      kLowSpeechProbability, /*speech_level_dbfs=*/-42.0f,
-      *controller_with_override);
-
-  // Make sure that an adaptation occurred.
-  ASSERT_GT(volume, 0);
-
-  // Check that the test signal triggers a larger downward adaptation for
-  // `controller`, which is allowed to reach a lower gain.
-  EXPECT_GT(volume_with_override, volume);
-  // Check that the gain selected by `controller_with_override` equals the
-  // minimum value overridden via field trial.
-  EXPECT_EQ(volume_with_override, kMinInputVolume);
-}
-
-// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
-// specified with a valid value, the mic level never gets lowered beyond the
-// override value in the presence of clipping when RMS error is not empty.
-// TODO(webrtc:7494): Revisit the test after moving the number of update wait
-// frames to APM config. The test passes but internally the gain update timing
-// differs.
-TEST(InputVolumeControllerTest,
-     MinInputVolumeCheckMinLevelWithClippingWithRmsError) {
-  constexpr int kMinInputVolume = 250;
-
-  // Create and initialize two AGCs by specifying and leaving unspecified the
-  // relevant field trial.
-  const auto factory = []() {
-    std::unique_ptr<InputVolumeController> controller =
-        CreateInputVolumeController(kClippedLevelStep, kClippedRatioThreshold,
-                                    kClippedWaitFrames);
-    controller->Initialize();
-    return controller;
-  };
-  std::unique_ptr<InputVolumeController> controller = factory();
-  std::unique_ptr<InputVolumeController> controller_with_override;
-  {
-    test::ScopedFieldTrials field_trial(
-        GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
-    controller_with_override = factory();
-  }
-
-  // Create a test input signal which containts 80% of clipped samples.
-  AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
-                           1);
-  WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
-                          audio_buffer);
-
-  // Simulate 4 seconds of clipping; it is expected to trigger a downward
-  // adjustment of the analog gain.
-  const int volume = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      kHighSpeechProbability,
-      /*speech_level_dbfs=*/-18.0f, *controller);
-  const int volume_with_override = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      kHighSpeechProbability,
-      /*speech_level_dbfs=*/-18.0f, *controller_with_override);
-
-  // Make sure that an adaptation occurred.
-  ASSERT_GT(volume, 0);
-
-  // Check that the test signal triggers a larger downward adaptation for
-  // `controller`, which is allowed to reach a lower gain.
-  EXPECT_GT(volume_with_override, volume);
-
-  // Check that the gain selected by `controller_with_override` equals the
-  // minimum value overridden via field trial.
-  EXPECT_EQ(volume_with_override, kMinInputVolume);
-}
-
-// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
-// specified with a value lower than the `clipped_level_min`, the behavior of
-// the analog gain controller is the same as that obtained when the field trial
-// is not specified.
-TEST(InputVolumeControllerTest, MinInputVolumeCompareMicLevelWithClipping) {
-  // Create and initialize two AGCs by specifying and leaving unspecified the
-  // relevant field trial.
-  const auto factory = []() {
-    // Use a large clipped level step to more quickly decrease the analog gain
-    // with clipping.
-    InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig;
-    config.clipped_level_step = 64;
-    config.clipped_ratio_threshold = kClippedRatioThreshold;
-    config.clipped_wait_frames = kClippedWaitFrames;
-    auto controller = std::make_unique<InputVolumeController>(
-        /*num_capture_channels=*/1, config);
-    controller->Initialize();
-    return controller;
-  };
-  std::unique_ptr<InputVolumeController> controller = factory();
-  std::unique_ptr<InputVolumeController> controller_with_override;
-  {
-    constexpr int kMinInputVolume = 20;
-    static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >=
-                      kMinInputVolume,
-                  "Use a lower override value.");
-    test::ScopedFieldTrials field_trial(
-        GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
-    controller_with_override = factory();
-  }
-
-  // Create a test input signal which containts 80% of clipped samples.
-  AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
-                           1);
-  WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
-                          audio_buffer);
-
-  // Simulate 4 seconds of clipping; it is expected to trigger a downward
-  // adjustment of the analog gain. Use low speech probability to limit the
-  // volume changes to clipping handling.
-  const int volume = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      kLowSpeechProbability, /*speech_level_dbfs=*/-18, *controller);
-  const int volume_with_override = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      kLowSpeechProbability, /*speech_level_dbfs=*/-18,
-      *controller_with_override);
-
-  // Make sure that an adaptation occurred.
-  ASSERT_GT(volume, 0);
-
-  // Check that the selected analog gain is the same for both controllers and
-  // that it equals the minimum level reached when clipping is handled. That is
-  // expected because the minimum microphone level override is less than the
-  // minimum level used when clipping is detected.
-  EXPECT_EQ(volume, volume_with_override);
-  EXPECT_EQ(volume_with_override,
-            kDefaultInputVolumeControllerConfig.clipped_level_min);
-}
-
-// Checks that, when the "WebRTC-Audio-Agc2-MinInputVolume" field trial is
-// specified with a value lower than the `clipped_level_min`, the behavior of
-// the analog gain controller is the same as that obtained when the field trial
-// is not specified.
-// TODO(webrtc:7494): Revisit the test after moving the number of update wait
-// frames to APM config. The test passes but internally the gain update timing
-// differs.
-TEST(InputVolumeControllerTest,
-     MinInputVolumeCompareMicLevelWithClippingWithRmsError) {
-  // Create and initialize two AGCs by specifying and leaving unspecified the
-  // relevant field trial.
-  const auto factory = []() {
-    // Use a large clipped level step to more quickly decrease the analog gain
-    // with clipping.
-    InputVolumeControllerConfig config = kDefaultInputVolumeControllerConfig;
-    config.clipped_level_step = 64;
-    config.clipped_ratio_threshold = kClippedRatioThreshold;
-    config.clipped_wait_frames = kClippedWaitFrames;
-    auto controller = std::make_unique<InputVolumeController>(
-        /*num_capture_channels=*/1, config);
-    controller->Initialize();
-    return controller;
-  };
-  std::unique_ptr<InputVolumeController> controller = factory();
-  std::unique_ptr<InputVolumeController> controller_with_override;
-  {
-    constexpr int kMinInputVolume = 20;
-    static_assert(kDefaultInputVolumeControllerConfig.clipped_level_min >=
-                      kMinInputVolume,
-                  "Use a lower override value.");
-    test::ScopedFieldTrials field_trial(
-        GetAgcMinInputVolumeFieldTrialEnabled(kMinInputVolume));
-    controller_with_override = factory();
-  }
-
-  // Create a test input signal which containts 80% of clipped samples.
-  AudioBuffer audio_buffer(kSampleRateHz, 1, kSampleRateHz, 1, kSampleRateHz,
-                           1);
-  WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
-                          audio_buffer);
-
-  const int volume = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      /*speech_probability=*/0.7f,
-      /*speech_level_dbfs=*/-18.0f, *controller);
-  const int volume_with_override = CallAnalyzeAndRecommend(
-      /*num_calls=*/400, kInitialInputVolume, audio_buffer,
-      /*speech_probability=*/0.7f,
-      /*speech_level_dbfs=*/-18.0f, *controller_with_override);
-
-  // Make sure that an adaptation occurred.
-  ASSERT_GT(volume, 0);
-
-  // Check that the selected analog gain is the same for both controllers and
-  // that it equals the minimum level reached when clipping is handled. That is
-  // expected because the minimum microphone level override is less than the
-  // minimum level used when clipping is detected.
-  EXPECT_EQ(volume, volume_with_override);
-  EXPECT_EQ(volume_with_override,
-            kDefaultInputVolumeControllerConfig.clipped_level_min);
-}
-
 // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_level_step`.
 // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_ratio_threshold`.
 // TODO(bugs.webrtc.org/12774): Test the bahavior of `clipped_wait_frames`.
@@ -1416,6 +1094,42 @@
   ASSERT_GT(volume_wait_100, kInputVolume);
 }
 
+INSTANTIATE_TEST_SUITE_P(,
+                         InputVolumeControllerParametrizedTest,
+                         ::testing::Values(12, 20));
+
+TEST(InputVolumeControllerTest,
+     MinInputVolumeEnforcedWithClippingWhenAboveClippedLevelMin) {
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = 80, .clipped_level_min = 70});
+
+  // Trigger a downward adjustment caused by clipping input. Use a low speech
+  // probability to limit the volume changes to clipping handling.
+  WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
+                          helper.audio_buffer);
+  constexpr int kNumCalls = 800;
+  helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability,
+                         /*speech_level_dbfs=*/-18.0f, kNumCalls);
+
+  EXPECT_EQ(helper.controller.recommended_input_volume(), 80);
+}
+
+TEST(InputVolumeControllerTest,
+     ClippedlevelMinEnforcedWithClippingWhenAboveMinInputVolume) {
+  InputVolumeControllerTestHelper helper(
+      /*config=*/{.min_input_volume = 70, .clipped_level_min = 80});
+
+  // Trigger a downward adjustment caused by clipping input. Use a low speech
+  // probability to limit the volume changes to clipping handling.
+  WriteAudioBufferSamples(/*samples_value=*/4000.0f, /*clipped_ratio=*/0.8f,
+                          helper.audio_buffer);
+  constexpr int kNumCalls = 800;
+  helper.CallAgcSequence(/*applied_input_volume=*/100, kLowSpeechProbability,
+                         /*speech_level_dbfs=*/-18.0f, kNumCalls);
+
+  EXPECT_EQ(helper.controller.recommended_input_volume(), 80);
+}
+
 TEST(InputVolumeControllerTest, SpeechRatioThresholdIsEffective) {
   constexpr int kInputVolume = kInitialInputVolume;
   // Create two input volume controllers with 10 frames between volume updates
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index d28a44b..e92ae6d 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -69,29 +69,6 @@
       "WebRTC-Aec3SetupSpecificDefaultConfigDefaultsKillSwitch");
 }
 
-// If the "WebRTC-Audio-TransientSuppressorVadMode" field trial is unspecified,
-// returns `TransientSuppressor::VadMode::kDefault`, otherwise parses the field
-// trial and returns the specified mode:
-// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-Default returns `kDefault`;
-// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-RnnVad returns `kRnnVad`;
-// - WebRTC-Audio-TransientSuppressorVadMode/Enabled-NoVad returns `kNoVad`.
-TransientSuppressor::VadMode GetTransientSuppressorVadMode() {
-  constexpr char kFieldTrial[] = "WebRTC-Audio-TransientSuppressorVadMode";
-  std::string full_name = webrtc::field_trial::FindFullName(kFieldTrial);
-  if (full_name.empty() || absl::EndsWith(full_name, "-Default")) {
-    return TransientSuppressor::VadMode::kDefault;
-  }
-  if (absl::EndsWith(full_name, "-RnnVad")) {
-    return TransientSuppressor::VadMode::kRnnVad;
-  }
-  if (absl::EndsWith(full_name, "-NoVad")) {
-    return TransientSuppressor::VadMode::kNoVad;
-  }
-  // Fallback to default.
-  RTC_LOG(LS_WARNING) << "Invalid parameter for " << kFieldTrial;
-  return TransientSuppressor::VadMode::kDefault;
-}
-
 // Identify the native processing rate that best handles a sample rate.
 int SuitableProcessRate(int minimum_rate,
                         int max_splitting_rate,
@@ -325,17 +302,44 @@
   return error_code;
 }
 
-const absl::optional<AudioProcessingImpl::GainController2ConfigOverride>
-GetGainController2ConfigOverride() {
+using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod;
+
+void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) {
+  switch (method) {
+    case DownmixMethod::kAverageChannels:
+      buffer.set_downmixing_by_averaging();
+      break;
+    case DownmixMethod::kUseFirstChannel:
+      buffer.set_downmixing_to_specific_channel(/*channel=*/0);
+      break;
+  }
+}
+
+constexpr int kUnspecifiedDataDumpInputVolume = -100;
+
+}  // namespace
+
+// Throughout webrtc, it's assumed that success is represented by zero.
+static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero");
+
+absl::optional<AudioProcessingImpl::GainController2ExperimentParams>
+AudioProcessingImpl::GetGainController2ExperimentParams() {
   constexpr char kFieldTrialName[] = "WebRTC-Audio-GainController2";
 
   if (!field_trial::IsEnabled(kFieldTrialName)) {
     return absl::nullopt;
   }
 
-  constexpr InputVolumeController::Config kDefaultInputVolumeControllerConfig;
-
   FieldTrialFlag enabled("Enabled", false);
+
+  // Whether the gain control should switch to AGC2. Enabled by default.
+  FieldTrialParameter<bool> switch_to_agc2("switch_to_agc2", true);
+
+  // AGC2 input volume controller configuration.
+  constexpr InputVolumeController::Config kDefaultInputVolumeControllerConfig;
+  FieldTrialConstrained<int> min_input_volume(
+      "min_input_volume", kDefaultInputVolumeControllerConfig.min_input_volume,
+      0, 255);
   FieldTrialConstrained<int> clipped_level_min(
       "clipped_level_min",
       kDefaultInputVolumeControllerConfig.clipped_level_min, 0, 255);
@@ -369,9 +373,9 @@
       "speech_ratio_threshold",
       kDefaultInputVolumeControllerConfig.speech_ratio_threshold, 0, 1);
 
+  // AGC2 adaptive digital controller configuration.
   constexpr AudioProcessing::Config::GainController2::AdaptiveDigital
       kDefaultAdaptiveDigitalConfig;
-
   FieldTrialConstrained<double> headroom_db(
       "headroom_db", kDefaultAdaptiveDigitalConfig.headroom_db, 0,
       absl::nullopt);
@@ -390,83 +394,102 @@
       kDefaultAdaptiveDigitalConfig.max_output_noise_level_dbfs, absl::nullopt,
       0);
 
+  // Transient suppressor.
+  FieldTrialParameter<bool> disallow_transient_suppressor_usage(
+      "disallow_transient_suppressor_usage", false);
+
   // Field-trial based override for the input volume controller and adaptive
   // digital configs.
-  const std::string field_trial_name =
-      field_trial::FindFullName(kFieldTrialName);
-
   ParseFieldTrial(
-      {&enabled, &clipped_level_min, &clipped_level_step,
-       &clipped_ratio_threshold, &clipped_wait_frames,
+      {&enabled, &switch_to_agc2, &min_input_volume, &clipped_level_min,
+       &clipped_level_step, &clipped_ratio_threshold, &clipped_wait_frames,
        &enable_clipping_predictor, &target_range_max_dbfs,
        &target_range_min_dbfs, &update_input_volume_wait_frames,
        &speech_probability_threshold, &speech_ratio_threshold, &headroom_db,
        &max_gain_db, &initial_gain_db, &max_gain_change_db_per_second,
-       &max_output_noise_level_dbfs},
-      field_trial_name);
-
+       &max_output_noise_level_dbfs, &disallow_transient_suppressor_usage},
+      field_trial::FindFullName(kFieldTrialName));
   // Checked already by `IsEnabled()` before parsing, therefore always true.
   RTC_DCHECK(enabled);
 
-  return AudioProcessingImpl::GainController2ConfigOverride{
-      .input_volume_controller_config =
-          {
-              .clipped_level_min = static_cast<int>(clipped_level_min.Get()),
-              .clipped_level_step = static_cast<int>(clipped_level_step.Get()),
-              .clipped_ratio_threshold =
-                  static_cast<float>(clipped_ratio_threshold.Get()),
-              .clipped_wait_frames =
-                  static_cast<int>(clipped_wait_frames.Get()),
-              .enable_clipping_predictor =
-                  static_cast<bool>(enable_clipping_predictor.Get()),
-              .target_range_max_dbfs =
-                  static_cast<int>(target_range_max_dbfs.Get()),
-              .target_range_min_dbfs =
-                  static_cast<int>(target_range_min_dbfs.Get()),
-              .update_input_volume_wait_frames =
-                  static_cast<int>(update_input_volume_wait_frames.Get()),
-              .speech_probability_threshold =
-                  static_cast<float>(speech_probability_threshold.Get()),
-              .speech_ratio_threshold =
-                  static_cast<float>(speech_ratio_threshold.Get()),
-          },
-      .adaptive_digital_config =
-          {
-              .headroom_db = static_cast<float>(headroom_db.Get()),
-              .max_gain_db = static_cast<float>(max_gain_db.Get()),
-              .initial_gain_db = static_cast<float>(initial_gain_db.Get()),
-              .max_gain_change_db_per_second =
-                  static_cast<float>(max_gain_change_db_per_second.Get()),
-              .max_output_noise_level_dbfs =
-                  static_cast<float>(max_output_noise_level_dbfs.Get()),
-          },
-  };
+  const bool do_not_change_agc_config = !switch_to_agc2.Get();
+  if (do_not_change_agc_config && !disallow_transient_suppressor_usage.Get()) {
+    // Return an unspecifed value since, in this case, both the AGC2 and TS
+    // configurations won't be adjusted.
+    return absl::nullopt;
+  }
+  using Params = AudioProcessingImpl::GainController2ExperimentParams;
+  if (do_not_change_agc_config) {
+    // Return a value that leaves the AGC2 config unchanged and that always
+    // disables TS.
+    return Params{.agc2_config = absl::nullopt,
+                  .disallow_transient_suppressor_usage = true};
+  }
+  // Return a value that switches all the gain control to AGC2.
+  return Params{
+      .agc2_config =
+          Params::Agc2Config{
+              .input_volume_controller =
+                  {
+                      .min_input_volume = min_input_volume.Get(),
+                      .clipped_level_min = clipped_level_min.Get(),
+                      .clipped_level_step = clipped_level_step.Get(),
+                      .clipped_ratio_threshold =
+                          static_cast<float>(clipped_ratio_threshold.Get()),
+                      .clipped_wait_frames = clipped_wait_frames.Get(),
+                      .enable_clipping_predictor =
+                          enable_clipping_predictor.Get(),
+                      .target_range_max_dbfs = target_range_max_dbfs.Get(),
+                      .target_range_min_dbfs = target_range_min_dbfs.Get(),
+                      .update_input_volume_wait_frames =
+                          update_input_volume_wait_frames.Get(),
+                      .speech_probability_threshold = static_cast<float>(
+                          speech_probability_threshold.Get()),
+                      .speech_ratio_threshold =
+                          static_cast<float>(speech_ratio_threshold.Get()),
+                  },
+              .adaptive_digital_controller =
+                  {
+                      .headroom_db = static_cast<float>(headroom_db.Get()),
+                      .max_gain_db = static_cast<float>(max_gain_db.Get()),
+                      .initial_gain_db =
+                          static_cast<float>(initial_gain_db.Get()),
+                      .max_gain_change_db_per_second = static_cast<float>(
+                          max_gain_change_db_per_second.Get()),
+                      .max_output_noise_level_dbfs =
+                          static_cast<float>(max_output_noise_level_dbfs.Get()),
+                  }},
+      .disallow_transient_suppressor_usage =
+          disallow_transient_suppressor_usage.Get()};
 }
 
-// If `disallow_transient_supporessor_usage` is true, disables transient
-// suppression. When `gain_controller2_config_override` is specified,
-// switches all gain control to AGC2.
-AudioProcessing::Config AdjustConfig(
+AudioProcessing::Config AudioProcessingImpl::AdjustConfig(
     const AudioProcessing::Config& config,
-    bool disallow_transient_supporessor_usage,
-    const absl::optional<AudioProcessingImpl::GainController2ConfigOverride>&
-        gain_controller2_config_override) {
+    const absl::optional<AudioProcessingImpl::GainController2ExperimentParams>&
+        experiment_params) {
+  if (!experiment_params.has_value() ||
+      (!experiment_params->agc2_config.has_value() &&
+       !experiment_params->disallow_transient_suppressor_usage)) {
+    // When the experiment parameters are unspecified or when the AGC and TS
+    // configuration are not overridden, return the unmodified configuration.
+    return config;
+  }
+
   AudioProcessing::Config adjusted_config = config;
 
   // Override the transient suppressor configuration.
-  if (disallow_transient_supporessor_usage) {
+  if (experiment_params->disallow_transient_suppressor_usage) {
     adjusted_config.transient_suppression.enabled = false;
   }
 
   // Override the auto gain control configuration if the AGC1 analog gain
-  // controller is active and `gain_controller2_config_override` is
-  // specified.
+  // controller is active and `experiment_params->agc2_config` is specified.
   const bool agc1_analog_enabled =
       config.gain_controller1.enabled &&
       (config.gain_controller1.mode ==
            AudioProcessing::Config::GainController1::kAdaptiveAnalog ||
        config.gain_controller1.analog_gain_controller.enabled);
-  if (agc1_analog_enabled && gain_controller2_config_override.has_value()) {
+  if (agc1_analog_enabled && experiment_params->agc2_config.has_value()) {
     // Check that the unadjusted AGC config meets the preconditions.
     const bool hybrid_agc_config_detected =
         config.gain_controller1.enabled &&
@@ -499,7 +522,7 @@
       adjusted_config.gain_controller2.enabled = true;
       adjusted_config.gain_controller2.input_volume_controller.enabled = true;
       adjusted_config.gain_controller2.adaptive_digital =
-          gain_controller2_config_override->adaptive_digital_config;
+          experiment_params->agc2_config->adaptive_digital_controller;
       adjusted_config.gain_controller2.adaptive_digital.enabled = true;
     }
   }
@@ -507,26 +530,21 @@
   return adjusted_config;
 }
 
-using DownmixMethod = AudioProcessing::Config::Pipeline::DownmixMethod;
-
-void SetDownmixMethod(AudioBuffer& buffer, DownmixMethod method) {
-  switch (method) {
-    case DownmixMethod::kAverageChannels:
-      buffer.set_downmixing_by_averaging();
-      break;
-    case DownmixMethod::kUseFirstChannel:
-      buffer.set_downmixing_to_specific_channel(/*channel=*/0);
-      break;
+TransientSuppressor::VadMode AudioProcessingImpl::GetTransientSuppressorVadMode(
+    const absl::optional<AudioProcessingImpl::GainController2ExperimentParams>&
+        params) {
+  if (params.has_value() && params->agc2_config.has_value() &&
+      !params->disallow_transient_suppressor_usage) {
+    // When the experiment is active, the gain control switches to AGC2 and TS
+    // can be active, use the RNN VAD to control TS. This choice will also
+    // disable the internal RNN VAD in AGC2.
+    return TransientSuppressor::VadMode::kRnnVad;
   }
+  // If TS is disabled, the returned value does not matter. If enabled, use the
+  // default VAD.
+  return TransientSuppressor::VadMode::kDefault;
 }
 
-constexpr int kUnspecifiedDataDumpInputVolume = -100;
-
-}  // namespace
-
-// Throughout webrtc, it's assumed that success is represented by zero.
-static_assert(AudioProcessing::kNoError == 0, "kNoError must be zero");
-
 AudioProcessingImpl::SubmoduleStates::SubmoduleStates(
     bool capture_post_processor_enabled,
     bool render_pre_processor_enabled,
@@ -644,20 +662,17 @@
     : data_dumper_(new ApmDataDumper(instance_count_.fetch_add(1) + 1)),
       use_setup_specific_default_aec3_config_(
           UseSetupSpecificDefaultAec3Congfig()),
-      gain_controller2_config_override_(GetGainController2ConfigOverride()),
+      gain_controller2_experiment_params_(GetGainController2ExperimentParams()),
       use_denormal_disabler_(
           !field_trial::IsEnabled("WebRTC-ApmDenormalDisablerKillSwitch")),
-      disallow_transient_supporessor_usage_(
-          field_trial::IsEnabled("WebRTC-ApmTransientSuppressorKillSwitch")),
-      transient_suppressor_vad_mode_(GetTransientSuppressorVadMode()),
+      transient_suppressor_vad_mode_(
+          GetTransientSuppressorVadMode(gain_controller2_experiment_params_)),
       capture_runtime_settings_(RuntimeSettingQueueSize()),
       render_runtime_settings_(RuntimeSettingQueueSize()),
       capture_runtime_settings_enqueuer_(&capture_runtime_settings_),
       render_runtime_settings_enqueuer_(&render_runtime_settings_),
       echo_control_factory_(std::move(echo_control_factory)),
-      config_(AdjustConfig(config,
-                           disallow_transient_supporessor_usage_,
-                           gain_controller2_config_override_)),
+      config_(AdjustConfig(config, gain_controller2_experiment_params_)),
       submodule_states_(!!capture_post_processor,
                         !!render_pre_processor,
                         !!capture_analyzer),
@@ -893,8 +908,7 @@
   MutexLock lock_capture(&mutex_capture_);
 
   const auto adjusted_config =
-      AdjustConfig(config, disallow_transient_supporessor_usage_,
-                   gain_controller2_config_override_);
+      AdjustConfig(config, gain_controller2_experiment_params_);
   RTC_LOG(LS_INFO) << "AudioProcessing::ApplyConfig: "
                    << adjusted_config.ToString();
 
@@ -2340,11 +2354,16 @@
   if (!submodules_.gain_controller2 || config_has_changed) {
     const bool use_internal_vad =
         transient_suppressor_vad_mode_ != TransientSuppressor::VadMode::kRnnVad;
+    const bool input_volume_controller_config_overridden =
+        gain_controller2_experiment_params_.has_value() &&
+        gain_controller2_experiment_params_->agc2_config.has_value();
+    const InputVolumeController::Config input_volume_controller_config =
+        input_volume_controller_config_overridden
+            ? gain_controller2_experiment_params_->agc2_config
+                  ->input_volume_controller
+            : InputVolumeController::Config{};
     submodules_.gain_controller2 = std::make_unique<GainController2>(
-        config_.gain_controller2,
-        gain_controller2_config_override_.has_value()
-            ? gain_controller2_config_override_->input_volume_controller_config
-            : InputVolumeController::Config{},
+        config_.gain_controller2, input_volume_controller_config,
         proc_fullband_sample_rate_hz(), num_proc_channels(), use_internal_vad);
     submodules_.gain_controller2->SetCaptureOutputUsed(
         capture_.capture_output_used);
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index 0f74c30..8ee07ed 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -138,14 +138,6 @@
 
   AudioProcessing::Config GetConfig() const override;
 
-  // TODO(bugs.webrtc.org/7494): Remove when the related field trial is
-  // removed.
-  struct GainController2ConfigOverride {
-    InputVolumeController::Config input_volume_controller_config;
-    AudioProcessing::Config::GainController2::AdaptiveDigital
-        adaptive_digital_config;
-  };
-
  protected:
   // Overridden in a mock.
   virtual void InitializeLocked()
@@ -199,19 +191,47 @@
   static std::atomic<int> instance_count_;
   const bool use_setup_specific_default_aec3_config_;
 
-  // TODO(bugs.webrtc.org/7494): Remove when the linked field trial is removed.
-  // Override based on the "WebRTC-Audio-GainController2" field trial for the
-  // AGC2 input volume controller and adaptive digital controller configuration.
-  const absl::optional<GainController2ConfigOverride>
-      gain_controller2_config_override_;
+  // Parameters for the "GainController2" experiment which determines whether
+  // the following APM sub-modules are created and, if so, their configurations:
+  // AGC2 (`gain_controller2`), AGC1 (`gain_control`, `agc_manager`) and TS
+  // (`transient_suppressor`).
+  // TODO(bugs.webrtc.org/7494): Remove when the "WebRTC-Audio-GainController2"
+  // field trial is removed.
+  struct GainController2ExperimentParams {
+    struct Agc2Config {
+      InputVolumeController::Config input_volume_controller;
+      AudioProcessing::Config::GainController2::AdaptiveDigital
+          adaptive_digital_controller;
+    };
+    // When `agc2_config` is specified, all gain control switches to AGC2 and
+    // the configuration is overridden.
+    absl::optional<Agc2Config> agc2_config;
+    // When true, the transient suppressor submodule is never created regardless
+    // of the APM configuration.
+    bool disallow_transient_suppressor_usage;
+  };
+  // Specified when the "WebRTC-Audio-GainController2" field trial is specified.
+  // TODO(bugs.webrtc.org/7494): Remove when the "WebRTC-Audio-GainController2"
+  // field trial is removed.
+  const absl::optional<GainController2ExperimentParams>
+      gain_controller2_experiment_params_;
+
+  // Parses the "WebRTC-Audio-GainController2" field trial. If disabled, returns
+  // an unspecified value.
+  static absl::optional<GainController2ExperimentParams>
+  GetGainController2ExperimentParams();
+
+  // When `experiment_params` is specified, returns an APM configuration
+  // modified according to the experiment parameters. Otherwise returns
+  // `config`.
+  static AudioProcessing::Config AdjustConfig(
+      const AudioProcessing::Config& config,
+      const absl::optional<GainController2ExperimentParams>& experiment_params);
+  static TransientSuppressor::VadMode GetTransientSuppressorVadMode(
+      const absl::optional<GainController2ExperimentParams>& experiment_params);
 
   const bool use_denormal_disabler_;
 
-  // When true, the transient suppressor submodule is never created regardless
-  // of the APM configuration.
-  // TODO(bugs.webrtc.org/13663): Remove when the linked field trial is removed.
-  const bool disallow_transient_supporessor_usage_;
-
   const TransientSuppressor::VadMode transient_suppressor_vad_mode_;
 
   SwapQueue<RuntimeSetting> capture_runtime_settings_;
diff --git a/modules/audio_processing/audio_processing_impl_unittest.cc b/modules/audio_processing/audio_processing_impl_unittest.cc
index 65bda71..e48a5d8 100644
--- a/modules/audio_processing/audio_processing_impl_unittest.cc
+++ b/modules/audio_processing/audio_processing_impl_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "modules/audio_processing/audio_processing_impl.h"
 
+#include <algorithm>
 #include <array>
 #include <memory>
 #include <tuple>
@@ -131,32 +132,6 @@
   static constexpr float ProcessSample(float x) { return 2.f * x; }
 };
 
-// Creates a simple `AudioProcessing` instance for APM input volume testing
-// with AGC1 analog and/or AGC2 input volume controller enabled and AGC2
-// digital controller enabled.
-rtc::scoped_refptr<AudioProcessing> CreateApmForInputVolumeTest(
-    bool agc1_analog_gain_controller_enabled,
-    bool agc2_input_volume_controller_enabled) {
-  webrtc::AudioProcessing::Config config;
-  // Enable AGC1 analog controller.
-  config.gain_controller1.enabled = agc1_analog_gain_controller_enabled;
-  config.gain_controller1.analog_gain_controller.enabled =
-      agc1_analog_gain_controller_enabled;
-  // Enable AG2 input volume controller
-  config.gain_controller2.input_volume_controller.enabled =
-      agc2_input_volume_controller_enabled;
-  // Enable AGC2 adaptive digital controller.
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
-      false;
-  config.gain_controller2.enabled = true;
-  config.gain_controller2.adaptive_digital.enabled = true;
-
-  auto apm(AudioProcessingBuilder().Create());
-  apm->ApplyConfig(config);
-
-  return apm;
-}
-
 // Runs `apm` input processing for volume adjustments for `num_frames` random
 // frames starting from the volume `initial_volume`. This includes three steps:
 // 1) Set the input volume 2) Process the stream 3) Set the new recommended
@@ -183,99 +158,6 @@
   return recommended_input_volume;
 }
 
-constexpr char kMinMicLevelFieldTrial[] =
-    "WebRTC-Audio-2ndAgcMinMicLevelExperiment";
-constexpr char kMinInputVolumeFieldTrial[] = "WebRTC-Audio-Agc2-MinInputVolume";
-constexpr int kMinInputVolume = 12;
-
-std::string GetMinMicLevelExperimentFieldTrial(absl::optional<int> value) {
-  char field_trial_buffer[128];
-  rtc::SimpleStringBuilder builder(field_trial_buffer);
-  if (value.has_value()) {
-    RTC_DCHECK_GE(*value, 0);
-    RTC_DCHECK_LE(*value, 255);
-    builder << kMinMicLevelFieldTrial << "/Enabled-" << *value << "/";
-    builder << kMinInputVolumeFieldTrial << "/Enabled-" << *value << "/";
-  } else {
-    builder << kMinMicLevelFieldTrial << "/Disabled/";
-    builder << kMinInputVolumeFieldTrial << "/Disabled/";
-  }
-  return builder.str();
-}
-
-// TODO(webrtc:7494): Remove the fieldtrial from the input volume tests when
-// "WebRTC-Audio-2ndAgcMinMicLevelExperiment" and
-// "WebRTC-Audio-Agc2-MinInputVolume" are removed.
-class InputVolumeStartupParameterizedTest
-    : public ::testing::TestWithParam<
-          std::tuple<int, absl::optional<int>, bool, bool>> {
- protected:
-  InputVolumeStartupParameterizedTest()
-      : field_trials_(
-            GetMinMicLevelExperimentFieldTrial(std::get<1>(GetParam()))) {}
-  int GetStartupVolume() const { return std::get<0>(GetParam()); }
-  int GetMinVolume() const {
-    return std::get<1>(GetParam()).value_or(kMinInputVolume);
-  }
-  bool GetAgc1AnalogControllerEnabled() const {
-    return std::get<2>(GetParam());
-  }
-  bool GetAgc2InputVolumeControllerEnabled() const {
-    return std::get<3>(GetParam());
-  }
-
- private:
-  test::ScopedFieldTrials field_trials_;
-};
-
-class InputVolumeNotZeroParameterizedTest
-    : public ::testing::TestWithParam<
-          std::tuple<int, int, absl::optional<int>, bool, bool>> {
- protected:
-  InputVolumeNotZeroParameterizedTest()
-      : field_trials_(
-            GetMinMicLevelExperimentFieldTrial(std::get<2>(GetParam()))) {}
-  int GetStartupVolume() const { return std::get<0>(GetParam()); }
-  int GetVolume() const { return std::get<1>(GetParam()); }
-  int GetMinVolume() const {
-    return std::get<2>(GetParam()).value_or(kMinInputVolume);
-  }
-  bool GetMinMicLevelExperimentEnabled() {
-    return std::get<2>(GetParam()).has_value();
-  }
-  bool GetAgc1AnalogControllerEnabled() const {
-    return std::get<3>(GetParam());
-  }
-  bool GetAgc2InputVolumeControllerEnabled() const {
-    return std::get<4>(GetParam());
-  }
-
- private:
-  test::ScopedFieldTrials field_trials_;
-};
-
-class InputVolumeZeroParameterizedTest
-    : public ::testing::TestWithParam<
-          std::tuple<int, absl::optional<int>, bool, bool>> {
- protected:
-  InputVolumeZeroParameterizedTest()
-      : field_trials_(
-            GetMinMicLevelExperimentFieldTrial(std::get<1>(GetParam()))) {}
-  int GetStartupVolume() const { return std::get<0>(GetParam()); }
-  int GetMinVolume() const {
-    return std::get<1>(GetParam()).value_or(kMinInputVolume);
-  }
-  bool GetAgc1AnalogControllerEnabled() const {
-    return std::get<2>(GetParam());
-  }
-  bool GetAgc2InputVolumeControllerEnabled() const {
-    return std::get<3>(GetParam());
-  }
-
- private:
-  test::ScopedFieldTrials field_trials_;
-};
-
 }  // namespace
 
 TEST(AudioProcessingImplTest, AudioParameterChangeTriggersInit) {
@@ -644,8 +526,10 @@
 TEST(AudioProcessingImplTest,
      ProcessWithAgc2AndTransientSuppressorVadModeDefault) {
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-TransientSuppressorVadMode/Enabled-Default/");
-  rtc::scoped_refptr<AudioProcessing> apm = AudioProcessingBuilder().Create();
+      "WebRTC-Audio-GainController2/Disabled/");
+  auto apm = AudioProcessingBuilder()
+                 .SetConfig({.gain_controller1{.enabled = false}})
+                 .Create();
   ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
   webrtc::AudioProcessing::Config apm_config;
   apm_config.gain_controller1.enabled = false;
@@ -675,7 +559,7 @@
 TEST(AudioProcessingImplTest,
      ProcessWithAgc2AndTransientSuppressorVadModeRnnVad) {
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-TransientSuppressorVadMode/Enabled-RnnVad/");
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
   rtc::scoped_refptr<AudioProcessing> apm = AudioProcessingBuilder().Create();
   ASSERT_EQ(apm->Initialize(), AudioProcessing::kNoError);
   webrtc::AudioProcessing::Config apm_config;
@@ -958,224 +842,116 @@
   }
 }
 
-TEST(AudioProcessingImplTest, CanDisableTransientSuppressor) {
-  // Do not explicitly disable "WebRTC-ApmTransientSuppressorKillSwitch" since
-  // to check that, by default, it is disabled.
-  auto apm = AudioProcessingBuilder()
-                 .SetConfig({.transient_suppression = {.enabled = false}})
-                 .Create();
-  EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
-}
-
-TEST(AudioProcessingImplTest, CanEnableTransientSuppressor) {
-  // Do not explicitly disable "WebRTC-ApmTransientSuppressorKillSwitch" since
-  // to check that, by default, it is disabled.
-  auto apm = AudioProcessingBuilder()
-                 .SetConfig({.transient_suppression = {.enabled = true}})
-                 .Create();
-  EXPECT_TRUE(apm->GetConfig().transient_suppression.enabled);
-}
-
-TEST(AudioProcessingImplTest, CanDisableTransientSuppressorIfUsageAllowed) {
-  // Disable the field trial that disallows to enable transient suppression.
-  test::ScopedFieldTrials field_trials(
-      "WebRTC-ApmTransientSuppressorKillSwitch/Disabled/");
-  auto apm = AudioProcessingBuilder()
-                 .SetConfig({.transient_suppression = {.enabled = false}})
-                 .Create();
-  EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
-}
-
-TEST(AudioProcessingImplTest, CanEnableTransientSuppressorIfUsageAllowed) {
-  // Disable the field trial that disallows to enable transient suppression.
-  test::ScopedFieldTrials field_trials(
-      "WebRTC-ApmTransientSuppressorKillSwitch/Disabled/");
-  auto apm = AudioProcessingBuilder()
-                 .SetConfig({.transient_suppression = {.enabled = true}})
-                 .Create();
-  EXPECT_TRUE(apm->GetConfig().transient_suppression.enabled);
-}
-
-TEST(AudioProcessingImplTest,
-     CannotEnableTransientSuppressorIfUsageDisallowed) {
-  // Enable the field trial that disallows to enable transient suppression.
-  test::ScopedFieldTrials field_trials(
-      "WebRTC-ApmTransientSuppressorKillSwitch/Enabled/");
-  auto apm = AudioProcessingBuilder()
-                 .SetConfig({.transient_suppression = {.enabled = true}})
-                 .Create();
-  EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
-}
-
-// TODO(bugs.webrtc.org/7494): Test AGCs with different multi-channel configs.
-
-// Tests that the minimum startup volume is applied at the startup.
-TEST_P(InputVolumeStartupParameterizedTest,
-       VerifyStartupMinVolumeAppliedAtStartup) {
-  const int applied_startup_input_volume = GetStartupVolume();
-  const int expected_volume =
-      std::max(applied_startup_input_volume, GetMinVolume());
-  const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
-  const bool agc2_input_volume_controller_enabled =
-      GetAgc2InputVolumeControllerEnabled();
-  auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
-                                         agc2_input_volume_controller_enabled);
-
-  const int recommended_input_volume =
-      ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
-
-  if (!agc1_analog_controller_enabled &&
-      !agc2_input_volume_controller_enabled) {
-    // No input volume changes if none of the analog controllers is enabled.
-    ASSERT_EQ(recommended_input_volume, applied_startup_input_volume);
-  } else {
-    ASSERT_EQ(recommended_input_volume, expected_volume);
-  }
-}
-
-// Tests that the minimum input volume is applied if the volume is manually
-// adjusted to a non-zero value 1) always for AGC2 input volume controller and
-// 2) only if "WebRTC-Audio-2ndAgcMinMicLevelExperiment" is enabled for AGC1
-// analog controller.
-TEST_P(InputVolumeNotZeroParameterizedTest,
-       VerifyMinVolumeMaybeAppliedAfterManualVolumeAdjustments) {
-  const int applied_startup_input_volume = GetStartupVolume();
-  const int applied_input_volume = GetVolume();
-  const int expected_volume = std::max(applied_input_volume, GetMinVolume());
-  const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
-  const bool agc2_input_volume_controller_enabled =
-      GetAgc2InputVolumeControllerEnabled();
-  auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
-                                         agc2_input_volume_controller_enabled);
-
-  ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
-  const int recommended_input_volume =
-      ProcessInputVolume(*apm, /*num_frames=*/1, applied_input_volume);
-
-  ASSERT_NE(applied_input_volume, 0);
-
-  if (!agc1_analog_controller_enabled &&
-      !agc2_input_volume_controller_enabled) {
-    // No input volume changes if none of the analog controllers is enabled.
-    ASSERT_EQ(recommended_input_volume, applied_input_volume);
-  } else {
-    if (GetMinMicLevelExperimentEnabled() ||
-        (!agc1_analog_controller_enabled &&
-         agc2_input_volume_controller_enabled)) {
-      ASSERT_EQ(recommended_input_volume, expected_volume);
-    } else {
-      ASSERT_EQ(recommended_input_volume, applied_input_volume);
+class ApmInputVolumeControllerParametrizedTest
+    : public ::testing::TestWithParam<
+          std::tuple<int, int, AudioProcessing::Config>> {
+ protected:
+  ApmInputVolumeControllerParametrizedTest()
+      : sample_rate_hz_(std::get<0>(GetParam())),
+        num_channels_(std::get<1>(GetParam())),
+        channels_(num_channels_),
+        channel_pointers_(num_channels_) {
+    const int frame_size = sample_rate_hz_ / 100;
+    for (int c = 0; c < num_channels_; ++c) {
+      channels_[c].resize(frame_size);
+      channel_pointers_[c] = channels_[c].data();
+      std::fill(channels_[c].begin(), channels_[c].end(), 0.0f);
     }
   }
+
+  int sample_rate_hz() const { return sample_rate_hz_; }
+  int num_channels() const { return num_channels_; }
+  AudioProcessing::Config GetConfig() const { return std::get<2>(GetParam()); }
+
+  float* const* channel_pointers() { return channel_pointers_.data(); }
+
+ private:
+  const int sample_rate_hz_;
+  const int num_channels_;
+  std::vector<std::vector<float>> channels_;
+  std::vector<float*> channel_pointers_;
+};
+
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+       EnforceMinInputVolumeAtStartupWithZeroVolume) {
+  const StreamConfig stream_config(sample_rate_hz(), num_channels());
+  auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+  apm->set_stream_analog_level(0);
+  apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+                     channel_pointers());
+  EXPECT_GT(apm->recommended_stream_analog_level(), 0);
 }
 
-// Tests that the minimum input volume is not applied if the volume is manually
-// adjusted to zero.
-TEST_P(InputVolumeZeroParameterizedTest,
-       VerifyMinVolumeNotAppliedAfterManualVolumeAdjustments) {
-  constexpr int kZeroVolume = 0;
-  const int applied_startup_input_volume = GetStartupVolume();
-  const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
-  const bool agc2_input_volume_controller_enabled =
-      GetAgc2InputVolumeControllerEnabled();
-  auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
-                                         agc2_input_volume_controller_enabled);
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+       EnforceMinInputVolumeAtStartupWithNonZeroVolume) {
+  const StreamConfig stream_config(sample_rate_hz(), num_channels());
+  auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
 
-  const int recommended_input_volume_after_startup =
-      ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
-  const int recommended_input_volume =
-      ProcessInputVolume(*apm, /*num_frames=*/1, kZeroVolume);
+  constexpr int kStartupVolume = 3;
+  apm->set_stream_analog_level(kStartupVolume);
+  apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+                     channel_pointers());
+  EXPECT_GT(apm->recommended_stream_analog_level(), kStartupVolume);
+}
 
-  if (!agc1_analog_controller_enabled &&
-      !agc2_input_volume_controller_enabled) {
-    // No input volume changes if none of the analog controllers is enabled.
-    ASSERT_EQ(recommended_input_volume, kZeroVolume);
-  } else {
-    ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup);
-    ASSERT_EQ(recommended_input_volume, kZeroVolume);
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+       EnforceMinInputVolumeAfterManualVolumeAdjustment) {
+  const auto config = GetConfig();
+  if (config.gain_controller1.enabled) {
+    // After a downward manual adjustment, AGC1 slowly converges to the minimum
+    // input volume.
+    GTEST_SKIP() << "Does not apply to AGC1";
   }
+  const StreamConfig stream_config(sample_rate_hz(), num_channels());
+  auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
+
+  apm->set_stream_analog_level(20);
+  apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+                     channel_pointers());
+  constexpr int kManuallyAdjustedVolume = 3;
+  apm->set_stream_analog_level(kManuallyAdjustedVolume);
+  apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+                     channel_pointers());
+  EXPECT_GT(apm->recommended_stream_analog_level(), kManuallyAdjustedVolume);
 }
 
-// Tests that the minimum input volume is applied if the volume is not zero
-// before it is automatically adjusted.
-TEST_P(InputVolumeNotZeroParameterizedTest,
-       VerifyMinVolumeAppliedAfterAutomaticVolumeAdjustments) {
-  const int applied_startup_input_volume = GetStartupVolume();
-  const int applied_input_volume = GetVolume();
-  const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
-  const bool agc2_input_volume_controller_enabled =
-      GetAgc2InputVolumeControllerEnabled();
-  auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
-                                         agc2_input_volume_controller_enabled);
+TEST_P(ApmInputVolumeControllerParametrizedTest,
+       DoNotEnforceMinInputVolumeAfterManualVolumeAdjustmentToZero) {
+  const StreamConfig stream_config(sample_rate_hz(), num_channels());
+  auto apm = AudioProcessingBuilder().SetConfig(GetConfig()).Create();
 
-  ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
-  const int recommended_input_volume =
-      ProcessInputVolume(*apm, /*num_frames=*/400, applied_input_volume);
-
-  ASSERT_NE(applied_input_volume, 0);
-
-  if (!agc1_analog_controller_enabled &&
-      !agc2_input_volume_controller_enabled) {
-    // No input volume changes if none of the analog controllers is enabled.
-    ASSERT_EQ(recommended_input_volume, applied_input_volume);
-  } else {
-    if (recommended_input_volume != applied_input_volume) {
-      ASSERT_GE(recommended_input_volume, GetMinVolume());
-    }
-  }
+  apm->set_stream_analog_level(100);
+  apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+                     channel_pointers());
+  apm->set_stream_analog_level(0);
+  apm->ProcessStream(channel_pointers(), stream_config, stream_config,
+                     channel_pointers());
+  EXPECT_EQ(apm->recommended_stream_analog_level(), 0);
 }
 
-// Tests that the minimum input volume is not applied if the volume is zero
-// before it is automatically adjusted.
-TEST_P(InputVolumeZeroParameterizedTest,
-       VerifyMinVolumeNotAppliedAfterAutomaticVolumeAdjustments) {
-  constexpr int kZeroVolume = 0;
-  const int applied_startup_input_volume = GetStartupVolume();
-  const bool agc1_analog_controller_enabled = GetAgc1AnalogControllerEnabled();
-  const bool agc2_input_volume_controller_enabled =
-      GetAgc2InputVolumeControllerEnabled();
-  auto apm = CreateApmForInputVolumeTest(agc1_analog_controller_enabled,
-                                         agc2_input_volume_controller_enabled);
-
-  const int recommended_input_volume_after_startup =
-      ProcessInputVolume(*apm, /*num_frames=*/1, applied_startup_input_volume);
-  const int recommended_input_volume =
-      ProcessInputVolume(*apm, /*num_frames=*/400, kZeroVolume);
-
-  if (!agc1_analog_controller_enabled &&
-      !agc2_input_volume_controller_enabled) {
-    // No input volume changes if none of the analog controllers is enabled.
-    ASSERT_EQ(recommended_input_volume, kZeroVolume);
-  } else {
-    ASSERT_NE(recommended_input_volume, recommended_input_volume_after_startup);
-    ASSERT_EQ(recommended_input_volume, kZeroVolume);
-  }
-}
-
-INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
-                         InputVolumeStartupParameterizedTest,
-                         ::testing::Combine(::testing::Values(0, 5, 30),
-                                            ::testing::Values(absl::nullopt,
-                                                              20),
-                                            ::testing::Bool(),
-                                            ::testing::Bool()));
-
-INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
-                         InputVolumeNotZeroParameterizedTest,
-                         ::testing::Combine(::testing::Values(0, 5, 15),
-                                            ::testing::Values(1, 5, 30),
-                                            ::testing::Values(absl::nullopt,
-                                                              20),
-                                            ::testing::Bool(),
-                                            ::testing::Bool()));
-
-INSTANTIATE_TEST_SUITE_P(AudioProcessingImplTest,
-                         InputVolumeZeroParameterizedTest,
-                         ::testing::Combine(::testing::Values(0, 5, 15),
-                                            ::testing::Values(absl::nullopt,
-                                                              20),
-                                            ::testing::Bool(),
-                                            ::testing::Bool()));
+INSTANTIATE_TEST_SUITE_P(
+    AudioProcessingImplTest,
+    ApmInputVolumeControllerParametrizedTest,
+    ::testing::Combine(
+        ::testing::Values(8000, 16000, 32000, 48000),  // Sample rates.
+        ::testing::Values(1, 2),                       // Number of channels.
+        ::testing::Values(
+            // Full AGC1.
+            AudioProcessing::Config{
+                .gain_controller1 = {.enabled = true,
+                                     .analog_gain_controller =
+                                         {.enabled = true,
+                                          .enable_digital_adaptive = true}},
+                .gain_controller2 = {.enabled = false}},
+            // Hybrid AGC.
+            AudioProcessing::Config{
+                .gain_controller1 = {.enabled = true,
+                                     .analog_gain_controller =
+                                         {.enabled = true,
+                                          .enable_digital_adaptive = false}},
+                .gain_controller2 = {.enabled = true,
+                                     .adaptive_digital = {.enabled = true}}})));
 
 // When the input volume is not emulated and no input volume controller is
 // active, the recommended volume must always be the applied volume.
@@ -1237,53 +1013,336 @@
   EXPECT_EQ(ProcessInputVolume(*apm, kOneFrame, /*initial_volume=*/135), 135);
 }
 
-class GainController2FieldTrialParametrizedTest
+TEST(AudioProcessingImplTest,
+     Agc2FieldTrialDoNotSwitchToFullAgc2WhenNoAgcIsActive) {
+  constexpr AudioProcessing::Config kOriginal{
+      .gain_controller1{.enabled = false},
+      .gain_controller2{.enabled = false},
+  };
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+}
+
+TEST(AudioProcessingImplTest,
+     Agc2FieldTrialDoNotSwitchToFullAgc2WithAgc1Agc2InputVolumeControllers) {
+  constexpr AudioProcessing::Config kOriginal{
+      .gain_controller1{.enabled = true,
+                        .analog_gain_controller{.enabled = true}},
+      .gain_controller2{.enabled = true,
+                        .input_volume_controller{.enabled = true}},
+  };
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, kOriginal.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, kOriginal.gain_controller2);
+}
+
+class Agc2FieldTrialParametrizedTest
     : public ::testing::TestWithParam<AudioProcessing::Config> {};
 
-TEST_P(GainController2FieldTrialParametrizedTest,
-       CheckAgc2AdaptiveDigitalOverridesApplied) {
+TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfDisabled) {
+  const AudioProcessing::Config original = GetParam();
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-GainController2/"
-      "Enabled,"
-      "enable_clipping_predictor:true,"
+      "WebRTC-Audio-GainController2/Disabled/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(original);
+  adjusted = apm->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, DoNotChangeConfigIfNoOverride) {
+  const AudioProcessing::Config original = GetParam();
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,"
+      "switch_to_agc2:false,"
+      "disallow_transient_suppressor_usage:false/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(original);
+  adjusted = apm->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, DoNotSwitchToFullAgc2) {
+  const AudioProcessing::Config original = GetParam();
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:false/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(original);
+  adjusted = apm->GetConfig();
+  EXPECT_EQ(adjusted.gain_controller1, original.gain_controller1);
+  EXPECT_EQ(adjusted.gain_controller2, original.gain_controller2);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, SwitchToFullAgc2) {
+  const AudioProcessing::Config original = GetParam();
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.gain_controller1.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(original);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(adjusted.gain_controller1.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+       SwitchToFullAgc2AndOverrideInputVolumeControllerParameters) {
+  const AudioProcessing::Config original = GetParam();
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true,"
+      "min_input_volume:123,"
       "clipped_level_min:20,"
       "clipped_level_step:30,"
       "clipped_ratio_threshold:0.4,"
       "clipped_wait_frames:50,"
+      "enable_clipping_predictor:true,"
       "target_range_max_dbfs:-6,"
       "target_range_min_dbfs:-70,"
       "update_input_volume_wait_frames:80,"
       "speech_probability_threshold:0.9,"
-      "speech_ratio_threshold:1.0,"
+      "speech_ratio_threshold:1.0/");
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.gain_controller1.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(original);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(adjusted.gain_controller1.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+       SwitchToFullAgc2AndOverrideAdaptiveDigitalControllerParameters) {
+  const AudioProcessing::Config original = GetParam();
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,switch_to_agc2:true,"
       "headroom_db:10,"
       "max_gain_db:20,"
       "initial_gain_db:7,"
       "max_gain_change_db_per_second:5,"
       "max_output_noise_level_dbfs:-40/");
 
-  auto adjusted_config =
-      AudioProcessingBuilder().SetConfig(GetParam()).Create()->GetConfig();
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(original).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.gain_controller1.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+  ASSERT_NE(adjusted.gain_controller2.adaptive_digital,
+            original.gain_controller2.adaptive_digital);
+  EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10);
+  EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20);
+  EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7);
+  EXPECT_EQ(
+      adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second,
+      5);
+  EXPECT_EQ(
+      adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs,
+      -40);
 
-  EXPECT_FALSE(adjusted_config.gain_controller1.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.input_volume_controller.enabled);
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(original);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(adjusted.gain_controller1.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.input_volume_controller.enabled);
+  EXPECT_TRUE(adjusted.gain_controller2.adaptive_digital.enabled);
+  ASSERT_NE(adjusted.gain_controller2.adaptive_digital,
+            original.gain_controller2.adaptive_digital);
+  EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.headroom_db, 10);
+  EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.max_gain_db, 20);
+  EXPECT_EQ(adjusted.gain_controller2.adaptive_digital.initial_gain_db, 7);
+  EXPECT_EQ(
+      adjusted.gain_controller2.adaptive_digital.max_gain_change_db_per_second,
+      5);
+  EXPECT_EQ(
+      adjusted.gain_controller2.adaptive_digital.max_output_noise_level_dbfs,
+      -40);
+}
 
-  EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital.headroom_db, 10);
-  EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital.max_gain_db, 20);
-  EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital.initial_gain_db,
-            7);
-  EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital
-                .max_gain_change_db_per_second,
-            5);
-  EXPECT_EQ(adjusted_config.gain_controller2.adaptive_digital
-                .max_output_noise_level_dbfs,
-            -40);
+TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithTs) {
+  AudioProcessing::Config config = GetParam();
+  config.transient_suppression.enabled = true;
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Disabled/");
+  auto apm = AudioProcessingBuilder().SetConfig(config).Create();
+
+  constexpr int kSampleRateHz = 48000;
+  constexpr int kNumChannels = 1;
+  std::array<float, kSampleRateHz / 100> buffer;
+  float* channel_pointers[] = {buffer.data()};
+  StreamConfig stream_config(kSampleRateHz, kNumChannels);
+  Random random_generator(2341U);
+  constexpr int kFramesToProcess = 10;
+  int volume = 100;
+  for (int i = 0; i < kFramesToProcess; ++i) {
+    SCOPED_TRACE(i);
+    RandomizeSampleVector(&random_generator, buffer);
+    apm->set_stream_analog_level(volume);
+    ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+                                 channel_pointers),
+              kNoErr);
+    volume = apm->recommended_stream_analog_level();
+  }
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest, ProcessSucceedsWithoutTs) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,"
+      "switch_to_agc2:false,"
+      "disallow_transient_suppressor_usage:true/");
+  auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create();
+
+  constexpr int kSampleRateHz = 48000;
+  constexpr int kNumChannels = 1;
+  std::array<float, kSampleRateHz / 100> buffer;
+  float* channel_pointers[] = {buffer.data()};
+  StreamConfig stream_config(kSampleRateHz, kNumChannels);
+  Random random_generator(2341U);
+  constexpr int kFramesToProcess = 10;
+  int volume = 100;
+  for (int i = 0; i < kFramesToProcess; ++i) {
+    SCOPED_TRACE(i);
+    RandomizeSampleVector(&random_generator, buffer);
+    apm->set_stream_analog_level(volume);
+    ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+                                 channel_pointers),
+              kNoErr);
+    volume = apm->recommended_stream_analog_level();
+  }
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+       ProcessSucceedsWhenSwitchToFullAgc2WithTs) {
+  AudioProcessing::Config config = GetParam();
+  config.transient_suppression.enabled = true;
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,"
+      "switch_to_agc2:true,"
+      "disallow_transient_suppressor_usage:false/");
+  auto apm = AudioProcessingBuilder().SetConfig(config).Create();
+
+  constexpr int kSampleRateHz = 48000;
+  constexpr int kNumChannels = 1;
+  std::array<float, kSampleRateHz / 100> buffer;
+  float* channel_pointers[] = {buffer.data()};
+  StreamConfig stream_config(kSampleRateHz, kNumChannels);
+  Random random_generator(2341U);
+  constexpr int kFramesToProcess = 10;
+  int volume = 100;
+  for (int i = 0; i < kFramesToProcess; ++i) {
+    SCOPED_TRACE(i);
+    RandomizeSampleVector(&random_generator, buffer);
+    apm->set_stream_analog_level(volume);
+    ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+                                 channel_pointers),
+              kNoErr);
+    volume = apm->recommended_stream_analog_level();
+  }
+}
+
+TEST_P(Agc2FieldTrialParametrizedTest,
+       ProcessSucceedsWhenSwitchToFullAgc2WithoutTs) {
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,"
+      "switch_to_agc2:true,"
+      "disallow_transient_suppressor_usage:true/");
+  auto apm = AudioProcessingBuilder().SetConfig(GetParam()).Create();
+
+  constexpr int kSampleRateHz = 48000;
+  constexpr int kNumChannels = 1;
+  std::array<float, kSampleRateHz / 100> buffer;
+  float* channel_pointers[] = {buffer.data()};
+  StreamConfig stream_config(kSampleRateHz, kNumChannels);
+  Random random_generator(2341U);
+  constexpr int kFramesToProcess = 10;
+  int volume = 100;
+  for (int i = 0; i < kFramesToProcess; ++i) {
+    SCOPED_TRACE(i);
+    RandomizeSampleVector(&random_generator, buffer);
+    apm->set_stream_analog_level(volume);
+    ASSERT_EQ(apm->ProcessStream(channel_pointers, stream_config, stream_config,
+                                 channel_pointers),
+              kNoErr);
+    volume = apm->recommended_stream_analog_level();
+  }
 }
 
 INSTANTIATE_TEST_SUITE_P(
     AudioProcessingImplTest,
-    GainController2FieldTrialParametrizedTest,
+    Agc2FieldTrialParametrizedTest,
     ::testing::Values(
         // Full AGC1.
         AudioProcessing::Config{
@@ -1301,314 +1360,132 @@
             .gain_controller2 = {.enabled = true,
                                  .adaptive_digital = {.enabled = true}}}));
 
-TEST(AudioProcessingImplGainController2FieldTrialTest,
-     ConfigAdjustedWhenExperimentEnabledAndAgc1AnalogEnabled) {
-  constexpr AudioProcessing::Config::GainController2::AdaptiveDigital
-      kDefaultAdaptiveDigitalConfig;
+TEST(AudioProcessingImplTest, CanDisableTransientSuppressor) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = false}};
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.transient_suppression.enabled);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest, CanEnableTs) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = true}};
+
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_TRUE(adjusted.transient_suppression.enabled);
+
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_TRUE(adjusted.transient_suppression.enabled);
+}
+
+TEST(AudioProcessingImplTest, CanDisableTsWithAgc2FieldTrialDisabled) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = false}};
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-GainController2/"
-      "Enabled,"
-      "enable_clipping_predictor:true,"
-      "clipped_level_min:20,"
-      "clipped_level_step:30,"
-      "clipped_ratio_threshold:0.4,"
-      "clipped_wait_frames:50,"
-      "target_range_max_dbfs:-6,"
-      "target_range_min_dbfs:-70,"
-      "update_input_volume_wait_frames:80,"
-      "speech_probability_threshold:0.9,"
-      "speech_ratio_threshold:1.0,"
-      "headroom_db:10,"
-      "max_gain_db:20,"
-      "initial_gain_db:7,"
-      "max_gain_change_db_per_second:3,"
-      "max_output_noise_level_dbfs:-40/");
+      "WebRTC-Audio-GainController2/Disabled/");
 
-  AudioProcessingBuilderForTesting apm_builder;
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.transient_suppression.enabled);
 
-  // Set a config with analog AGC1 enabled.
-  AudioProcessing::Config config;
-  config.gain_controller1.enabled = true;
-  config.gain_controller1.analog_gain_controller.enabled = true;
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive = true;
-  config.gain_controller2.enabled = false;
-  config.gain_controller1.mode =
-      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
-
-  EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled);
-
-  apm_builder.SetConfig(config);
-
-  auto apm = apm_builder.Create();
-  auto adjusted_config = apm->GetConfig();
-
-  // Expect the config to be adjusted.
-  EXPECT_FALSE(adjusted_config.gain_controller1.enabled);
-  EXPECT_FALSE(adjusted_config.gain_controller1.analog_gain_controller.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.input_volume_controller.enabled);
-  EXPECT_NE(adjusted_config.gain_controller2.adaptive_digital,
-            kDefaultAdaptiveDigitalConfig);
-
-  // Change config back and compare.
-  adjusted_config.gain_controller1.enabled = config.gain_controller1.enabled;
-  adjusted_config.gain_controller1.analog_gain_controller.enabled =
-      config.gain_controller1.analog_gain_controller.enabled;
-  adjusted_config.gain_controller2.enabled = config.gain_controller2.enabled;
-  adjusted_config.gain_controller2.adaptive_digital.enabled =
-      config.gain_controller2.adaptive_digital.enabled;
-  adjusted_config.gain_controller2.input_volume_controller.enabled =
-      config.gain_controller2.input_volume_controller.enabled;
-  adjusted_config.gain_controller2.adaptive_digital =
-      config.gain_controller2.adaptive_digital;
-
-  EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString()));
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
 }
 
-TEST(AudioProcessingImplGainController2FieldTrialTest,
-     ConfigAdjustedWhenExperimentEnabledAndHybridAgcEnabled) {
-  constexpr AudioProcessing::Config::GainController2::AdaptiveDigital
-      kDefaultAdaptiveDigitalConfig;
+TEST(AudioProcessingImplTest, CanEnableTsWithAgc2FieldTrialDisabled) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = true}};
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-GainController2/"
-      "Enabled,"
-      "enable_clipping_predictor:true,"
-      "clipped_level_min:20,"
-      "clipped_level_step:30,"
-      "clipped_ratio_threshold:0.4,"
-      "clipped_wait_frames:50,"
-      "target_range_max_dbfs:-6,"
-      "target_range_min_dbfs:-70,"
-      "update_input_volume_wait_frames:80,"
-      "speech_probability_threshold:0.9,"
-      "speech_ratio_threshold:1.0,"
-      "headroom_db:10,"
-      "max_gain_db:20,"
-      "initial_gain_db:7,"
-      "max_gain_change_db_per_second:3,"
-      "max_output_noise_level_dbfs:-40/");
+      "WebRTC-Audio-GainController2/Disabled/");
 
-  AudioProcessingBuilderForTesting apm_builder;
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_TRUE(adjusted.transient_suppression.enabled);
 
-  // Set a config with hybrid AGC enabled.
-  AudioProcessing::Config config;
-  config.gain_controller1.enabled = true;
-  config.gain_controller1.analog_gain_controller.enabled = true;
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
-      false;
-  config.gain_controller2.enabled = true;
-  config.gain_controller2.adaptive_digital.enabled = true;
-  config.gain_controller1.mode =
-      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
-
-  EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled);
-
-  apm_builder.SetConfig(config);
-
-  auto apm = apm_builder.Create();
-  auto adjusted_config = apm->GetConfig();
-
-  // Expect the config to be adjusted.
-  EXPECT_FALSE(adjusted_config.gain_controller1.enabled);
-  EXPECT_FALSE(adjusted_config.gain_controller1.analog_gain_controller.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_TRUE(adjusted_config.gain_controller2.input_volume_controller.enabled);
-  EXPECT_NE(adjusted_config.gain_controller2.adaptive_digital,
-            kDefaultAdaptiveDigitalConfig);
-
-  // Change config back and compare.
-  adjusted_config.gain_controller1.enabled = config.gain_controller1.enabled;
-  adjusted_config.gain_controller1.analog_gain_controller.enabled =
-      config.gain_controller1.analog_gain_controller.enabled;
-  adjusted_config.gain_controller2.enabled = config.gain_controller2.enabled;
-  adjusted_config.gain_controller2.adaptive_digital.enabled =
-      config.gain_controller2.adaptive_digital.enabled;
-  adjusted_config.gain_controller2.input_volume_controller.enabled =
-      config.gain_controller2.input_volume_controller.enabled;
-  adjusted_config.gain_controller2.adaptive_digital =
-      config.gain_controller2.adaptive_digital;
-
-  EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString()));
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_TRUE(adjusted.transient_suppression.enabled);
 }
 
-TEST(AudioProcessingImplGainController2FieldTrialTest,
-     ConfigNotAdjustedWhenExperimentEnabledAndAgc1AnalogNotEnabled) {
+TEST(AudioProcessingImplTest,
+     CanDisableTsWithAgc2FieldTrialEnabledAndUsageAllowed) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = false}};
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-GainController2/"
-      "Enabled,"
-      "enable_clipping_predictor:true,"
-      "clipped_level_min:20,"
-      "clipped_level_step:30,"
-      "clipped_ratio_threshold:0.4,"
-      "clipped_wait_frames:50,"
-      "target_range_max_dbfs:-6,"
-      "target_range_min_dbfs:-70,"
-      "update_input_volume_wait_frames:80,"
-      "speech_probability_threshold:0.9,"
-      "speech_ratio_threshold:1.0,"
-      "headroom_db:10,"
-      "max_gain_db:20,"
-      "initial_gain_db:7,"
-      "max_gain_change_db_per_second:3,"
-      "max_output_noise_level_dbfs:-40/");
+      "WebRTC-Audio-GainController2/Enabled,"
+      "disallow_transient_suppressor_usage:false/");
 
-  AudioProcessingBuilderForTesting apm_builder;
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.transient_suppression.enabled);
 
-  // Set a config with analog AGC1 not enabled.
-  AudioProcessing::Config config;
-  config.gain_controller1.enabled = false;
-  config.gain_controller1.analog_gain_controller.enabled = true;
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive = true;
-  config.gain_controller2.enabled = false;
-  config.gain_controller1.mode =
-      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
-
-  EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled);
-
-  apm_builder.SetConfig(config);
-
-  auto apm = apm_builder.Create();
-  auto adjusted_config = apm->GetConfig();
-
-  EXPECT_EQ(config.gain_controller1.enabled,
-            adjusted_config.gain_controller1.enabled);
-  EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled,
-            adjusted_config.gain_controller1.analog_gain_controller.enabled);
-  EXPECT_EQ(config.gain_controller2.enabled,
-            adjusted_config.gain_controller2.enabled);
-  EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled,
-            adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_FALSE(
-      adjusted_config.gain_controller2.input_volume_controller.enabled);
-
-  EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString()));
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(adjusted.transient_suppression.enabled);
 }
 
-TEST(AudioProcessingImplGainController2FieldTrialTest,
-     ConfigNotAdjustedWhenExperimentEnabledAndHybridAgcNotEnabled) {
+TEST(AudioProcessingImplTest,
+     CanEnableTsWithAgc2FieldTrialEnabledAndUsageAllowed) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = true}};
   webrtc::test::ScopedFieldTrials field_trials(
-      "WebRTC-Audio-GainController2/"
-      "Enabled,"
-      "enable_clipping_predictor:true,"
-      "clipped_level_min:20,"
-      "clipped_level_step:30,"
-      "clipped_ratio_threshold:0.4,"
-      "clipped_wait_frames:50,"
-      "target_range_max_dbfs:-6,"
-      "target_range_min_dbfs:-70,"
-      "update_input_volume_wait_frames:80,"
-      "speech_probability_threshold:0.9,"
-      "speech_ratio_threshold:1.0,"
-      "headroom_db:10,"
-      "max_gain_db:20,"
-      "initial_gain_db:7,"
-      "max_gain_change_db_per_second:3,"
-      "max_output_noise_level_dbfs:-40/");
+      "WebRTC-Audio-GainController2/Enabled,"
+      "disallow_transient_suppressor_usage:false/");
 
-  AudioProcessingBuilderForTesting apm_builder;
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_TRUE(adjusted.transient_suppression.enabled);
 
-  // Set a config with hybrid AGC analog not enabled.
-  AudioProcessing::Config config;
-  config.gain_controller1.enabled = false;
-  config.gain_controller1.analog_gain_controller.enabled = true;
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
-      false;
-  config.gain_controller2.enabled = true;
-  config.gain_controller2.adaptive_digital.enabled = true;
-  config.gain_controller1.mode =
-      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
-
-  EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled);
-
-  apm_builder.SetConfig(config);
-
-  auto apm = apm_builder.Create();
-  auto adjusted_config = apm->GetConfig();
-
-  EXPECT_EQ(config.gain_controller1.enabled,
-            adjusted_config.gain_controller1.enabled);
-  EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled,
-            adjusted_config.gain_controller1.analog_gain_controller.enabled);
-  EXPECT_EQ(config.gain_controller2.enabled,
-            adjusted_config.gain_controller2.enabled);
-  EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled,
-            adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_FALSE(
-      adjusted_config.gain_controller2.input_volume_controller.enabled);
-
-  EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString()));
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_TRUE(adjusted.transient_suppression.enabled);
 }
 
-TEST(AudioProcessingImplGainController2FieldTrialTest,
-     ConfigNotAdjustedWhenExperimentNotEnabledAndAgc1AnalogEnabled) {
-  AudioProcessingBuilderForTesting apm_builder;
+TEST(AudioProcessingImplTest,
+     CannotEnableTsWithAgc2FieldTrialEnabledAndUsageDisallowed) {
+  constexpr AudioProcessing::Config kOriginal = {
+      .transient_suppression = {.enabled = true}};
+  webrtc::test::ScopedFieldTrials field_trials(
+      "WebRTC-Audio-GainController2/Enabled,"
+      "disallow_transient_suppressor_usage:true/");
 
-  // Set a config with analog AGC1 analog enabled.
-  AudioProcessing::Config config;
-  config.gain_controller1.enabled = true;
-  config.gain_controller1.analog_gain_controller.enabled = true;
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive = true;
-  config.gain_controller2.enabled = false;
-  config.gain_controller1.mode =
-      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
+  // Test config application via `AudioProcessing` ctor.
+  auto adjusted =
+      AudioProcessingBuilder().SetConfig(kOriginal).Create()->GetConfig();
+  EXPECT_FALSE(adjusted.transient_suppression.enabled);
 
-  EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled);
-
-  apm_builder.SetConfig(config);
-
-  auto apm = apm_builder.Create();
-  auto adjusted_config = apm->GetConfig();
-
-  EXPECT_EQ(config.gain_controller1.enabled,
-            adjusted_config.gain_controller1.enabled);
-  EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled,
-            adjusted_config.gain_controller1.analog_gain_controller.enabled);
-  EXPECT_EQ(config.gain_controller2.enabled,
-            adjusted_config.gain_controller2.enabled);
-  EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled,
-            adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_FALSE(
-      adjusted_config.gain_controller2.input_volume_controller.enabled);
-
-  EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString()));
-}
-
-TEST(AudioProcessingImplGainController2FieldTrialTest,
-     ConfigNotAdjustedWhenExperimentNotEnabledAndHybridAgcEnabled) {
-  AudioProcessingBuilderForTesting apm_builder;
-
-  // Set a config with hybrid AGC enabled.
-  AudioProcessing::Config config;
-  config.gain_controller1.enabled = true;
-  config.gain_controller1.analog_gain_controller.enabled = true;
-  config.gain_controller1.analog_gain_controller.enable_digital_adaptive =
-      false;
-  config.gain_controller2.enabled = true;
-  config.gain_controller2.adaptive_digital.enabled = true;
-  config.gain_controller1.mode =
-      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
-
-  EXPECT_FALSE(config.gain_controller2.input_volume_controller.enabled);
-
-  apm_builder.SetConfig(config);
-
-  auto apm = apm_builder.Create();
-  auto adjusted_config = apm->GetConfig();
-
-  EXPECT_EQ(config.gain_controller1.enabled,
-            adjusted_config.gain_controller1.enabled);
-  EXPECT_EQ(config.gain_controller1.analog_gain_controller.enabled,
-            adjusted_config.gain_controller1.analog_gain_controller.enabled);
-  EXPECT_EQ(config.gain_controller2.enabled,
-            adjusted_config.gain_controller2.enabled);
-  EXPECT_EQ(config.gain_controller2.adaptive_digital.enabled,
-            adjusted_config.gain_controller2.adaptive_digital.enabled);
-  EXPECT_FALSE(
-      adjusted_config.gain_controller2.input_volume_controller.enabled);
-
-  EXPECT_THAT(adjusted_config.ToString(), ::testing::StrEq(config.ToString()));
+  // Test config application via `AudioProcessing::ApplyConfig()`.
+  auto apm = AudioProcessingBuilder().Create();
+  apm->ApplyConfig(kOriginal);
+  adjusted = apm->GetConfig();
+  EXPECT_FALSE(apm->GetConfig().transient_suppression.enabled);
 }
 
 }  // namespace webrtc