Add APM test of pre-amplifier gain

This tests that both the ApplyConfig() and SetRuntimeSetting() route of
APM configuration correctly induce the pre-amplifier gain effect in APM.

Bug: webrtc:11045
Change-Id: Iddaa1c19487c6f68ed6eb1be6913ec2dfd284b04
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158083
Commit-Queue: Sam Zackrisson <saza@webrtc.org>
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29607}
diff --git a/modules/audio_processing/audio_processing_unittest.cc b/modules/audio_processing/audio_processing_unittest.cc
index 5f2ce87..c7d325c 100644
--- a/modules/audio_processing/audio_processing_unittest.cc
+++ b/modules/audio_processing/audio_processing_unittest.cc
@@ -16,6 +16,7 @@
 #include <cmath>
 #include <limits>
 #include <memory>
+#include <numeric>
 #include <queue>
 
 #include "absl/flags/flag.h"
@@ -172,6 +173,18 @@
   return true;
 }
 
+rtc::ArrayView<int16_t> GetMutableFrameData(AudioFrame* frame) {
+  int16_t* ptr = frame->mutable_data();
+  const size_t len = frame->samples_per_channel() * frame->num_channels();
+  return rtc::ArrayView<int16_t>(ptr, len);
+}
+
+rtc::ArrayView<const int16_t> GetFrameData(const AudioFrame& frame) {
+  const int16_t* ptr = frame.data();
+  const size_t len = frame.samples_per_channel() * frame.num_channels();
+  return rtc::ArrayView<const int16_t>(ptr, len);
+}
+
 void EnableAllAPComponents(AudioProcessing* ap) {
   AudioProcessing::Config apm_config = ap->GetConfig();
   apm_config.echo_canceller.enabled = true;
@@ -842,6 +855,75 @@
   }
 }
 
+// This test repeatedly reconfigures the pre-amplifier in APM, processes a
+// number of frames, and checks that output signal has the right level.
+TEST_F(ApmTest, PreAmplifier) {
+  // Fill the audio frame with a sawtooth pattern.
+  rtc::ArrayView<int16_t> frame_data = GetMutableFrameData(frame_);
+  const size_t samples_per_channel = frame_->samples_per_channel();
+  for (size_t i = 0; i < samples_per_channel; i++) {
+    for (size_t ch = 0; ch < frame_->num_channels(); ++ch) {
+      frame_data[i + ch * samples_per_channel] = 10000 * ((i % 3) - 1);
+    }
+  }
+  // Cache the frame in tmp_frame.
+  AudioFrame tmp_frame;
+  tmp_frame.CopyFrom(*frame_);
+
+  auto compute_power = [](const AudioFrame& frame) {
+    rtc::ArrayView<const int16_t> data = GetFrameData(frame);
+    return std::accumulate(data.begin(), data.end(), 0.0f,
+                           [](float a, float b) { return a + b * b; }) /
+           data.size() / 32768 / 32768;
+  };
+
+  const float input_power = compute_power(tmp_frame);
+  // Double-check that the input data is large compared to the error kEpsilon.
+  constexpr float kEpsilon = 1e-4f;
+  RTC_DCHECK_GE(input_power, 10 * kEpsilon);
+
+  // 1. Enable pre-amp with 0 dB gain.
+  AudioProcessing::Config config = apm_->GetConfig();
+  config.pre_amplifier.enabled = true;
+  config.pre_amplifier.fixed_gain_factor = 1.0f;
+  apm_->ApplyConfig(config);
+
+  for (int i = 0; i < 20; ++i) {
+    frame_->CopyFrom(tmp_frame);
+    EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+  }
+  float output_power = compute_power(*frame_);
+  EXPECT_NEAR(output_power, input_power, kEpsilon);
+  config = apm_->GetConfig();
+  EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.0f);
+
+  // 2. Change pre-amp gain via ApplyConfig.
+  config.pre_amplifier.fixed_gain_factor = 2.0f;
+  apm_->ApplyConfig(config);
+
+  for (int i = 0; i < 20; ++i) {
+    frame_->CopyFrom(tmp_frame);
+    EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+  }
+  output_power = compute_power(*frame_);
+  EXPECT_NEAR(output_power, 4 * input_power, kEpsilon);
+  config = apm_->GetConfig();
+  EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 2.0f);
+
+  // 3. Change pre-amp gain via a RuntimeSetting.
+  apm_->SetRuntimeSetting(
+      AudioProcessing::RuntimeSetting::CreateCapturePreGain(1.5f));
+
+  for (int i = 0; i < 20; ++i) {
+    frame_->CopyFrom(tmp_frame);
+    EXPECT_EQ(apm_->kNoError, ProcessStreamChooser(kIntFormat));
+  }
+  output_power = compute_power(*frame_);
+  EXPECT_NEAR(output_power, 2.25 * input_power, kEpsilon);
+  config = apm_->GetConfig();
+  EXPECT_EQ(config.pre_amplifier.fixed_gain_factor, 1.5f);
+}
+
 TEST_F(ApmTest, GainControl) {
   AudioProcessing::Config config = apm_->GetConfig();
   config.gain_controller1.enabled = false;