Robustify the faster alignment in AEC3 to avoid resets
The faster AEC3 alignment introduced recently may in cases cause the alignment (and the AEC3) to repeatedly reset. This CL avoids these resets by handling buffer issues (which are triggering the resets) separately during the initial coarse alignment phase. Change-Id: Idf5e2ffda2591906da8060d03ec8ca73cdaedf53 Bug: webrtc:8798,chromium:805815 Reviewed-on: https://webrtc-review.googlesource.com/43480 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#21758}
This commit is contained in:
parent
b9b07eaf28
commit
a76ef9d0b4
@ -55,6 +55,7 @@ rtc_static_library("audio_processing") {
|
|||||||
"aec3/comfort_noise_generator.h",
|
"aec3/comfort_noise_generator.h",
|
||||||
"aec3/decimator.cc",
|
"aec3/decimator.cc",
|
||||||
"aec3/decimator.h",
|
"aec3/decimator.h",
|
||||||
|
"aec3/delay_estimate.h",
|
||||||
"aec3/downsampled_render_buffer.cc",
|
"aec3/downsampled_render_buffer.cc",
|
||||||
"aec3/downsampled_render_buffer.h",
|
"aec3/downsampled_render_buffer.h",
|
||||||
"aec3/echo_canceller3.cc",
|
"aec3/echo_canceller3.cc",
|
||||||
|
|||||||
@ -56,6 +56,7 @@ class BlockProcessorImpl final : public BlockProcessor {
|
|||||||
BlockProcessorMetrics metrics_;
|
BlockProcessorMetrics metrics_;
|
||||||
RenderDelayBuffer::BufferingEvent render_event_;
|
RenderDelayBuffer::BufferingEvent render_event_;
|
||||||
size_t capture_call_counter_ = 0;
|
size_t capture_call_counter_ = 0;
|
||||||
|
rtc::Optional<DelayEstimate> estimated_delay_;
|
||||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
|
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(BlockProcessorImpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,14 +127,17 @@ void BlockProcessorImpl::ProcessCapture(
|
|||||||
RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun !=
|
RTC_DCHECK(RenderDelayBuffer::BufferingEvent::kRenderOverrun !=
|
||||||
render_event_);
|
render_event_);
|
||||||
if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) {
|
if (render_event_ == RenderDelayBuffer::BufferingEvent::kRenderUnderrun) {
|
||||||
echo_path_variability.delay_change =
|
if (estimated_delay_ &&
|
||||||
EchoPathVariability::DelayAdjustment::kDelayReset;
|
estimated_delay_->quality == DelayEstimate::Quality::kRefined) {
|
||||||
delay_controller_->Reset();
|
echo_path_variability.delay_change =
|
||||||
capture_properly_started_ = false;
|
EchoPathVariability::DelayAdjustment::kDelayReset;
|
||||||
render_properly_started_ = false;
|
delay_controller_->Reset();
|
||||||
|
capture_properly_started_ = false;
|
||||||
|
render_properly_started_ = false;
|
||||||
|
|
||||||
RTC_LOG(LS_WARNING) << "Reset due to render buffer underrrun at block "
|
RTC_LOG(LS_WARNING) << "Reset due to render buffer underrrun at block "
|
||||||
<< capture_call_counter_;
|
<< capture_call_counter_;
|
||||||
|
}
|
||||||
} else if (render_event_ == RenderDelayBuffer::BufferingEvent::kApiCallSkew) {
|
} else if (render_event_ == RenderDelayBuffer::BufferingEvent::kApiCallSkew) {
|
||||||
// There have been too many render calls in a row. Reset to avoid noncausal
|
// There have been too many render calls in a row. Reset to avoid noncausal
|
||||||
// echo.
|
// echo.
|
||||||
@ -152,22 +156,23 @@ void BlockProcessorImpl::ProcessCapture(
|
|||||||
|
|
||||||
// Compute and and apply the render delay required to achieve proper signal
|
// Compute and and apply the render delay required to achieve proper signal
|
||||||
// alignment.
|
// alignment.
|
||||||
rtc::Optional<size_t> estimated_delay = delay_controller_->GetDelay(
|
estimated_delay_ = delay_controller_->GetDelay(
|
||||||
render_buffer_->GetDownsampledRenderBuffer(), (*capture_block)[0]);
|
render_buffer_->GetDownsampledRenderBuffer(), (*capture_block)[0]);
|
||||||
|
|
||||||
if (estimated_delay) {
|
if (estimated_delay_) {
|
||||||
bool delay_change = render_buffer_->SetDelay(*estimated_delay);
|
if (render_buffer_->CausalDelay(estimated_delay_->delay)) {
|
||||||
|
bool delay_change = render_buffer_->SetDelay(estimated_delay_->delay);
|
||||||
if (delay_change) {
|
if (delay_change) {
|
||||||
RTC_LOG(LS_WARNING) << "Delay changed to " << *estimated_delay
|
RTC_LOG(LS_WARNING) << "Delay changed to " << estimated_delay_->delay
|
||||||
<< " at block " << capture_call_counter_;
|
<< " at block " << capture_call_counter_;
|
||||||
if (render_buffer_->CausalDelay()) {
|
|
||||||
echo_path_variability.delay_change =
|
echo_path_variability.delay_change =
|
||||||
EchoPathVariability::DelayAdjustment::kNewDetectedDelay;
|
EchoPathVariability::DelayAdjustment::kNewDetectedDelay;
|
||||||
} else {
|
}
|
||||||
// A noncausal delay has been detected. This can only happen if there is
|
} else {
|
||||||
// clockdrift, an audio pipeline issue has occurred or the specified
|
// A noncausal delay has been detected. This can only happen if there is
|
||||||
// minimum delay is too short. Perform a full reset.
|
// clockdrift, an audio pipeline issue has occurred, an unreliable delay
|
||||||
|
// estimate is used or the specified minimum delay is too short.
|
||||||
|
if (estimated_delay_->quality == DelayEstimate::Quality::kRefined) {
|
||||||
echo_path_variability.delay_change =
|
echo_path_variability.delay_change =
|
||||||
EchoPathVariability::DelayAdjustment::kDelayReset;
|
EchoPathVariability::DelayAdjustment::kDelayReset;
|
||||||
delay_controller_->Reset();
|
delay_controller_->Reset();
|
||||||
|
|||||||
@ -165,8 +165,7 @@ TEST(BlockProcessor, DISABLED_SubmoduleIntegration) {
|
|||||||
.Times(kNumBlocks)
|
.Times(kNumBlocks)
|
||||||
.WillRepeatedly(Return(0));
|
.WillRepeatedly(Return(0));
|
||||||
EXPECT_CALL(*render_delay_controller_mock, GetDelay(_, _))
|
EXPECT_CALL(*render_delay_controller_mock, GetDelay(_, _))
|
||||||
.Times(kNumBlocks)
|
.Times(kNumBlocks);
|
||||||
.WillRepeatedly(Return(9));
|
|
||||||
EXPECT_CALL(*echo_remover_mock, ProcessCapture(_, _, _, _))
|
EXPECT_CALL(*echo_remover_mock, ProcessCapture(_, _, _, _))
|
||||||
.Times(kNumBlocks);
|
.Times(kNumBlocks);
|
||||||
EXPECT_CALL(*echo_remover_mock, UpdateEchoLeakageStatus(_))
|
EXPECT_CALL(*echo_remover_mock, UpdateEchoLeakageStatus(_))
|
||||||
|
|||||||
29
modules/audio_processing/aec3/delay_estimate.h
Normal file
29
modules/audio_processing/aec3/delay_estimate.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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_DELAY_ESTIMATE_H_
|
||||||
|
#define MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
// Stores delay_estimates.
|
||||||
|
struct DelayEstimate {
|
||||||
|
enum class Quality { kCoarse, kRefined };
|
||||||
|
|
||||||
|
DelayEstimate(Quality quality, size_t delay)
|
||||||
|
: quality(quality), delay(delay) {}
|
||||||
|
|
||||||
|
Quality quality;
|
||||||
|
size_t delay;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
|
|
||||||
|
#endif // MODULES_AUDIO_PROCESSING_AEC3_DELAY_ESTIMATE_H_
|
||||||
@ -48,7 +48,7 @@ void EchoPathDelayEstimator::Reset() {
|
|||||||
matched_filter_.Reset();
|
matched_filter_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc::Optional<size_t> EchoPathDelayEstimator::EstimateDelay(
|
rtc::Optional<DelayEstimate> EchoPathDelayEstimator::EstimateDelay(
|
||||||
const DownsampledRenderBuffer& render_buffer,
|
const DownsampledRenderBuffer& render_buffer,
|
||||||
rtc::ArrayView<const float> capture) {
|
rtc::ArrayView<const float> capture) {
|
||||||
RTC_DCHECK_EQ(kBlockSize, capture.size());
|
RTC_DCHECK_EQ(kBlockSize, capture.size());
|
||||||
@ -64,24 +64,25 @@ rtc::Optional<size_t> EchoPathDelayEstimator::EstimateDelay(
|
|||||||
16000 / down_sampling_factor_, 1);
|
16000 / down_sampling_factor_, 1);
|
||||||
matched_filter_.Update(render_buffer, downsampled_capture);
|
matched_filter_.Update(render_buffer, downsampled_capture);
|
||||||
|
|
||||||
rtc::Optional<size_t> aggregated_matched_filter_lag =
|
rtc::Optional<DelayEstimate> aggregated_matched_filter_lag =
|
||||||
matched_filter_lag_aggregator_.Aggregate(
|
matched_filter_lag_aggregator_.Aggregate(
|
||||||
matched_filter_.GetLagEstimates());
|
matched_filter_.GetLagEstimates());
|
||||||
|
|
||||||
// TODO(peah): Move this logging outside of this class once EchoCanceller3
|
// TODO(peah): Move this logging outside of this class once EchoCanceller3
|
||||||
// development is done.
|
// development is done.
|
||||||
data_dumper_->DumpRaw("aec3_echo_path_delay_estimator_delay",
|
data_dumper_->DumpRaw(
|
||||||
aggregated_matched_filter_lag
|
"aec3_echo_path_delay_estimator_delay",
|
||||||
? static_cast<int>(*aggregated_matched_filter_lag *
|
aggregated_matched_filter_lag
|
||||||
down_sampling_factor_)
|
? static_cast<int>(aggregated_matched_filter_lag->delay *
|
||||||
: -1);
|
down_sampling_factor_)
|
||||||
|
: -1);
|
||||||
|
|
||||||
// Return the detected delay in samples as the aggregated matched filter lag
|
// Return the detected delay in samples as the aggregated matched filter lag
|
||||||
// compensated by the down sampling factor for the signal being correlated.
|
// compensated by the down sampling factor for the signal being correlated.
|
||||||
return aggregated_matched_filter_lag
|
if (aggregated_matched_filter_lag) {
|
||||||
? rtc::Optional<size_t>(*aggregated_matched_filter_lag *
|
aggregated_matched_filter_lag->delay *= down_sampling_factor_;
|
||||||
down_sampling_factor_)
|
}
|
||||||
: rtc::nullopt;
|
return aggregated_matched_filter_lag;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "api/optional.h"
|
#include "api/optional.h"
|
||||||
#include "modules/audio_processing/aec3/decimator.h"
|
#include "modules/audio_processing/aec3/decimator.h"
|
||||||
|
#include "modules/audio_processing/aec3/delay_estimate.h"
|
||||||
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
|
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
|
||||||
#include "modules/audio_processing/aec3/matched_filter.h"
|
#include "modules/audio_processing/aec3/matched_filter.h"
|
||||||
#include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h"
|
#include "modules/audio_processing/aec3/matched_filter_lag_aggregator.h"
|
||||||
@ -36,7 +37,7 @@ class EchoPathDelayEstimator {
|
|||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
// Produce a delay estimate if such is avaliable.
|
// Produce a delay estimate if such is avaliable.
|
||||||
rtc::Optional<size_t> EstimateDelay(
|
rtc::Optional<DelayEstimate> EstimateDelay(
|
||||||
const DownsampledRenderBuffer& render_buffer,
|
const DownsampledRenderBuffer& render_buffer,
|
||||||
rtc::ArrayView<const float> capture);
|
rtc::ArrayView<const float> capture);
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
|
|||||||
delay_samples + 2 * config.delay.api_call_jitter_blocks * 64);
|
delay_samples + 2 * config.delay.api_call_jitter_blocks * 64);
|
||||||
EchoPathDelayEstimator estimator(&data_dumper, config);
|
EchoPathDelayEstimator estimator(&data_dumper, config);
|
||||||
|
|
||||||
rtc::Optional<size_t> estimated_delay_samples;
|
rtc::Optional<DelayEstimate> estimated_delay_samples;
|
||||||
for (size_t k = 0; k < (500 + (delay_samples) / kBlockSize); ++k) {
|
for (size_t k = 0; k < (500 + (delay_samples) / kBlockSize); ++k) {
|
||||||
RandomizeSampleVector(&random_generator, render[0]);
|
RandomizeSampleVector(&random_generator, render[0]);
|
||||||
signal_delay_buffer.Delay(render[0], capture);
|
signal_delay_buffer.Delay(render[0], capture);
|
||||||
@ -92,7 +92,7 @@ TEST(EchoPathDelayEstimator, DelayEstimation) {
|
|||||||
// Due to the internal down-sampling done inside the delay estimator
|
// Due to the internal down-sampling done inside the delay estimator
|
||||||
// the estimated delay cannot be expected to be exact to the true delay.
|
// the estimated delay cannot be expected to be exact to the true delay.
|
||||||
EXPECT_NEAR(delay_samples,
|
EXPECT_NEAR(delay_samples,
|
||||||
*estimated_delay_samples -
|
estimated_delay_samples->delay -
|
||||||
(config.delay.api_call_jitter_blocks + 1) * 64,
|
(config.delay.api_call_jitter_blocks + 1) * 64,
|
||||||
config.delay.down_sampling_factor);
|
config.delay.down_sampling_factor);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -30,7 +30,7 @@ void MatchedFilterLagAggregator::Reset() {
|
|||||||
significant_candidate_found_ = false;
|
significant_candidate_found_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc::Optional<size_t> MatchedFilterLagAggregator::Aggregate(
|
rtc::Optional<DelayEstimate> MatchedFilterLagAggregator::Aggregate(
|
||||||
rtc::ArrayView<const MatchedFilter::LagEstimate> lag_estimates) {
|
rtc::ArrayView<const MatchedFilter::LagEstimate> lag_estimates) {
|
||||||
// Choose the strongest lag estimate as the best one.
|
// Choose the strongest lag estimate as the best one.
|
||||||
float best_accuracy = 0.f;
|
float best_accuracy = 0.f;
|
||||||
@ -69,9 +69,9 @@ rtc::Optional<size_t> MatchedFilterLagAggregator::Aggregate(
|
|||||||
|
|
||||||
if (histogram_[candidate] > 25) {
|
if (histogram_[candidate] > 25) {
|
||||||
significant_candidate_found_ = true;
|
significant_candidate_found_ = true;
|
||||||
return candidate;
|
return DelayEstimate(DelayEstimate::Quality::kRefined, candidate);
|
||||||
} else if (!significant_candidate_found_) {
|
} else if (!significant_candidate_found_) {
|
||||||
return candidate;
|
return DelayEstimate(DelayEstimate::Quality::kCoarse, candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return rtc::nullopt;
|
return rtc::nullopt;
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api/optional.h"
|
#include "api/optional.h"
|
||||||
|
#include "modules/audio_processing/aec3/delay_estimate.h"
|
||||||
#include "modules/audio_processing/aec3/matched_filter.h"
|
#include "modules/audio_processing/aec3/matched_filter.h"
|
||||||
#include "rtc_base/constructormagic.h"
|
#include "rtc_base/constructormagic.h"
|
||||||
|
|
||||||
@ -32,7 +33,7 @@ class MatchedFilterLagAggregator {
|
|||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
// Aggregates the provided lag estimates.
|
// Aggregates the provided lag estimates.
|
||||||
rtc::Optional<size_t> Aggregate(
|
rtc::Optional<DelayEstimate> Aggregate(
|
||||||
rtc::ArrayView<const MatchedFilter::LagEstimate> lag_estimates);
|
rtc::ArrayView<const MatchedFilter::LagEstimate> lag_estimates);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -40,9 +40,10 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) {
|
|||||||
EXPECT_TRUE(aggregator.Aggregate(lag_estimates));
|
EXPECT_TRUE(aggregator.Aggregate(lag_estimates));
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc::Optional<size_t> aggregated_lag = aggregator.Aggregate(lag_estimates);
|
rtc::Optional<DelayEstimate> aggregated_lag =
|
||||||
|
aggregator.Aggregate(lag_estimates);
|
||||||
EXPECT_TRUE(aggregated_lag);
|
EXPECT_TRUE(aggregated_lag);
|
||||||
EXPECT_EQ(kLag1, *aggregated_lag);
|
EXPECT_EQ(kLag1, aggregated_lag->delay);
|
||||||
|
|
||||||
lag_estimates[0] = MatchedFilter::LagEstimate(0.5f, true, kLag1, true);
|
lag_estimates[0] = MatchedFilter::LagEstimate(0.5f, true, kLag1, true);
|
||||||
lag_estimates[1] = MatchedFilter::LagEstimate(1.f, true, kLag2, true);
|
lag_estimates[1] = MatchedFilter::LagEstimate(1.f, true, kLag2, true);
|
||||||
@ -50,13 +51,13 @@ TEST(MatchedFilterLagAggregator, MostAccurateLagChosen) {
|
|||||||
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
|
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
|
||||||
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
||||||
EXPECT_TRUE(aggregated_lag);
|
EXPECT_TRUE(aggregated_lag);
|
||||||
EXPECT_EQ(kLag1, *aggregated_lag);
|
EXPECT_EQ(kLag1, aggregated_lag->delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
||||||
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
||||||
EXPECT_TRUE(aggregated_lag);
|
EXPECT_TRUE(aggregated_lag);
|
||||||
EXPECT_EQ(kLag2, *aggregated_lag);
|
EXPECT_EQ(kLag2, aggregated_lag->delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifies that varying lag estimates causes lag estimates to not be deemed
|
// Verifies that varying lag estimates causes lag estimates to not be deemed
|
||||||
@ -67,7 +68,7 @@ TEST(MatchedFilterLagAggregator,
|
|||||||
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
|
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
|
||||||
MatchedFilterLagAggregator aggregator(&data_dumper, 100);
|
MatchedFilterLagAggregator aggregator(&data_dumper, 100);
|
||||||
|
|
||||||
rtc::Optional<size_t> aggregated_lag;
|
rtc::Optional<DelayEstimate> aggregated_lag;
|
||||||
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
|
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
|
||||||
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, 10, true);
|
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, 10, true);
|
||||||
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
||||||
@ -97,9 +98,10 @@ TEST(MatchedFilterLagAggregator,
|
|||||||
MatchedFilterLagAggregator aggregator(&data_dumper, kLag);
|
MatchedFilterLagAggregator aggregator(&data_dumper, kLag);
|
||||||
for (size_t k = 0; k < kNumLagsBeforeDetection * 10; ++k) {
|
for (size_t k = 0; k < kNumLagsBeforeDetection * 10; ++k) {
|
||||||
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag, false);
|
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag, false);
|
||||||
rtc::Optional<size_t> aggregated_lag = aggregator.Aggregate(lag_estimates);
|
rtc::Optional<DelayEstimate> aggregated_lag =
|
||||||
|
aggregator.Aggregate(lag_estimates);
|
||||||
EXPECT_FALSE(aggregated_lag);
|
EXPECT_FALSE(aggregated_lag);
|
||||||
EXPECT_EQ(kLag, *aggregated_lag);
|
EXPECT_EQ(kLag, aggregated_lag->delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,19 +114,19 @@ TEST(MatchedFilterLagAggregator, DISABLED_PersistentAggregatedLag) {
|
|||||||
ApmDataDumper data_dumper(0);
|
ApmDataDumper data_dumper(0);
|
||||||
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
|
std::vector<MatchedFilter::LagEstimate> lag_estimates(1);
|
||||||
MatchedFilterLagAggregator aggregator(&data_dumper, std::max(kLag1, kLag2));
|
MatchedFilterLagAggregator aggregator(&data_dumper, std::max(kLag1, kLag2));
|
||||||
rtc::Optional<size_t> aggregated_lag;
|
rtc::Optional<DelayEstimate> aggregated_lag;
|
||||||
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
|
for (size_t k = 0; k < kNumLagsBeforeDetection; ++k) {
|
||||||
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true);
|
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, true, kLag1, true);
|
||||||
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(aggregated_lag);
|
EXPECT_TRUE(aggregated_lag);
|
||||||
EXPECT_EQ(kLag1, *aggregated_lag);
|
EXPECT_EQ(kLag1, aggregated_lag->delay);
|
||||||
|
|
||||||
for (size_t k = 0; k < kNumLagsBeforeDetection * 40; ++k) {
|
for (size_t k = 0; k < kNumLagsBeforeDetection * 40; ++k) {
|
||||||
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, false, kLag2, true);
|
lag_estimates[0] = MatchedFilter::LagEstimate(1.f, false, kLag2, true);
|
||||||
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
aggregated_lag = aggregator.Aggregate(lag_estimates);
|
||||||
EXPECT_TRUE(aggregated_lag);
|
EXPECT_TRUE(aggregated_lag);
|
||||||
EXPECT_EQ(kLag1, *aggregated_lag);
|
EXPECT_EQ(kLag1, aggregated_lag->delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,7 @@ class MockRenderDelayBuffer : public RenderDelayBuffer {
|
|||||||
MOCK_METHOD0(GetRenderBuffer, RenderBuffer*());
|
MOCK_METHOD0(GetRenderBuffer, RenderBuffer*());
|
||||||
MOCK_CONST_METHOD0(GetDownsampledRenderBuffer,
|
MOCK_CONST_METHOD0(GetDownsampledRenderBuffer,
|
||||||
const DownsampledRenderBuffer&());
|
const DownsampledRenderBuffer&());
|
||||||
MOCK_CONST_METHOD0(CausalDelay, bool());
|
MOCK_CONST_METHOD1(CausalDelay, bool(size_t delay));
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderBuffer* FakeGetRenderBuffer() { return &render_buffer_; }
|
RenderBuffer* FakeGetRenderBuffer() { return &render_buffer_; }
|
||||||
|
|||||||
@ -25,11 +25,10 @@ class MockRenderDelayController : public RenderDelayController {
|
|||||||
virtual ~MockRenderDelayController() = default;
|
virtual ~MockRenderDelayController() = default;
|
||||||
|
|
||||||
MOCK_METHOD0(Reset, void());
|
MOCK_METHOD0(Reset, void());
|
||||||
MOCK_METHOD1(SetDelay, void(size_t render_delay));
|
|
||||||
MOCK_METHOD2(
|
MOCK_METHOD2(
|
||||||
GetDelay,
|
GetDelay,
|
||||||
rtc::Optional<size_t>(const DownsampledRenderBuffer& render_buffer,
|
rtc::Optional<DelayEstimate>(const DownsampledRenderBuffer& render_buffer,
|
||||||
rtc::ArrayView<const float> capture));
|
rtc::ArrayView<const float> capture));
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@ -47,7 +47,7 @@ class RenderDelayBufferImpl final : public RenderDelayBuffer {
|
|||||||
return low_rate_;
|
return low_rate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CausalDelay() const override;
|
bool CausalDelay(size_t delay) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int instance_count_;
|
static int instance_count_;
|
||||||
@ -304,10 +304,14 @@ bool RenderDelayBufferImpl::SetDelay(size_t delay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns whether the specified delay is causal.
|
// Returns whether the specified delay is causal.
|
||||||
bool RenderDelayBufferImpl::CausalDelay() const {
|
bool RenderDelayBufferImpl::CausalDelay(size_t delay) const {
|
||||||
return !internal_delay_ ||
|
// Compute the internal delay and limit the delay to the allowed range.
|
||||||
*internal_delay_ >=
|
int internal_delay = MaxExternalDelayToInternalDelay(delay);
|
||||||
static_cast<int>(config_.delay.min_echo_path_delay_blocks);
|
internal_delay =
|
||||||
|
std::min(MaxDelay(), static_cast<size_t>(std::max(internal_delay, 0)));
|
||||||
|
|
||||||
|
return internal_delay >=
|
||||||
|
static_cast<int>(config_.delay.min_echo_path_delay_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maps the externally computed delay to the delay used internally.
|
// Maps the externally computed delay to the delay used internally.
|
||||||
|
|||||||
@ -68,7 +68,7 @@ class RenderDelayBuffer {
|
|||||||
virtual const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const = 0;
|
virtual const DownsampledRenderBuffer& GetDownsampledRenderBuffer() const = 0;
|
||||||
|
|
||||||
// Returns whether the current delay is noncausal.
|
// Returns whether the current delay is noncausal.
|
||||||
virtual bool CausalDelay() const = 0;
|
virtual bool CausalDelay(size_t delay) const = 0;
|
||||||
|
|
||||||
// Returns the maximum non calusal offset that can occur in the delay buffer.
|
// Returns the maximum non calusal offset that can occur in the delay buffer.
|
||||||
static int DelayEstimatorOffset(const EchoCanceller3Config& config);
|
static int DelayEstimatorOffset(const EchoCanceller3Config& config);
|
||||||
|
|||||||
@ -32,9 +32,9 @@ class RenderDelayControllerImpl final : public RenderDelayController {
|
|||||||
int sample_rate_hz);
|
int sample_rate_hz);
|
||||||
~RenderDelayControllerImpl() override;
|
~RenderDelayControllerImpl() override;
|
||||||
void Reset() override;
|
void Reset() override;
|
||||||
void SetDelay(size_t render_delay) override;
|
rtc::Optional<DelayEstimate> GetDelay(
|
||||||
rtc::Optional<size_t> GetDelay(const DownsampledRenderBuffer& render_buffer,
|
const DownsampledRenderBuffer& render_buffer,
|
||||||
rtc::ArrayView<const float> capture) override;
|
rtc::ArrayView<const float> capture) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int instance_count_;
|
static int instance_count_;
|
||||||
@ -42,7 +42,7 @@ class RenderDelayControllerImpl final : public RenderDelayController {
|
|||||||
const int delay_headroom_blocks_;
|
const int delay_headroom_blocks_;
|
||||||
const int hysteresis_limit_1_blocks_;
|
const int hysteresis_limit_1_blocks_;
|
||||||
const int hysteresis_limit_2_blocks_;
|
const int hysteresis_limit_2_blocks_;
|
||||||
rtc::Optional<size_t> delay_;
|
rtc::Optional<DelayEstimate> delay_;
|
||||||
EchoPathDelayEstimator delay_estimator_;
|
EchoPathDelayEstimator delay_estimator_;
|
||||||
std::vector<float> delay_buf_;
|
std::vector<float> delay_buf_;
|
||||||
int delay_buf_index_ = 0;
|
int delay_buf_index_ = 0;
|
||||||
@ -50,34 +50,40 @@ class RenderDelayControllerImpl final : public RenderDelayController {
|
|||||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
|
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderDelayControllerImpl);
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t ComputeNewBufferDelay(const rtc::Optional<size_t>& current_delay,
|
DelayEstimate ComputeNewBufferDelay(
|
||||||
int delay_headroom_blocks,
|
const rtc::Optional<DelayEstimate>& current_delay,
|
||||||
int hysteresis_limit_1_blocks,
|
int delay_headroom_blocks,
|
||||||
int hysteresis_limit_2_blocks,
|
int hysteresis_limit_1_blocks,
|
||||||
size_t delay_samples) {
|
int hysteresis_limit_2_blocks,
|
||||||
|
DelayEstimate estimated_delay) {
|
||||||
// The below division is not exact and the truncation is intended.
|
// The below division is not exact and the truncation is intended.
|
||||||
const int echo_path_delay_blocks = delay_samples >> kBlockSizeLog2;
|
const int echo_path_delay_blocks = estimated_delay.delay >> kBlockSizeLog2;
|
||||||
|
|
||||||
// Compute the buffer delay increase required to achieve the desired latency.
|
// Compute the buffer delay increase required to achieve the desired latency.
|
||||||
size_t new_delay =
|
size_t new_delay_blocks =
|
||||||
std::max(echo_path_delay_blocks - delay_headroom_blocks, 0);
|
std::max(echo_path_delay_blocks - delay_headroom_blocks, 0);
|
||||||
|
|
||||||
|
DelayEstimate new_delay(estimated_delay.quality, new_delay_blocks);
|
||||||
|
|
||||||
// Add hysteresis.
|
// Add hysteresis.
|
||||||
if (current_delay) {
|
if (current_delay) {
|
||||||
if (new_delay > *current_delay) {
|
size_t current_delay_blocks = current_delay->delay;
|
||||||
if (new_delay <= *current_delay + hysteresis_limit_1_blocks) {
|
if (new_delay_blocks > current_delay_blocks) {
|
||||||
new_delay = *current_delay;
|
if (new_delay_blocks <=
|
||||||
|
current_delay_blocks + hysteresis_limit_1_blocks) {
|
||||||
|
new_delay_blocks = current_delay_blocks;
|
||||||
}
|
}
|
||||||
} else if (new_delay < *current_delay) {
|
} else if (new_delay_blocks < current_delay_blocks) {
|
||||||
size_t hysteresis_limit = std::max(
|
size_t hysteresis_limit = std::max(
|
||||||
static_cast<int>(*current_delay) - hysteresis_limit_2_blocks, 0);
|
static_cast<int>(current_delay_blocks) - hysteresis_limit_2_blocks,
|
||||||
if (new_delay >= hysteresis_limit) {
|
0);
|
||||||
new_delay = *current_delay;
|
if (new_delay_blocks >= hysteresis_limit) {
|
||||||
|
new_delay_blocks = current_delay_blocks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new_delay;
|
return DelayEstimate(estimated_delay.quality, new_delay_blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
int RenderDelayControllerImpl::instance_count_ = 0;
|
int RenderDelayControllerImpl::instance_count_ = 0;
|
||||||
@ -109,16 +115,7 @@ void RenderDelayControllerImpl::Reset() {
|
|||||||
delay_estimator_.Reset();
|
delay_estimator_.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderDelayControllerImpl::SetDelay(size_t render_delay) {
|
rtc::Optional<DelayEstimate> RenderDelayControllerImpl::GetDelay(
|
||||||
if (delay_ != render_delay) {
|
|
||||||
// If a the delay set does not match the actual delay, reset the delay
|
|
||||||
// controller.
|
|
||||||
Reset();
|
|
||||||
delay_ = render_delay;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc::Optional<size_t> RenderDelayControllerImpl::GetDelay(
|
|
||||||
const DownsampledRenderBuffer& render_buffer,
|
const DownsampledRenderBuffer& render_buffer,
|
||||||
rtc::ArrayView<const float> capture) {
|
rtc::ArrayView<const float> capture) {
|
||||||
RTC_DCHECK_EQ(kBlockSize, capture.size());
|
RTC_DCHECK_EQ(kBlockSize, capture.size());
|
||||||
@ -136,19 +133,21 @@ rtc::Optional<size_t> RenderDelayControllerImpl::GetDelay(
|
|||||||
|
|
||||||
if (delay_samples) {
|
if (delay_samples) {
|
||||||
// Compute and set new render delay buffer delay.
|
// Compute and set new render delay buffer delay.
|
||||||
delay_ = ComputeNewBufferDelay(
|
|
||||||
delay_, delay_headroom_blocks_, hysteresis_limit_1_blocks_,
|
|
||||||
hysteresis_limit_2_blocks_, static_cast<int>(*delay_samples));
|
|
||||||
|
|
||||||
metrics_.Update(static_cast<int>(*delay_samples), delay_ ? *delay_ : 0);
|
delay_ = ComputeNewBufferDelay(delay_, delay_headroom_blocks_,
|
||||||
|
hysteresis_limit_1_blocks_,
|
||||||
|
hysteresis_limit_2_blocks_, *delay_samples);
|
||||||
|
|
||||||
|
metrics_.Update(static_cast<int>(delay_samples->delay),
|
||||||
|
delay_ ? delay_->delay : 0);
|
||||||
} else {
|
} else {
|
||||||
metrics_.Update(rtc::nullopt, delay_ ? *delay_ : 0);
|
metrics_.Update(rtc::nullopt, delay_ ? delay_->delay : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
data_dumper_->DumpRaw("aec3_render_delay_controller_delay",
|
data_dumper_->DumpRaw("aec3_render_delay_controller_delay",
|
||||||
delay_samples ? *delay_samples : 0);
|
delay_samples ? delay_samples->delay : 0);
|
||||||
data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay",
|
data_dumper_->DumpRaw("aec3_render_delay_controller_buffer_delay",
|
||||||
delay_ ? *delay_ : 0);
|
delay_ ? delay_->delay : 0);
|
||||||
|
|
||||||
return delay_;
|
return delay_;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "api/array_view.h"
|
#include "api/array_view.h"
|
||||||
#include "api/optional.h"
|
#include "api/optional.h"
|
||||||
|
#include "modules/audio_processing/aec3/delay_estimate.h"
|
||||||
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
|
#include "modules/audio_processing/aec3/downsampled_render_buffer.h"
|
||||||
#include "modules/audio_processing/aec3/render_delay_buffer.h"
|
#include "modules/audio_processing/aec3/render_delay_buffer.h"
|
||||||
#include "modules/audio_processing/include/audio_processing.h"
|
#include "modules/audio_processing/include/audio_processing.h"
|
||||||
@ -31,11 +32,8 @@ class RenderDelayController {
|
|||||||
// Resets the delay controller.
|
// Resets the delay controller.
|
||||||
virtual void Reset() = 0;
|
virtual void Reset() = 0;
|
||||||
|
|
||||||
// Receives the externally used delay.
|
|
||||||
virtual void SetDelay(size_t render_delay) = 0;
|
|
||||||
|
|
||||||
// Aligns the render buffer content with the capture signal.
|
// Aligns the render buffer content with the capture signal.
|
||||||
virtual rtc::Optional<size_t> GetDelay(
|
virtual rtc::Optional<DelayEstimate> GetDelay(
|
||||||
const DownsampledRenderBuffer& render_buffer,
|
const DownsampledRenderBuffer& render_buffer,
|
||||||
rtc::ArrayView<const float> capture) = 0;
|
rtc::ArrayView<const float> capture) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -61,9 +61,9 @@ TEST(RenderDelayController, NoRenderSignal) {
|
|||||||
RenderDelayController::Create(
|
RenderDelayController::Create(
|
||||||
config, RenderDelayBuffer::DelayEstimatorOffset(config), rate));
|
config, RenderDelayBuffer::DelayEstimatorOffset(config), rate));
|
||||||
for (size_t k = 0; k < 100; ++k) {
|
for (size_t k = 0; k < 100; ++k) {
|
||||||
EXPECT_EQ(config.delay.min_echo_path_delay_blocks,
|
auto delay = delay_controller->GetDelay(
|
||||||
delay_controller->GetDelay(
|
delay_buffer->GetDownsampledRenderBuffer(), block);
|
||||||
delay_buffer->GetDownsampledRenderBuffer(), block));
|
EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay->delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ TEST(RenderDelayController, NoRenderSignal) {
|
|||||||
// Verifies the basic API call sequence.
|
// Verifies the basic API call sequence.
|
||||||
TEST(RenderDelayController, BasicApiCalls) {
|
TEST(RenderDelayController, BasicApiCalls) {
|
||||||
std::vector<float> capture_block(kBlockSize, 0.f);
|
std::vector<float> capture_block(kBlockSize, 0.f);
|
||||||
rtc::Optional<size_t> delay_blocks = 0;
|
rtc::Optional<DelayEstimate> delay_blocks;
|
||||||
for (size_t num_matched_filters = 4; num_matched_filters == 10;
|
for (size_t num_matched_filters = 4; num_matched_filters == 10;
|
||||||
num_matched_filters++) {
|
num_matched_filters++) {
|
||||||
for (auto down_sampling_factor : kDownSamplingFactors) {
|
for (auto down_sampling_factor : kDownSamplingFactors) {
|
||||||
@ -97,7 +97,7 @@ TEST(RenderDelayController, BasicApiCalls) {
|
|||||||
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
|
render_delay_buffer->GetDownsampledRenderBuffer(), capture_block);
|
||||||
}
|
}
|
||||||
EXPECT_TRUE(delay_blocks);
|
EXPECT_TRUE(delay_blocks);
|
||||||
EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks);
|
EXPECT_EQ(config.delay.min_echo_path_delay_blocks, delay_blocks->delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ TEST(RenderDelayController, Alignment) {
|
|||||||
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
|
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
|
||||||
|
|
||||||
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
|
for (size_t delay_samples : {15, 50, 150, 200, 800, 4000}) {
|
||||||
rtc::Optional<size_t> delay_blocks;
|
rtc::Optional<DelayEstimate> delay_blocks;
|
||||||
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
|
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
|
||||||
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
||||||
RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
|
RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
|
||||||
@ -145,7 +145,7 @@ TEST(RenderDelayController, Alignment) {
|
|||||||
std::max(0, static_cast<int>(delay_samples / kBlockSize) -
|
std::max(0, static_cast<int>(delay_samples / kBlockSize) -
|
||||||
kDelayHeadroomBlocks);
|
kDelayHeadroomBlocks);
|
||||||
|
|
||||||
EXPECT_EQ(expected_delay_blocks, delay_blocks);
|
EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,7 +169,7 @@ TEST(RenderDelayController, NonCausalAlignment) {
|
|||||||
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
|
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
|
||||||
|
|
||||||
for (int delay_samples : {-15, -50, -150, -200}) {
|
for (int delay_samples : {-15, -50, -150, -200}) {
|
||||||
rtc::Optional<size_t> delay_blocks;
|
rtc::Optional<DelayEstimate> delay_blocks;
|
||||||
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
|
SCOPED_TRACE(ProduceDebugText(rate, -delay_samples));
|
||||||
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
||||||
RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
|
RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
|
||||||
@ -211,7 +211,7 @@ TEST(RenderDelayController, AlignmentWithJitter) {
|
|||||||
std::vector<std::vector<float>> render_block(
|
std::vector<std::vector<float>> render_block(
|
||||||
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
|
NumBandsForRate(rate), std::vector<float>(kBlockSize, 0.f));
|
||||||
for (size_t delay_samples : {15, 50, 300, 800}) {
|
for (size_t delay_samples : {15, 50, 300, 800}) {
|
||||||
rtc::Optional<size_t> delay_blocks;
|
rtc::Optional<DelayEstimate> delay_blocks;
|
||||||
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
|
SCOPED_TRACE(ProduceDebugText(rate, delay_samples));
|
||||||
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
std::unique_ptr<RenderDelayBuffer> render_delay_buffer(
|
||||||
RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
|
RenderDelayBuffer::Create(config, NumBandsForRate(rate)));
|
||||||
@ -250,7 +250,7 @@ TEST(RenderDelayController, AlignmentWithJitter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ASSERT_TRUE(delay_blocks);
|
ASSERT_TRUE(delay_blocks);
|
||||||
EXPECT_EQ(expected_delay_blocks, *delay_blocks);
|
EXPECT_EQ(expected_delay_blocks, delay_blocks->delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user