Corrected and robustified the detection of the delay in the AEC3 filter
This CL changes the filter delay detection to rely on the largest peak while the correctness of the filter is changed to be based on the performance achieved by the filter. Bug: webrtc:8397,chromium:774867 Change-Id: I70c953815192478f9a8e0da9f2b8fd9edac3f481 Reviewed-on: https://webrtc-review.googlesource.com/10803 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#20321}
This commit is contained in:
parent
8b35df7277
commit
40659c3eaf
@ -23,49 +23,27 @@ namespace webrtc {
|
||||
namespace {
|
||||
|
||||
// Computes delay of the adaptive filter.
|
||||
rtc::Optional<size_t> EstimateFilterDelay(
|
||||
int EstimateFilterDelay(
|
||||
const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||
adaptive_filter_frequency_response) {
|
||||
const auto& H2 = adaptive_filter_frequency_response;
|
||||
|
||||
size_t reliable_delays_sum = 0;
|
||||
size_t num_reliable_delays = 0;
|
||||
|
||||
constexpr size_t kUpperBin = kFftLengthBy2 - 5;
|
||||
constexpr float kMinPeakMargin = 10.f;
|
||||
const size_t kTailPartition = H2.size() - 1;
|
||||
RTC_DCHECK_GE(kAdaptiveFilterLength, H2.size());
|
||||
std::array<int, kAdaptiveFilterLength> delays;
|
||||
delays.fill(0);
|
||||
for (size_t k = 1; k < kUpperBin; ++k) {
|
||||
// Find the maximum of H2[j].
|
||||
int peak = 0;
|
||||
size_t peak = 0;
|
||||
for (size_t j = 0; j < H2.size(); ++j) {
|
||||
if (H2[j][k] > H2[peak][k]) {
|
||||
peak = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Count the peak as a delay only if the peak is sufficiently larger than
|
||||
// the tail.
|
||||
if (kMinPeakMargin * H2[kTailPartition][k] < H2[peak][k]) {
|
||||
reliable_delays_sum += peak;
|
||||
++num_reliable_delays;
|
||||
}
|
||||
++delays[peak];
|
||||
}
|
||||
|
||||
// Return no delay if not sufficient delays have been found.
|
||||
if (num_reliable_delays < 21) {
|
||||
return rtc::Optional<size_t>();
|
||||
}
|
||||
|
||||
const size_t delay = reliable_delays_sum / num_reliable_delays;
|
||||
// Sanity check that the peak is not caused by a false strong DC-component in
|
||||
// the filter.
|
||||
for (size_t k = 1; k < kUpperBin; ++k) {
|
||||
if (H2[delay][k] > H2[delay][0]) {
|
||||
RTC_DCHECK_GT(H2.size(), delay);
|
||||
return rtc::Optional<size_t>(delay);
|
||||
}
|
||||
}
|
||||
return rtc::Optional<size_t>();
|
||||
return std::distance(delays.begin(),
|
||||
std::max_element(delays.begin(), delays.end()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@ -130,14 +108,15 @@ void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||
force_zero_gain_ = (++force_zero_gain_counter_) < kNumBlocksPerSecond / 5;
|
||||
|
||||
// Estimate delays.
|
||||
filter_delay_ = EstimateFilterDelay(adaptive_filter_frequency_response);
|
||||
filter_delay_ = rtc::Optional<size_t>(
|
||||
EstimateFilterDelay(adaptive_filter_frequency_response));
|
||||
external_delay_ =
|
||||
external_delay_samples
|
||||
? rtc::Optional<size_t>(*external_delay_samples / kBlockSize)
|
||||
: rtc::Optional<size_t>();
|
||||
|
||||
// Update the ERL and ERLE measures.
|
||||
if (filter_delay_ && capture_block_counter_ >= 2 * kNumBlocksPerSecond) {
|
||||
if (converged_filter && capture_block_counter_ >= 2 * kNumBlocksPerSecond) {
|
||||
const auto& X2 = render_buffer.Spectrum(*filter_delay_);
|
||||
erle_estimator_.Update(X2, Y2, E2_main);
|
||||
erl_estimator_.Update(X2, Y2);
|
||||
@ -171,8 +150,7 @@ void AecState::Update(const std::vector<std::array<float, kFftLengthBy2Plus1>>&
|
||||
// Flag whether the linear filter estimate is usable.
|
||||
usable_linear_estimate_ =
|
||||
(!echo_saturation_) && (converged_filter || SufficientFilterUpdates()) &&
|
||||
filter_delay_ && capture_block_counter_ >= 2 * kNumBlocksPerSecond &&
|
||||
external_delay_;
|
||||
capture_block_counter_ >= 2 * kNumBlocksPerSecond && external_delay_;
|
||||
|
||||
// After an amount of active render samples for which an echo should have been
|
||||
// detected in the capture signal if the ERL was not infinite, flag that a
|
||||
|
||||
@ -161,35 +161,6 @@ TEST(AecState, NormalUsage) {
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the a non-significant delay is correctly identified.
|
||||
TEST(AecState, NonSignificantDelay) {
|
||||
AecState state(AudioProcessing::Config::EchoCanceller3{});
|
||||
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30,
|
||||
std::vector<size_t>(1, 30));
|
||||
std::array<float, kFftLengthBy2Plus1> E2_main;
|
||||
std::array<float, kFftLengthBy2Plus1> Y2;
|
||||
std::array<float, kBlockSize> x;
|
||||
EchoPathVariability echo_path_variability(false, false);
|
||||
std::array<float, kBlockSize> s;
|
||||
s.fill(100.f);
|
||||
x.fill(0.f);
|
||||
|
||||
std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(30);
|
||||
for (auto& v : frequency_response) {
|
||||
v.fill(0.01f);
|
||||
}
|
||||
|
||||
std::array<float, kAdaptiveFilterTimeDomainLength> impulse_response;
|
||||
impulse_response.fill(0.f);
|
||||
|
||||
// Verify that a non-significant filter delay is identified correctly.
|
||||
state.HandleEchoPathChange(echo_path_variability);
|
||||
state.Update(frequency_response, impulse_response, true,
|
||||
rtc::Optional<size_t>(), render_buffer, E2_main, Y2, x, s,
|
||||
false);
|
||||
EXPECT_FALSE(state.FilterDelay());
|
||||
}
|
||||
|
||||
// Verifies the delay for a converged filter is correctly identified.
|
||||
TEST(AecState, ConvergedFilterDelay) {
|
||||
constexpr int kFilterLength = 10;
|
||||
@ -243,7 +214,8 @@ TEST(AecState, ExternalDelay) {
|
||||
x.fill(0.f);
|
||||
RenderBuffer render_buffer(Aec3Optimization::kNone, 3, 30,
|
||||
std::vector<size_t>(1, 30));
|
||||
std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(30);
|
||||
std::vector<std::array<float, kFftLengthBy2Plus1>> frequency_response(
|
||||
kAdaptiveFilterLength);
|
||||
for (auto& v : frequency_response) {
|
||||
v.fill(0.01f);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user