Add replacement interface for webrtc::GainConrol

The pointer-to-submodule interfaces are being removed.
This CL:
1) introduces AudioProcessing::Config::GainController1 with most config,
2) adds functions to APM for setting and getting analog gain,
3) creates a temporary GainControlConfigProxy to support the transition
   to the new config.
4) Moves the lock references in GainControlForExperimentalAgc and
   GainControlImpl into the GainControlConfigProxy, as it becomes the
   sole AGC object with functionality exposed to the client.

Bug: webrtc:9947, webrtc:9878
Change-Id: Ic31e15e9bb26d6497a92b77874e0b6cab21ff2b2
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/126485
Commit-Queue: Sam Zackrisson <saza@webrtc.org>
Reviewed-by: Fredrik Solenberg <solenberg@webrtc.org>
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27316}
diff --git a/media/engine/apm_helpers.cc b/media/engine/apm_helpers.cc
index 164ca3c..374c095 100644
--- a/media/engine/apm_helpers.cc
+++ b/media/engine/apm_helpers.cc
@@ -11,7 +11,6 @@
 #include "media/engine/apm_helpers.h"
 
 #include "modules/audio_processing/include/audio_processing.h"
-#include "modules/audio_processing/include/gain_control.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 
@@ -24,59 +23,18 @@
   constexpr int kMinVolumeLevel = 0;
   constexpr int kMaxVolumeLevel = 255;
 
+  AudioProcessing::Config config = apm->GetConfig();
+#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID)
+  config.gain_controller1.mode = config.gain_controller1.kFixedDigital;
+#else
+  config.gain_controller1.mode = config.gain_controller1.kAdaptiveAnalog;
+#endif
+  RTC_LOG(LS_INFO) << "Setting AGC mode to " << config.gain_controller1.mode;
   // This is the initialization which used to happen in VoEBase::Init(), but
   // which is not covered by the WVoE::ApplyOptions().
-  GainControl* gc = apm->gain_control();
-  if (gc->set_analog_level_limits(kMinVolumeLevel, kMaxVolumeLevel) != 0) {
-    RTC_DLOG(LS_ERROR) << "Failed to set analog level limits with minimum: "
-                       << kMinVolumeLevel
-                       << " and maximum: " << kMaxVolumeLevel;
-  }
-}
-
-AgcConfig GetAgcConfig(AudioProcessing* apm) {
-  RTC_DCHECK(apm);
-  AgcConfig result;
-  result.targetLeveldBOv = apm->gain_control()->target_level_dbfs();
-  result.digitalCompressionGaindB = apm->gain_control()->compression_gain_db();
-  result.limiterEnable = apm->gain_control()->is_limiter_enabled();
-  return result;
-}
-
-void SetAgcConfig(AudioProcessing* apm, const AgcConfig& config) {
-  RTC_DCHECK(apm);
-  GainControl* gc = apm->gain_control();
-  if (gc->set_target_level_dbfs(config.targetLeveldBOv) != 0) {
-    RTC_LOG(LS_ERROR) << "Failed to set target level: "
-                      << config.targetLeveldBOv;
-  }
-  if (gc->set_compression_gain_db(config.digitalCompressionGaindB) != 0) {
-    RTC_LOG(LS_ERROR) << "Failed to set compression gain: "
-                      << config.digitalCompressionGaindB;
-  }
-  if (gc->enable_limiter(config.limiterEnable) != 0) {
-    RTC_LOG(LS_ERROR) << "Failed to set limiter on/off: "
-                      << config.limiterEnable;
-  }
-}
-
-void SetAgcStatus(AudioProcessing* apm, bool enable) {
-  RTC_DCHECK(apm);
-#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID)
-  GainControl::Mode agc_mode = GainControl::kFixedDigital;
-#else
-  GainControl::Mode agc_mode = GainControl::kAdaptiveAnalog;
-#endif
-  GainControl* gc = apm->gain_control();
-  if (gc->set_mode(agc_mode) != 0) {
-    RTC_LOG(LS_ERROR) << "Failed to set AGC mode: " << agc_mode;
-    return;
-  }
-  if (gc->Enable(enable) != 0) {
-    RTC_LOG(LS_ERROR) << "Failed to enable/disable AGC: " << enable;
-    return;
-  }
-  RTC_LOG(LS_INFO) << "AGC set to " << enable << " with mode " << agc_mode;
+  config.gain_controller1.analog_level_minimum = kMinVolumeLevel;
+  config.gain_controller1.analog_level_maximum = kMaxVolumeLevel;
+  apm->ApplyConfig(config);
 }
 
 void SetEcStatus(AudioProcessing* apm, bool enable, EcModes mode) {
diff --git a/media/engine/apm_helpers.h b/media/engine/apm_helpers.h
index 6881622..ac61768 100644
--- a/media/engine/apm_helpers.h
+++ b/media/engine/apm_helpers.h
@@ -22,18 +22,9 @@
   kEcAecm,        // AEC mobile.
 };
 
-struct AgcConfig {
-  uint16_t targetLeveldBOv;
-  uint16_t digitalCompressionGaindB;
-  bool limiterEnable;
-};
-
 namespace apm_helpers {
 
 void Init(AudioProcessing* apm);
-AgcConfig GetAgcConfig(AudioProcessing* apm);
-void SetAgcConfig(AudioProcessing* apm, const AgcConfig& config);
-void SetAgcStatus(AudioProcessing* apm, bool enable);
 void SetEcStatus(AudioProcessing* apm, bool enable, EcModes mode);
 void SetEcMetricsStatus(AudioProcessing* apm, bool enable);
 void SetAecmMode(AudioProcessing* apm, bool enable_cng);
diff --git a/media/engine/apm_helpers_unittest.cc b/media/engine/apm_helpers_unittest.cc
index 5b0ed23..e418795 100644
--- a/media/engine/apm_helpers_unittest.cc
+++ b/media/engine/apm_helpers_unittest.cc
@@ -18,8 +18,6 @@
 namespace webrtc {
 namespace {
 
-constexpr AgcConfig kDefaultAgcConfig = {3, 9, true};
-
 struct TestHelper {
   TestHelper() {
     // This replicates the conditions from voe_auto_test.
@@ -39,63 +37,6 @@
 };
 }  // namespace
 
-TEST(ApmHelpersTest, AgcConfig_DefaultConfiguration) {
-  TestHelper helper;
-  AgcConfig agc_config = apm_helpers::GetAgcConfig(helper.apm());
-
-  EXPECT_EQ(kDefaultAgcConfig.targetLeveldBOv, agc_config.targetLeveldBOv);
-  EXPECT_EQ(kDefaultAgcConfig.digitalCompressionGaindB,
-            agc_config.digitalCompressionGaindB);
-  EXPECT_EQ(kDefaultAgcConfig.limiterEnable, agc_config.limiterEnable);
-}
-
-TEST(ApmHelpersTest, AgcConfig_GetAndSet) {
-  const AgcConfig agc_config = {11, 17, false};
-
-  TestHelper helper;
-  apm_helpers::SetAgcConfig(helper.apm(), agc_config);
-  AgcConfig actual_config = apm_helpers::GetAgcConfig(helper.apm());
-
-  EXPECT_EQ(agc_config.digitalCompressionGaindB,
-            actual_config.digitalCompressionGaindB);
-  EXPECT_EQ(agc_config.limiterEnable, actual_config.limiterEnable);
-  EXPECT_EQ(agc_config.targetLeveldBOv, actual_config.targetLeveldBOv);
-}
-
-TEST(ApmHelpersTest, AgcStatus_DefaultMode) {
-  TestHelper helper;
-  GainControl* gc = helper.apm()->gain_control();
-  EXPECT_FALSE(gc->is_enabled());
-#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
-  EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode());
-#elif defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID)
-  EXPECT_EQ(GainControl::kFixedDigital, gc->mode());
-#else
-  EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode());
-#endif
-}
-
-TEST(ApmHelpersTest, AgcStatus_EnableDisable) {
-  TestHelper helper;
-  GainControl* gc = helper.apm()->gain_control();
-#if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID)
-  apm_helpers::SetAgcStatus(helper.apm(), false);
-  EXPECT_FALSE(gc->is_enabled());
-  EXPECT_EQ(GainControl::kFixedDigital, gc->mode());
-
-  apm_helpers::SetAgcStatus(helper.apm(), true);
-  EXPECT_TRUE(gc->is_enabled());
-  EXPECT_EQ(GainControl::kFixedDigital, gc->mode());
-#else
-  apm_helpers::SetAgcStatus(helper.apm(), false);
-  EXPECT_FALSE(gc->is_enabled());
-  EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode());
-  apm_helpers::SetAgcStatus(helper.apm(), true);
-  EXPECT_TRUE(gc->is_enabled());
-  EXPECT_EQ(GainControl::kAdaptiveAnalog, gc->mode());
-#endif
-}
-
 TEST(ApmHelpersTest, EcStatus_DefaultMode) {
   TestHelper helper;
   webrtc::AudioProcessing::Config config = helper.apm()->GetConfig();
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 9fc9d08..cbf66ba 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -260,10 +260,6 @@
   // Connect the ADM to our audio path.
   adm()->RegisterAudioCallback(audio_state()->audio_transport());
 
-  // Save the default AGC configuration settings. This must happen before
-  // calling ApplyOptions or the default will be overwritten.
-  default_agc_config_ = webrtc::apm_helpers::GetAgcConfig(apm());
-
   // Set default engine options.
   {
     AudioOptions options;
@@ -428,24 +424,6 @@
             << "Disabling AGC since built-in AGC will be used instead";
       }
     }
