Add FixedGainController and move GainController2 in APM.

The FixedGainController (FGC) applies a fixed gain. It will also
control the limiter. The limiter will be landed over the next several
CLs.

The GainController2 is a 'private submodule' of APM. It will control
the new automatic gain controller (AGC). It controls the AGC through
Initialize() and ApplyConfig().

This CL contains

* build changes to make modules/audio_processing/agc2 an independent
  target

* a new MutableFloatAudioFrame which is the audio interface between
  AGC2 and APM

* move of the fixed gain application from GainController2 to
  FixedGainController.

If you are a googler, there is more information in this doc:
https://docs.google.com/document/d/1RV2Doet3MZtUPAHVva61Vjo20iyd1bmmm3aR8znWpzo/edit#

Bug: webrtc:7949
Change-Id: Ief95cbbce83c3aafe54638fd2ab881c9fb8bdc3a
Reviewed-on: https://webrtc-review.googlesource.com/50440
Commit-Queue: Alex Loiko <aleloi@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22046}
diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn
index ba0cf99..60605e9 100644
--- a/modules/audio_processing/BUILD.gn
+++ b/modules/audio_processing/BUILD.gn
@@ -120,8 +120,6 @@
     "agc/loudness_histogram.h",
     "agc/utility.cc",
     "agc/utility.h",
-    "agc2/gain_controller2.cc",
-    "agc2/gain_controller2.h",
     "audio_buffer.cc",
     "audio_buffer.h",
     "audio_processing_impl.cc",
@@ -151,6 +149,8 @@
     "gain_control_for_experimental_agc.h",
     "gain_control_impl.cc",
     "gain_control_impl.h",
+    "gain_controller2.cc",
+    "gain_controller2.h",
     "include/audio_processing.cc",
     "include/audio_processing.h",
     "include/config.cc",
@@ -215,6 +215,7 @@
     ":aec_core",
     ":aec_dump_interface",
     ":apm_logging",
+    ":audio_frame_view",
     ":audio_processing_c",
     ":audio_processing_statistics",
     "..:module_api",
@@ -234,6 +235,7 @@
     "../../system_wrappers:cpu_features_api",
     "../../system_wrappers:field_trial_api",
     "../../system_wrappers:metrics_api",
+    "agc2",
     "vad",
   ]
 
@@ -287,6 +289,15 @@
   ]
 }
 
+rtc_source_set("audio_frame_view") {
+  sources = [
+    "include/audio_frame_view.h",
+  ]
+  deps = [
+    "../../api:array_view",
+  ]
+}
+
 rtc_source_set("aec_dump_interface") {
   sources = [
     "include/aec_dump.cc",
@@ -294,6 +305,7 @@
   ]
 
   deps = [
+    ":audio_frame_view",
     "../../api:array_view",
     "../../rtc_base:rtc_base_approved",
   ]
@@ -585,6 +597,7 @@
       "../../test:test_support",
       "../audio_coding:neteq_input_audio_tools",
       "aec_dump:mock_aec_dump_unittests",
+      "agc2:fixed_digital_unittests",
       "test/conversational_speech:unittest",
       "vad:vad_unittests",
       "//testing/gtest",
@@ -653,7 +666,6 @@
         "aec3/suppression_filter_unittest.cc",
         "aec3/suppression_gain_unittest.cc",
         "aec3/vector_math_unittest.cc",
-        "agc2/gain_controller2_unittest.cc",
         "audio_processing_impl_locking_unittest.cc",
         "audio_processing_impl_unittest.cc",
         "audio_processing_unittest.cc",
@@ -665,6 +677,7 @@
         "echo_detector/moving_max_unittest.cc",
         "echo_detector/normalized_covariance_estimator_unittest.cc",
         "gain_control_unittest.cc",
+        "gain_controller2_unittest.cc",
         "level_controller/level_controller_unittest.cc",
         "level_estimator_unittest.cc",
         "low_cut_filter_unittest.cc",
diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.cc b/modules/audio_processing/aec_dump/aec_dump_impl.cc
index 594bf85..4deb192 100644
--- a/modules/audio_processing/aec_dump/aec_dump_impl.cc
+++ b/modules/audio_processing/aec_dump/aec_dump_impl.cc
@@ -94,11 +94,13 @@
   worker_queue_->PostTask(std::unique_ptr<rtc::QueuedTask>(std::move(task)));
 }
 
