AEC3: Added parametrization of the comfort noise floor


Bug: webrtc:8671
Change-Id: I2431b1dd8dbe35fc8742c0640c3b35166e8ef6b7
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/171480
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Commit-Queue: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30876}
diff --git a/api/audio/echo_canceller3_config.cc b/api/audio/echo_canceller3_config.cc
index 2438738..17af8f8 100644
--- a/api/audio/echo_canceller3_config.cc
+++ b/api/audio/echo_canceller3_config.cc
@@ -226,6 +226,8 @@
   res = res & Limit(&c->echo_model.render_pre_window_size, 0, 100);
   res = res & Limit(&c->echo_model.render_post_window_size, 0, 100);
 
+  res = res & Limit(&c->comfort_noise.noise_floor_dbfs, -200.f, 0.f);
+
   res = res & Limit(&c->suppressor.nearend_average_blocks, 1, 5000);
 
   res = res &
diff --git a/api/audio/echo_canceller3_config.h b/api/audio/echo_canceller3_config.h
index 523cf94..421b25b 100644
--- a/api/audio/echo_canceller3_config.h
+++ b/api/audio/echo_canceller3_config.h
@@ -154,6 +154,10 @@
     size_t render_post_window_size = 1;
   } echo_model;
 
+  struct ComfortNoise {
+    float noise_floor_dbfs = -96.03406f;
+  } comfort_noise;
+
   struct Suppressor {
     Suppressor();
     Suppressor(const Suppressor& e);
diff --git a/api/audio/echo_canceller3_config_json.cc b/api/audio/echo_canceller3_config_json.cc
index 7734e32..06b8f27 100644
--- a/api/audio/echo_canceller3_config_json.cc
+++ b/api/audio/echo_canceller3_config_json.cc
@@ -312,6 +312,10 @@
               &cfg.echo_model.render_post_window_size);
   }
 