-    webrtc::apm_helpers::SetAgcStatus(apm(), *options.auto_gain_control);
-  }
-
-  if (options.tx_agc_target_dbov || options.tx_agc_digital_compression_gain ||
-      options.tx_agc_limiter) {
-    // Override default_agc_config_. Generally, an unset option means "leave
-    // the VoE bits alone" in this function, so we want whatever is set to be
-    // stored as the new "default". If we didn't, then setting e.g.
-    // tx_agc_target_dbov would reset digital compression gain and limiter
-    // settings.
-    default_agc_config_.targetLeveldBOv = options.tx_agc_target_dbov.value_or(
-        default_agc_config_.targetLeveldBOv);
-    default_agc_config_.digitalCompressionGaindB =
-        options.tx_agc_digital_compression_gain.value_or(
-            default_agc_config_.digitalCompressionGaindB);
-    default_agc_config_.limiterEnable =
-        options.tx_agc_limiter.value_or(default_agc_config_.limiterEnable);
-    webrtc::apm_helpers::SetAgcConfig(apm(), default_agc_config_);
   }
 
   if (options.noise_suppression) {
@@ -524,6 +502,22 @@
 
   webrtc::AudioProcessing::Config apm_config = apm()->GetConfig();
 
+  if (options.auto_gain_control) {
+    const bool enabled = *options.auto_gain_control;
+    apm_config.gain_controller1.enabled = enabled;
+    RTC_LOG(LS_INFO) << "Setting AGC to " << enabled;
+  }
+  if (options.tx_agc_target_dbov) {
+    apm_config.gain_controller1.target_level_dbfs = *options.tx_agc_target_dbov;
+  }
+  if (options.tx_agc_digital_compression_gain) {
+    apm_config.gain_controller1.compression_gain_db =
+        *options.tx_agc_digital_compression_gain;
+  }
+  if (options.tx_agc_limiter) {
+    apm_config.gain_controller1.enable_limiter = *options.tx_agc_limiter;
+  }
+
   if (options.highpass_filter) {
     apm_config.high_pass_filter.enabled = *options.highpass_filter;
   }
diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h
index 3bce78d..1cf1ccb 100644
--- a/media/engine/webrtc_voice_engine.h
+++ b/media/engine/webrtc_voice_engine.h
@@ -82,10 +82,6 @@
   // Stops AEC dump.
   void StopAecDump() override;
 
-  const webrtc::AudioProcessing::Config GetApmConfigForTest() const {
-    return apm()->GetConfig();
-  }
-
  private:
   // Every option that is "set" will be applied. Every option not "set" will be
   // ignored. This allows us to selectively turn on and off different options
@@ -124,7 +120,6 @@
   bool is_dumping_aec_ = false;
   bool initialized_ = false;
 
-  webrtc::AgcConfig default_agc_config_;
   // Cache received extended_filter_aec, delay_agnostic_aec and experimental_ns
   // values, and apply them in case they are missing in the audio options.
   // We need to do this because SetExtraOptions() will revert to defaults for
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index a8935e1..8930b99 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -77,11 +77,12 @@
 
 constexpr int kRtpHistoryMs = 5000;
 
-constexpr webrtc::GainControl::Mode kDefaultAgcMode =
+constexpr webrtc::AudioProcessing::Config::GainController1::Mode
+    kDefaultAgcMode =
 #if defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID)
-    webrtc::GainControl::kFixedDigital;
+        webrtc::AudioProcessing::Config::GainController1::kFixedDigital;
 #else
-    webrtc::GainControl::kAdaptiveAnalog;
+        webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog;
 #endif
 
 constexpr webrtc::NoiseSuppression::Level kDefaultNsLevel =
@@ -169,7 +170,6 @@
   explicit WebRtcVoiceEngineTestFake(const char* field_trials)
       : apm_(new rtc::RefCountedObject<
              StrictMock<webrtc::test::MockAudioProcessing>>()),
-        apm_gc_(*apm_->gain_control()),
         apm_ns_(*apm_->noise_suppression()),
         call_(),
         override_field_trials_(field_trials) {
@@ -181,17 +181,8 @@
     EXPECT_CALL(*apm_, SetExtraOptions(testing::_));
     EXPECT_CALL(*apm_, DetachAecDump());
     // Default Options.
-    EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-    EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
-    EXPECT_CALL(apm_gc_, set_analog_level_limits(0, 255)).WillOnce(Return(0));
     EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
     EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0));
-    // Init does not overwrite default AGC config.
-    EXPECT_CALL(apm_gc_, target_level_dbfs()).WillOnce(Return(1));
-    EXPECT_CALL(apm_gc_, compression_gain_db()).WillRepeatedly(Return(5));
-    EXPECT_CALL(apm_gc_, is_limiter_enabled()).WillRepeatedly(Return(true));
-    EXPECT_CALL(apm_gc_, set_compression_gain_db(5)).WillRepeatedly(Return(0));
-    EXPECT_CALL(apm_gc_, enable_limiter(true)).WillRepeatedly(Return(0));
     // TODO(kwiberg): We should use mock factories here, but a bunch of
     // the tests here probe the specific set of codecs provided by the builtin
     // factories. Those tests should probably be moved elsewhere.
@@ -207,6 +198,8 @@
     EXPECT_TRUE(IsEchoCancellationEnabled());
     EXPECT_TRUE(IsHighPassFilterEnabled());
     EXPECT_TRUE(IsTypingDetectionEnabled());
+    VerifyGainControlEnabledCorrectly();
+    VerifyGainControlDefaultSettings();
   }
 
   bool SetupChannel() {
@@ -737,22 +730,34 @@
     }
   }
 
+  void VerifyGainControlEnabledCorrectly() {
+    EXPECT_TRUE(apm_config_.gain_controller1.enabled);
+    EXPECT_EQ(kDefaultAgcMode, apm_config_.gain_controller1.mode);
+    EXPECT_EQ(0, apm_config_.gain_controller1.analog_level_minimum);
+    EXPECT_EQ(255, apm_config_.gain_controller1.analog_level_maximum);
+  }
+
+  void VerifyGainControlDefaultSettings() {
+    EXPECT_EQ(3, apm_config_.gain_controller1.target_level_dbfs);
+    EXPECT_EQ(9, apm_config_.gain_controller1.compression_gain_db);
+    EXPECT_TRUE(apm_config_.gain_controller1.enable_limiter);
+  }
+
   bool IsEchoCancellationEnabled() {
-    return engine_->GetApmConfigForTest().echo_canceller.enabled;
+    return apm_config_.echo_canceller.enabled;
   }
 
   bool IsHighPassFilterEnabled() {
-    return engine_->GetApmConfigForTest().high_pass_filter.enabled;
+    return apm_config_.high_pass_filter.enabled;
   }
 
   bool IsTypingDetectionEnabled() {
-    return engine_->GetApmConfigForTest().voice_detection.enabled;
+    return apm_config_.voice_detection.enabled;
   }
 
  protected:
   StrictMock<webrtc::test::MockAudioDeviceModule> adm_;
   rtc::scoped_refptr<StrictMock<webrtc::test::MockAudioProcessing>> apm_;
-  webrtc::test::MockGainControl& apm_gc_;
   webrtc::test::MockNoiseSuppression& apm_ns_;
   cricket::FakeCall call_;
   std::unique_ptr<cricket::WebRtcVoiceEngine> engine_;
@@ -2308,18 +2313,40 @@
 TEST_F(WebRtcVoiceEngineTestFake, TxAgcConfigViaOptions) {
   EXPECT_TRUE(SetupSendStream());
   EXPECT_CALL(adm_, BuiltInAGCIsAvailable())
-      .Times(1)
+      .Times(testing::AtLeast(1))
       .WillRepeatedly(Return(false));
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).Times(1).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).Times(1).WillOnce(Return(0));
-  send_parameters_.options.tx_agc_target_dbov = 3;
-  send_parameters_.options.tx_agc_digital_compression_gain = 9;
-  send_parameters_.options.tx_agc_limiter = true;
-  send_parameters_.options.auto_gain_control = true;
-  EXPECT_CALL(apm_gc_, set_target_level_dbfs(3)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, set_compression_gain_db(9)).WillRepeatedly(Return(0));
-  EXPECT_CALL(apm_gc_, enable_limiter(true)).WillRepeatedly(Return(0));
+  const auto& agc_config = apm_config_.gain_controller1;
+
+  // Ensure default options.
+  VerifyGainControlEnabledCorrectly();
+  VerifyGainControlDefaultSettings();
+
+  send_parameters_.options.auto_gain_control = false;
   SetSendParameters(send_parameters_);
+  EXPECT_FALSE(agc_config.enabled);
+  send_parameters_.options.auto_gain_control = absl::nullopt;
+
+  send_parameters_.options.tx_agc_target_dbov = 5;
+  SetSendParameters(send_parameters_);
+  EXPECT_EQ(5, agc_config.target_level_dbfs);
+  send_parameters_.options.tx_agc_target_dbov = absl::nullopt;
+
+  send_parameters_.options.tx_agc_digital_compression_gain = 10;
+  SetSendParameters(send_parameters_);
+  EXPECT_EQ(10, agc_config.compression_gain_db);
+  send_parameters_.options.tx_agc_digital_compression_gain = absl::nullopt;
+
+  send_parameters_.options.tx_agc_limiter = false;
+  SetSendParameters(send_parameters_);
+  EXPECT_FALSE(agc_config.enable_limiter);
+  send_parameters_.options.tx_agc_limiter = absl::nullopt;
+
+  SetSendParameters(send_parameters_);
+  // Expect all options to have been preserved.
+  EXPECT_FALSE(agc_config.enabled);
+  EXPECT_EQ(5, agc_config.target_level_dbfs);
+  EXPECT_EQ(10, agc_config.compression_gain_db);
+  EXPECT_FALSE(agc_config.enable_limiter);
 }
 
 TEST_F(WebRtcVoiceEngineTestFake, SetAudioNetworkAdaptorViaOptions) {
@@ -2898,22 +2925,18 @@
   EXPECT_TRUE(IsEchoCancellationEnabled());
 
   // Turn off AGC
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0));
   send_parameters_.options.auto_gain_control = false;
   SetSendParameters(send_parameters_);
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  EXPECT_FALSE(apm_config_.gain_controller1.enabled);
 
   // Turn AGC back on
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
   send_parameters_.options.auto_gain_control = true;
   SetSendParameters(send_parameters_);
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  EXPECT_TRUE(apm_config_.gain_controller1.enabled);
 
   // Turn off other options.
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   send_parameters_.options.noise_suppression = false;
@@ -2922,14 +2945,14 @@
   SetSendParameters(send_parameters_);
   EXPECT_TRUE(IsEchoCancellationEnabled());
   EXPECT_FALSE(IsHighPassFilterEnabled());
