AEC3: Removing the need of a buffer for the stationarity estimator of the render signal.
Change-Id: I6983e1d8bdd048a5d92209e3023c687f82d383d5 Bug: webrtc:9193,chromium:836790 Change-Id: I6983e1d8bdd048a5d92209e3023c687f82d383d5 Reviewed-on: https://webrtc-review.googlesource.com/72760 Reviewed-by: Per Åhgren <peah@webrtc.org> Commit-Queue: Jesus de Vicente Pena <devicentepena@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23065}
This commit is contained in:
parent
28899d0791
commit
9558192711
@ -10,51 +10,40 @@
|
||||
|
||||
#include "modules/audio_processing/aec3/echo_audibility.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "modules/audio_processing/aec3/aec3_common.h"
|
||||
#include "modules/audio_processing/aec3/stationarity_estimator.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
namespace {
|
||||
constexpr int kUnitializedIndex = -1;
|
||||
} // namespace
|
||||
|
||||
EchoAudibility::EchoAudibility() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void EchoAudibility::Reset() {
|
||||
render_stationarity_.Reset();
|
||||
render_write_prev_ = kUnitializedIndex;
|
||||
}
|
||||
|
||||
EchoAudibility::~EchoAudibility() = default;
|
||||
|
||||
void EchoAudibility::UpdateAfterStableDelay(const RenderBuffer& render_buffer,
|
||||
size_t delay_blocks,
|
||||
size_t capture_block_counter) {
|
||||
RTC_DCHECK_GT(capture_block_counter, delay_blocks);
|
||||
|
||||
size_t num_lookahead = std::min(StationarityEstimator::GetMaxNumLookAhead(),
|
||||
render_buffer.Headroom() - delay_blocks + 1);
|
||||
int render_block_number = capture_block_counter - delay_blocks;
|
||||
|
||||
for (size_t k = 0; k < (num_lookahead + 1); ++k) {
|
||||
// Delay changes can potentially make that not all the farend blocks
|
||||
// are seen. That effect is assumed to have a minimum effect in the
|
||||
// estimation.
|
||||
render_stationarity_.Update(render_buffer.Spectrum(delay_blocks - k),
|
||||
render_block_number + k);
|
||||
}
|
||||
render_stationarity_.UpdateStationarityFlags(render_block_number,
|
||||
num_lookahead);
|
||||
void EchoAudibility::UpdateRenderStationarityFlags(
|
||||
const RenderBuffer& render_buffer,
|
||||
size_t delay_blocks) {
|
||||
int idx_at_delay =
|
||||
render_buffer.OffsetSpectrumIndex(render_buffer.Position(), delay_blocks);
|
||||
size_t num_lookahead = render_buffer.Headroom() - delay_blocks + 1;
|
||||
render_stationarity_.UpdateStationarityFlags(
|
||||
render_buffer.GetSpectrumBuffer(), idx_at_delay, num_lookahead);
|
||||
}
|
||||
|
||||
void EchoAudibility::UpdateBeforeStableDelay(
|
||||
void EchoAudibility::UpdateRenderNoiseEstimator(
|
||||
const RenderBuffer& render_buffer) {
|
||||
// If the delay is not set, the read position in the buffer cannot be trust
|
||||
// and the write position in the render buffer should be used instead
|
||||
|
||||
if (first_update_) {
|
||||
if (render_write_prev_ == kUnitializedIndex) {
|
||||
render_write_prev_ = render_buffer.GetWritePositionSpectrum();
|
||||
first_update_ = false;
|
||||
return;
|
||||
}
|
||||
int render_write_current = render_buffer.GetWritePositionSpectrum();
|
||||
@ -62,7 +51,7 @@ void EchoAudibility::UpdateBeforeStableDelay(
|
||||
for (int idx = render_write_prev_; idx != render_write_current;
|
||||
idx = render_buffer.DecIdx(idx)) {
|
||||
render_stationarity_.UpdateNoiseEstimator(
|
||||
render_buffer.SpectrumFromPosition(idx));
|
||||
render_buffer.SpectrumAtIndex(idx));
|
||||
}
|
||||
|
||||
render_write_prev_ = render_write_current;
|
||||
@ -72,10 +61,10 @@ void EchoAudibility::Update(const RenderBuffer& render_buffer,
|
||||
size_t delay_blocks,
|
||||
size_t capture_block_counter,
|
||||
bool external_delay_seen) {
|
||||
UpdateRenderNoiseEstimator(render_buffer);
|
||||
|
||||
if (external_delay_seen) {
|
||||
UpdateAfterStableDelay(render_buffer, delay_blocks, capture_block_counter);
|
||||
} else {
|
||||
UpdateBeforeStableDelay(render_buffer);
|
||||
UpdateRenderStationarityFlags(render_buffer, delay_blocks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -55,13 +55,14 @@ class EchoAudibility {
|
||||
// Compute the residual scaling per frequency for the current frame.
|
||||
void ComputeResidualScaling();
|
||||
|
||||
void UpdateAfterStableDelay(const RenderBuffer& render_buffer,
|
||||
size_t delay_blocks,
|
||||
size_t capture_block_counter);
|
||||
// Updates the render stationarity flags for the current frame.
|
||||
void UpdateRenderStationarityFlags(const RenderBuffer& render_buffer,
|
||||
size_t delay_blocks);
|
||||
|
||||
void UpdateBeforeStableDelay(const RenderBuffer& render_buffer);
|
||||
// Updates the noise estimator with the new render data since the previous
|
||||
// call to this method.
|
||||
void UpdateRenderNoiseEstimator(const RenderBuffer& render_buffer);
|
||||
|
||||
bool first_update_ = true;
|
||||
int render_write_prev_;
|
||||
StationarityEstimator render_stationarity_;
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(EchoAudibility);
|
||||
|
||||
@ -45,10 +45,10 @@ class RenderBuffer {
|
||||
return spectrum_buffer_->buffer[position];
|
||||
}
|
||||
|
||||
// Get the spectrum directly from a position in the buffer.
|
||||
rtc::ArrayView<const float> SpectrumFromPosition(int position) const {
|
||||
RTC_CHECK_LT(position, spectrum_buffer_->size);
|
||||
int position_bound = std::min(position, spectrum_buffer_->size - 1);
|
||||
// Get the spectrum directly from an index in the buffer.
|
||||
rtc::ArrayView<const float> SpectrumAtIndex(int index) const {
|
||||
RTC_CHECK_LT(index, spectrum_buffer_->size);
|
||||
int position_bound = std::min(index, spectrum_buffer_->size - 1);
|
||||
position_bound = std::max(0, position_bound);
|
||||
return spectrum_buffer_->buffer[position_bound];
|
||||
}
|
||||
@ -65,6 +65,11 @@ class RenderBuffer {
|
||||
return fft_buffer_->read;
|
||||
}
|
||||
|
||||
// Applies an offset to a buffer index and returns it.
|
||||
int OffsetSpectrumIndex(int index, int offset) const {
|
||||
return spectrum_buffer_->OffsetIndex(index, offset);
|
||||
}
|
||||
|
||||
// Returns the write postion in the circular buffer.
|
||||
int GetWritePositionSpectrum() const { return spectrum_buffer_->write; }
|
||||
|
||||
@ -79,7 +84,7 @@ class RenderBuffer {
|
||||
void SetRenderActivity(bool activity) { render_activity_ = activity; }
|
||||
|
||||
// Returns the headroom between the write and the read positions in the
|
||||
// buffer;
|
||||
// buffer.
|
||||
int Headroom() const {
|
||||
// The write and read indices are decreased over time.
|
||||
int headroom =
|
||||
@ -93,9 +98,12 @@ class RenderBuffer {
|
||||
return headroom;
|
||||
}
|
||||
|
||||
// Decrease an index that is used for accessing the buffer.
|
||||
// Decreases an index that is used for accessing the buffer.
|
||||
int DecIdx(int idx) const { return spectrum_buffer_->DecIndex(idx); }
|
||||
|
||||
// Returns a reference to the spectrum buffer.
|
||||
const VectorBuffer& GetSpectrumBuffer() const { return *spectrum_buffer_; }
|
||||
|
||||
private:
|
||||
const MatrixBuffer* const block_buffer_;
|
||||
const VectorBuffer* const spectrum_buffer_;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "modules/audio_processing/aec3/aec3_common.h"
|
||||
#include "modules/audio_processing/aec3/vector_buffer.h"
|
||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||
#include "rtc_base/atomicops.h"
|
||||
|
||||
@ -25,17 +26,11 @@ constexpr float kMinNoisePower = 10.f;
|
||||
constexpr int kHangoverBlocks = kNumBlocksPerSecond / 20;
|
||||
constexpr int kNBlocksAverageInitPhase = 20;
|
||||
constexpr int kNBlocksInitialPhase = kNumBlocksPerSecond * 2.;
|
||||
constexpr size_t kLongWindowSize = 13;
|
||||
} // namespace
|
||||
|
||||
StationarityEstimator::StationarityEstimator()
|
||||
: data_dumper_(
|
||||
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))),
|
||||
idx_lookahead_(kLongWindowSize, 0),
|
||||
idx_lookback_(kLongWindowSize, 0) {
|
||||
static_assert(StationarityEstimator::CircularBuffer::GetBufferSize() >=
|
||||
(kLongWindowSize + 1),
|
||||
"Mismatch between the window size and the buffer size.");
|
||||
new ApmDataDumper(rtc::AtomicOps::Increment(&instance_count_))) {
|
||||
Reset();
|
||||
}
|
||||
|
||||
@ -47,14 +42,6 @@ void StationarityEstimator::Reset() {
|
||||
stationarity_flags_.fill(false);
|
||||
}
|
||||
|
||||
void StationarityEstimator::Update(rtc::ArrayView<const float> spectrum,
|
||||
int block_number) {
|
||||
if (!buffer_.IsBlockNumberAlreadyUpdated(block_number)) {
|
||||
noise_.Update(spectrum);
|
||||
WriteInfoFrameInSlot(block_number, spectrum);
|
||||
}
|
||||
}
|
||||
|
||||
// Update just the noise estimator. Usefull until the delay is known
|
||||
void StationarityEstimator::UpdateNoiseEstimator(
|
||||
rtc::ArrayView<const float> spectrum) {
|
||||
@ -62,48 +49,54 @@ void StationarityEstimator::UpdateNoiseEstimator(
|
||||
data_dumper_->DumpRaw("aec3_stationarity_noise_spectrum", noise_.Spectrum());
|
||||
}
|
||||
|
||||
void StationarityEstimator::UpdateStationarityFlags(size_t current_block_number,
|
||||
size_t num_lookahead) {
|
||||
RTC_DCHECK_GE(idx_lookahead_.capacity(),
|
||||
std::min(num_lookahead + 1, kLongWindowSize));
|
||||
idx_lookahead_.resize(std::min(num_lookahead + 1, kLongWindowSize));
|
||||
idx_lookback_.resize(0);
|
||||
GetSlotsAheadBack(current_block_number);
|
||||
void StationarityEstimator::UpdateStationarityFlags(
|
||||
const VectorBuffer& spectrum_buffer,
|
||||
int idx_current,
|
||||
int num_lookahead) {
|
||||
std::array<int, kWindowLength> indexes;
|
||||
int num_lookahead_bounded = std::min(num_lookahead, kWindowLength - 1);
|
||||
int idx = idx_current;
|
||||
|
||||
if (num_lookahead_bounded < kWindowLength - 1) {
|
||||
int num_lookback = (kWindowLength - 1) - num_lookahead_bounded;
|
||||
idx = spectrum_buffer.OffsetIndex(idx_current, num_lookback);
|
||||
}
|
||||
// For estimating the stationarity properties of the current frame, the
|
||||
// power for each band is accumulated for several consecutive spectra in the
|
||||
// method EstimateBandStationarity.
|
||||
// In order to avoid getting the indexes of the spectra for every band with
|
||||
// its associated overhead, those indexes are stored in an array and then use
|
||||
// when the estimation is done.
|
||||
indexes[0] = idx;
|
||||
for (size_t k = 1; k < indexes.size(); ++k) {
|
||||
indexes[k] = spectrum_buffer.DecIndex(indexes[k - 1]);
|
||||
}
|
||||
RTC_DCHECK_EQ(
|
||||
spectrum_buffer.DecIndex(indexes[kWindowLength - 1]),
|
||||
spectrum_buffer.OffsetIndex(idx_current, -(num_lookahead_bounded + 1)));
|
||||
|
||||
for (size_t k = 0; k < stationarity_flags_.size(); ++k) {
|
||||
stationarity_flags_[k] = EstimateBandStationarity(k);
|
||||
stationarity_flags_[k] =
|
||||
EstimateBandStationarity(spectrum_buffer, indexes, k);
|
||||
}
|
||||
UpdateHangover();
|
||||
SmoothStationaryPerFreq();
|
||||
|
||||
data_dumper_->DumpRaw("aec3_stationarity_noise_spectrum", noise_.Spectrum());
|
||||
}
|
||||
|
||||
void StationarityEstimator::WriteInfoFrameInSlot(
|
||||
int block_number,
|
||||
rtc::ArrayView<const float> spectrum) {
|
||||
size_t slot = buffer_.SetBlockNumberInSlot(block_number);
|
||||
for (size_t k = 0; k < spectrum.size(); ++k) {
|
||||
buffer_.SetElementProperties(spectrum[k], slot, k);
|
||||
}
|
||||
}
|
||||
|
||||
bool StationarityEstimator::EstimateBandStationarity(size_t band) const {
|
||||
bool StationarityEstimator::EstimateBandStationarity(
|
||||
const VectorBuffer& spectrum_buffer,
|
||||
const std::array<int, kWindowLength>& indexes,
|
||||
size_t band) const {
|
||||
constexpr float kThrStationarity = 10.f;
|
||||
float acumPower = 0.f;
|
||||
for (auto slot : idx_lookahead_) {
|
||||
acumPower += buffer_.GetPowerBand(slot, band);
|
||||
float acum_power = 0.f;
|
||||
for (auto idx : indexes) {
|
||||
acum_power += spectrum_buffer.buffer[idx][band];
|
||||
}
|
||||
for (auto slot : idx_lookback_) {
|
||||
acumPower += buffer_.GetPowerBand(slot, band);
|
||||
}
|
||||
|
||||
// Generally windowSize is equal to kLongWindowSize
|
||||
float windowSize = idx_lookahead_.size() + idx_lookback_.size();
|
||||
float noise = windowSize * GetStationarityPowerBand(band);
|
||||
float noise = kWindowLength * GetStationarityPowerBand(band);
|
||||
RTC_CHECK_LT(0.f, noise);
|
||||
bool stationary = acumPower < kThrStationarity * noise;
|
||||
data_dumper_->DumpRaw("aec3_stationarity_long_ratio", acumPower / noise);
|
||||
bool stationary = acum_power < kThrStationarity * noise;
|
||||
data_dumper_->DumpRaw("aec3_stationarity_long_ratio", acum_power / noise);
|
||||
return stationary;
|
||||
}
|
||||
|
||||
@ -126,32 +119,6 @@ void StationarityEstimator::UpdateHangover() {
|
||||
}
|
||||
}
|
||||
|
||||
void StationarityEstimator::GetSlotsAheadBack(size_t current_block_number) {
|
||||
for (size_t block = 0; block < idx_lookahead_.size(); ++block) {
|
||||
idx_lookahead_[block] = buffer_.GetSlotNumber(current_block_number + block);
|
||||
}
|
||||
size_t num_lookback_blocks;
|
||||
if (idx_lookahead_.size() >= kLongWindowSize) {
|
||||
RTC_CHECK_EQ(idx_lookahead_.size(), kLongWindowSize);
|
||||
num_lookback_blocks = 0;
|
||||
} else {
|
||||
num_lookback_blocks = kLongWindowSize - idx_lookahead_.size();
|
||||
}
|
||||
if (current_block_number < num_lookback_blocks) {
|
||||
idx_lookback_.resize(0);
|
||||
} else {
|
||||
for (size_t block = 0; block < num_lookback_blocks; ++block) {
|
||||
int block_number = current_block_number - block - 1;
|
||||
if (!buffer_.IsBlockNumberAlreadyUpdated(block_number)) {
|
||||
break;
|
||||
} else {
|
||||
RTC_DCHECK_GE(idx_lookback_.capacity(), idx_lookback_.size() + 1);
|
||||
idx_lookback_.push_back(buffer_.GetSlotNumber(block_number));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StationarityEstimator::SmoothStationaryPerFreq() {
|
||||
std::array<bool, kFftLengthBy2Plus1> all_ahead_stationary_smooth;
|
||||
for (size_t k = 1; k < kFftLengthBy2Plus1 - 1; ++k) {
|
||||
@ -230,10 +197,4 @@ float StationarityEstimator::NoiseSpectrum::UpdateBandBySmoothing(
|
||||
return power_band_noise_updated;
|
||||
}
|
||||
|
||||
StationarityEstimator::CircularBuffer::CircularBuffer() {
|
||||
for (auto slot : slots_) {
|
||||
slot.block_number_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
#include "api/array_view.h"
|
||||
#include "modules/audio_processing/aec3/aec3_common.h"
|
||||
#include "modules/audio_processing/aec3/vector_buffer.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
@ -30,38 +31,30 @@ class StationarityEstimator {
|
||||
// Reset the stationarity estimator.
|
||||
void Reset();
|
||||
|
||||
// Update the stationarity estimator.
|
||||
void Update(rtc::ArrayView<const float> spectrum, int block_number);
|
||||
|
||||
// Update just the noise estimator. Usefull until the delay is known
|
||||
void UpdateNoiseEstimator(rtc::ArrayView<const float> spectrum);
|
||||
|
||||
// Update the flag indicating whether this current frame is stationary. For
|
||||
// getting a more robust estimation, it looks at future and/or past frames.
|
||||
void UpdateStationarityFlags(size_t current_block_number,
|
||||
size_t num_lookahead);
|
||||
void UpdateStationarityFlags(const VectorBuffer& spectrum_buffer,
|
||||
int idx_current,
|
||||
int num_lookahead);
|
||||
|
||||
// Returns true if the current band is stationary.
|
||||
bool IsBandStationary(size_t band) const {
|
||||
return stationarity_flags_[band] && (hangovers_[band] == 0);
|
||||
}
|
||||
|
||||
static constexpr size_t GetMaxNumLookAhead() {
|
||||
return CircularBuffer::GetBufferSize() - 2;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kWindowLength = 13;
|
||||
// Returns the power of the stationary noise spectrum at a band.
|
||||
float GetStationarityPowerBand(size_t k) const { return noise_.Power(k); }
|
||||
|
||||
// Write into the slot the information about the current frame that
|
||||
// is needed for the stationarity detection.
|
||||
void WriteInfoFrameInSlot(int block_number,
|
||||
rtc::ArrayView<const float> spectrum);
|
||||
|
||||
// Get an estimation of the stationarity for the current band by looking
|
||||
// at the past/present/future available data.
|
||||
bool EstimateBandStationarity(size_t band) const;
|
||||
bool EstimateBandStationarity(const VectorBuffer& spectrum_buffer,
|
||||
const std::array<int, kWindowLength>& indexes,
|
||||
size_t band) const;
|
||||
|
||||
// True if all bands at the current point are stationary.
|
||||
bool AreAllBandsStationary();
|
||||
@ -70,9 +63,6 @@ class StationarityEstimator {
|
||||
// frame.
|
||||
void UpdateHangover();
|
||||
|
||||
// Get the slots that contain past/present and future data.
|
||||
void GetSlotsAheadBack(size_t current_block_number);
|
||||
|
||||
// Smooth the stationarity detection by looking at neighbouring frequency
|
||||
// bands.
|
||||
void SmoothStationaryPerFreq();
|
||||
@ -109,52 +99,11 @@ class StationarityEstimator {
|
||||
size_t block_counter_;
|
||||
};
|
||||
|
||||
// The class circular buffer stores the data needed to take a decission
|
||||
// on whether the current frame is stationary by looking at data from the
|
||||
// future, present and/or past. This buffer stores that data that is
|
||||
// represented by the struct Element a bit bellow.
|
||||
class CircularBuffer {
|
||||
public:
|
||||
static constexpr int kCircularBufferSize = 16;
|
||||
struct Element {
|
||||
int block_number_;
|
||||
std::array<float, kFftLengthBy2Plus1> power_spectrum_;
|
||||
};
|
||||
CircularBuffer();
|
||||
|
||||
static constexpr int GetBufferSize() { return kCircularBufferSize; }
|
||||
|
||||
bool IsBlockNumberAlreadyUpdated(int block_number) const {
|
||||
size_t slot_number = GetSlotNumber(block_number);
|
||||
return slots_[slot_number].block_number_ == block_number;
|
||||
}
|
||||
|
||||
size_t GetSlotNumber(int block_number) const {
|
||||
return block_number & (kCircularBufferSize - 1);
|
||||
}
|
||||
size_t SetBlockNumberInSlot(int block_number) {
|
||||
size_t slot = GetSlotNumber(block_number);
|
||||
slots_[slot].block_number_ = block_number;
|
||||
return slot;
|
||||
}
|
||||
void SetElementProperties(float band_power, int slot, int band) {
|
||||
slots_[slot].power_spectrum_[band] = band_power;
|
||||
}
|
||||
float GetPowerBand(size_t slot, size_t band) const {
|
||||
return slots_[slot].power_spectrum_[band];
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Element, kCircularBufferSize> slots_;
|
||||
};
|
||||
static int instance_count_;
|
||||
std::unique_ptr<ApmDataDumper> data_dumper_;
|
||||
NoiseSpectrum noise_;
|
||||
std::vector<size_t> idx_lookahead_;
|
||||
std::vector<size_t> idx_lookback_;
|
||||
std::array<int, kFftLengthBy2Plus1> hangovers_;
|
||||
std::array<bool, kFftLengthBy2Plus1> stationarity_flags_;
|
||||
CircularBuffer buffer_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user