AEC3: Average the spectrum of multiple nearend frames in the suppressor.

Reduce noise of the nearend spectrum estimation by averaging multiple
frames.

Bug: webrtc:9420,chromium:853699
Change-Id: Iad7e68b1209a369e263b2d892791943e42bfbb3f
Reviewed-on: https://webrtc-review.googlesource.com/83960
Reviewed-by: Jesus de Vicente Pena <devicentepena@webrtc.org>
Commit-Queue: Gustaf Ullberg <gustaf@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23655}
diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h
index ed39b66..98bd463 100644
--- a/api/audio/echo_canceller3_config.h
+++ b/api/audio/echo_canceller3_config.h
@@ -157,6 +157,7 @@
 
   struct Suppressor {
     size_t bands_with_reliable_coherence = 5;
+    size_t nearend_average_blocks = 4;
   } suppressor;
 };
 }  // namespace webrtc
diff --git a/modules/audio_processing/aec3/BUILD.gn b/modules/audio_processing/aec3/BUILD.gn
index c0f632a..237f71a 100644
--- a/modules/audio_processing/aec3/BUILD.gn
+++ b/modules/audio_processing/aec3/BUILD.gn
@@ -69,6 +69,8 @@
     "matched_filter_lag_aggregator.h",
     "matrix_buffer.cc",
     "matrix_buffer.h",
+    "moving_average.cc",
+    "moving_average.h",
     "render_buffer.cc",
     "render_buffer.h",
     "render_delay_buffer.cc",
diff --git a/modules/audio_processing/aec3/echo_canceller3.cc b/modules/audio_processing/aec3/echo_canceller3.cc
index 3acb31b..0caf179 100644
--- a/modules/audio_processing/aec3/echo_canceller3.cc
+++ b/modules/audio_processing/aec3/echo_canceller3.cc
@@ -41,6 +41,11 @@
   return !field_trial::IsEnabled("WebRTC-Aec3ReverbModellingKillSwitch");
 }
 
+bool EnableSuppressorNearendAveraging() {
+  return !field_trial::IsEnabled(
+      "WebRTC-Aec3SuppressorNearendAveragingKillSwitch");
+}
+
 // Method for adjusting config parameter dependencies..
 EchoCanceller3Config AdjustConfig(const EchoCanceller3Config& config) {
   EchoCanceller3Config adjusted_cfg = config;
@@ -89,6 +94,10 @@
     adjusted_cfg.ep_strength.reverb_based_on_render = false;
   }
 
+  if (!EnableSuppressorNearendAveraging()) {
+    adjusted_cfg.suppressor.nearend_average_blocks = 1;
+  }
+
   return adjusted_cfg;
 }
 
