Add noise suppression settings to AudioProcessing::Config

This Config configuration will eventually replace the AudioProcessing::noise_suppression() interface.

This also introduces a proxy NoiseSuppression, returned by AudioProcessing::noise_suppression.
Without this proxy, ApplyConfig could overwrite NS settings for clients who currently use noise_suppression(). For example, the following code will not preserve the noise suppression level:

apm->noise_suppression()->set_level(NoiseSuppression::kHigh);
auto cfg = apm->GetConfig();
apm->ApplyConfig(cfg);

The NoiseSuppression instance returned by noise_suppression() has no way to update the config inside APM, so GetConfig() will return an out-of-date config which is then re-applied. This CL adds a proxy that makes this update, by forwarding Enable() and set_level() calls to ApplyConfig().

Drive-by change: AudioProcessing::Config substructs are reordered to mirror the capture processing pipeline.

Tested: Ran ToT and this CL builds of audioproc_f and verified identical settings/aecdumps.
Bug: webrtc:9947
Change-Id: I823eade894be115c254d656562564108b2b63b1f
Reviewed-on: https://webrtc-review.googlesource.com/c/116521
Reviewed-by: Alex Loiko <aleloi@webrtc.org>
Commit-Queue: Sam Zackrisson <saza@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26248}
diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn
index 714ad4b..ab5a704 100644
--- a/modules/audio_processing/BUILD.gn
+++ b/modules/audio_processing/BUILD.gn
@@ -134,6 +134,7 @@
     ":audio_processing_statistics",
     ":config",
     ":gain_control_interface",
+    ":noise_suppression_proxy",
     "../..:webrtc_common",
     "../../api:array_view",
     "../../api/audio:aec3_config",
@@ -189,6 +190,17 @@
   ]
 }
 
+rtc_source_set("noise_suppression_proxy") {
+  sources = [
+    "noise_suppression_proxy.cc",
+    "noise_suppression_proxy.h",
+  ]
+  deps = [
+    ":api",
+    "../../rtc_base:macromagic",
+  ]
+}
+
 rtc_source_set("audio_processing_statistics") {
   visibility = [ "*" ]
   sources = [
diff --git a/modules/audio_processing/audio_processing_impl.cc b/modules/audio_processing/audio_processing_impl.cc
index eec4c33..5f0d676 100644
--- a/modules/audio_processing/audio_processing_impl.cc
+++ b/modules/audio_processing/audio_processing_impl.cc
@@ -34,6 +34,7 @@
 #include "modules/audio_processing/logging/apm_data_dumper.h"
 #include "modules/audio_processing/low_cut_filter.h"
 #include "modules/audio_processing/noise_suppression_impl.h"
+#include "modules/audio_processing/noise_suppression_proxy.h"
 #include "modules/audio_processing/residual_echo_detector.h"
 #include "modules/audio_processing/transient/transient_suppressor.h"
 #include "modules/audio_processing/voice_detection_impl.h"
@@ -107,6 +108,23 @@
   return uppermost_native_rate;
 }
 
+NoiseSuppression::Level NsConfigLevelToInterfaceLevel(
+    AudioProcessing::Config::NoiseSuppression::Level level) {
+  using NsConfig = AudioProcessing::Config::NoiseSuppression;
+  switch (level) {
+    case NsConfig::kLow:
+      return NoiseSuppression::kLow;
+    case NsConfig::kModerate:
+      return NoiseSuppression::kModerate;
+    case NsConfig::kHigh:
+      return NoiseSuppression::kHigh;
+    case NsConfig::kVeryHigh:
+      return NoiseSuppression::kVeryHigh;
+    default:
+      RTC_NOTREACHED();
+  }
+}
+
 // 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;
@@ -232,9 +250,12 @@
 struct AudioProcessingImpl::ApmPublicSubmodules {
   ApmPublicSubmodules() {}
   // 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<GainControlForExperimentalAgc>
       gain_control_for_experimental_agc;
@@ -380,6 +401,8 @@
         new LevelEstimatorImpl(&crit_capture_));
     public_submodules_->noise_suppression.reset(
         new NoiseSuppressionImpl(&crit_capture_));
+    public_submodules_->noise_suppression_proxy.reset(new NoiseSuppressionProxy(
+        this, public_submodules_->noise_suppression.get()));
     public_submodules_->voice_detection.reset(
         new VoiceDetectionImpl(&crit_capture_));
     public_submodules_->gain_control_for_experimental_agc.reset(
@@ -664,6 +687,11 @@
           ? EchoCancellationImpl::SuppressionLevel::kModerateSuppression
           : EchoCancellationImpl::SuppressionLevel::kHighSuppression);
 
+  public_submodules_->noise_suppression->Enable(
+      config.noise_suppression.enabled);
+  public_submodules_->noise_suppression->set_level(
+      NsConfigLevelToInterfaceLevel(config.noise_suppression.level));
+
   InitializeLowCutFilter();
 
   RTC_LOG(LS_INFO) << "Highpass filter activated: "
@@ -1690,7 +1718,7 @@
 }
 
 NoiseSuppression* AudioProcessingImpl::noise_suppression() const {
-  return public_submodules_->noise_suppression.get();
+  return public_submodules_->noise_suppression_proxy.get();
 }
 
 VoiceDetection* AudioProcessingImpl::voice_detection() const {
diff --git a/modules/audio_processing/audio_processing_impl_locking_unittest.cc b/modules/audio_processing/audio_processing_impl_locking_unittest.cc
index 3609124..6d1de62 100644
--- a/modules/audio_processing/audio_processing_impl_locking_unittest.cc
+++ b/modules/audio_processing/audio_processing_impl_locking_unittest.cc
@@ -527,21 +527,20 @@
 void AudioProcessingImplLockTest::SetUp() {
   test_config_ = static_cast<TestConfig>(GetParam());
 
-  ASSERT_EQ(apm_->kNoError, apm_->level_estimator()->Enable(true));
   ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
 
   ASSERT_EQ(apm_->kNoError,
             apm_->gain_control()->set_mode(GainControl::kAdaptiveDigital));
   ASSERT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true));
 