+  if (rtc::GetValueFromJsonObject(aec3_root, "comfort_noise", &section)) {
+    ReadParam(section, "noise_floor_dbfs", &cfg.comfort_noise.noise_floor_dbfs);
+  }
+
   Json::Value subsection;
   if (rtc::GetValueFromJsonObject(aec3_root, "suppressor", &section)) {
     ReadParam(section, "nearend_average_blocks",
@@ -626,6 +630,10 @@
       << config.echo_model.render_post_window_size;
   ost << "},";
 
+  ost << "\"comfort_noise\": {";
+  ost << "\"noise_floor_dbfs\": " << config.comfort_noise.noise_floor_dbfs;
+  ost << "},";
+
   ost << "\"suppressor\": {";
   ost << "\"nearend_average_blocks\": "
       << config.suppressor.nearend_average_blocks << ",";
diff --git a/api/audio/test/echo_canceller3_config_json_unittest.cc b/api/audio/test/echo_canceller3_config_json_unittest.cc
index f5c2675..b63f233 100644
--- a/api/audio/test/echo_canceller3_config_json_unittest.cc
+++ b/api/audio/test/echo_canceller3_config_json_unittest.cc
@@ -23,6 +23,7 @@
   cfg.filter.refined.error_floor = 2.f;
   cfg.filter.shadow_initial.length_blocks = 7u;
   cfg.filter.coarse_initial.length_blocks = 3u;
+  cfg.comfort_noise.noise_floor_dbfs = 100.f;
   cfg.suppressor.normal_tuning.mask_hf.enr_suppress = .5f;
   cfg.suppressor.subband_nearend_detection.nearend_average_blocks = 3;
   cfg.suppressor.subband_nearend_detection.subband1 = {1, 3};
@@ -51,6 +52,8 @@
             cfg_transformed.filter.main.error_floor);
   EXPECT_EQ(cfg.filter.refined.error_floor,
             cfg_transformed.filter.refined.error_floor);
+  EXPECT_EQ(cfg.comfort_noise.noise_floor_dbfs,
+            cfg_transformed.comfort_noise.noise_floor_dbfs);
   EXPECT_EQ(cfg.suppressor.normal_tuning.mask_hf.enr_suppress,
             cfg_transformed.suppressor.normal_tuning.mask_hf.enr_suppress);
   EXPECT_EQ(cfg.suppressor.subband_nearend_detection.nearend_average_blocks,
diff --git a/modules/audio_processing/aec3/comfort_noise_generator.cc b/modules/audio_processing/aec3/comfort_noise_generator.cc
index 16c4a2b..de5227c 100644
--- a/modules/audio_processing/aec3/comfort_noise_generator.cc
+++ b/modules/audio_processing/aec3/comfort_noise_generator.cc
@@ -31,6 +31,13 @@
 
 namespace {
 
+// Computes the noise floor value that matches a WGN input of noise_floor_dbfs.
+float GetNoiseFloorFactor(float noise_floor_dbfs) {
+  // kdBfsNormalization = 20.f*log10(32768.f).
+  constexpr float kdBfsNormalization = 90.30899869919436f;
+  return 64.f * powf(10.f, (kdBfsNormalization + noise_floor_dbfs) * 0.1f);
+}
+
 // Table of sqrt(2) * sin(2*pi*i/32).
 constexpr float kSqrt2Sin[32] = {
     +0.0000000f, +0.2758994f, +0.5411961f, +0.7856950f, +1.0000000f,
@@ -92,11 +99,13 @@
 
 }  // namespace
 
-ComfortNoiseGenerator::ComfortNoiseGenerator(Aec3Optimization optimization,
+ComfortNoiseGenerator::ComfortNoiseGenerator(const EchoCanceller3Config& config,
+                                             Aec3Optimization optimization,
                                              size_t num_capture_channels)
     : optimization_(optimization),
       seed_(42),
       num_capture_channels_(num_capture_channels),
+      noise_floor_(GetNoiseFloorFactor(config.comfort_noise.noise_floor_dbfs)),
       N2_initial_(
           std::make_unique<std::vector<std::array<float, kFftLengthBy2Plus1>>>(
               num_capture_channels_)),
@@ -153,16 +162,13 @@
       }
     }
 
-    // Limit the noise to a floor matching a WGN input of -96 dBFS.
-    constexpr float kNoiseFloor = 17.1267f;
-
     for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
       for (auto& n : N2_[ch]) {
-        n = std::max(n, kNoiseFloor);
+        n = std::max(n, noise_floor_);
       }
       if (N2_initial_) {
         for (auto& n : (*N2_initial_)[ch]) {
-          n = std::max(n, kNoiseFloor);
+          n = std::max(n, noise_floor_);
         }
       }
     }
diff --git a/modules/audio_processing/aec3/comfort_noise_generator.h b/modules/audio_processing/aec3/comfort_noise_generator.h
index 776ed1b..16eaf35 100644
--- a/modules/audio_processing/aec3/comfort_noise_generator.h
+++ b/modules/audio_processing/aec3/comfort_noise_generator.h
@@ -41,7 +41,8 @@
 // Generates the comfort noise.
 class ComfortNoiseGenerator {
  public:
-  ComfortNoiseGenerator(Aec3Optimization optimization,
+  ComfortNoiseGenerator(const EchoCanceller3Config& config,
+                        Aec3Optimization optimization,
                         size_t num_capture_channels);
   ComfortNoiseGenerator() = delete;
   ~ComfortNoiseGenerator();
@@ -64,6 +65,7 @@
   const Aec3Optimization optimization_;
   uint32_t seed_;
   const size_t num_capture_channels_;
+  const float noise_floor_;
   std::unique_ptr<std::vector<std::array<float, kFftLengthBy2Plus1>>>
       N2_initial_;
   std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_smoothed_;
diff --git a/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc b/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc
index 02c26cc..a9da175 100644
--- a/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc
+++ b/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc
@@ -13,6 +13,7 @@
 #include <algorithm>
 #include <numeric>
 
+#include "api/audio/echo_canceller3_config.h"
 #include "modules/audio_processing/aec3/aec_state.h"
 #include "rtc_base/random.h"
 #include "rtc_base/system/arch.h"
@@ -33,8 +34,9 @@
 
 TEST(ComfortNoiseGenerator, CorrectLevel) {
   constexpr size_t kNumChannels = 5;
-  ComfortNoiseGenerator cng(DetectOptimization(), kNumChannels);
-  AecState aec_state(EchoCanceller3Config{}, kNumChannels);
+  EchoCanceller3Config config;
+  ComfortNoiseGenerator cng(config, DetectOptimization(), kNumChannels);
+  AecState aec_state(config, kNumChannels);
 
   std::vector<std::array<float, kFftLengthBy2Plus1>> N2(kNumChannels);
   std::vector<FftData> n_lower(kNumChannels);
diff --git a/modules/audio_processing/aec3/echo_remover.cc b/modules/audio_processing/aec3/echo_remover.cc
index 06f3b45..a3cd22f 100644
--- a/modules/audio_processing/aec3/echo_remover.cc
+++ b/modules/audio_processing/aec3/echo_remover.cc
@@ -200,7 +200,7 @@
                         optimization_,
                         sample_rate_hz,
                         num_capture_channels),
-      cng_(optimization_, num_capture_channels_),
+      cng_(config_, optimization_, num_capture_channels_),
       suppression_filter_(optimization_,
                           sample_rate_hz_,
                           num_capture_channels_),