+  EXPECT_TRUE(apm_config_.gain_controller1.enabled);
 
   // Set options again to ensure it has no impact.
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   SetSendParameters(send_parameters_);
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  EXPECT_TRUE(apm_config_.gain_controller1.enabled);
 }
 
 TEST_F(WebRtcVoiceEngineTestFake, SetOptionOverridesViaChannels) {
@@ -2948,11 +2971,6 @@
       .WillRepeatedly(Return(false));
   EXPECT_CALL(adm_, Recording()).Times(2).WillRepeatedly(Return(false));
   EXPECT_CALL(adm_, InitRecording()).Times(2).WillRepeatedly(Return(0));
-  webrtc::AudioProcessing::Config apm_config;
-  EXPECT_CALL(*apm_, GetConfig())
-      .WillRepeatedly(ReturnPointee(&apm_config));
-  EXPECT_CALL(*apm_, ApplyConfig(_))
-      .WillRepeatedly(SaveArg<0>(&apm_config));
   EXPECT_CALL(*apm_, SetExtraOptions(testing::_)).Times(10);
 
   std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel1(
@@ -2979,26 +2997,25 @@
   parameters_options_all.options.echo_cancellation = true;
   parameters_options_all.options.auto_gain_control = true;
   parameters_options_all.options.noise_suppression = true;
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).Times(2).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).Times(2).WillRepeatedly(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).Times(2).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(true)).Times(2).WillRepeatedly(Return(0));
   EXPECT_TRUE(channel1->SetSendParameters(parameters_options_all));
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  VerifyGainControlEnabledCorrectly();
   EXPECT_EQ(parameters_options_all.options, channel1->options());
   EXPECT_TRUE(channel2->SetSendParameters(parameters_options_all));
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  VerifyGainControlEnabledCorrectly();
   EXPECT_EQ(parameters_options_all.options, channel2->options());
 
   // unset NS
   cricket::AudioSendParameters parameters_options_no_ns = send_parameters_;
   parameters_options_no_ns.options.noise_suppression = false;
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   EXPECT_TRUE(channel1->SetSendParameters(parameters_options_no_ns));
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  VerifyGainControlEnabledCorrectly();
   cricket::AudioOptions expected_options = parameters_options_all.options;
   expected_options.echo_cancellation = true;
   expected_options.auto_gain_control = true;
@@ -3008,49 +3025,44 @@
   // unset AGC
   cricket::AudioSendParameters parameters_options_no_agc = send_parameters_;
   parameters_options_no_agc.options.auto_gain_control = false;
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0));
   EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc));
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  EXPECT_FALSE(apm_config_.gain_controller1.enabled);
   expected_options.echo_cancellation = true;
   expected_options.auto_gain_control = false;
   expected_options.noise_suppression = true;
   EXPECT_EQ(expected_options, channel2->options());
 
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0));
   EXPECT_TRUE(channel_->SetSendParameters(parameters_options_all));
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  VerifyGainControlEnabledCorrectly();
 
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(true)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   channel1->SetSend(true);
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  VerifyGainControlEnabledCorrectly();
 
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(true)).WillOnce(Return(0));
   channel2->SetSend(true);
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  EXPECT_FALSE(apm_config_.gain_controller1.enabled);
 
   // Make sure settings take effect while we are sending.
   cricket::AudioSendParameters parameters_options_no_agc_nor_ns =
       send_parameters_;
   parameters_options_no_agc_nor_ns.options.auto_gain_control = false;
   parameters_options_no_agc_nor_ns.options.noise_suppression = false;
-  EXPECT_CALL(apm_gc_, set_mode(kDefaultAgcMode)).WillOnce(Return(0));
-  EXPECT_CALL(apm_gc_, Enable(false)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, set_level(kDefaultNsLevel)).WillOnce(Return(0));
   EXPECT_CALL(apm_ns_, Enable(false)).WillOnce(Return(0));
   EXPECT_TRUE(channel2->SetSendParameters(parameters_options_no_agc_nor_ns));
   EXPECT_TRUE(IsEchoCancellationEnabled());
+  EXPECT_FALSE(apm_config_.gain_controller1.enabled);
   expected_options.echo_cancellation = true;
   expected_options.auto_gain_control = false;
   expected_options.noise_suppression = false;
@@ -3065,11 +3077,6 @@
   std::unique_ptr<cricket::WebRtcVoiceMediaChannel> channel;
   webrtc::RtpParameters parameters;
 
-  webrtc::AudioProcessing::Config apm_config;
-  EXPECT_CALL(*apm_, GetConfig())
-      .WillRepeatedly(ReturnPointee(&apm_config));
-  EXPECT_CALL(*apm_, ApplyConfig(_))
-      .WillRepeatedly(SaveArg<0>(&apm_config));
   EXPECT_CALL(*apm_, SetExtraOptions(testing::_)).Times(3);
 
   channel.reset(static_cast<cricket::WebRtcVoiceMediaChannel*>(
diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn
index 5b64ef3..4c84235 100644
--- a/modules/audio_processing/BUILD.gn
+++ b/modules/audio_processing/BUILD.gn
@@ -154,6 +154,7 @@
     ":audio_processing_c",
     ":audio_processing_statistics",
     ":config",
+    ":gain_control_config_proxy",
     ":gain_control_interface",
     ":noise_suppression_proxy",
     "../..:webrtc_common",
@@ -214,6 +215,19 @@
   ]
 }
 
+rtc_source_set("gain_control_config_proxy") {
+  sources = [
+    "gain_control_config_proxy.cc",
+    "gain_control_config_proxy.h",
+  ]
+  deps = [
+    ":api",
+    ":gain_control_interface",
+    "../../rtc_base:criticalsection",
+    "../../rtc_base:macromagic",
+  ]
+}
+
 rtc_source_set("noise_suppression_proxy") {
   sources = [
     "noise_suppression_proxy.cc",
@@ -414,6 +428,7 @@
       "config_unittest.cc",
       "echo_cancellation_impl_unittest.cc",
       "echo_control_mobile_unittest.cc",
+      "gain_control_config_proxy_unittest.cc",
       "gain_controller2_unittest.cc",
       "splitting_filter_unittest.cc",
       "test/fake_recording_device_unittest.cc",
@@ -438,6 +453,7 @@
       ":audioproc_test_utils",
       ":config",
       ":file_audio_generator_unittests",
+      ":gain_control_config_proxy",
       ":mocks",
       "../..:webrtc_common",
       "../../api:array_view",
diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc
index 706c052..a915222 100644
--- a/modules/audio_processing/aec_dump/aec_dump_impl.cc
+++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc
@@ -188,6 +188,10 @@
       setting->set_custom_render_processing_setting(x);
       break;
     }
+    case AudioProcessing::RuntimeSetting::Type::kCaptureCompressionGain:
+      // Runtime AGC1 compression gain is ignored.
+      // TODO(http://bugs.webrtc.org/10432): Store compression gain in aecdumps.
+      break;
     case AudioProcessing::RuntimeSetting::Type::kNotSpecified:
       RTC_NOTREACHED();
       break;
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index 504eb31..4d24ee6 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -28,6 +28,7 @@
 #include "modules/audio_processing/common.h"
 #include "modules/audio_processing/echo_cancellation_impl.h"
 #include "modules/audio_processing/echo_control_mobile_impl.h"
+#include "modules/audio_processing/gain_control_config_proxy.h"
 #include "modules/audio_processing/gain_control_for_experimental_agc.h"
 #include "modules/audio_processing/gain_control_impl.h"
 #include "modules/audio_processing/gain_controller2.h"
@@ -127,6 +128,19 @@
   }
 }
 
+GainControl::Mode Agc1ConfigModeToInterfaceMode(
+    AudioProcessing::Config::GainController1::Mode mode) {
+  using Agc1Config = AudioProcessing::Config::GainController1;
+  switch (mode) {
+    case Agc1Config::kAdaptiveAnalog:
+      return GainControl::kAdaptiveAnalog;
+    case Agc1Config::kAdaptiveDigital:
+      return GainControl::kAdaptiveDigital;
+    case Agc1Config::kFixedDigital:
+      return GainControl::kFixedDigital;
+  }
+}
+
 // Maximum lengths that frame of samples being passed from the render side to
 // the capture side can have (does not apply to AEC3).
 static const size_t kMaxAllowedValuesOfSamplesPerBand = 160;
@@ -254,13 +268,14 @@
   // Accessed externally of APM without any lock acquired.
   // TODO(bugs.webrtc.org/9947): Move these submodules into private_submodules_
   // when their pointer-to-submodule API functions are gone.
-  std::unique_ptr<GainControlImpl> gain_control;
   std::unique_ptr<LevelEstimatorImpl> level_estimator;
   std::unique_ptr<NoiseSuppressionImpl> noise_suppression;
   std::unique_ptr<NoiseSuppressionProxy> noise_suppression_proxy;
   std::unique_ptr<VoiceDetectionImpl> voice_detection;
+  std::unique_ptr<GainControlImpl> gain_control;
   std::unique_ptr<GainControlForExperimentalAgc>
       gain_control_for_experimental_agc;
+  std::unique_ptr<GainControlConfigProxy> gain_control_config_proxy;
 
   // Accessed internally from both render and capture.
   std::unique_ptr<TransientSuppressor> transient_suppressor;
@@ -393,7 +408,7 @@
   capture_nonlocked_.echo_controller_enabled =
       static_cast<bool>(echo_control_factory_);
 
-  public_submodules_->gain_control.reset(new GainControlImpl(&crit_capture_));
+  public_submodules_->gain_control.reset(new GainControlImpl());
   public_submodules_->level_estimator.reset(
       new LevelEstimatorImpl(&crit_capture_));
   public_submodules_->noise_suppression.reset(
@@ -403,8 +418,10 @@
   public_submodules_->voice_detection.reset(
       new VoiceDetectionImpl(&crit_capture_));
   public_submodules_->gain_control_for_experimental_agc.reset(
-      new GainControlForExperimentalAgc(public_submodules_->gain_control.get(),
-                                        &crit_capture_));
+      new GainControlForExperimentalAgc(
+          public_submodules_->gain_control.get()));
+  public_submodules_->gain_control_config_proxy.reset(
+      new GainControlConfigProxy(&crit_capture_, this, agc1()));
 
   // If no echo detector is injected, use the ResidualEchoDetector.
   if (!private_submodules_->echo_detector) {
@@ -680,6 +697,20 @@
        config_.echo_canceller.legacy_moderate_suppression_level !=
            config.echo_canceller.legacy_moderate_suppression_level);
 