-  ASSERT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true));
-  ASSERT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true));
-
-  AudioProcessing::Config apm_config;
+  AudioProcessing::Config apm_config = apm_->GetConfig();
   apm_config.echo_canceller.enabled =
       (test_config_.aec_type != AecType::AecTurnedOff);
   apm_config.echo_canceller.mobile_mode =
       (test_config_.aec_type == AecType::BasicWebRtcAecSettingsWithAecMobile);
+  apm_config.noise_suppression.enabled = true;
+  apm_config.voice_detection.enabled = true;
+  apm_config.level_estimation.enabled = true;
   apm_->ApplyConfig(apm_config);
 
   Config config;
@@ -584,7 +583,7 @@
     EXPECT_FALSE(apm_config.echo_canceller.enabled);
   }
   EXPECT_TRUE(apm_->gain_control()->is_enabled());
-  EXPECT_TRUE(apm_->noise_suppression()->is_enabled());
+  EXPECT_TRUE(apm_config.noise_suppression.enabled);
 
   // The below return values are not testable.
   apm_->noise_suppression()->speech_probability();
diff --git a/modules/audio_processing/include/audio_processing.h b/modules/audio_processing/include/audio_processing.h
index b88a86f..afe5f6c 100644
--- a/modules/audio_processing/include/audio_processing.h
+++ b/modules/audio_processing/include/audio_processing.h
@@ -240,6 +240,17 @@
   // by changing the default values in the AudioProcessing::Config struct.
   // The config is applied by passing the struct to the ApplyConfig method.
   struct Config {
+    // Enabled the pre-amplifier. It amplifies the capture signal
+    // before any other processing is done.
+    struct PreAmplifier {
+      bool enabled = false;
+      float fixed_gain_factor = 1.f;
+    } pre_amplifier;
+
+    struct HighPassFilter {
+      bool enabled = false;
+    } high_pass_filter;
+
     struct EchoCanceller {
       bool enabled = false;
       bool mobile_mode = false;
@@ -248,20 +259,17 @@
       bool legacy_moderate_suppression_level = false;
     } echo_canceller;
 
-    struct ResidualEchoDetector {
-      bool enabled = true;
-    } residual_echo_detector;
-
-    struct HighPassFilter {
+    // Enables background noise suppression.
+    struct NoiseSuppression {
       bool enabled = false;
-    } high_pass_filter;
+      enum Level { kLow, kModerate, kHigh, kVeryHigh };
+      Level level = kModerate;
+    } noise_suppression;
 
-    // Enabled the pre-amplifier. It amplifies the capture signal
-    // before any other processing is done.
-    struct PreAmplifier {
+    // Enables reporting of |has_voice| in webrtc::AudioProcessingStats.
+    struct VoiceDetection {
       bool enabled = false;
-      float fixed_gain_factor = 1.f;
-    } pre_amplifier;
+    } voice_detection;
 
     // Enables the next generation AGC functionality. This feature replaces the
     // standard methods of gain control in the previous AGC. Enabling this
@@ -283,16 +291,15 @@
       } adaptive_digital;
     } gain_controller2;
 
