mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

Bug: webrtc:342905193 No-Try: True Change-Id: Icc968be43b8830038ea9a1f5f604307220457807 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/361021 Auto-Submit: Florent Castelli <orphis@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Florent Castelli <orphis@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42911}
199 lines
7.5 KiB
C++
199 lines
7.5 KiB
C++
/*
|
|
* 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/residual_echo_estimator.h"
|
|
|
|
#include <numeric>
|
|
|
|
#include "api/audio/echo_canceller3_config.h"
|
|
#include "modules/audio_processing/aec3/aec3_fft.h"
|
|
#include "modules/audio_processing/aec3/aec_state.h"
|
|
#include "modules/audio_processing/aec3/render_delay_buffer.h"
|
|
#include "modules/audio_processing/test/echo_canceller_test_tools.h"
|
|
#include "rtc_base/random.h"
|
|
#include "rtc_base/strings/string_builder.h"
|
|
#include "test/gtest.h"
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
constexpr int kSampleRateHz = 48000;
|
|
constexpr size_t kNumBands = NumBandsForRate(kSampleRateHz);
|
|
constexpr float kEpsilon = 1e-4f;
|
|
} // namespace
|
|
|
|
class ResidualEchoEstimatorTest {
|
|
public:
|
|
ResidualEchoEstimatorTest(size_t num_render_channels,
|
|
size_t num_capture_channels,
|
|
const EchoCanceller3Config& config)
|
|
: num_render_channels_(num_render_channels),
|
|
num_capture_channels_(num_capture_channels),
|
|
config_(config),
|
|
estimator_(config_, num_render_channels_),
|
|
aec_state_(config_, num_capture_channels_),
|
|
render_delay_buffer_(RenderDelayBuffer::Create(config_,
|
|
kSampleRateHz,
|
|
num_render_channels_)),
|
|
E2_refined_(num_capture_channels_),
|
|
S2_linear_(num_capture_channels_),
|
|
Y2_(num_capture_channels_),
|
|
R2_(num_capture_channels_),
|
|
R2_unbounded_(num_capture_channels_),
|
|
x_(kNumBands, num_render_channels_),
|
|
H2_(num_capture_channels_,
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>>(10)),
|
|
h_(num_capture_channels_,
|
|
std::vector<float>(
|
|
GetTimeDomainLength(config_.filter.refined.length_blocks),
|
|
0.0f)),
|
|
random_generator_(42U),
|
|
output_(num_capture_channels_) {
|
|
for (auto& H2_ch : H2_) {
|
|
for (auto& H2_k : H2_ch) {
|
|
H2_k.fill(0.01f);
|
|
}
|
|
H2_ch[2].fill(10.f);
|
|
H2_ch[2][0] = 0.1f;
|
|
}
|
|
|
|
for (auto& subtractor_output : output_) {
|
|
subtractor_output.Reset();
|
|
subtractor_output.s_refined.fill(100.f);
|
|
}
|
|
y_.fill(0.f);
|
|
|
|
constexpr float kLevel = 10.f;
|
|
for (auto& E2_refined_ch : E2_refined_) {
|
|
E2_refined_ch.fill(kLevel);
|
|
}
|
|
S2_linear_[0].fill(kLevel);
|
|
for (auto& Y2_ch : Y2_) {
|
|
Y2_ch.fill(kLevel);
|
|
}
|
|
}
|
|
|
|
void RunOneFrame(bool dominant_nearend) {
|
|
RandomizeSampleVector(&random_generator_,
|
|
x_.View(/*band=*/0, /*channel=*/0));
|
|
render_delay_buffer_->Insert(x_);
|
|
if (first_frame_) {
|
|
render_delay_buffer_->Reset();
|
|
first_frame_ = false;
|
|
}
|
|
render_delay_buffer_->PrepareCaptureProcessing();
|
|
|
|
aec_state_.Update(delay_estimate_, H2_, h_,
|
|
*render_delay_buffer_->GetRenderBuffer(), E2_refined_,
|
|
Y2_, output_);
|
|
|
|
estimator_.Estimate(aec_state_, *render_delay_buffer_->GetRenderBuffer(),
|
|
S2_linear_, Y2_, dominant_nearend, R2_, R2_unbounded_);
|
|
}
|
|
|
|
rtc::ArrayView<const std::array<float, kFftLengthBy2Plus1>> R2() const {
|
|
return R2_;
|
|
}
|
|
|
|
private:
|
|
const size_t num_render_channels_;
|
|
const size_t num_capture_channels_;
|
|
const EchoCanceller3Config& config_;
|
|
ResidualEchoEstimator estimator_;
|
|
AecState aec_state_;
|
|
std::unique_ptr<RenderDelayBuffer> render_delay_buffer_;
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>> E2_refined_;
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>> S2_linear_;
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>> Y2_;
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>> R2_;
|
|
std::vector<std::array<float, kFftLengthBy2Plus1>> R2_unbounded_;
|
|
Block x_;
|
|
std::vector<std::vector<std::array<float, kFftLengthBy2Plus1>>> H2_;
|
|
std::vector<std::vector<float>> h_;
|
|
Random random_generator_;
|
|
std::vector<SubtractorOutput> output_;
|
|
std::array<float, kBlockSize> y_;
|
|
std::optional<DelayEstimate> delay_estimate_;
|
|
bool first_frame_ = true;
|
|
};
|
|
|
|
class ResidualEchoEstimatorMultiChannel
|
|
: public ::testing::Test,
|
|
public ::testing::WithParamInterface<std::tuple<size_t, size_t>> {};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(MultiChannel,
|
|
ResidualEchoEstimatorMultiChannel,
|
|
::testing::Combine(::testing::Values(1, 2, 4),
|
|
::testing::Values(1, 2, 4)));
|
|
|
|
TEST_P(ResidualEchoEstimatorMultiChannel, BasicTest) {
|
|
const size_t num_render_channels = std::get<0>(GetParam());
|
|
const size_t num_capture_channels = std::get<1>(GetParam());
|
|
|
|
EchoCanceller3Config config;
|
|
ResidualEchoEstimatorTest residual_echo_estimator_test(
|
|
num_render_channels, num_capture_channels, config);
|
|
for (int k = 0; k < 1993; ++k) {
|
|
residual_echo_estimator_test.RunOneFrame(/*dominant_nearend=*/false);
|
|
}
|
|
}
|
|
|
|
TEST(ResidualEchoEstimatorMultiChannel, ReverbTest) {
|
|
const size_t num_render_channels = 1;
|
|
const size_t num_capture_channels = 1;
|
|
const size_t nFrames = 100;
|
|
|
|
EchoCanceller3Config reference_config;
|
|
reference_config.ep_strength.default_len = 0.95f;
|
|
reference_config.ep_strength.nearend_len = 0.95f;
|
|
EchoCanceller3Config config_use_nearend_len = reference_config;
|
|
config_use_nearend_len.ep_strength.default_len = 0.95f;
|
|
config_use_nearend_len.ep_strength.nearend_len = 0.83f;
|
|
|
|
ResidualEchoEstimatorTest reference_residual_echo_estimator_test(
|
|
num_render_channels, num_capture_channels, reference_config);
|
|
ResidualEchoEstimatorTest use_nearend_len_residual_echo_estimator_test(
|
|
num_render_channels, num_capture_channels, config_use_nearend_len);
|
|
|
|
std::vector<float> acum_energy_reference_R2(num_capture_channels, 0.0f);
|
|
std::vector<float> acum_energy_R2(num_capture_channels, 0.0f);
|
|
for (size_t frame = 0; frame < nFrames; ++frame) {
|
|
bool dominant_nearend = frame <= nFrames / 2 ? false : true;
|
|
reference_residual_echo_estimator_test.RunOneFrame(dominant_nearend);
|
|
use_nearend_len_residual_echo_estimator_test.RunOneFrame(dominant_nearend);
|
|
const auto& reference_R2 = reference_residual_echo_estimator_test.R2();
|
|
const auto& R2 = use_nearend_len_residual_echo_estimator_test.R2();
|
|
ASSERT_EQ(reference_R2.size(), R2.size());
|
|
for (size_t ch = 0; ch < reference_R2.size(); ++ch) {
|
|
float energy_reference_R2 = std::accumulate(
|
|
reference_R2[ch].cbegin(), reference_R2[ch].cend(), 0.0f);
|
|
float energy_R2 = std::accumulate(R2[ch].cbegin(), R2[ch].cend(), 0.0f);
|
|
if (dominant_nearend) {
|
|
EXPECT_GE(energy_reference_R2, energy_R2);
|
|
} else {
|
|
EXPECT_NEAR(energy_reference_R2, energy_R2, kEpsilon);
|
|
}
|
|
acum_energy_reference_R2[ch] += energy_reference_R2;
|
|
acum_energy_R2[ch] += energy_R2;
|
|
}
|
|
if (frame == nFrames / 2 || frame == nFrames - 1) {
|
|
for (size_t ch = 0; ch < acum_energy_reference_R2.size(); ch++) {
|
|
if (dominant_nearend) {
|
|
EXPECT_GT(acum_energy_reference_R2[ch], acum_energy_R2[ch]);
|
|
} else {
|
|
EXPECT_NEAR(acum_energy_reference_R2[ch], acum_energy_R2[ch],
|
|
kEpsilon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace webrtc
|