+  const bool agc1_config_changed =
+      config_.gain_controller1.enabled != config.gain_controller1.enabled ||
+      config_.gain_controller1.mode != config.gain_controller1.mode ||
+      config_.gain_controller1.target_level_dbfs !=
+          config.gain_controller1.target_level_dbfs ||
+      config_.gain_controller1.compression_gain_db !=
+          config.gain_controller1.compression_gain_db ||
+      config_.gain_controller1.enable_limiter !=
+          config.gain_controller1.enable_limiter ||
+      config_.gain_controller1.analog_level_minimum !=
+          config.gain_controller1.analog_level_minimum ||
+      config_.gain_controller1.analog_level_maximum !=
+          config.gain_controller1.analog_level_maximum;
+
   config_ = config;
 
   if (aec_config_changed) {
@@ -696,6 +727,10 @@
   RTC_LOG(LS_INFO) << "Highpass filter activated: "
                    << config_.high_pass_filter.enabled;
 
+  if (agc1_config_changed) {
+    ApplyAgc1Config(config_.gain_controller1);
+  }
+
   const bool config_ok = GainController2::Validate(config_.gain_controller2);
   if (!config_ok) {
     RTC_LOG(LS_ERROR) << "AudioProcessing module config error\n"
@@ -730,6 +765,38 @@
   }
 }
 
+void AudioProcessingImpl::ApplyAgc1Config(
+    const Config::GainController1& config) {
+  GainControl* agc = agc1();
+  int error = agc->Enable(config.enabled);
+  RTC_DCHECK_EQ(kNoError, error);
+  error = agc->set_mode(Agc1ConfigModeToInterfaceMode(config.mode));
+  RTC_DCHECK_EQ(kNoError, error);
+  error = agc->set_target_level_dbfs(config.target_level_dbfs);
+  RTC_DCHECK_EQ(kNoError, error);
+  error = agc->set_compression_gain_db(config.compression_gain_db);
+  RTC_DCHECK_EQ(kNoError, error);
+  error = agc->enable_limiter(config.enable_limiter);
+  RTC_DCHECK_EQ(kNoError, error);
+  error = agc->set_analog_level_limits(config.analog_level_minimum,
+                                       config.analog_level_maximum);
+  RTC_DCHECK_EQ(kNoError, error);
+}
+
+GainControl* AudioProcessingImpl::agc1() {
+  if (constants_.use_experimental_agc) {
+    return public_submodules_->gain_control_for_experimental_agc.get();
+  }
+  return public_submodules_->gain_control.get();
+}
+
+const GainControl* AudioProcessingImpl::agc1() const {
+  if (constants_.use_experimental_agc) {
+    return public_submodules_->gain_control_for_experimental_agc.get();
+  }
+  return public_submodules_->gain_control.get();
+}
+
 void AudioProcessingImpl::SetExtraOptions(const webrtc::Config& config) {
   // Run in a single-threaded manner when setting the extra options.
   rtc::CritScope cs_render(&crit_render_);
@@ -793,6 +860,7 @@
       RTC_NOTREACHED();
       return;
     case RuntimeSetting::Type::kCapturePreGain:
+    case RuntimeSetting::Type::kCaptureCompressionGain:
       capture_runtime_settings_enqueuer_.Enqueue(setting);
       return;
   }
@@ -919,6 +987,15 @@
         }
         // TODO(bugs.chromium.org/9138): Log setting handling by Aec Dump.
         break;
+      case RuntimeSetting::Type::kCaptureCompressionGain: {
+        float value;
+        setting.GetFloat(&value);
+        int int_value = static_cast<int>(value + .5f);
+        config_.gain_controller1.compression_gain_db = int_value;
+        int error = agc1()->set_compression_gain_db(int_value);
+        RTC_DCHECK_EQ(kNoError, error);
+        break;
+      }
       case RuntimeSetting::Type::kCustomRenderProcessingRuntimeSetting:
         RTC_NOTREACHED();
         break;
@@ -941,9 +1018,8 @@
           private_submodules_->render_pre_processor->SetRuntimeSetting(setting);
         }
         break;
-      case RuntimeSetting::Type::kCapturePreGain:
-        RTC_NOTREACHED();
-        break;
+      case RuntimeSetting::Type::kCapturePreGain:          // fall-through
+      case RuntimeSetting::Type::kCaptureCompressionGain:  // fall-through
       case RuntimeSetting::Type::kNotSpecified:
         RTC_NOTREACHED();
         break;
@@ -1235,7 +1311,7 @@
 
   if (private_submodules_->echo_controller) {
     // Detect and flag any change in the analog gain.
-    int analog_mic_level = gain_control()->stream_analog_level();
+    int analog_mic_level = agc1()->stream_analog_level();
     capture_.echo_path_gain_change =
         capture_.prev_analog_mic_level != analog_mic_level &&
         capture_.prev_analog_mic_level != -1;
@@ -1389,7 +1465,7 @@
 
   if (config_.gain_controller2.enabled) {
     private_submodules_->gain_controller2->NotifyAnalogLevel(
-        gain_control()->stream_analog_level());
+        agc1()->stream_analog_level());
     private_submodules_->gain_controller2->Process(capture_buffer);
   }
 
@@ -1620,6 +1696,17 @@
   return capture_.delay_offset_ms;
 }
 
+void AudioProcessingImpl::set_stream_analog_level(int level) {
+  rtc::CritScope cs_capture(&crit_capture_);
+  int error = agc1()->set_stream_analog_level(level);
+  RTC_DCHECK_EQ(kNoError, error);
+}
+
+int AudioProcessingImpl::recommended_stream_analog_level() const {
+  rtc::CritScope cs_capture(&crit_capture_);
+  return agc1()->stream_analog_level();
+}
+
 void AudioProcessingImpl::AttachAecDump(std::unique_ptr<AecDump> aec_dump) {
   RTC_DCHECK(aec_dump);
   rtc::CritScope cs_render(&crit_render_);
@@ -1707,10 +1794,7 @@
 }
 
 GainControl* AudioProcessingImpl::gain_control() const {
-  if (constants_.use_experimental_agc) {
-    return public_submodules_->gain_control_for_experimental_agc.get();
-  }
-  return public_submodules_->gain_control.get();
+  return public_submodules_->gain_control_config_proxy.get();
 }
 
 LevelEstimator* AudioProcessingImpl::level_estimator() const {
@@ -2032,7 +2116,7 @@
   audio_proc_state.delay = capture_nonlocked_.stream_delay_ms;
   audio_proc_state.drift =
       private_submodules_->echo_cancellation->stream_drift_samples();
-  audio_proc_state.level = gain_control()->stream_analog_level();
+  audio_proc_state.level = agc1()->stream_analog_level();
   audio_proc_state.keypress = capture_.key_pressed;
   aec_dump_->AddAudioProcessingState(audio_proc_state);
 }
diff --git a/modules/audio_processing/audio_processing_impl.h b/modules/audio_processing/audio_processing_impl.h
index f164407..9cd4f78 100644
--- a/modules/audio_processing/audio_processing_impl.h
+++ b/modules/audio_processing/audio_processing_impl.h
@@ -84,6 +84,8 @@
   void set_delay_offset_ms(int offset) override;
   int delay_offset_ms() const override;
   void set_stream_key_pressed(bool key_pressed) override;
+  void set_stream_analog_level(int level) override;
+  int recommended_stream_analog_level() const override;
 
   // Render-side exclusive methods possibly running APM in a
   // multi-threaded manner. Acquire the render lock.
@@ -255,6 +257,13 @@
   void HandleCaptureRuntimeSettings()
       RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
   void HandleRenderRuntimeSettings() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_render_);
+  void ApplyAgc1Config(const Config::GainController1& agc_config)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
+
+  // Returns a direct pointer to the AGC1 submodule: either a GainControlImpl
+  // or GainControlForExperimentalAgc instance.
+  GainControl* agc1();
+  const GainControl* agc1() const;
 
   void EmptyQueuedRenderAudio();
   void AllocateRenderQueue()
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index d1e5801..b5fb2b8 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -182,17 +182,21 @@
   apm_config.echo_canceller.enabled = true;
 #if defined(WEBRTC_AUDIOPROC_FIXED_PROFILE)
   apm_config.echo_canceller.mobile_mode = true;
-  EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveDigital));
-  EXPECT_NOERR(ap->gain_control()->Enable(true));
+
+  apm_config.gain_controller1.enabled = true;
+  apm_config.gain_controller1.mode =
+      AudioProcessing::Config::GainController1::kAdaptiveDigital;
 #elif defined(WEBRTC_AUDIOPROC_FLOAT_PROFILE)
   // TODO(peah): Update tests to instead use AEC3.
   apm_config.echo_canceller.use_legacy_aec = true;
   apm_config.echo_canceller.mobile_mode = false;
   apm_config.echo_canceller.legacy_moderate_suppression_level = true;
 
-  EXPECT_NOERR(ap->gain_control()->set_mode(GainControl::kAdaptiveAnalog));
-  EXPECT_NOERR(ap->gain_control()->set_analog_level_limits(0, 255));
-  EXPECT_NOERR(ap->gain_control()->Enable(true));
+  apm_config.gain_controller1.enabled = true;
+  apm_config.gain_controller1.mode =
+      AudioProcessing::Config::GainController1::kAdaptiveAnalog;
+  apm_config.gain_controller1.analog_level_minimum = 0;
+  apm_config.gain_controller1.analog_level_maximum = 255;
 #endif
 
   apm_config.high_pass_filter.enabled = true;
@@ -958,12 +962,7 @@
         apm_->gain_control()->set_mode(mode[i]));
     EXPECT_EQ(mode[i], apm_->gain_control()->mode());
   }
-  // Testing invalid target levels
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_target_level_dbfs(-3));
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_target_level_dbfs(-40));
-  // Testing valid target levels
+  // Testing target levels
   EXPECT_EQ(apm_->kNoError,
       apm_->gain_control()->set_target_level_dbfs(
       apm_->gain_control()->target_level_dbfs()));
@@ -975,13 +974,7 @@
     EXPECT_EQ(level_dbfs[i], apm_->gain_control()->target_level_dbfs());
   }
 
