AEC3: Compensate comfort noise level for loss due to filter bank

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 <gustaf@webrtc.org>
Reviewed-by: Per Åhgren <peah@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25526}
This commit is contained in:
Gustaf Ullberg
2018-11-06 17:14:11 +01:00
committed by Commit Bot
parent 83b00f020e
commit 020e583291
2 changed files with 21 additions and 114 deletions

View File

@ -24,6 +24,7 @@
#include <numeric>
#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<float, kFftLengthBy2Plus1>& 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<float, kFftLengthBy2Plus1> 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<int16_t, kFftLengthBy2 - 1> random_values_int;
TableRandomValue(random_values_int.data(), random_values_int.size(), seed);
std::array<float, kFftLengthBy2 - 1> sin;
std::array<float, kFftLengthBy2 - 1> 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<float>());
std::transform(sin.begin(), sin.end(), N.begin() + 1, N_low->im.begin() + 1,
std::multiplies<float>());
// 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<float, kFftLengthBy2Plus1>& N2,
void GenerateComfortNoise(Aec3Optimization optimization,
const std::array<float, kFftLengthBy2Plus1>& N2,
uint32_t* seed,
FftData* lower_band_noise,
FftData* upper_band_noise) {
@ -106,8 +53,8 @@ void EstimateComfortNoise(const std::array<float, kFftLengthBy2Plus1>& N2,
// Compute square root spectrum.
std::array<float, kFftLengthBy2Plus1> 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<float, kFftLengthBy2Plus1>& N2,
std::array<int16_t, kFftLengthBy2 - 1> 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<float, kFftLengthBy2 - 1> sin;
std::array<float, kFftLengthBy2 - 1> 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<float, kFftLengthBy2Plus1>& 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<float, kFftLengthBy2Plus1>& 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

View File

@ -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<float, kFftLengthBy2Plus1> 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<float>() * 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