-void AecDumpImpl::AddCaptureStreamInput(const FloatAudioFrame& src) {
+void AecDumpImpl::AddCaptureStreamInput(
+    const AudioFrameView<const float>& src) {
   capture_stream_info_.AddInput(src);
 }
 
-void AecDumpImpl::AddCaptureStreamOutput(const FloatAudioFrame& src) {
+void AecDumpImpl::AddCaptureStreamOutput(
+    const AudioFrameView<const float>& src) {
   capture_stream_info_.AddOutput(src);
 }
 
@@ -134,7 +136,8 @@
   worker_queue_->PostTask(std::unique_ptr<rtc::QueuedTask>(std::move(task)));
 }
 
-void AecDumpImpl::WriteRenderStreamMessage(const FloatAudioFrame& src) {
+void AecDumpImpl::WriteRenderStreamMessage(
+    const AudioFrameView<const float>& src) {
   auto task = CreateWriteToFileTask();
   auto* event = task->GetEvent();
 
diff --git a/modules/audio_processing/aec_dump/aec_dump_impl.h b/modules/audio_processing/aec_dump/aec_dump_impl.h
index 5be876b..36d72e9 100644
--- a/modules/audio_processing/aec_dump/aec_dump_impl.h
+++ b/modules/audio_processing/aec_dump/aec_dump_impl.h
@@ -54,15 +54,16 @@
 
   void WriteInitMessage(const InternalAPMStreamsConfig& api_format) override;
 
-  void AddCaptureStreamInput(const FloatAudioFrame& src) override;
-  void AddCaptureStreamOutput(const FloatAudioFrame& src) override;
+  void AddCaptureStreamInput(const AudioFrameView<const float>& src) override;
+  void AddCaptureStreamOutput(const AudioFrameView<const float>& src) override;
   void AddCaptureStreamInput(const AudioFrame& frame) override;
   void AddCaptureStreamOutput(const AudioFrame& frame) override;
   void AddAudioProcessingState(const AudioProcessingState& state) override;
   void WriteCaptureStreamMessage() override;
 
   void WriteRenderStreamMessage(const AudioFrame& frame) override;
-  void WriteRenderStreamMessage(const FloatAudioFrame& src) override;
+  void WriteRenderStreamMessage(
+      const AudioFrameView<const float>& src) override;
 
   void WriteConfig(const InternalAPMConfig& config) override;
 
diff --git a/modules/audio_processing/aec_dump/capture_stream_info.cc b/modules/audio_processing/aec_dump/capture_stream_info.cc
index e3284d8..dd48fd4 100644
--- a/modules/audio_processing/aec_dump/capture_stream_info.cc
+++ b/modules/audio_processing/aec_dump/capture_stream_info.cc
@@ -19,7 +19,7 @@
 
 CaptureStreamInfo::~CaptureStreamInfo() = default;
 
-void CaptureStreamInfo::AddInput(const FloatAudioFrame& src) {
+void CaptureStreamInfo::AddInput(const AudioFrameView<const float>& src) {
   RTC_DCHECK(task_);
   auto* stream = task_->GetEvent()->mutable_stream();
 
@@ -30,7 +30,7 @@
   }
 }
 
-void CaptureStreamInfo::AddOutput(const FloatAudioFrame& src) {
+void CaptureStreamInfo::AddOutput(const AudioFrameView<const float>& src) {
   RTC_DCHECK(task_);
   auto* stream = task_->GetEvent()->mutable_stream();
 
diff --git a/modules/audio_processing/aec_dump/capture_stream_info.h b/modules/audio_processing/aec_dump/capture_stream_info.h
index 9999c3f..91bb1fa 100644
--- a/modules/audio_processing/aec_dump/capture_stream_info.h
+++ b/modules/audio_processing/aec_dump/capture_stream_info.h
@@ -37,8 +37,8 @@
  public:
   explicit CaptureStreamInfo(std::unique_ptr<WriteToFileTask> task);
   ~CaptureStreamInfo();
-  void AddInput(const FloatAudioFrame& src);
-  void AddOutput(const FloatAudioFrame& src);
+  void AddInput(const AudioFrameView<const float>& src);
+  void AddOutput(const AudioFrameView<const float>& src);
 
   void AddInput(const AudioFrame& frame);
   void AddOutput(const AudioFrame& frame);
diff --git a/modules/audio_processing/aec_dump/mock_aec_dump.h b/modules/audio_processing/aec_dump/mock_aec_dump.h
index 6df6f28..8cfabdd 100644
--- a/modules/audio_processing/aec_dump/mock_aec_dump.h
+++ b/modules/audio_processing/aec_dump/mock_aec_dump.h
@@ -29,8 +29,10 @@
   MOCK_METHOD1(WriteInitMessage,
                void(const InternalAPMStreamsConfig& streams_config));
 
-  MOCK_METHOD1(AddCaptureStreamInput, void(const FloatAudioFrame& src));
-  MOCK_METHOD1(AddCaptureStreamOutput, void(const FloatAudioFrame& src));
+  MOCK_METHOD1(AddCaptureStreamInput,
+               void(const AudioFrameView<const float>& src));
+  MOCK_METHOD1(AddCaptureStreamOutput,
+               void(const AudioFrameView<const float>& src));
   MOCK_METHOD1(AddCaptureStreamInput, void(const AudioFrame& frame));
   MOCK_METHOD1(AddCaptureStreamOutput, void(const AudioFrame& frame));
   MOCK_METHOD1(AddAudioProcessingState,
@@ -38,7 +40,8 @@
   MOCK_METHOD0(WriteCaptureStreamMessage, void());
 
   MOCK_METHOD1(WriteRenderStreamMessage, void(const AudioFrame& frame));
-  MOCK_METHOD1(WriteRenderStreamMessage, void(const FloatAudioFrame& src));
+  MOCK_METHOD1(WriteRenderStreamMessage,
+               void(const AudioFrameView<const float>& src));
 
   MOCK_METHOD1(WriteConfig, void(const InternalAPMConfig& config));
 };
diff --git a/modules/audio_processing/agc2/BUILD.gn b/modules/audio_processing/agc2/BUILD.gn
new file mode 100644
index 0000000..8702e0f
--- /dev/null
+++ b/modules/audio_processing/agc2/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright (c) 2017 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.
+
+import("../../../webrtc.gni")
+
+rtc_source_set("agc2") {
+  sources = [
+    "agc2_common.h",
+    "fixed_gain_controller.cc",
+    "fixed_gain_controller.h",
+  ]
+
+  configs += [ "..:apm_debug_dump" ]
+
+  deps = [
+    "..:apm_logging",
+    "..:audio_frame_view",
+    "../../../api:array_view",
+    "../../../common_audio",
+    "../../../rtc_base:checks",
+    "../../../rtc_base:rtc_base_approved",
+  ]
+}
+
+rtc_source_set("fixed_digital_unittests") {
+  testonly = true
+  configs += [ "..:apm_debug_dump" ]
+
+  sources = [
+    "fixed_gain_controller_unittest.cc",
+    "vector_float_frame.cc",
+    "vector_float_frame.h",
+  ]
+  deps = [
+    ":agc2",
+    "..:apm_logging",
+    "..:audio_frame_view",
+    "../../../api:array_view",
+    "../../../rtc_base:rtc_base_tests_utils",
+  ]
+}
diff --git a/modules/audio_processing/agc2/agc2_common.h b/modules/audio_processing/agc2/agc2_common.h
new file mode 100644
index 0000000..c668d0a
--- /dev/null
+++ b/modules/audio_processing/agc2/agc2_common.h
@@ -0,0 +1,23 @@
+/*
+ *  Copyright (c) 2018 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_AGC2_AGC2_COMMON_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_
+
+namespace webrtc {
+
+constexpr float kMinSampleValue = -32768.f;
+constexpr float kMaxSampleValue = 32767.f;
+
+// TODO(aleloi): add the other constants as more AGC2 components are
+// added.
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_AGC2_COMMON_H_
diff --git a/modules/audio_processing/agc2/fixed_gain_controller.cc b/modules/audio_processing/agc2/fixed_gain_controller.cc
new file mode 100644
index 0000000..a332365
--- /dev/null
+++ b/modules/audio_processing/agc2/fixed_gain_controller.cc
@@ -0,0 +1,90 @@
+/*
+ *  Copyright (c) 2018 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/agc2/fixed_gain_controller.h"
+
+#include <algorithm>
+#include <cmath>
+
+#include "api/array_view.h"
+#include "common_audio/include/audio_util.h"
+#include "modules/audio_processing/agc2/agc2_common.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_minmax.h"
+
+namespace webrtc {
+namespace {
+
+// Returns true when the gain factor is so close to 1 that it would
+// not affect int16 samples.
+bool CloseToOne(float gain_factor) {
+  return 1.f - 1.f / kMaxSampleValue <= gain_factor &&
+         gain_factor <= 1.f + 1.f / kMaxSampleValue;
+}
+}  // namespace
+
+FixedGainController::FixedGainController(ApmDataDumper* apm_data_dumper)
+    : apm_data_dumper_(apm_data_dumper) {
+  RTC_DCHECK_LT(0.f, gain_to_apply_);
+  RTC_DLOG(LS_INFO) << "Gain to apply: " << gain_to_apply_;
+}
+
+void FixedGainController::SetGain(float gain_to_apply_db) {
+  // Changes in gain_to_apply_ cause discontinuities. We assume
+  // gain_to_apply_ is set in the beginning of the call. If it is
+  // frequently changed, we should add interpolation between the
+  // values.
+  gain_to_apply_ = DbToRatio(gain_to_apply_db);
+}
+
+void FixedGainController::SetSampleRate(size_t sample_rate_hz) {
+  // TODO(aleloi): propagate the new sample rate to the GainCurveApplier.
+}
+
+void FixedGainController::EnableLimiter(bool enable_limiter) {
+  enable_limiter_ = enable_limiter;
+}
+
+void FixedGainController::Process(AudioFrameView<float> signal) {
+  // Apply fixed digital gain; interpolate if necessary. One of the
+  // planned usages of the FGC is to only use the limiter. In that
+  // case, the gain would be 1.0. Not doing the multiplications speeds
+  // it up considerably. Hence the check.
+  if (!CloseToOne(gain_to_apply_)) {
+    for (size_t k = 0; k < signal.num_channels(); ++k) {
+      rtc::ArrayView<float> channel_view = signal.channel(k);
+      for (auto& sample : channel_view) {
+        sample *= gain_to_apply_;
+      }
+    }
+  }
+
+  // Use the limiter (if configured to).
+  if (enable_limiter_) {
+    // TODO(aleloi): Process the signal with the
+    // GainCurveApplier. This will be done in the upcoming CLs.
+
+    // Dump data for debug.
+    const auto channel_view = signal.channel(0);
+    apm_data_dumper_->DumpRaw("agc2_fixed_digital_gain_curve_applier",
+                              channel_view.size(), channel_view.data());
+  }
+
+  // Hard-clipping.
+  for (size_t k = 0; k < signal.num_channels(); ++k) {
+    rtc::ArrayView<float> channel_view = signal.channel(k);
+    for (auto& sample : channel_view) {
+      sample = rtc::SafeClamp(sample, kMinSampleValue, kMaxSampleValue);
+    }
+  }
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/fixed_gain_controller.h b/modules/audio_processing/agc2/fixed_gain_controller.h
new file mode 100644
index 0000000..decc22e
--- /dev/null
+++ b/modules/audio_processing/agc2/fixed_gain_controller.h
@@ -0,0 +1,37 @@
+/*
+ *  Copyright (c) 2018 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_AGC2_FIXED_GAIN_CONTROLLER_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_FIXED_GAIN_CONTROLLER_H_
+
+#include "modules/audio_processing/include/audio_frame_view.h"
+
+namespace webrtc {
+class ApmDataDumper;
+
+class FixedGainController {
+ public:
+  explicit FixedGainController(ApmDataDumper* apm_data_dumper);
+
+  void Process(AudioFrameView<float> signal);
+
+  void SetGain(float gain_to_apply_db);
+  void SetSampleRate(size_t sample_rate_hz);
+  void EnableLimiter(bool enable_limiter);
+
+ private:
+  float gain_to_apply_ = 1.f;
+  ApmDataDumper* apm_data_dumper_;
+  bool enable_limiter_ = true;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_FIXED_GAIN_CONTROLLER_H_
diff --git a/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc b/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc
new file mode 100644
index 0000000..9336fd7
--- /dev/null
+++ b/modules/audio_processing/agc2/fixed_gain_controller_unittest.cc
@@ -0,0 +1,109 @@
+/*
+ *  Copyright (c) 2018 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/agc2/fixed_gain_controller.h"
+
+#include "api/array_view.h"
+#include "modules/audio_processing/agc2/vector_float_frame.h"
+#include "modules/audio_processing/logging/apm_data_dumper.h"
+#include "rtc_base/gunit.h"
+
+namespace webrtc {
+namespace {
+
+constexpr float kInputLevelLinear = 15000.f;
+
+constexpr float kGainToApplyDb = 15.f;
+
+float RunFixedGainControllerWithConstantInput(FixedGainController* fixed_gc,
+                                              const float input_level,
+                                              const size_t num_frames,
+                                              const int sample_rate) {
+  // Give time to the level etimator to converge.
+  for (size_t i = 0; i < num_frames; ++i) {
+    VectorFloatFrame vectors_with_float_frame(
+        1, rtc::CheckedDivExact(sample_rate, 100), input_level);
+    fixed_gc->Process(vectors_with_float_frame.float_frame_view());
+  }
+
+  // Process the last frame with constant input level.
+  VectorFloatFrame vectors_with_float_frame_last(
+      1, rtc::CheckedDivExact(sample_rate, 100), input_level);
+  fixed_gc->Process(vectors_with_float_frame_last.float_frame_view());
+
+  // Return the last sample from the last processed frame.
+  const auto channel =
+      vectors_with_float_frame_last.float_frame_view().channel(0);
+  return channel[channel.size() - 1];
+}
+ApmDataDumper test_data_dumper(0);
+
+FixedGainController CreateFixedGainController(float gain_to_apply,
+                                              size_t rate,
+                                              bool enable_limiter) {
+  FixedGainController fgc(&test_data_dumper);
+  fgc.SetGain(gain_to_apply);
+  fgc.SetSampleRate(gain_to_apply);
+  fgc.EnableLimiter(enable_limiter);
+  return fgc;
+}
+
+}  // namespace
+
+TEST(AutomaticGainController2FixedDigital, CreateUseWithoutLimiter) {
+  const int kSampleRate = 48000;
+  FixedGainController fixed_gc =
+      CreateFixedGainController(kGainToApplyDb, kSampleRate, false);
+  VectorFloatFrame vectors_with_float_frame(
+      1, rtc::CheckedDivExact(kSampleRate, 100), kInputLevelLinear);
+  auto float_frame = vectors_with_float_frame.float_frame_view();
+  fixed_gc.Process(float_frame);
+  const auto channel = float_frame.channel(0);
+  EXPECT_LT(kInputLevelLinear, channel[0]);
+}
+
+TEST(AutomaticGainController2FixedDigital, CreateUseWithLimiter) {
+  const int kSampleRate = 44000;
+  FixedGainController fixed_gc =
+      CreateFixedGainController(kGainToApplyDb, kSampleRate, true);
+  VectorFloatFrame vectors_with_float_frame(
+      1, rtc::CheckedDivExact(kSampleRate, 100), kInputLevelLinear);
+  auto float_frame = vectors_with_float_frame.float_frame_view();
+  fixed_gc.Process(float_frame);
+  const auto channel = float_frame.channel(0);
+  EXPECT_LT(kInputLevelLinear, channel[0]);
+}
+
+TEST(AutomaticGainController2FixedDigital, GainShouldChangeOnSetGain) {
+  constexpr float input_level = 1000.f;
+  constexpr size_t num_frames = 5;
+  constexpr size_t kSampleRate = 8000;
+  constexpr float gain_db_no_change = 0.f;
+  constexpr float gain_db_factor_10 = 20.f;
+
+  FixedGainController fixed_gc_no_saturation =
+      CreateFixedGainController(gain_db_no_change, kSampleRate, false);
+
+  // Signal level is unchanged with 0 db gain.
+  EXPECT_FLOAT_EQ(
+      RunFixedGainControllerWithConstantInput(
+          &fixed_gc_no_saturation, input_level, num_frames, kSampleRate),
+      input_level);
+
+  fixed_gc_no_saturation.SetGain(gain_db_factor_10);
+
+  // +20db should increase signal by a factor of 10.
+  EXPECT_FLOAT_EQ(
+      RunFixedGainControllerWithConstantInput(
+          &fixed_gc_no_saturation, input_level, num_frames, kSampleRate),
+      input_level * 10);
+}
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/vector_float_frame.cc b/modules/audio_processing/agc2/vector_float_frame.cc
new file mode 100644
index 0000000..a70d815
--- /dev/null
+++ b/modules/audio_processing/agc2/vector_float_frame.cc
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2018 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/agc2/vector_float_frame.h"
+
+namespace webrtc {
+
+namespace {
+
+std::vector<float*> ConstructChannelPointers(
+    std::vector<std::vector<float>>* x) {
+  std::vector<float*> channel_ptrs;
+  for (auto& v : *x) {
+    channel_ptrs.push_back(v.data());
+  }
+  return channel_ptrs;
+}
+}  // namespace
+
+VectorFloatFrame::VectorFloatFrame(int num_channels,
+                                   int samples_per_channel,
+                                   float start_value)
+    : channels_(num_channels,
+                std::vector<float>(samples_per_channel, start_value)),
+      channel_ptrs_(ConstructChannelPointers(&channels_)),
+      float_frame_view_(channel_ptrs_.data(),
+                        channels_.size(),
+                        samples_per_channel) {}
+
+VectorFloatFrame::~VectorFloatFrame() = default;
+
+}  // namespace webrtc
diff --git a/modules/audio_processing/agc2/vector_float_frame.h b/modules/audio_processing/agc2/vector_float_frame.h
new file mode 100644
index 0000000..0e86089
--- /dev/null
+++ b/modules/audio_processing/agc2/vector_float_frame.h
@@ -0,0 +1,39 @@
+/*
+ *  Copyright (c) 2018 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_AGC2_VECTOR_FLOAT_FRAME_H_
+#define MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_
+
+#include <vector>
+
+#include "modules/audio_processing/include/audio_frame_view.h"
+
+namespace webrtc {
+
+// A construct consisting of a multi-channel audio frame, and a FloatFrame view
+// of it.
+class VectorFloatFrame {
+ public:
+  VectorFloatFrame(int num_channels,
+                   int samples_per_channel,
+                   float start_value);
+  const AudioFrameView<float>& float_frame_view() { return float_frame_view_; }
+
+  ~VectorFloatFrame();
+
+ private:
+  std::vector<std::vector<float>> channels_;
+  std::vector<float*> channel_ptrs_;
+  AudioFrameView<float> float_frame_view_;
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AGC2_VECTOR_FLOAT_FRAME_H_
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index 6275726..a9e6b05 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -20,7 +20,6 @@
 #include "common_audio/signal_processing/include/signal_processing_library.h"
 #include "modules/audio_processing/aec/aec_core.h"
 #include "modules/audio_processing/agc/agc_manager_direct.h"
-#include "modules/audio_processing/agc2/gain_controller2.h"
 #include "modules/audio_processing/audio_buffer.h"
 #include "modules/audio_processing/beamformer/nonlinear_beamformer.h"
 #include "modules/audio_processing/common.h"
@@ -28,6 +27,7 @@
 #include "modules/audio_processing/echo_control_mobile_impl.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"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
@@ -1425,7 +1425,7 @@
     const size_t num_channels =
         formats_.api_format.reverse_input_stream().num_channels();
     aec_dump_->WriteRenderStreamMessage(
-        FloatAudioFrame(src, num_channels, channel_size));
+        AudioFrameView<const float>(src, num_channels, channel_size));
   }
   render_.render_audio->CopyFrom(src,
                                  formats_.api_format.reverse_input_stream());
@@ -1993,7 +1993,7 @@
   const size_t channel_size = formats_.api_format.input_stream().num_frames();
   const size_t num_channels = formats_.api_format.input_stream().num_channels();
   aec_dump_->AddCaptureStreamInput(
-      FloatAudioFrame(src, num_channels, channel_size));
+      AudioFrameView<const float>(src, num_channels, channel_size));
   RecordAudioProcessingState();
 }
 
@@ -2013,8 +2013,8 @@
   const size_t channel_size = formats_.api_format.output_stream().num_frames();
   const size_t num_channels =
       formats_.api_format.output_stream().num_channels();
-  aec_dump_->AddCaptureStreamOutput(
-      FloatAudioFrame(processed_capture_stream, num_channels, channel_size));
+  aec_dump_->AddCaptureStreamOutput(AudioFrameView<const float>(
+      processed_capture_stream, num_channels, channel_size));
   aec_dump_->WriteCaptureStreamMessage();
 }
 
diff --git a/modules/audio_processing/agc2/gain_controller2.cc b/modules/audio_processing/gain_controller2.cc
similarity index 69%
rename from modules/audio_processing/agc2/gain_controller2.cc
rename to modules/audio_processing/gain_controller2.cc
index b02aa59..aa866c1 100644
--- a/modules/audio_processing/agc2/gain_controller2.cc
+++ b/modules/audio_processing/gain_controller2.cc
@@ -8,16 +8,13 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "modules/audio_processing/agc2/gain_controller2.h"
+#include "modules/audio_processing/gain_controller2.h"
 
-#include <cmath>
-
-#include "common_audio/include/audio_util.h"
 #include "modules/audio_processing/audio_buffer.h"
+#include "modules/audio_processing/include/audio_frame_view.h"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/atomicops.h"
 #include "rtc_base/checks.h"
-#include "rtc_base/numerics/safe_minmax.h"
 
 namespace webrtc {
 
@@ -26,8 +23,7 @@
 GainController2::GainController2()
     : data_dumper_(
           new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
-      sample_rate_hz_(AudioProcessing::kSampleRate48kHz),
-      fixed_gain_(1.f) {}
+      gain_controller_(data_dumper_.get()) {}
 
 GainController2::~GainController2() = default;
 
@@ -36,28 +32,23 @@
              sample_rate_hz == AudioProcessing::kSampleRate16kHz ||
              sample_rate_hz == AudioProcessing::kSampleRate32kHz ||
              sample_rate_hz == AudioProcessing::kSampleRate48kHz);
-  sample_rate_hz_ = sample_rate_hz;
+  gain_controller_.SetSampleRate(sample_rate_hz);
   data_dumper_->InitiateNewSetOfRecordings();
-  data_dumper_->DumpRaw("sample_rate_hz", sample_rate_hz_);
-  data_dumper_->DumpRaw("fixed_gain_linear", fixed_gain_);
+  data_dumper_->DumpRaw("sample_rate_hz", sample_rate_hz);
 }
 
 void GainController2::Process(AudioBuffer* audio) {
-  if (fixed_gain_ == 1.f)
-    return;
-
-  for (size_t k = 0; k < audio->num_channels(); ++k) {
-    for (size_t j = 0; j < audio->num_frames(); ++j) {
-      audio->channels_f()[k][j] = rtc::SafeClamp(
-          fixed_gain_ * audio->channels_f()[k][j], -32768.f, 32767.f);
-    }
-  }
+  AudioFrameView<float> float_frame(audio->channels_f(), audio->num_channels(),
+                                    audio->num_frames());
+  gain_controller_.Process(float_frame);
 }
 
 void GainController2::ApplyConfig(
     const AudioProcessing::Config::GainController2& config) {
   RTC_DCHECK(Validate(config));
-  fixed_gain_ = DbToRatio(config.fixed_gain_db);
+  config_ = config;
+  gain_controller_.SetGain(config_.fixed_gain_db);
+  gain_controller_.EnableLimiter(config_.enable_limiter);
 }
 
 bool GainController2::Validate(
@@ -69,7 +60,8 @@
     const AudioProcessing::Config::GainController2& config) {
   std::stringstream ss;
   ss << "{enabled: " << (config.enabled ? "true" : "false") << ", "
-     << "fixed_gain_dB: " << config.fixed_gain_db << "}";
+     << "fixed_gain_dB: " << config.fixed_gain_db << ", "
+     << "enable_limiter: " << (config.enable_limiter ? "true" : "false") << "}";
   return ss.str();
 }
 
diff --git a/modules/audio_processing/agc2/gain_controller2.h b/modules/audio_processing/gain_controller2.h
similarity index 80%
rename from modules/audio_processing/agc2/gain_controller2.h
rename to modules/audio_processing/gain_controller2.h
index 1170687..6de4564 100644
--- a/modules/audio_processing/agc2/gain_controller2.h
+++ b/modules/audio_processing/gain_controller2.h
@@ -8,12 +8,13 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef MODULES_AUDIO_PROCESSING_AGC2_GAIN_CONTROLLER2_H_
-#define MODULES_AUDIO_PROCESSING_AGC2_GAIN_CONTROLLER2_H_
+#ifndef MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_
+#define MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_
 
 #include <memory>
 #include <string>
 
+#include "modules/audio_processing/agc2/fixed_gain_controller.h"
 #include "modules/audio_processing/include/audio_processing.h"
 #include "rtc_base/constructormagic.h"
 
@@ -24,8 +25,6 @@
 
 // Gain Controller 2 aims to automatically adjust levels by acting on the
 // microphone gain and/or applying digital gain.
-//
-// Temporarily implements a fixed gain mode with hard-clipping.
 class GainController2 {
  public:
   GainController2();
@@ -42,12 +41,12 @@
  private:
   static int instance_count_;
   std::unique_ptr<ApmDataDumper> data_dumper_;
-  int sample_rate_hz_;
-  float fixed_gain_;
+  FixedGainController gain_controller_;
+  AudioProcessing::Config::GainController2 config_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(GainController2);
 };
 
 }  // namespace webrtc
 
-#endif  // MODULES_AUDIO_PROCESSING_AGC2_GAIN_CONTROLLER2_H_
+#endif  // MODULES_AUDIO_PROCESSING_GAIN_CONTROLLER2_H_
diff --git a/modules/audio_processing/agc2/gain_controller2_unittest.cc b/modules/audio_processing/gain_controller2_unittest.cc
similarity index 91%
rename from modules/audio_processing/agc2/gain_controller2_unittest.cc
rename to modules/audio_processing/gain_controller2_unittest.cc
index 46f654d..0c3b383 100644
--- a/modules/audio_processing/agc2/gain_controller2_unittest.cc
+++ b/modules/audio_processing/gain_controller2_unittest.cc
@@ -11,8 +11,8 @@
 #include <algorithm>
 
 #include "api/array_view.h"
-#include "modules/audio_processing/agc2/gain_controller2.h"
 #include "modules/audio_processing/audio_buffer.h"
+#include "modules/audio_processing/gain_controller2.h"
 #include "rtc_base/checks.h"
 #include "test/gtest.h"
 
@@ -61,11 +61,12 @@
   config.fixed_gain_db = 5.f;
 
   config.enabled = false;
-  EXPECT_EQ("{enabled: false, fixed_gain_dB: 5}",
+  config.enable_limiter = true;
+  EXPECT_EQ("{enabled: false, fixed_gain_dB: 5, enable_limiter: true}",
             GainController2::ToString(config));
 
   config.enabled = true;
-  EXPECT_EQ("{enabled: true, fixed_gain_dB: 5}",
+  EXPECT_EQ("{enabled: true, fixed_gain_dB: 5, enable_limiter: true}",
             GainController2::ToString(config));
 }
 
@@ -81,7 +82,7 @@
   AudioProcessing::Config::GainController2 config;
 
   // Check that samples are not modified when the fixed gain is 0 dB.
-  ASSERT_EQ(config.fixed_gain_db, 0.f);
+  config.fixed_gain_db = 0.f;
   gain_controller2->ApplyConfig(config);
   gain_controller2->Process(&ab);
   EXPECT_EQ(ab.channels_f()[0][0], sample_value);
diff --git a/modules/audio_processing/include/aec_dump.h b/modules/audio_processing/include/aec_dump.h
index 0c8d227..2035bf4 100644
--- a/modules/audio_processing/include/aec_dump.h
+++ b/modules/audio_processing/include/aec_dump.h
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "api/array_view.h"
+#include "modules/audio_processing/include/audio_frame_view.h"
 
 namespace webrtc {
 
@@ -65,37 +66,6 @@
   size_t render_output_num_channels = 0;
 };
 
-// Class to pass audio data in float** format. This is to avoid
-// dependence on AudioBuffer, and avoid problems associated with
-// rtc::ArrayView<rtc::ArrayView>.
-class FloatAudioFrame {
- public:
-  // |num_channels| and |channel_size| describe the float**
-  // |audio_samples|. |audio_samples| is assumed to point to a
-  // two-dimensional |num_channels * channel_size| array of floats.
-  FloatAudioFrame(const float* const* audio_samples,
-                  size_t num_channels,
-                  size_t channel_size)
-      : audio_samples_(audio_samples),
-        num_channels_(num_channels),
-        channel_size_(channel_size) {}
-
-  FloatAudioFrame() = delete;
-
-  size_t num_channels() const { return num_channels_; }
-
-  rtc::ArrayView<const float> channel(size_t idx) const {
-    RTC_DCHECK_LE(0, idx);
-    RTC_DCHECK_LE(idx, num_channels_);
-    return rtc::ArrayView<const float>(audio_samples_[idx], channel_size_);
-  }
-
- private:
-  const float* const* audio_samples_;
-  size_t num_channels_;
-  size_t channel_size_;
-};
-
 // An interface for recording configuration and input/output streams
 // of the Audio Processing Module. The recordings are called
 // 'aec-dumps' and are stored in a protobuf format defined in
@@ -122,8 +92,10 @@
   // Logs Event::Type STREAM message. To log an input/output pair,
   // call the AddCapture* and AddAudioProcessingState methods followed
   // by a WriteCaptureStreamMessage call.
-  virtual void AddCaptureStreamInput(const FloatAudioFrame& src) = 0;
-  virtual void AddCaptureStreamOutput(const FloatAudioFrame& src) = 0;
+  virtual void AddCaptureStreamInput(
+      const AudioFrameView<const float>& src) = 0;
+  virtual void AddCaptureStreamOutput(
+      const AudioFrameView<const float>& src) = 0;
   virtual void AddCaptureStreamInput(const AudioFrame& frame) = 0;
   virtual void AddCaptureStreamOutput(const AudioFrame& frame) = 0;
   virtual void AddAudioProcessingState(const AudioProcessingState& state) = 0;
@@ -131,7 +103,8 @@
 
   // Logs Event::Type REVERSE_STREAM message.
   virtual void WriteRenderStreamMessage(const AudioFrame& frame) = 0;
-  virtual void WriteRenderStreamMessage(const FloatAudioFrame& src) = 0;
+  virtual void WriteRenderStreamMessage(
+      const AudioFrameView<const float>& src) = 0;
 
   // Logs Event::Type CONFIG message.
   virtual void WriteConfig(const InternalAPMConfig& config) = 0;
diff --git a/modules/audio_processing/include/audio_frame_view.h b/modules/audio_processing/include/audio_frame_view.h
new file mode 100644
index 0000000..86e593a
--- /dev/null
+++ b/modules/audio_processing/include/audio_frame_view.h
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (c) 2018 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_INCLUDE_AUDIO_FRAME_VIEW_H_
+#define MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_
+
+#include "api/array_view.h"
+
+// Class to pass audio data in T** format, where T is a numeric type.
+template <class T>
+class AudioFrameView {
+ public:
+  // |num_channels| and |channel_size| describe the T**
+  // |audio_samples|. |audio_samples| is assumed to point to a
+  // two-dimensional |num_channels * channel_size| array of floats.
+  AudioFrameView(T* const* audio_samples,
+                 size_t num_channels,
+                 size_t channel_size)
+      : audio_samples_(audio_samples),
+        num_channels_(num_channels),
+        channel_size_(channel_size) {}
+
+  AudioFrameView() = delete;
+
+  size_t num_channels() const { return num_channels_; }
+
+  size_t samples_per_channel() const { return channel_size_; }
+
+  rtc::ArrayView<T> channel(size_t idx) {
+    RTC_DCHECK_LE(0, idx);
+    RTC_DCHECK_LE(idx, num_channels_);
+    return rtc::ArrayView<T>(audio_samples_[idx], channel_size_);
+  }
+
+  rtc::ArrayView<const T> channel(size_t idx) const {
+    RTC_DCHECK_LE(0, idx);
+    RTC_DCHECK_LE(idx, num_channels_);
+    return rtc::ArrayView<const T>(audio_samples_[idx], channel_size_);
+  }
+
+ private:
+  T* const* audio_samples_;
+  size_t num_channels_;
+  size_t channel_size_;
+};
+
+#endif  // MODULES_AUDIO_PROCESSING_INCLUDE_AUDIO_FRAME_VIEW_H_
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index 50ec430..06c1f4a 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -284,6 +284,7 @@
     struct GainController2 {
       bool enabled = false;
       float fixed_gain_db = 0.f;
+      bool enable_limiter = true;
     } gain_controller2;
 
     // Explicit copy assignment implementation to avoid issues with memory