-  // Testing invalid compression gains
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_compression_gain_db(-1));
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_compression_gain_db(100));
-
-  // Testing valid compression gains
+  // Testing compression gains
   EXPECT_EQ(apm_->kNoError,
       apm_->gain_control()->set_compression_gain_db(
       apm_->gain_control()->compression_gain_db()));
@@ -990,6 +983,7 @@
   for (size_t i = 0; i < arraysize(gain_db); i++) {
     EXPECT_EQ(apm_->kNoError,
         apm_->gain_control()->set_compression_gain_db(gain_db[i]));
+    ProcessStreamChooser(kFloatFormat);
     EXPECT_EQ(gain_db[i], apm_->gain_control()->compression_gain_db());
   }
 
@@ -999,19 +993,7 @@
   EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(true));
   EXPECT_TRUE(apm_->gain_control()->is_limiter_enabled());
 
-  // Testing invalid level limits
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_analog_level_limits(-1, 512));
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_analog_level_limits(100000, 512));
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_analog_level_limits(512, -1));
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_analog_level_limits(512, 100000));
-  EXPECT_EQ(apm_->kBadParameterError,
-      apm_->gain_control()->set_analog_level_limits(512, 255));
-
-  // Testing valid level limits
+  // Testing level limits
   EXPECT_EQ(apm_->kNoError,
       apm_->gain_control()->set_analog_level_limits(
       apm_->gain_control()->analog_level_minimum(),
@@ -1038,6 +1020,46 @@
   EXPECT_FALSE(apm_->gain_control()->is_enabled());
 }
 
+#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
+TEST_F(ApmTest, GainControlDiesOnTooLowTargetLevelDbfs) {
+  EXPECT_DEATH(apm_->gain_control()->set_target_level_dbfs(-1), "");
+}
+
+TEST_F(ApmTest, GainControlDiesOnTooHighTargetLevelDbfs) {
+  EXPECT_DEATH(apm_->gain_control()->set_target_level_dbfs(32), "");
+}
+
+TEST_F(ApmTest, GainControlDiesOnTooLowCompressionGainDb) {
+  EXPECT_DEATH(apm_->gain_control()->set_compression_gain_db(-1), "");
+}
+
+TEST_F(ApmTest, GainControlDiesOnTooHighCompressionGainDb) {
+  EXPECT_DEATH(apm_->gain_control()->set_compression_gain_db(91), "");
+}
+
+TEST_F(ApmTest, GainControlDiesOnTooLowAnalogLevelLowerLimit) {
+  EXPECT_DEATH(apm_->gain_control()->set_analog_level_limits(-1, 512), "");
+}
+
+TEST_F(ApmTest, GainControlDiesOnTooHighAnalogLevelUpperLimit) {
+  EXPECT_DEATH(apm_->gain_control()->set_analog_level_limits(512, 65536), "");
+}
+
+TEST_F(ApmTest, GainControlDiesOnInvertedAnalogLevelLimits) {
+  EXPECT_DEATH(apm_->gain_control()->set_analog_level_limits(512, 255), "");
+}
+
+TEST_F(ApmTest, ApmDiesOnTooLowAnalogLevel) {
+  apm_->gain_control()->set_analog_level_limits(255, 512);
+  EXPECT_DEATH(apm_->set_stream_analog_level(254), "");
+}
+
+TEST_F(ApmTest, ApmDiesOnTooHighAnalogLevel) {
+  apm_->gain_control()->set_analog_level_limits(255, 512);
+  EXPECT_DEATH(apm_->set_stream_analog_level(513), "");
+}
+#endif
+
 void ApmTest::RunQuantizedVolumeDoesNotGetStuckTest(int sample_rate) {
   Init(sample_rate, sample_rate, sample_rate, 2, 2, 2, false);
   EXPECT_EQ(apm_->kNoError,
diff --git a/modules/audio_processing/gain_control_config_proxy.cc b/modules/audio_processing/gain_control_config_proxy.cc
new file mode 100644
index 0000000..28e2117
--- /dev/null
+++ b/modules/audio_processing/gain_control_config_proxy.cc
@@ -0,0 +1,132 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/gain_control_config_proxy.h"
+
+namespace webrtc {
+namespace {
+
+AudioProcessing::Config::GainController1::Mode InterfaceModeToConfigMode(
+    GainControl::Mode agc_mode) {
+  using AgcConfig = AudioProcessing::Config::GainController1;
+  switch (agc_mode) {
+    case GainControl::kAdaptiveAnalog:
+      return AgcConfig::kAdaptiveAnalog;
+    case GainControl::kAdaptiveDigital:
+      return AgcConfig::kAdaptiveDigital;
+    case GainControl::kFixedDigital:
+      return AgcConfig::kFixedDigital;
+  }
+}
+}  // namespace
+
+GainControlConfigProxy::GainControlConfigProxy(
+    rtc::CriticalSection* crit_capture,
+    AudioProcessing* apm,
+    GainControl* agc)
+    : crit_capture_(crit_capture), apm_(apm), agc_(agc) {
+  RTC_DCHECK(apm);
+  RTC_DCHECK(agc);
+  RTC_DCHECK(crit_capture);
+}
+
+GainControlConfigProxy::~GainControlConfigProxy() = default;
+
+int GainControlConfigProxy::set_stream_analog_level(int level) {
+  apm_->set_stream_analog_level(level);
+  return AudioProcessing::kNoError;
+}
+
+int GainControlConfigProxy::stream_analog_level() const {
+  return apm_->recommended_stream_analog_level();
+}
+
+int GainControlConfigProxy::Enable(bool enable) {
+  auto apm_config = apm_->GetConfig();
+  apm_config.gain_controller1.enabled = enable;
+  apm_->ApplyConfig(apm_config);
+  return AudioProcessing::kNoError;
+}
+
+int GainControlConfigProxy::set_mode(Mode mode) {
+  auto config = apm_->GetConfig();
+  config.gain_controller1.mode = InterfaceModeToConfigMode(mode);
+  apm_->ApplyConfig(config);
+  return AudioProcessing::kNoError;
+}
+
+int GainControlConfigProxy::set_target_level_dbfs(int level) {
+  auto config = apm_->GetConfig();
+  config.gain_controller1.target_level_dbfs = level;
+  apm_->ApplyConfig(config);
+  return AudioProcessing::kNoError;
+}
+
+int GainControlConfigProxy::set_compression_gain_db(int gain) {
+  apm_->SetRuntimeSetting(
+      AudioProcessing::RuntimeSetting::CreateCompressionGainDb(gain));
+  return AudioProcessing::kNoError;
+}
+
+int GainControlConfigProxy::enable_limiter(bool enable) {
+  auto config = apm_->GetConfig();
+  config.gain_controller1.enable_limiter = enable;
+  apm_->ApplyConfig(config);
+  return AudioProcessing::kNoError;
+}
+
+int GainControlConfigProxy::set_analog_level_limits(int minimum, int maximum) {
+  auto config = apm_->GetConfig();
+  config.gain_controller1.analog_level_minimum = minimum;
+  config.gain_controller1.analog_level_maximum = maximum;
+  apm_->ApplyConfig(config);
+  return AudioProcessing::kNoError;
+}
+
+bool GainControlConfigProxy::is_limiter_enabled() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->is_limiter_enabled();
+}
+
+int GainControlConfigProxy::compression_gain_db() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->compression_gain_db();
+}
+
+bool GainControlConfigProxy::is_enabled() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->is_enabled();
+}
+
+GainControl::Mode GainControlConfigProxy::mode() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->mode();
+}
+
+int GainControlConfigProxy::target_level_dbfs() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->target_level_dbfs();
+}
+
+int GainControlConfigProxy::analog_level_minimum() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->analog_level_minimum();
+}
+
+int GainControlConfigProxy::analog_level_maximum() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->analog_level_maximum();
+}
+
+bool GainControlConfigProxy::stream_is_saturated() const {
+  rtc::CritScope cs_capture(crit_capture_);
+  return agc_->stream_is_saturated();
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/gain_control_config_proxy.h b/modules/audio_processing/gain_control_config_proxy.h
new file mode 100644
index 0000000..04ed536
--- /dev/null
+++ b/modules/audio_processing/gain_control_config_proxy.h
@@ -0,0 +1,62 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROL_CONFIG_PROXY_H_
+#define MODULES_AUDIO_PROCESSING_GAIN_CONTROL_CONFIG_PROXY_H_
+
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/audio_processing/include/gain_control.h"
+#include "rtc_base/critical_section.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+// This class forwards all gain control configuration to the audio processing
+// module, for compatibility with AudioProcessing::Config.
+class GainControlConfigProxy : public GainControl {
+ public:
+  GainControlConfigProxy(rtc::CriticalSection* crit_capture,
+                         AudioProcessing* apm,
+                         GainControl* agc);
+  GainControlConfigProxy(const GainControlConfigProxy&) = delete;
+  GainControlConfigProxy& operator=(const GainControlConfigProxy&) = delete;
+
+  ~GainControlConfigProxy() override;
+
+ private:
+  // GainControl API during processing.
+  int set_stream_analog_level(int level) override;
+  int stream_analog_level() const override;
+
+  // GainControl config setters.
+  int Enable(bool enable) override;
+  int set_mode(Mode mode) override;
+  int set_target_level_dbfs(int level) override;
+  int set_compression_gain_db(int gain) override;
+  int enable_limiter(bool enable) override;
+  int set_analog_level_limits(int minimum, int maximum) override;
+
+  // GainControl config getters.
+  bool is_enabled() const override;
+  bool is_limiter_enabled() const override;
+  int compression_gain_db() const override;
+  int target_level_dbfs() const override;
+  int analog_level_minimum() const override;
+  int analog_level_maximum() const override;
+  bool stream_is_saturated() const override;
+  Mode mode() const override;
+
+  rtc::CriticalSection* crit_capture_ = nullptr;
+  AudioProcessing* apm_ = nullptr;
+  GainControl* agc_ RTC_GUARDED_BY(crit_capture_) = nullptr;
+};
+
+}  // namespace webrtc
+#endif  // MODULES_AUDIO_PROCESSING_GAIN_CONTROL_CONFIG_PROXY_H_
diff --git a/modules/audio_processing/gain_control_config_proxy_unittest.cc b/modules/audio_processing/gain_control_config_proxy_unittest.cc
new file mode 100644
index 0000000..e5204e8
--- /dev/null
+++ b/modules/audio_processing/gain_control_config_proxy_unittest.cc
@@ -0,0 +1,162 @@
+/*
+ *  Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/audio_processing/gain_control_config_proxy.h"
+#include "modules/audio_processing/include/audio_processing.h"
+#include "modules/audio_processing/include/mock_audio_processing.h"
+#include "rtc_base/critical_section.h"
+#include "rtc_base/ref_counted_object.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+class GainControlConfigProxyTest : public testing::Test {
+ protected:
+  GainControlConfigProxyTest()
+      : apm_(new rtc::RefCountedObject<
+             testing::StrictMock<test::MockAudioProcessing>>()),
+        agc_(),
+        proxy_(&lock_, apm_, &agc_) {
+    EXPECT_CALL(*apm_, GetConfig())
+        .WillRepeatedly(testing::ReturnPointee(&apm_config_));
+    EXPECT_CALL(*apm_, ApplyConfig(testing::_))
+        .WillRepeatedly(testing::SaveArg<0>(&apm_config_));
+  }
+
+  GainControl* proxy() { return &proxy_; }
+
+  rtc::scoped_refptr<testing::StrictMock<test::MockAudioProcessing>> apm_;
+  testing::StrictMock<test::MockGainControl> agc_;
+  AudioProcessing::Config apm_config_;
+
+ private:
+  rtc::CriticalSection lock_;
+  GainControlConfigProxy proxy_;
+};
+
+// GainControl API during processing.
+TEST_F(GainControlConfigProxyTest, SetStreamAnalogLevel) {
+  EXPECT_CALL(*apm_, set_stream_analog_level(100));
+  proxy()->set_stream_analog_level(100);
+}
+
+TEST_F(GainControlConfigProxyTest, StreamAnalogLevel) {
+  EXPECT_CALL(*apm_, recommended_stream_analog_level())
+      .WillOnce(testing::Return(100));
+  EXPECT_EQ(100, proxy()->stream_analog_level());
+}
+
+// GainControl config setters.
+TEST_F(GainControlConfigProxyTest, SetEnable) {
+  proxy()->Enable(true);
+  EXPECT_TRUE(apm_config_.gain_controller1.enabled);
+
+  proxy()->Enable(false);
+  EXPECT_FALSE(apm_config_.gain_controller1.enabled);
+}
+
+TEST_F(GainControlConfigProxyTest, SetMode) {
+  proxy()->set_mode(GainControl::Mode::kAdaptiveAnalog);
+  EXPECT_EQ(apm_config_.gain_controller1.kAdaptiveAnalog,
+            apm_config_.gain_controller1.mode);
+
+  proxy()->set_mode(GainControl::Mode::kAdaptiveDigital);
+  EXPECT_EQ(apm_config_.gain_controller1.kAdaptiveDigital,
+            apm_config_.gain_controller1.mode);
+
+  proxy()->set_mode(GainControl::Mode::kFixedDigital);
+  EXPECT_EQ(apm_config_.gain_controller1.kFixedDigital,
+            apm_config_.gain_controller1.mode);
+}
+
+TEST_F(GainControlConfigProxyTest, SetTargetLevelDbfs) {
+  proxy()->set_target_level_dbfs(17);
+  EXPECT_EQ(17, apm_config_.gain_controller1.target_level_dbfs);
+}
+
+TEST_F(GainControlConfigProxyTest, SetCompressionGainDb) {
+  AudioProcessing::RuntimeSetting setting;
+  EXPECT_CALL(*apm_, SetRuntimeSetting(testing::_))
+      .WillOnce(testing::SaveArg<0>(&setting));
+  proxy()->set_compression_gain_db(17);
+  EXPECT_EQ(AudioProcessing::RuntimeSetting::Type::kCaptureCompressionGain,
+            setting.type());
+  float value;
+  setting.GetFloat(&value);
+  EXPECT_EQ(17, static_cast<int>(value + .5f));
+}
+
+TEST_F(GainControlConfigProxyTest, SetEnableLimiter) {
+  proxy()->enable_limiter(true);
+  EXPECT_TRUE(apm_config_.gain_controller1.enable_limiter);
+  proxy()->enable_limiter(false);
+  EXPECT_FALSE(apm_config_.gain_controller1.enable_limiter);
+}
+
+TEST_F(GainControlConfigProxyTest, SetAnalogLevelLimits) {
+  proxy()->set_analog_level_limits(100, 300);
+  EXPECT_EQ(100, apm_config_.gain_controller1.analog_level_minimum);
+  EXPECT_EQ(300, apm_config_.gain_controller1.analog_level_maximum);
+}
+
+TEST_F(GainControlConfigProxyTest, GetEnabled) {
+  EXPECT_CALL(agc_, is_enabled())
+      .WillOnce(testing::Return(true))
+      .WillOnce(testing::Return(false));
+  EXPECT_TRUE(proxy()->is_enabled());
+  EXPECT_FALSE(proxy()->is_enabled());
+}
+
+TEST_F(GainControlConfigProxyTest, GetLimiterEnabled) {
+  EXPECT_CALL(agc_, is_enabled())
+      .WillOnce(testing::Return(true))
+      .WillOnce(testing::Return(false));
+  EXPECT_TRUE(proxy()->is_enabled());
+  EXPECT_FALSE(proxy()->is_enabled());
+}
+
+TEST_F(GainControlConfigProxyTest, GetCompressionGainDb) {
+  EXPECT_CALL(agc_, compression_gain_db()).WillOnce(testing::Return(17));
+  EXPECT_EQ(17, proxy()->compression_gain_db());
+}
+
+TEST_F(GainControlConfigProxyTest, GetTargetLevelDbfs) {
+  EXPECT_CALL(agc_, target_level_dbfs()).WillOnce(testing::Return(17));
+  EXPECT_EQ(17, proxy()->target_level_dbfs());
+}
+
+TEST_F(GainControlConfigProxyTest, GetAnalogLevelMinimum) {
+  EXPECT_CALL(agc_, analog_level_minimum()).WillOnce(testing::Return(17));
+  EXPECT_EQ(17, proxy()->analog_level_minimum());
+}
+
+TEST_F(GainControlConfigProxyTest, GetAnalogLevelMaximum) {
+  EXPECT_CALL(agc_, analog_level_maximum()).WillOnce(testing::Return(17));
+  EXPECT_EQ(17, proxy()->analog_level_maximum());
+}
+
+TEST_F(GainControlConfigProxyTest, GetStreamIsSaturated) {
+  EXPECT_CALL(agc_, stream_is_saturated())
+      .WillOnce(testing::Return(true))
+      .WillOnce(testing::Return(false));
+  EXPECT_TRUE(proxy()->stream_is_saturated());
+  EXPECT_FALSE(proxy()->stream_is_saturated());
+}
+
+TEST_F(GainControlConfigProxyTest, GetMode) {
+  EXPECT_CALL(agc_, mode())
+      .WillOnce(testing::Return(GainControl::Mode::kAdaptiveAnalog))
+      .WillOnce(testing::Return(GainControl::Mode::kAdaptiveDigital))
+      .WillOnce(testing::Return(GainControl::Mode::kFixedDigital));
+  EXPECT_EQ(GainControl::Mode::kAdaptiveAnalog, proxy()->mode());
+  EXPECT_EQ(GainControl::Mode::kAdaptiveDigital, proxy()->mode());
+  EXPECT_EQ(GainControl::Mode::kFixedDigital, proxy()->mode());
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/gain_control_for_experimental_agc.cc b/modules/audio_processing/gain_control_for_experimental_agc.cc
index 5cb22f8..9e4d4f5 100644
--- a/modules/audio_processing/gain_control_for_experimental_agc.cc
+++ b/modules/audio_processing/gain_control_for_experimental_agc.cc
@@ -20,13 +20,11 @@
 int GainControlForExperimentalAgc::instance_counter_ = 0;
 
 GainControlForExperimentalAgc::GainControlForExperimentalAgc(
-    GainControl* gain_control,
-    rtc::CriticalSection* crit_capture)
+    GainControl* gain_control)
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_counter_))),
       real_gain_control_(gain_control),
-      volume_(0),
-      crit_capture_(crit_capture) {}
+      volume_(0) {}
 
 GainControlForExperimentalAgc::~GainControlForExperimentalAgc() = default;
 
@@ -39,7 +37,6 @@
 }
 
 int GainControlForExperimentalAgc::set_stream_analog_level(int level) {
-  rtc::CritScope cs_capture(crit_capture_);
   data_dumper_->DumpRaw("experimental_gain_control_set_stream_analog_level", 1,
                         &level);
   do_log_level_ = true;
@@ -47,8 +44,7 @@
   return AudioProcessing::kNoError;
 }
 
-int GainControlForExperimentalAgc::stream_analog_level() {
-  rtc::CritScope cs_capture(crit_capture_);
+int GainControlForExperimentalAgc::stream_analog_level() const {
   if (do_log_level_) {
     data_dumper_->DumpRaw("experimental_gain_control_stream_analog_level", 1,
                           &volume_);
@@ -107,12 +103,10 @@
 }
 
 void GainControlForExperimentalAgc::SetMicVolume(int volume) {
-  rtc::CritScope cs_capture(crit_capture_);
   volume_ = volume;
 }
 
 int GainControlForExperimentalAgc::GetMicVolume() {
-  rtc::CritScope cs_capture(crit_capture_);
   return volume_;
 }
 
diff --git a/modules/audio_processing/gain_control_for_experimental_agc.h b/modules/audio_processing/gain_control_for_experimental_agc.h
index 8f5681f..59328cd 100644
--- a/modules/audio_processing/gain_control_for_experimental_agc.h
+++ b/modules/audio_processing/gain_control_for_experimental_agc.h
@@ -13,8 +13,6 @@
 
 #include "modules/audio_processing/agc/agc_manager_direct.h"
 #include "modules/audio_processing/include/audio_processing.h"
-#include "rtc_base/constructor_magic.h"
-#include "rtc_base/critical_section.h"
 #include "rtc_base/thread_checker.h"
 
 namespace webrtc {
@@ -35,15 +33,18 @@
 class GainControlForExperimentalAgc : public GainControl,
                                       public VolumeCallbacks {
  public:
-  GainControlForExperimentalAgc(GainControl* gain_control,
-                                rtc::CriticalSection* crit_capture);
+  explicit GainControlForExperimentalAgc(GainControl* gain_control);
+  GainControlForExperimentalAgc(const GainControlForExperimentalAgc&) = delete;
+  GainControlForExperimentalAgc& operator=(
+      const GainControlForExperimentalAgc&) = delete;
+
   ~GainControlForExperimentalAgc() override;
 
   // GainControl implementation.
   int Enable(bool enable) override;
   bool is_enabled() const override;
   int set_stream_analog_level(int level) override;
-  int stream_analog_level() override;
+  int stream_analog_level() const override;
   int set_mode(Mode mode) override;
   Mode mode() const override;
   int set_target_level_dbfs(int level) override;
@@ -67,10 +68,8 @@
   std::unique_ptr<ApmDataDumper> data_dumper_;
   GainControl* real_gain_control_;
   int volume_;
-  rtc::CriticalSection* crit_capture_;
-  bool do_log_level_ = true;
+  mutable bool do_log_level_ = true;
   static int instance_counter_;
-  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(GainControlForExperimentalAgc);
 };
 
 }  // namespace webrtc
diff --git a/modules/audio_processing/gain_control_impl.cc b/modules/audio_processing/gain_control_impl.cc
index cd21e4c..47cbe52 100644
--- a/modules/audio_processing/gain_control_impl.cc
+++ b/modules/audio_processing/gain_control_impl.cc
@@ -89,9 +89,8 @@
 
 int GainControlImpl::instance_counter_ = 0;
 
-GainControlImpl::GainControlImpl(rtc::CriticalSection* crit_capture)
-    : crit_capture_(crit_capture),
-      data_dumper_(new ApmDataDumper(instance_counter_)),
+GainControlImpl::GainControlImpl()
+    : data_dumper_(new ApmDataDumper(instance_counter_)),
       mode_(kAdaptiveAnalog),
       minimum_capture_level_(0),
       maximum_capture_level_(255),
@@ -100,15 +99,12 @@
       compression_gain_db_(9),
       analog_capture_level_(0),
       was_analog_level_set_(false),
-      stream_is_saturated_(false) {
-  RTC_DCHECK(crit_capture);
-}
+      stream_is_saturated_(false) {}
 
 GainControlImpl::~GainControlImpl() {}
 
 void GainControlImpl::ProcessRenderAudio(
     rtc::ArrayView<const int16_t> packed_render_audio) {
-  rtc::CritScope cs_capture(crit_capture_);
   if (!enabled_) {
     return;
   }
@@ -131,8 +127,6 @@
 }
 
 int GainControlImpl::AnalyzeCaptureAudio(AudioBuffer* audio) {
-  rtc::CritScope cs(crit_capture_);
-
   if (!enabled_) {
     return AudioProcessing::kNoError;
   }
@@ -178,8 +172,6 @@
 
 int GainControlImpl::ProcessCaptureAudio(AudioBuffer* audio,
                                          bool stream_has_echo) {
-  rtc::CritScope cs(crit_capture_);
-
   if (!enabled_) {
     return AudioProcessing::kNoError;
   }
@@ -235,13 +227,11 @@
 }
 
 int GainControlImpl::compression_gain_db() const {
-  rtc::CritScope cs(crit_capture_);
   return compression_gain_db_;
 }
 
 // TODO(ajm): ensure this is called under kAdaptiveAnalog.
 int GainControlImpl::set_stream_analog_level(int level) {
-  rtc::CritScope cs(crit_capture_);
   data_dumper_->DumpRaw("gain_control_set_stream_analog_level", 1, &level);
 
   was_analog_level_set_ = true;
@@ -253,8 +243,7 @@
   return AudioProcessing::kNoError;
 }
 
-int GainControlImpl::stream_analog_level() {
-  rtc::CritScope cs(crit_capture_);
+int GainControlImpl::stream_analog_level() const {
   data_dumper_->DumpRaw("gain_control_stream_analog_level", 1,
                         &analog_capture_level_);
   // TODO(ajm): enable this assertion?
@@ -264,7 +253,6 @@
 }
 
 int GainControlImpl::Enable(bool enable) {
-  rtc::CritScope cs_capture(crit_capture_);
   if (enable && !enabled_) {
     enabled_ = enable;  // Must be set before Initialize() is called.
 
@@ -278,12 +266,10 @@
 }
 
 bool GainControlImpl::is_enabled() const {
-  rtc::CritScope cs(crit_capture_);
   return enabled_;
 }
 
 int GainControlImpl::set_mode(Mode mode) {
-  rtc::CritScope cs_capture(crit_capture_);
   if (MapSetting(mode) == -1) {
     return AudioProcessing::kBadParameterError;
   }
@@ -296,7 +282,6 @@
 }
 
 GainControl::Mode GainControlImpl::mode() const {
-  rtc::CritScope cs(crit_capture_);
   return mode_;
 }
 
@@ -316,7 +301,6 @@
   size_t num_proc_channels_local = 0u;
   int sample_rate_hz_local = 0;
   {
-    rtc::CritScope cs(crit_capture_);
 
     minimum_capture_level_ = minimum;
     maximum_capture_level_ = maximum;
@@ -331,17 +315,14 @@
 }
 
 int GainControlImpl::analog_level_minimum() const {
-  rtc::CritScope cs(crit_capture_);
   return minimum_capture_level_;
 }
 
 int GainControlImpl::analog_level_maximum() const {
-  rtc::CritScope cs(crit_capture_);
   return maximum_capture_level_;
 }
 
 bool GainControlImpl::stream_is_saturated() const {
-  rtc::CritScope cs(crit_capture_);
   return stream_is_saturated_;
 }
 
@@ -349,13 +330,11 @@
   if (level > 31 || level < 0) {
     return AudioProcessing::kBadParameterError;
   }
-  rtc::CritScope cs(crit_capture_);
   target_level_dbfs_ = level;
   return Configure();
 }
 
 int GainControlImpl::target_level_dbfs() const {
-  rtc::CritScope cs(crit_capture_);
   return target_level_dbfs_;
 }
 
@@ -363,24 +342,20 @@
   if (gain < 0 || gain > 90) {
     return AudioProcessing::kBadParameterError;
   }
-  rtc::CritScope cs(crit_capture_);
   compression_gain_db_ = gain;
   return Configure();
 }
 
 int GainControlImpl::enable_limiter(bool enable) {
-  rtc::CritScope cs(crit_capture_);
   limiter_enabled_ = enable;
   return Configure();
 }
 
 bool GainControlImpl::is_limiter_enabled() const {
-  rtc::CritScope cs(crit_capture_);
   return limiter_enabled_;
 }
 
 void GainControlImpl::Initialize(size_t num_proc_channels, int sample_rate_hz) {
-  rtc::CritScope cs_capture(crit_capture_);
   data_dumper_->InitiateNewSetOfRecordings();
 
   num_proc_channels_ = num_proc_channels;
diff --git a/modules/audio_processing/gain_control_impl.h b/modules/audio_processing/gain_control_impl.h
index 9dfe0f1..36b84ee 100644
--- a/modules/audio_processing/gain_control_impl.h
+++ b/modules/audio_processing/gain_control_impl.h
@@ -20,8 +20,6 @@
 #include "api/array_view.h"
 #include "modules/audio_processing/include/gain_control.h"
 #include "rtc_base/constructor_magic.h"
-#include "rtc_base/critical_section.h"
-#include "rtc_base/thread_annotations.h"
 
 namespace webrtc {
 
@@ -30,7 +28,10 @@
 
 class GainControlImpl : public GainControl {
  public:
-  explicit GainControlImpl(rtc::CriticalSection* crit_capture);
+  GainControlImpl();
+  GainControlImpl(const GainControlImpl&) = delete;
+  GainControlImpl& operator=(const GainControlImpl&) = delete;
+
   ~GainControlImpl() override;
 
   void ProcessRenderAudio(rtc::ArrayView<const int16_t> packed_render_audio);
@@ -44,7 +45,7 @@
 
   // GainControl implementation.
   bool is_enabled() const override;
-  int stream_analog_level() override;
+  int stream_analog_level() const override;
   bool is_limiter_enabled() const override;
   Mode mode() const override;
 
@@ -66,31 +67,28 @@
   int analog_level_maximum() const override;
   bool stream_is_saturated() const override;
 
-  int Configure() RTC_EXCLUSIVE_LOCKS_REQUIRED(crit_capture_);
-
-  rtc::CriticalSection* const crit_capture_;
+  int Configure();
 
   std::unique_ptr<ApmDataDumper> data_dumper_;
 
   bool enabled_ = false;
 
-  Mode mode_ RTC_GUARDED_BY(crit_capture_);
-  int minimum_capture_level_ RTC_GUARDED_BY(crit_capture_);
-  int maximum_capture_level_ RTC_GUARDED_BY(crit_capture_);
-  bool limiter_enabled_ RTC_GUARDED_BY(crit_capture_);
-  int target_level_dbfs_ RTC_GUARDED_BY(crit_capture_);
-  int compression_gain_db_ RTC_GUARDED_BY(crit_capture_);
-  int analog_capture_level_ RTC_GUARDED_BY(crit_capture_);
-  bool was_analog_level_set_ RTC_GUARDED_BY(crit_capture_);
-  bool stream_is_saturated_ RTC_GUARDED_BY(crit_capture_);
+  Mode mode_;
+  int minimum_capture_level_;
+  int maximum_capture_level_;
+  bool limiter_enabled_;
+  int target_level_dbfs_;
+  int compression_gain_db_;
+  int analog_capture_level_;
+  bool was_analog_level_set_;
+  bool stream_is_saturated_;
 
   std::vector<std::unique_ptr<GainController>> gain_controllers_;
 
-  absl::optional<size_t> num_proc_channels_ RTC_GUARDED_BY(crit_capture_);
-  absl::optional<int> sample_rate_hz_ RTC_GUARDED_BY(crit_capture_);
+  absl::optional<size_t> num_proc_channels_;
+  absl::optional<int> sample_rate_hz_;
 
   static int instance_counter_;
-  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(GainControlImpl);
 };
 }  // namespace webrtc
 
diff --git a/modules/audio_processing/gain_control_unittest.cc b/modules/audio_processing/gain_control_unittest.cc
index 891e78a..e249a11 100644
--- a/modules/audio_processing/gain_control_unittest.cc
+++ b/modules/audio_processing/gain_control_unittest.cc
@@ -72,8 +72,7 @@
                          int analog_level_max,
                          int achieved_stream_analog_level_reference,
                          rtc::ArrayView<const float> output_reference) {
-  rtc::CriticalSection crit_capture;
-  GainControlImpl gain_controller(&crit_capture);
+  GainControlImpl gain_controller;
   SetupComponent(sample_rate_hz, mode, target_level_dbfs, stream_analog_level,
                  compression_gain_db, enable_limiter, analog_level_min,
                  analog_level_max, &gain_controller);
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 6a0917a..1d421f5 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -239,6 +239,11 @@
   // The parameters and behavior of the audio processing module are controlled
   // by changing the default values in the AudioProcessing::Config struct.
   // The config is applied by passing the struct to the ApplyConfig method.
+  //
+  // This config is intended to be used during setup, and to enable/disable
+  // top-level processing effects. Use during processing may cause undesired
+  // submodule resets, affecting the audio quality. Use the RuntimeSetting
+  // construct for runtime configuration.
   struct Config {
     // Enabled the pre-amplifier. It amplifies the capture signal
     // before any other processing is done.
@@ -273,6 +278,59 @@
       bool enabled = false;
     } voice_detection;
 
+    // Enables automatic gain control (AGC) functionality.
+    // The automatic gain control (AGC) component brings the signal to an
+    // appropriate range. This is done by applying a digital gain directly and,
+    // in the analog mode, prescribing an analog gain to be applied at the audio
+    // HAL.
+    // Recommended to be enabled on the client-side.
+    struct GainController1 {
+      bool enabled = false;
+      enum Mode {
+        // Adaptive mode intended for use if an analog volume control is
+        // available on the capture device. It will require the user to provide
+        // coupling between the OS mixer controls and AGC through the
+        // stream_analog_level() functions.
+        // It consists of an analog gain prescription for the audio device and a
+        // digital compression stage.
+        kAdaptiveAnalog,
+        // Adaptive mode intended for situations in which an analog volume
+        // control is unavailable. It operates in a similar fashion to the
+        // adaptive analog mode, but with scaling instead applied in the digital
+        // domain. As with the analog mode, it additionally uses a digital
+        // compression stage.
+        kAdaptiveDigital,
+        // Fixed mode which enables only the digital compression stage also used
+        // by the two adaptive modes.
+        // It is distinguished from the adaptive modes by considering only a
+        // short time-window of the input signal. It applies a fixed gain
+        // through most of the input level range, and compresses (gradually
+        // reduces gain with increasing level) the input signal at higher
+        // levels. This mode is preferred on embedded devices where the capture
+        // signal level is predictable, so that a known gain can be applied.
+        kFixedDigital
+      };
+      Mode mode = kAdaptiveAnalog;
+      // Sets the target peak level (or envelope) of the AGC in dBFs (decibels
+      // from digital full-scale). The convention is to use positive values. For
+      // instance, passing in a value of 3 corresponds to -3 dBFs, or a target
+      // level 3 dB below full-scale. Limited to [0, 31].
+      int target_level_dbfs = 3;
+      // Sets the maximum gain the digital compression stage may apply, in dB. A
+      // higher number corresponds to greater compression, while a value of 0
+      // will leave the signal uncompressed. Limited to [0, 90].
+      // For updates after APM setup, use a RuntimeSetting instead.
+      int compression_gain_db = 9;
+      // When enabled, the compression stage will hard limit the signal to the
+      // target level. Otherwise, the signal will be compressed but not limited
+      // above the target level.
+      bool enable_limiter = true;
+      // Sets the minimum and maximum analog levels of the audio capture device.
+      // Must be set if an analog mode is used. Limited to [0, 65535].
+      int analog_level_minimum = 0;
+      int analog_level_maximum = 255;
+    } gain_controller1;
+
     // Enables the next generation AGC functionality. This feature replaces the
     // standard methods of gain control in the previous AGC. Enabling this
     // submodule enables an adaptive digital AGC followed by a limiter. By
@@ -332,6 +390,7 @@
     enum class Type {
       kNotSpecified,
       kCapturePreGain,
+      kCaptureCompressionGain,
       kCustomRenderProcessingRuntimeSetting
     };
 
@@ -343,6 +402,14 @@
       return {Type::kCapturePreGain, gain};
     }
 
+    // Corresponds to Config::GainController1::compression_gain_db, but for
+    // runtime configuration.
+    static RuntimeSetting CreateCompressionGainDb(int gain_db) {
+      RTC_DCHECK_GE(gain_db, 0);
+      RTC_DCHECK_LE(gain_db, 90);
+      return {Type::kCaptureCompressionGain, static_cast<float>(gain_db)};
+    }
+
     static RuntimeSetting CreateCustomRenderSetting(float payload) {
       return {Type::kCustomRenderProcessingRuntimeSetting, payload};
     }
@@ -489,6 +556,16 @@
                                    const StreamConfig& output_config,
                                    float* const* dest) = 0;
 
+  // This must be called prior to ProcessStream() if and only if adaptive analog
+  // gain control is enabled, to pass the current analog level from the audio
+  // HAL. Must be within the range provided in Config::GainController1.
+  virtual void set_stream_analog_level(int level) = 0;
+
+  // When an analog mode is set, this should be called after ProcessStream()
+  // to obtain the recommended new analog level for the audio HAL. It is the
+  // user's responsibility to apply this level.
+  virtual int recommended_stream_analog_level() const = 0;
+
   // This must be called if and only if echo processing is enabled.
   //
   // Sets the |delay| in ms between ProcessReverseStream() receiving a far-end
@@ -553,6 +630,12 @@
   // remote track.
   virtual AudioProcessingStats GetStatistics(bool has_remote_tracks) const = 0;
 
+  // DEPRECATED.
+  // TODO(https://crbug.com/webrtc/9878): Remove.
+  // Configure via AudioProcessing::ApplyConfig during setup.
+  // Set runtime settings via AudioProcessing::SetRuntimeSetting.
+  // Get stats via AudioProcessing::GetStatistics.
+  //
   // These provide access to the component interfaces and should never return
   // NULL. The pointers will be valid for the lifetime of the APM instance.
   // The memory for these objects is entirely managed internally.
diff --git a/modules/audio_processing/include/gain_control.h b/modules/audio_processing/include/gain_control.h
index 420b1c6..69208a7 100644
--- a/modules/audio_processing/include/gain_control.h
+++ b/modules/audio_processing/include/gain_control.h
@@ -31,7 +31,7 @@
   // When an analog mode is set, this should be called after |ProcessStream()|
   // to obtain the recommended new analog level for the audio HAL. It is the
   // users responsibility to apply this level.
-  virtual int stream_analog_level() = 0;
+  virtual int stream_analog_level() const = 0;
 
   enum Mode {
     // Adaptive mode intended for use if an analog volume control is available
diff --git a/modules/audio_processing/include/mock_audio_processing.h b/modules/audio_processing/include/mock_audio_processing.h
index f00a16d..7504b51 100644
--- a/modules/audio_processing/include/mock_audio_processing.h
+++ b/modules/audio_processing/include/mock_audio_processing.h
@@ -27,7 +27,7 @@
   MOCK_METHOD1(Enable, int(bool enable));
   MOCK_CONST_METHOD0(is_enabled, bool());
   MOCK_METHOD1(set_stream_analog_level, int(int level));
-  MOCK_METHOD0(stream_analog_level, int());
+  MOCK_CONST_METHOD0(stream_analog_level, int());
   MOCK_METHOD1(set_mode, int(Mode mode));
   MOCK_CONST_METHOD0(mode, Mode());
   MOCK_METHOD1(set_target_level_dbfs, int(int level));
@@ -163,6 +163,8 @@
   MOCK_METHOD1(set_stream_key_pressed, void(bool key_pressed));
   MOCK_METHOD1(set_delay_offset_ms, void(int offset));
   MOCK_CONST_METHOD0(delay_offset_ms, int());
+  MOCK_METHOD1(set_stream_analog_level, void(int));
+  MOCK_CONST_METHOD0(recommended_stream_analog_level, int());
 
   virtual void AttachAecDump(std::unique_ptr<AecDump> aec_dump) {}
   MOCK_METHOD0(DetachAecDump, void());
diff --git a/test/fuzzers/agc_fuzzer.cc b/test/fuzzers/agc_fuzzer.cc
index 9854a78..a330c7b 100644
--- a/test/fuzzers/agc_fuzzer.cc
+++ b/test/fuzzers/agc_fuzzer.cc
@@ -113,8 +113,7 @@
     return;
   }
   test::FuzzDataHelper fuzz_data(rtc::ArrayView<const uint8_t>(data, size));
-  rtc::CriticalSection crit_capture;
-  auto gci = absl::make_unique<GainControlImpl>(&crit_capture);
+  auto gci = absl::make_unique<GainControlImpl>();
   FuzzGainController(&fuzz_data, gci.get());
 }
 }  // namespace webrtc
diff --git a/test/fuzzers/audio_processing_configs_fuzzer.cc b/test/fuzzers/audio_processing_configs_fuzzer.cc
index cf41da2..89c0552 100644
--- a/test/fuzzers/audio_processing_configs_fuzzer.cc
+++ b/test/fuzzers/audio_processing_configs_fuzzer.cc
@@ -125,6 +125,8 @@
   apm_config.echo_canceller.mobile_mode = use_aecm;
   apm_config.residual_echo_detector.enabled = red;
   apm_config.high_pass_filter.enabled = hpf;
+  apm_config.gain_controller1.enabled = use_agc;
+  apm_config.gain_controller1.enable_limiter = use_agc_limiter;
   apm_config.gain_controller2.enabled = use_agc2;
   apm_config.gain_controller2.fixed_digital.gain_db = gain_controller2_gain_db;
   apm_config.gain_controller2.adaptive_digital.enabled =
@@ -141,10 +143,8 @@
   apm_config.voice_detection.enabled = use_vad;
   apm->ApplyConfig(apm_config);
 
-  apm->gain_control()->Enable(use_agc);
   apm->level_estimator()->Enable(use_le);
   apm->voice_detection()->Enable(use_vad);
-  apm->gain_control()->enable_limiter(use_agc_limiter);
 
   return apm;
 }