+    struct ResidualEchoDetector {
+      bool enabled = true;
+    } residual_echo_detector;
+
     // Enables reporting of |output_rms_dbfs| in webrtc::AudioProcessingStats.
     struct LevelEstimation {
       bool enabled = false;
     } level_estimation;
 
-    // Enables reporting of |has_voice| in webrtc::AudioProcessingStats.
-    struct VoiceDetection {
-      bool enabled = false;
-    } voice_detection;
-
     // Explicit copy assignment implementation to avoid issues with memory
     // sanitizer complaints in case of self-assignment.
     // TODO(peah): Add buildflag to ensure that this is only included for memory
diff --git a/modules/audio_processing/noise_suppression_proxy.cc b/modules/audio_processing/noise_suppression_proxy.cc
new file mode 100644
index 0000000..a83c9b2
--- /dev/null
+++ b/modules/audio_processing/noise_suppression_proxy.cc
@@ -0,0 +1,71 @@
+/*
+ *  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/noise_suppression_proxy.h"
+
+namespace webrtc {
+NoiseSuppressionProxy::NoiseSuppressionProxy(AudioProcessing* apm,
+                                             NoiseSuppression* ns)
+    : apm_(apm), ns_(ns) {}
+
+NoiseSuppressionProxy::~NoiseSuppressionProxy() {}
+
+int NoiseSuppressionProxy::Enable(bool enable) {
+  AudioProcessing::Config config = apm_->GetConfig();
+  if (config.noise_suppression.enabled != enable) {
+    config.noise_suppression.enabled = enable;
+    apm_->ApplyConfig(config);
+  }
+  return AudioProcessing::kNoError;
+}
+
+bool NoiseSuppressionProxy::is_enabled() const {
+  return ns_->is_enabled();
+}
+
+int NoiseSuppressionProxy::set_level(Level level) {
+  AudioProcessing::Config config = apm_->GetConfig();
+  using NsConfig = AudioProcessing::Config::NoiseSuppression;
+  NsConfig::Level new_level;
+  switch (level) {
+    case NoiseSuppression::kLow:
+      new_level = NsConfig::kLow;
+      break;
+    case NoiseSuppression::kModerate:
+      new_level = NsConfig::kModerate;
+      break;
+    case NoiseSuppression::kHigh:
+      new_level = NsConfig::kHigh;
+      break;
+    case NoiseSuppression::kVeryHigh:
+      new_level = NsConfig::kVeryHigh;
+      break;
+    default:
+      RTC_NOTREACHED();
+  }
+  if (config.noise_suppression.level != new_level) {
+    config.noise_suppression.level = new_level;
+    apm_->ApplyConfig(config);
+  }
+  return AudioProcessing::kNoError;
+}
+
+NoiseSuppression::Level NoiseSuppressionProxy::level() const {
+  return ns_->level();
+}
+
+float NoiseSuppressionProxy::speech_probability() const {
+  return ns_->speech_probability();
+}
+
+std::vector<float> NoiseSuppressionProxy::NoiseEstimate() {
+  return ns_->NoiseEstimate();
+}
+}  // namespace webrtc
diff --git a/modules/audio_processing/noise_suppression_proxy.h b/modules/audio_processing/noise_suppression_proxy.h
new file mode 100644
index 0000000..b5afe92
--- /dev/null
+++ b/modules/audio_processing/noise_suppression_proxy.h
@@ -0,0 +1,45 @@
+/*
+ *  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_NOISE_SUPPRESSION_PROXY_H_
+#define MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_PROXY_H_
+
+#include <vector>
+
+#include "modules/audio_processing/include/audio_processing.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+// This class ensures interoperability with the pointer-to-submodule interface
+// AudioProcessing::noise_suppression() and AudioProcessing::ApplyConfig:
+// Enable(..) and set_level(..) calls are applied via
+// AudioProcessing::ApplyConfig, while all other function calls are forwarded
+// directly to a wrapped NoiseSuppression instance.
+class NoiseSuppressionProxy : public NoiseSuppression {
+ public:
+  NoiseSuppressionProxy(AudioProcessing* apm, NoiseSuppression* ns);
+  ~NoiseSuppressionProxy() override;
+
+  // NoiseSuppression implementation.
+  int Enable(bool enable) override;
+  bool is_enabled() const override;
+  int set_level(Level level) override;
+  Level level() const override;
+  float speech_probability() const override;
+  std::vector<float> NoiseEstimate() override;
+
+ private:
+  AudioProcessing* apm_;
+  NoiseSuppression* ns_;
+  RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(NoiseSuppressionProxy);
+};
+}  // namespace webrtc
+
+#endif  // MODULES_AUDIO_PROCESSING_NOISE_SUPPRESSION_PROXY_H_
diff --git a/modules/audio_processing/test/aec_dump_based_simulator.cc b/modules/audio_processing/test/aec_dump_based_simulator.cc
index c5cff96..230d56b 100644
--- a/modules/audio_processing/test/aec_dump_based_simulator.cc
+++ b/modules/audio_processing/test/aec_dump_based_simulator.cc
@@ -424,8 +424,7 @@
 
     if (msg.has_ns_enabled() || settings_.use_ns) {
       bool enable = settings_.use_ns ? *settings_.use_ns : msg.ns_enabled();
-      RTC_CHECK_EQ(AudioProcessing::kNoError,
-                   ap_->noise_suppression()->Enable(enable));
+      apm_config.noise_suppression.enabled = enable;
       if (settings_.use_verbose_logging) {
         std::cout << " ns_enabled: " << (enable ? "true" : "false")
                   << std::endl;
@@ -434,9 +433,8 @@
 
     if (msg.has_ns_level() || settings_.ns_level) {
       int level = settings_.ns_level ? *settings_.ns_level : msg.ns_level();
-      RTC_CHECK_EQ(AudioProcessing::kNoError,
-                   ap_->noise_suppression()->set_level(
-                       static_cast<NoiseSuppression::Level>(level)));
+      apm_config.noise_suppression.level =
+          static_cast<AudioProcessing::Config::NoiseSuppression::Level>(level);
       if (settings_.use_verbose_logging) {
         std::cout << " ns_level: " << level << std::endl;
       }
diff --git a/modules/audio_processing/test/debug_dump_replayer.cc b/modules/audio_processing/test/debug_dump_replayer.cc
index 9c4fdd9..9c1257a 100644
--- a/modules/audio_processing/test/debug_dump_replayer.cc
+++ b/modules/audio_processing/test/debug_dump_replayer.cc
@@ -219,6 +219,26 @@
           msg.aec_suppression_level()) ==
       EchoCancellationImpl::SuppressionLevel::kModerateSuppression;
 
+  // HPF configs.
+  RTC_CHECK(msg.has_hpf_enabled());
+  apm_config.high_pass_filter.enabled = msg.hpf_enabled();
+
+  // Preamp configs.
+  RTC_CHECK(msg.has_pre_amplifier_enabled());
+  apm_config.pre_amplifier.enabled = msg.pre_amplifier_enabled();
+  apm_config.pre_amplifier.fixed_gain_factor =
+      msg.pre_amplifier_fixed_gain_factor();
+
+  // NS configs.
+  RTC_CHECK(msg.has_ns_enabled());
+  RTC_CHECK(msg.has_ns_level());
+  apm_config.noise_suppression.enabled = msg.ns_enabled();
+  apm_config.noise_suppression.level =
+      static_cast<AudioProcessing::Config::NoiseSuppression::Level>(
+          msg.ns_level());
+
+  apm_->ApplyConfig(apm_config);
+
   // AGC configs.
   RTC_CHECK(msg.has_agc_enabled());
   RTC_CHECK_EQ(AudioProcessing::kNoError,
@@ -232,27 +252,6 @@
   RTC_CHECK(msg.has_agc_limiter_enabled());
   RTC_CHECK_EQ(AudioProcessing::kNoError,
                apm_->gain_control()->enable_limiter(msg.agc_limiter_enabled()));
-
-  // HPF configs.
-  RTC_CHECK(msg.has_hpf_enabled());
-  apm_config.high_pass_filter.enabled = msg.hpf_enabled();
-
-  // NS configs.
-  RTC_CHECK(msg.has_ns_enabled());
-  RTC_CHECK_EQ(AudioProcessing::kNoError,
-               apm_->noise_suppression()->Enable(msg.ns_enabled()));
-
-  RTC_CHECK(msg.has_ns_level());
-  RTC_CHECK_EQ(AudioProcessing::kNoError,
-               apm_->noise_suppression()->set_level(
-                   static_cast<NoiseSuppression::Level>(msg.ns_level())));
-
-  RTC_CHECK(msg.has_pre_amplifier_enabled());
-  apm_config.pre_amplifier.enabled = msg.pre_amplifier_enabled();
-  apm_config.pre_amplifier.fixed_gain_factor =
-      msg.pre_amplifier_fixed_gain_factor();
-
-  apm_->ApplyConfig(apm_config);
 }
 
 void DebugDumpReplayer::LoadNextMessage() {
diff --git a/modules/audio_processing/test/debug_dump_test.cc b/modules/audio_processing/test/debug_dump_test.cc
index 7d0a2bd..0f3ebff 100644
--- a/modules/audio_processing/test/debug_dump_test.cc
+++ b/modules/audio_processing/test/debug_dump_test.cc
@@ -588,8 +588,9 @@
   generator.StartRecording();
   generator.Process(100);
 
-  NoiseSuppression* ns = generator.apm()->noise_suppression();
-  EXPECT_EQ(AudioProcessing::kNoError, ns->Enable(!ns->is_enabled()));
+  AudioProcessing::Config apm_config = generator.apm()->GetConfig();
+  apm_config.noise_suppression.enabled = !apm_config.noise_suppression.enabled;
+  generator.apm()->ApplyConfig(apm_config);
 
   generator.Process(100);
   generator.StopRecording();
diff --git a/test/fuzzers/audio_processing_configs_fuzzer.cc b/test/fuzzers/audio_processing_configs_fuzzer.cc
index d864854..6791071 100644
--- a/test/fuzzers/audio_processing_configs_fuzzer.cc
+++ b/test/fuzzers/audio_processing_configs_fuzzer.cc
@@ -171,12 +171,11 @@
                 kPeak;
   apm_config.gain_controller2.adaptive_digital.use_saturation_protector =
       use_agc2_adaptive_digital_saturation_protector;
+  apm_config.noise_suppression.enabled = use_ns;
   apm_config.voice_detection.enabled = use_vad;
-
   apm->ApplyConfig(apm_config);
 
   apm->gain_control()->Enable(use_agc);
-  apm->noise_suppression()->Enable(use_ns);
   apm->level_estimator()->Enable(use_le);
   apm->voice_detection()->Enable(use_vad);
   apm->gain_control()->enable_limiter(use_agc_limiter);