Separating the AEC3 suppressor gain rampup behavior for call startup and in-call resets
This CL introduces a different rampup behavir for the call startup and after resets that may occur due to delay changes, clock-drift and audio path glitches. Bug: chromium:819111, webrtc:8979 Change-Id: Ied1d7896be7f0c69aa6deb61475117021ca6ab09 Reviewed-on: https://webrtc-review.googlesource.com/60002 Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Reviewed-by: Jesus de Vicente Pena <devicentepena@webrtc.org> Commit-Queue: Per Åhgren <peah@webrtc.org> Cr-Commit-Position: refs/heads/master@{#22312}
This commit is contained in:
@ -88,6 +88,8 @@ rtc_static_library("aec3") {
|
|||||||
"suppression_filter.h",
|
"suppression_filter.h",
|
||||||
"suppression_gain.cc",
|
"suppression_gain.cc",
|
||||||
"suppression_gain.h",
|
"suppression_gain.h",
|
||||||
|
"suppression_gain_limiter.cc",
|
||||||
|
"suppression_gain_limiter.h",
|
||||||
"vector_buffer.cc",
|
"vector_buffer.cc",
|
||||||
"vector_buffer.h",
|
"vector_buffer.h",
|
||||||
"vector_math.h",
|
"vector_math.h",
|
||||||
|
@ -63,7 +63,8 @@ AecState::AecState(const EchoCanceller3Config& config)
|
|||||||
config_(config),
|
config_(config),
|
||||||
max_render_(config_.filter.main.length_blocks, 0.f),
|
max_render_(config_.filter.main.length_blocks, 0.f),
|
||||||
reverb_decay_(fabsf(config_.ep_strength.default_len)),
|
reverb_decay_(fabsf(config_.ep_strength.default_len)),
|
||||||
gain_rampup_increase_(ComputeGainRampupIncrease(config_)) {}
|
gain_rampup_increase_(ComputeGainRampupIncrease(config_)),
|
||||||
|
suppression_gain_limiter_(config_) {}
|
||||||
|
|
||||||
AecState::~AecState() = default;
|
AecState::~AecState() = default;
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ void AecState::HandleEchoPathChange(
|
|||||||
render_received_ = false;
|
render_received_ = false;
|
||||||
blocks_with_active_render_ = 0;
|
blocks_with_active_render_ = 0;
|
||||||
initial_state_ = true;
|
initial_state_ = true;
|
||||||
|
suppression_gain_limiter_.Reset();
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(peah): Refine the reset scheme according to the type of gain and
|
// TODO(peah): Refine the reset scheme according to the type of gain and
|
||||||
@ -96,7 +98,6 @@ void AecState::HandleEchoPathChange(
|
|||||||
full_reset();
|
full_reset();
|
||||||
} else if (echo_path_variability.delay_change !=
|
} else if (echo_path_variability.delay_change !=
|
||||||
EchoPathVariability::DelayAdjustment::kBufferFlush) {
|
EchoPathVariability::DelayAdjustment::kBufferFlush) {
|
||||||
active_render_seen_ = false;
|
|
||||||
full_reset();
|
full_reset();
|
||||||
} else if (echo_path_variability.delay_change !=
|
} else if (echo_path_variability.delay_change !=
|
||||||
EchoPathVariability::DelayAdjustment::kDelayReset) {
|
EchoPathVariability::DelayAdjustment::kDelayReset) {
|
||||||
@ -136,7 +137,7 @@ void AecState::Update(
|
|||||||
|
|
||||||
// Update the limit on the echo suppression after an echo path change to avoid
|
// Update the limit on the echo suppression after an echo path change to avoid
|
||||||
// an initial echo burst.
|
// an initial echo burst.
|
||||||
UpdateSuppressorGainLimit(render_buffer.GetRenderActivity());
|
suppression_gain_limiter_.Update(render_buffer.GetRenderActivity());
|
||||||
|
|
||||||
// Update the ERL and ERLE measures.
|
// Update the ERL and ERLE measures.
|
||||||
if (converged_filter && capture_block_counter_ >= 2 * kNumBlocksPerSecond) {
|
if (converged_filter && capture_block_counter_ >= 2 * kNumBlocksPerSecond) {
|
||||||
@ -360,6 +361,7 @@ void AecState::UpdateReverb(const std::vector<float>& impulse_response) {
|
|||||||
|
|
||||||
data_dumper_->DumpRaw("aec3_reverb_decay", reverb_decay_);
|
data_dumper_->DumpRaw("aec3_reverb_decay", reverb_decay_);
|
||||||
data_dumper_->DumpRaw("aec3_reverb_tail_energy", tail_energy_);
|
data_dumper_->DumpRaw("aec3_reverb_tail_energy", tail_energy_);
|
||||||
|
data_dumper_->DumpRaw("aec3_suppression_gain_limit", SuppressionGainLimit());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
|
bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
|
||||||
@ -369,37 +371,6 @@ bool AecState::DetectActiveRender(rtc::ArrayView<const float> x) const {
|
|||||||
kFftLengthBy2;
|
kFftLengthBy2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the suppressor gain limit.
|
|
||||||
void AecState::UpdateSuppressorGainLimit(bool render_activity) {
|
|
||||||
const auto& rampup_conf = config_.echo_removal_control.gain_rampup;
|
|
||||||
if (!active_render_seen_ && render_activity) {
|
|
||||||
active_render_seen_ = true;
|
|
||||||
realignment_counter_ = rampup_conf.full_gain_blocks;
|
|
||||||
} else if (realignment_counter_ > 0) {
|
|
||||||
--realignment_counter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realignment_counter_ <= 0) {
|
|
||||||
suppressor_gain_limit_ = 1.f;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realignment_counter_ > rampup_conf.non_zero_gain_blocks) {
|
|
||||||
suppressor_gain_limit_ = 0.f;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (realignment_counter_ == rampup_conf.non_zero_gain_blocks) {
|
|
||||||
suppressor_gain_limit_ = rampup_conf.first_non_zero_gain;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RTC_DCHECK_LT(0.f, suppressor_gain_limit_);
|
|
||||||
suppressor_gain_limit_ =
|
|
||||||
std::min(1.f, suppressor_gain_limit_ * gain_rampup_increase_);
|
|
||||||
RTC_DCHECK_GE(1.f, suppressor_gain_limit_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x) {
|
bool AecState::DetectEchoSaturation(rtc::ArrayView<const float> x) {
|
||||||
RTC_DCHECK_LT(0, x.size());
|
RTC_DCHECK_LT(0, x.size());
|
||||||
const float max_sample = fabs(*std::max_element(
|
const float max_sample = fabs(*std::max_element(
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "modules/audio_processing/aec3/erl_estimator.h"
|
#include "modules/audio_processing/aec3/erl_estimator.h"
|
||||||
#include "modules/audio_processing/aec3/erle_estimator.h"
|
#include "modules/audio_processing/aec3/erle_estimator.h"
|
||||||
#include "modules/audio_processing/aec3/render_buffer.h"
|
#include "modules/audio_processing/aec3/render_buffer.h"
|
||||||
|
#include "modules/audio_processing/aec3/suppression_gain_limiter.h"
|
||||||
#include "rtc_base/constructormagic.h"
|
#include "rtc_base/constructormagic.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -91,7 +92,9 @@ class AecState {
|
|||||||
float ReverbDecay() const { return reverb_decay_; }
|
float ReverbDecay() const { return reverb_decay_; }
|
||||||
|
|
||||||
// Returns the upper limit for the echo suppression gain.
|
// Returns the upper limit for the echo suppression gain.
|
||||||
float SuppressionGainLimit() const { return suppressor_gain_limit_; }
|
float SuppressionGainLimit() const {
|
||||||
|
return suppression_gain_limiter_.Limit();
|
||||||
|
}
|
||||||
|
|
||||||
// Returns whether the echo in the capture signal is audible.
|
// Returns whether the echo in the capture signal is audible.
|
||||||
bool InaudibleEcho() const { return echo_audibility_.InaudibleEcho(); }
|
bool InaudibleEcho() const { return echo_audibility_.InaudibleEcho(); }
|
||||||
@ -156,9 +159,6 @@ class AecState {
|
|||||||
bool transparent_mode_ = false;
|
bool transparent_mode_ = false;
|
||||||
float previous_max_sample_ = 0.f;
|
float previous_max_sample_ = 0.f;
|
||||||
bool render_received_ = false;
|
bool render_received_ = false;
|
||||||
int realignment_counter_ = 0;
|
|
||||||
float suppressor_gain_limit_ = 1.f;
|
|
||||||
bool active_render_seen_ = false;
|
|
||||||
int filter_delay_ = 0;
|
int filter_delay_ = 0;
|
||||||
size_t blocks_since_last_saturation_ = 1000;
|
size_t blocks_since_last_saturation_ = 1000;
|
||||||
float tail_energy_ = 0.f;
|
float tail_energy_ = 0.f;
|
||||||
@ -179,6 +179,7 @@ class AecState {
|
|||||||
bool filter_has_had_time_to_converge_ = false;
|
bool filter_has_had_time_to_converge_ = false;
|
||||||
bool initial_state_ = true;
|
bool initial_state_ = true;
|
||||||
const float gain_rampup_increase_;
|
const float gain_rampup_increase_;
|
||||||
|
SuppressionGainUpperLimiter suppression_gain_limiter_;
|
||||||
|
|
||||||
RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
|
RTC_DISALLOW_COPY_AND_ASSIGN(AecState);
|
||||||
};
|
};
|
||||||
|
83
modules/audio_processing/aec3/suppression_gain_limiter.cc
Normal file
83
modules/audio_processing/aec3/suppression_gain_limiter.cc
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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_gain_limiter.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "modules/audio_processing/aec3/aec3_common.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Computes the gain rampup factor to use.
|
||||||
|
float ComputeGainRampupIncrease(
|
||||||
|
const EchoCanceller3Config::EchoRemovalControl::GainRampup& rampup_config) {
|
||||||
|
return powf(1.f / rampup_config.first_non_zero_gain,
|
||||||
|
1.f / rampup_config.non_zero_gain_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
SuppressionGainUpperLimiter::SuppressionGainUpperLimiter(
|
||||||
|
const EchoCanceller3Config& config)
|
||||||
|
: rampup_config_(config.echo_removal_control.gain_rampup),
|
||||||
|
gain_rampup_increase_(ComputeGainRampupIncrease(rampup_config_)) {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuppressionGainUpperLimiter::Reset() {
|
||||||
|
recent_reset_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SuppressionGainUpperLimiter::Update(bool render_activity) {
|
||||||
|
if (recent_reset_ && !call_startup_phase_) {
|
||||||
|
// Only enforce 250 ms full suppression after in-call resets,
|
||||||
|
constexpr int kMuteFramesAfterReset = kNumBlocksPerSecond / 4;
|
||||||
|
realignment_counter_ = kMuteFramesAfterReset;
|
||||||
|
} else if (!active_render_seen_ && render_activity) {
|
||||||
|
// Enforce a tailormade suppression limiting during call startup.
|
||||||
|
active_render_seen_ = true;
|
||||||
|
realignment_counter_ = rampup_config_.full_gain_blocks;
|
||||||
|
} else if (realignment_counter_ > 0) {
|
||||||
|
if (--realignment_counter_ == 0) {
|
||||||
|
call_startup_phase_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
recent_reset_ = false;
|
||||||
|
|
||||||
|
// Do not enforce any gain limit on the suppressor.
|
||||||
|
if (realignment_counter_ <= 0) {
|
||||||
|
suppressor_gain_limit_ = 1.f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce full suppression.
|
||||||
|
if (realignment_counter_ > rampup_config_.non_zero_gain_blocks ||
|
||||||
|
(!call_startup_phase_ && realignment_counter_ > 0)) {
|
||||||
|
suppressor_gain_limit_ = 0.f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start increasing the gain limit.
|
||||||
|
if (realignment_counter_ == rampup_config_.non_zero_gain_blocks) {
|
||||||
|
suppressor_gain_limit_ = rampup_config_.first_non_zero_gain;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increase the gain limit until it reaches 1.f.
|
||||||
|
RTC_DCHECK_LT(0.f, suppressor_gain_limit_);
|
||||||
|
suppressor_gain_limit_ =
|
||||||
|
std::min(1.f, suppressor_gain_limit_ * gain_rampup_increase_);
|
||||||
|
RTC_DCHECK_GE(1.f, suppressor_gain_limit_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
49
modules/audio_processing/aec3/suppression_gain_limiter.h
Normal file
49
modules/audio_processing/aec3/suppression_gain_limiter.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_LIMITER_H_
|
||||||
|
#define MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_LIMITER_H_
|
||||||
|
|
||||||
|
#include "api/array_view.h"
|
||||||
|
#include "api/audio/echo_canceller3_config.h"
|
||||||
|
#include "rtc_base/constructormagic.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Class for applying a smoothly increasing limit for the suppression gain
|
||||||
|
// during call startup and after in-call resets.
|
||||||
|
class SuppressionGainUpperLimiter {
|
||||||
|
public:
|
||||||
|
explicit SuppressionGainUpperLimiter(const EchoCanceller3Config& config);
|
||||||
|
|
||||||
|
// Reset the limiting behavior.
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
// Updates the limiting behavior for the current capture bloc.
|
||||||
|
void Update(bool render_activity);
|
||||||
|
|
||||||
|
// Returns the current suppressor gain limit.
|
||||||
|
float Limit() const { return suppressor_gain_limit_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const EchoCanceller3Config::EchoRemovalControl::GainRampup rampup_config_;
|
||||||
|
const float gain_rampup_increase_;
|
||||||
|
bool call_startup_phase_ = true;
|
||||||
|
int realignment_counter_ = 0;
|
||||||
|
bool active_render_seen_ = false;
|
||||||
|
float suppressor_gain_limit_ = 1.f;
|
||||||
|
bool recent_reset_ = false;
|
||||||
|
|
||||||
|
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(SuppressionGainUpperLimiter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_AUDIO_PROCESSING_AEC3_SUPPRESSION_GAIN_LIMITER_H_
|
Reference in New Issue
Block a user