From 020e583291a8026b77e01d6e878a760977c24b32 Mon Sep 17 00:00:00 2001 From: Gustaf Ullberg Date: Tue, 6 Nov 2018 17:14:11 +0100 Subject: [PATCH] AEC3: Compensate comfort noise level for loss due to filter bank MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The analysis and synthesis windowing cause loss of power when cross-fading the noise where frames are completely uncorrelated (generated with random phase). This CL also removes duplicate code and enables platform specific optimizations for ARM in the comfort noise generation. Bug: webrtc:9967,chromium:902262 Change-Id: Iffd59b301876442079d4a5f2c7fac55a3522397c Reviewed-on: https://webrtc-review.googlesource.com/c/109581 Commit-Queue: Gustaf Ullberg Reviewed-by: Per Ã…hgren Cr-Commit-Position: refs/heads/master@{#25526} --- .../aec3/comfort_noise_generator.cc | 92 ++++--------------- .../aec3/comfort_noise_generator_unittest.cc | 43 +-------- 2 files changed, 21 insertions(+), 114 deletions(-) diff --git a/modules/audio_processing/aec3/comfort_noise_generator.cc b/modules/audio_processing/aec3/comfort_noise_generator.cc index f55d9d4fda..766e658639 100644 --- a/modules/audio_processing/aec3/comfort_noise_generator.cc +++ b/modules/audio_processing/aec3/comfort_noise_generator.cc @@ -24,6 +24,7 @@ #include #include "common_audio/signal_processing/include/signal_processing_library.h" +#include "modules/audio_processing/aec3/vector_math.h" #include "rtc_base/checks.h" namespace webrtc { @@ -40,64 +41,10 @@ void TableRandomValue(int16_t* vector, int16_t vector_length, uint32_t* seed) { } // namespace -namespace aec3 { +namespace { -#if defined(WEBRTC_ARCH_X86_FAMILY) - -void EstimateComfortNoise_SSE2(const std::array& N2, - uint32_t* seed, - FftData* lower_band_noise, - FftData* upper_band_noise) { - FftData* N_low = lower_band_noise; - FftData* N_high = upper_band_noise; - - // Compute square root spectrum. - std::array N; - for (size_t k = 0; k < kFftLengthBy2; k += 4) { - __m128 v = _mm_loadu_ps(&N2[k]); - v = _mm_sqrt_ps(v); - _mm_storeu_ps(&N[k], v); - } - - N[kFftLengthBy2] = sqrtf(N2[kFftLengthBy2]); - - // Compute the noise level for the upper bands. - constexpr float kOneByNumBands = 1.f / (kFftLengthBy2Plus1 / 2 + 1); - constexpr int kFftLengthBy2Plus1By2 = kFftLengthBy2Plus1 / 2; - const float high_band_noise_level = - std::accumulate(N.begin() + kFftLengthBy2Plus1By2, N.end(), 0.f) * - kOneByNumBands; - - // Generate complex noise. - std::array random_values_int; - TableRandomValue(random_values_int.data(), random_values_int.size(), seed); - - std::array sin; - std::array cos; - constexpr float kScale = 6.28318530717959f / 32768.0f; - std::transform(random_values_int.begin(), random_values_int.end(), - sin.begin(), [&](int16_t a) { return -sinf(kScale * a); }); - std::transform(random_values_int.begin(), random_values_int.end(), - cos.begin(), [&](int16_t a) { return cosf(kScale * a); }); - - // Form low-frequency noise via spectral shaping. - N_low->re[0] = N_low->re[kFftLengthBy2] = N_high->re[0] = - N_high->re[kFftLengthBy2] = 0.f; - std::transform(cos.begin(), cos.end(), N.begin() + 1, N_low->re.begin() + 1, - std::multiplies()); - std::transform(sin.begin(), sin.end(), N.begin() + 1, N_low->im.begin() + 1, - std::multiplies()); - - // Form the high-frequency noise via simple levelling. - std::transform(cos.begin(), cos.end(), N_high->re.begin() + 1, - [&](float a) { return high_band_noise_level * a; }); - std::transform(sin.begin(), sin.end(), N_high->im.begin() + 1, - [&](float a) { return high_band_noise_level * a; }); -} - -#endif - -void EstimateComfortNoise(const std::array& N2, +void GenerateComfortNoise(Aec3Optimization optimization, + const std::array& N2, uint32_t* seed, FftData* lower_band_noise, FftData* upper_band_noise) { @@ -106,8 +53,8 @@ void EstimateComfortNoise(const std::array& N2, // Compute square root spectrum. std::array N; - std::transform(N2.begin(), N2.end(), N.begin(), - [](float a) { return sqrtf(a); }); + std::copy(N2.begin(), N2.end(), N.begin()); + aec3::VectorMath(optimization).Sqrt(N); // Compute the noise level for the upper bands. constexpr float kOneByNumBands = 1.f / (kFftLengthBy2Plus1 / 2 + 1); @@ -120,13 +67,21 @@ void EstimateComfortNoise(const std::array& N2, std::array random_values_int; TableRandomValue(random_values_int.data(), random_values_int.size(), seed); + // The analysis and synthesis windowing cause loss of power when + // cross-fading the noise where frames are completely uncorrelated + // (generated with random phase), hence the factor sqrt(2). + // This is not the case for the speech signal where the input is overlapping + // (strong correlation). std::array sin; std::array cos; constexpr float kScale = 6.28318530717959f / 32768.0f; + constexpr float kSqrt2 = 1.4142135623f; std::transform(random_values_int.begin(), random_values_int.end(), - sin.begin(), [&](int16_t a) { return -sinf(kScale * a); }); + sin.begin(), + [&](int16_t a) { return -sinf(kScale * a) * kSqrt2; }); std::transform(random_values_int.begin(), random_values_int.end(), - cos.begin(), [&](int16_t a) { return cosf(kScale * a); }); + cos.begin(), + [&](int16_t a) { return cosf(kScale * a) * kSqrt2; }); // Form low-frequency noise via spectral shaping. N_low->re[0] = N_low->re[kFftLengthBy2] = N_high->re[0] = @@ -143,7 +98,7 @@ void EstimateComfortNoise(const std::array& N2, [&](float a) { return high_band_noise_level * a; }); } -} // namespace aec3 +} // namespace ComfortNoiseGenerator::ComfortNoiseGenerator(Aec3Optimization optimization) : optimization_(optimization), @@ -207,17 +162,8 @@ void ComfortNoiseGenerator::Compute( const std::array& N2 = N2_initial_ ? *N2_initial_ : N2_; - switch (optimization_) { -#if defined(WEBRTC_ARCH_X86_FAMILY) - case Aec3Optimization::kSse2: - aec3::EstimateComfortNoise_SSE2(N2, &seed_, lower_band_noise, - upper_band_noise); - break; -#endif - default: - aec3::EstimateComfortNoise(N2, &seed_, lower_band_noise, - upper_band_noise); - } + GenerateComfortNoise(optimization_, N2, &seed_, lower_band_noise, + upper_band_noise); } } // namespace webrtc diff --git a/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc b/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc index 77a09edcd6..10ba696036 100644 --- a/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc +++ b/modules/audio_processing/aec3/comfort_noise_generator_unittest.cc @@ -52,45 +52,6 @@ TEST(ComfortNoiseGenerator, NullUpperBandNoise) { #endif -#if defined(WEBRTC_ARCH_X86_FAMILY) -// Verifies that the optimized methods are bitexact to their reference -// counterparts. -TEST(ComfortNoiseGenerator, TestOptimizations) { - if (WebRtc_GetCPUInfo(kSSE2) != 0) { - Random random_generator(42U); - uint32_t seed = 42; - uint32_t seed_SSE2 = 42; - std::array N2; - FftData lower_band_noise; - FftData upper_band_noise; - FftData lower_band_noise_SSE2; - FftData upper_band_noise_SSE2; - for (int k = 0; k < 10; ++k) { - for (size_t j = 0; j < N2.size(); ++j) { - N2[j] = random_generator.Rand() * 1000.f; - } - - EstimateComfortNoise(N2, &seed, &lower_band_noise, &upper_band_noise); - EstimateComfortNoise_SSE2(N2, &seed_SSE2, &lower_band_noise_SSE2, - &upper_band_noise_SSE2); - for (size_t j = 0; j < lower_band_noise.re.size(); ++j) { - EXPECT_NEAR(lower_band_noise.re[j], lower_band_noise_SSE2.re[j], - 0.00001f); - EXPECT_NEAR(upper_band_noise.re[j], upper_band_noise_SSE2.re[j], - 0.00001f); - } - for (size_t j = 1; j < lower_band_noise.re.size() - 1; ++j) { - EXPECT_NEAR(lower_band_noise.im[j], lower_band_noise_SSE2.im[j], - 0.00001f); - EXPECT_NEAR(upper_band_noise.im[j], upper_band_noise_SSE2.im[j], - 0.00001f); - } - } - } -} - -#endif - TEST(ComfortNoiseGenerator, CorrectLevel) { ComfortNoiseGenerator cng(DetectOptimization()); AecState aec_state(EchoCanceller3Config{}); @@ -113,8 +74,8 @@ TEST(ComfortNoiseGenerator, CorrectLevel) { for (int k = 0; k < 10000; ++k) { cng.Compute(aec_state, N2, &n_lower, &n_upper); } - EXPECT_NEAR(N2[0], Power(n_lower), N2[0] / 10.f); - EXPECT_NEAR(N2[0], Power(n_upper), N2[0] / 10.f); + EXPECT_NEAR(2.f * N2[0], Power(n_lower), N2[0] / 10.f); + EXPECT_NEAR(2.f * N2[0], Power(n_upper), N2[0] / 10.f); } } // namespace aec3