diff --git a/modules/audio_processing/aec3/moving_average.cc b/modules/audio_processing/aec3/moving_average.cc
new file mode 100644
index 0000000..e9d64e6
--- /dev/null
+++ b/modules/audio_processing/aec3/moving_average.cc
@@ -0,0 +1,58 @@
+
+/*
+ *  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/aec3/moving_average.h"
+
+#include <algorithm>
+#include <functional>
+
+namespace webrtc {
+namespace aec3 {
+
+MovingAverage::MovingAverage(size_t num_elem, size_t mem_len)
+    : num_elem_(num_elem),
+      mem_len_(mem_len - 1),
+      scaling_(1.0f / static_cast<float>(mem_len)),
+      memory_(num_elem * mem_len_, 0.f),
+      mem_index_(0) {
+  RTC_DCHECK(num_elem_ > 0);
+  RTC_DCHECK(mem_len > 0);
+}
+
+MovingAverage::~MovingAverage() = default;
+
+void MovingAverage::Average(rtc::ArrayView<const float> input,
+                            rtc::ArrayView<float> output) {
+  RTC_DCHECK(input.size() == num_elem_);
+  RTC_DCHECK(output.size() == num_elem_);
+
+  // Sum all contributions.
+  std::copy(input.begin(), input.end(), output.begin());
+  for (auto i = memory_.begin(); i < memory_.end(); i += num_elem_) {
+    std::transform(i, i + num_elem_, output.begin(), output.begin(),
+                   std::plus<float>());
+  }
+
+  // Divide by mem_len_.
+  for (float& o : output) {
+    o *= scaling_;
+  }
+
+  // Update memory.
+  if (mem_len_ > 0) {
+    std::copy(input.begin(), input.end(),
+              memory_.begin() + mem_index_ * num_elem_);
+    mem_index_ = (mem_index_ + 1) % mem_len_;
+  }
+}
+
+}  // namespace aec3
+}  // namespace webrtc
diff --git a/modules/audio_processing/aec3/moving_average.h b/modules/audio_processing/aec3/moving_average.h
new file mode 100644
index 0000000..94497d7
--- /dev/null
+++ b/modules/audio_processing/aec3/moving_average.h
@@ -0,0 +1,43 @@
+/*
+ *  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_AEC3_MOVING_AVERAGE_H_
+#define MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_
+
+#include <vector>
+
+#include "api/array_view.h"
+
+namespace webrtc {
+namespace aec3 {
+
+class MovingAverage {
+ public:
+  // Creates an instance of MovingAverage that accepts inputs of length num_elem
+  // and averages over mem_len inputs.
+  MovingAverage(size_t num_elem, size_t mem_len);
+  ~MovingAverage();
+
+  // Computes the average of input and mem_len-1 previous inputs and stores the
+  // result in output.
+  void Average(rtc::ArrayView<const float> input, rtc::ArrayView<float> output);
+
+ private:
+  const size_t num_elem_;
+  const size_t mem_len_;
+  const float scaling_;
+  std::vector<float> memory_;
+  size_t mem_index_;
+};
+
+}  // namespace aec3
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_AEC3_MOVING_AVERAGE_H_
diff --git a/modules/audio_processing/aec3/suppression_gain.cc b/modules/audio_processing/aec3/suppression_gain.cc
index 2098fc2..6b86b87 100644
--- a/modules/audio_processing/aec3/suppression_gain.cc
+++ b/modules/audio_processing/aec3/suppression_gain.cc
@@ -20,6 +20,7 @@
 #include <functional>
 #include <numeric>
 
+#include "modules/audio_processing/aec3/moving_average.h"
 #include "modules/audio_processing/aec3/vector_math.h"
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "rtc_base/atomicops.h"
@@ -386,7 +387,9 @@
           static_cast<int>(config_.filter.config_change_duration_blocks)),
       coherence_gain_(sample_rate_hz,
                       config_.suppressor.bands_with_reliable_coherence),
-      enable_transparency_improvements_(EnableTransparencyImprovements()) {
+      enable_transparency_improvements_(EnableTransparencyImprovements()),
+      moving_average_(kFftLengthBy2Plus1,
+                      config.suppressor.nearend_average_blocks) {
   RTC_DCHECK_LT(0, state_change_duration_blocks_);
   one_by_state_change_duration_blocks_ = 1.f / state_change_duration_blocks_;
   last_gain_.fill(1.f);
@@ -413,11 +416,14 @@
   RTC_DCHECK(high_bands_gain);
   RTC_DCHECK(low_band_gain);
 
+  std::array<float, kFftLengthBy2Plus1> nearend_average;
+  moving_average_.Average(nearend_spectrum, nearend_average);
+
   // Compute gain for the lower band.
   bool low_noise_render = low_render_detector_.Detect(render);
   const absl::optional<int> narrow_peak_band =
       render_signal_analyzer.NarrowPeakBand();
-  LowerBandGain(low_noise_render, aec_state, nearend_spectrum, echo_spectrum,
+  LowerBandGain(low_noise_render, aec_state, nearend_average, echo_spectrum,
                 comfort_noise_spectrum, low_band_gain);
 
   // Adjust the gain for bands where the coherence indicates not echo.
diff --git a/modules/audio_processing/aec3/suppression_gain.h b/modules/audio_processing/aec3/suppression_gain.h
index f3719ee..ced3666 100644
--- a/modules/audio_processing/aec3/suppression_gain.h
+++ b/modules/audio_processing/aec3/suppression_gain.h
@@ -18,6 +18,7 @@
 #include "modules/audio_processing/aec3/aec3_common.h"
 #include "modules/audio_processing/aec3/aec_state.h"
 #include "modules/audio_processing/aec3/coherence_gain.h"
+#include "modules/audio_processing/aec3/moving_average.h"
 #include "modules/audio_processing/aec3/render_signal_analyzer.h"
 #include "rtc_base/constructormagic.h"
 
@@ -85,6 +86,7 @@
   int initial_state_change_counter_ = 0;
   CoherenceGain coherence_gain_;
   const bool enable_transparency_improvements_;
+  aec3::MovingAverage moving_average_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(SuppressionGain);
 };