blob: a160bec0451061098f2bc9f6ad17e8aabed1d486 [file] [log] [blame]
/*
* Copyright (c) 2017 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/suppression_filter.h"
#include <math.h>
#include <algorithm>
#include <cmath>
#include <numeric>
#include "test/gtest.h"
namespace webrtc {
namespace {
constexpr float kPi = 3.141592f;
void ProduceSinusoid(int sample_rate_hz,
float sinusoidal_frequency_hz,
size_t* sample_counter,
std::vector<std::vector<std::vector<float>>>* x) {
// Produce a sinusoid of the specified frequency.
for (size_t k = *sample_counter, j = 0; k < (*sample_counter + kBlockSize);
++k, ++j) {
for (size_t channel = 0; channel < (*x)[0].size(); ++channel) {
(*x)[0][channel][j] =
32767.f *
std::sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
}
}
*sample_counter = *sample_counter + kBlockSize;
for (size_t band = 1; band < x->size(); ++band) {
for (size_t channel = 0; channel < (*x)[band].size(); ++channel) {
std::fill((*x)[band][channel].begin(), (*x)[band][channel].end(), 0.f);
}
}
}
} // namespace
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
// Verifies the check for null suppressor output.
TEST(SuppressionFilterDeathTest, NullOutput) {
std::vector<FftData> cn(1);
std::vector<FftData> cn_high_bands(1);
std::vector<FftData> E(1);
std::array<float, kFftLengthBy2Plus1> gain;
EXPECT_DEATH(SuppressionFilter(Aec3Optimization::kNone, 16000, 1)
.ApplyGain(cn, cn_high_bands, gain, 1.0f, E, nullptr),
"");
}
// Verifies the check for allowed sample rate.
TEST(SuppressionFilterDeathTest, ProperSampleRate) {
EXPECT_DEATH(SuppressionFilter(Aec3Optimization::kNone, 16001, 1), "");
}
#endif
// Verifies that no comfort noise is added when the gain is 1.
TEST(SuppressionFilter, ComfortNoiseInUnityGain) {
SuppressionFilter filter(Aec3Optimization::kNone, 48000, 1);
std::vector<FftData> cn(1);
std::vector<FftData> cn_high_bands(1);
std::array<float, kFftLengthBy2Plus1> gain;
std::array<float, kFftLengthBy2> e_old_;
Aec3Fft fft;
e_old_.fill(0.f);
gain.fill(1.f);
cn[0].re.fill(1.f);
cn[0].im.fill(1.f);
cn_high_bands[0].re.fill(1.f);
cn_high_bands[0].im.fill(1.f);
std::vector<std::vector<std::vector<float>>> e(
3,
std::vector<std::vector<float>>(1, std::vector<float>(kBlockSize, 0.f)));
std::vector<std::vector<std::vector<float>>> e_ref = e;
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
for (size_t band = 0; band < e.size(); ++band) {
for (size_t channel = 0; channel < e[band].size(); ++channel) {
for (size_t sample = 0; sample < e[band][channel].size(); ++sample) {
EXPECT_EQ(e_ref[band][channel][sample], e[band][channel][sample]);
}
}
}
}
// Verifies that the suppressor is able to suppress a signal.
TEST(SuppressionFilter, SignalSuppression) {
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
constexpr size_t kNumChannels = 1;
SuppressionFilter filter(Aec3Optimization::kNone, kSampleRateHz, 1);
std::vector<FftData> cn(1);
std::vector<FftData> cn_high_bands(1);
std::array<float, kFftLengthBy2> e_old_;
Aec3Fft fft;
std::array<float, kFftLengthBy2Plus1> gain;
std::vector<std::vector<std::vector<float>>> e(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
e_old_.fill(0.f);
gain.fill(1.f);
std::for_each(gain.begin() + 10, gain.end(), [](float& a) { a = 0.f; });
cn[0].re.fill(0.f);
cn[0].im.fill(0.f);
cn_high_bands[0].re.fill(0.f);
cn_high_bands[0].im.fill(0.f);
size_t sample_counter = 0;
float e0_input = 0.f;
float e0_output = 0.f;
for (size_t k = 0; k < 100; ++k) {
ProduceSinusoid(16000, 16000 * 40 / kFftLengthBy2 / 2, &sample_counter, &e);
e0_input = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_input);
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
e0_output = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_output);
}
EXPECT_LT(e0_output, e0_input / 1000.f);
}
// Verifies that the suppressor is able to pass through a desired signal while
// applying suppressing for some frequencies.
TEST(SuppressionFilter, SignalTransparency) {
constexpr size_t kNumChannels = 1;
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
SuppressionFilter filter(Aec3Optimization::kNone, kSampleRateHz, 1);
std::vector<FftData> cn(1);
std::array<float, kFftLengthBy2> e_old_;
Aec3Fft fft;
std::vector<FftData> cn_high_bands(1);
std::array<float, kFftLengthBy2Plus1> gain;
std::vector<std::vector<std::vector<float>>> e(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
e_old_.fill(0.f);
gain.fill(1.f);
std::for_each(gain.begin() + 30, gain.end(), [](float& a) { a = 0.f; });
cn[0].re.fill(0.f);
cn[0].im.fill(0.f);
cn_high_bands[0].re.fill(0.f);
cn_high_bands[0].im.fill(0.f);
size_t sample_counter = 0;
float e0_input = 0.f;
float e0_output = 0.f;
for (size_t k = 0; k < 100; ++k) {
ProduceSinusoid(16000, 16000 * 10 / kFftLengthBy2 / 2, &sample_counter, &e);
e0_input = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_input);
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
e0_output = std::inner_product(e[0][0].begin(), e[0][0].end(),
e[0][0].begin(), e0_output);
}
EXPECT_LT(0.9f * e0_input, e0_output);
}
// Verifies that the suppressor delay.
TEST(SuppressionFilter, Delay) {
constexpr size_t kNumChannels = 1;
constexpr int kSampleRateHz = 48000;
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
SuppressionFilter filter(Aec3Optimization::kNone, kSampleRateHz, 1);
std::vector<FftData> cn(1);
std::vector<FftData> cn_high_bands(1);
std::array<float, kFftLengthBy2> e_old_;
Aec3Fft fft;
std::array<float, kFftLengthBy2Plus1> gain;
std::vector<std::vector<std::vector<float>>> e(
kNumBands, std::vector<std::vector<float>>(
kNumChannels, std::vector<float>(kBlockSize, 0.f)));
gain.fill(1.f);
cn[0].re.fill(0.f);
cn[0].im.fill(0.f);
cn_high_bands[0].re.fill(0.f);
cn_high_bands[0].im.fill(0.f);
for (size_t k = 0; k < 100; ++k) {
for (size_t band = 0; band < kNumBands; ++band) {
for (size_t channel = 0; channel < kNumChannels; ++channel) {
for (size_t sample = 0; sample < kBlockSize; ++sample) {
e[band][channel][sample] = k * kBlockSize + sample + channel;
}
}
}
std::vector<FftData> E(1);
fft.PaddedFft(e[0][0], e_old_, Aec3Fft::Window::kSqrtHanning, &E[0]);
std::copy(e[0][0].begin(), e[0][0].end(), e_old_.begin());
filter.ApplyGain(cn, cn_high_bands, gain, 1.f, E, &e);
if (k > 2) {
for (size_t band = 0; band < kNumBands; ++band) {
for (size_t channel = 0; channel < kNumChannels; ++channel) {
for (size_t sample = 0; sample < kBlockSize; ++sample) {
EXPECT_NEAR(k * kBlockSize + sample - kBlockSize + channel,
e[band][channel][sample], 0.01);
}
}
}
}
}
}
} // namespace webrtc