diff --git a/modules/audio_processing/BUILD.gn b/modules/audio_processing/BUILD.gn index 617440262c..0528479fdd 100644 --- a/modules/audio_processing/BUILD.gn +++ b/modules/audio_processing/BUILD.gn @@ -101,6 +101,8 @@ rtc_static_library("audio_processing") { "aec3/residual_echo_estimator.h", "aec3/shadow_filter_update_gain.cc", "aec3/shadow_filter_update_gain.h", + "aec3/skew_estimator.cc", + "aec3/skew_estimator.h", "aec3/subtractor.cc", "aec3/subtractor.h", "aec3/subtractor_output.h", @@ -665,6 +667,7 @@ if (rtc_include_tests) { "aec3/render_signal_analyzer_unittest.cc", "aec3/residual_echo_estimator_unittest.cc", "aec3/shadow_filter_update_gain_unittest.cc", + "aec3/skew_estimator_unittest.cc", "aec3/subtractor_unittest.cc", "aec3/suppression_filter_unittest.cc", "aec3/suppression_gain_unittest.cc", diff --git a/modules/audio_processing/aec3/render_delay_controller.cc b/modules/audio_processing/aec3/render_delay_controller.cc index b95188d661..f2a7b54290 100644 --- a/modules/audio_processing/aec3/render_delay_controller.cc +++ b/modules/audio_processing/aec3/render_delay_controller.cc @@ -19,6 +19,7 @@ #include "modules/audio_processing/aec3/aec3_common.h" #include "modules/audio_processing/aec3/echo_path_delay_estimator.h" #include "modules/audio_processing/aec3/render_delay_controller_metrics.h" +#include "modules/audio_processing/aec3/skew_estimator.h" #include "rtc_base/atomicops.h" #include "rtc_base/constructormagic.h" @@ -28,45 +29,6 @@ namespace { constexpr int kSkewHistorySizeLog2 = 8; -// Estimator of API call skew between render and capture. -class SkewEstimator { - public: - // Resets the estimation. - void Reset() { - skew_ = 0; - next_index_ = 0; - sufficient_skew_stored_ = false; - } - - // Updates the skew data for a render call. - void LogRenderCall() { ++skew_; } - - // Updates and computes the skew at a capture call. Returns an optional which - // is non-null if a reliable skew has been found. - rtc::Optional GetSkewFromCapture() { - --skew_; - - skew_history_[next_index_] = skew_; - if (++next_index_ == skew_history_.size()) { - next_index_ = 0; - sufficient_skew_stored_ = true; - } - - if (!sufficient_skew_stored_) { - return rtc::nullopt; - } - - return std::accumulate(skew_history_.begin(), skew_history_.end(), 0) >> - kSkewHistorySizeLog2; - } - - private: - int skew_ = 0; - std::array skew_history_; - size_t next_index_ = 0; - bool sufficient_skew_stored_ = false; -}; - class RenderDelayControllerImpl final : public RenderDelayController { public: RenderDelayControllerImpl(const EchoCanceller3Config& config, @@ -150,7 +112,8 @@ RenderDelayControllerImpl::RenderDelayControllerImpl( hysteresis_limit_2_blocks_( static_cast(config.delay.hysteresis_limit_2_blocks)), delay_estimator_(data_dumper_.get(), config), - delay_buf_(kBlockSize * non_causal_offset, 0.f) { + delay_buf_(kBlockSize * non_causal_offset, 0.f), + skew_estimator_(kSkewHistorySizeLog2) { RTC_DCHECK(ValidFullBandRate(sample_rate_hz)); delay_estimator_.LogDelayEstimationProperties(sample_rate_hz, delay_buf_.size()); diff --git a/modules/audio_processing/aec3/skew_estimator.cc b/modules/audio_processing/aec3/skew_estimator.cc new file mode 100644 index 0000000000..608a707d3e --- /dev/null +++ b/modules/audio_processing/aec3/skew_estimator.cc @@ -0,0 +1,46 @@ +/* + * 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/skew_estimator.h" + +#include +#include + +namespace webrtc { + +SkewEstimator::SkewEstimator(size_t skew_history_size_log2) + : skew_history_size_log2_(static_cast(skew_history_size_log2)), + skew_history_(1ULL << skew_history_size_log2_, 0) {} + +SkewEstimator::~SkewEstimator() = default; + +void SkewEstimator::Reset() { + skew_ = 0; + skew_sum_ = 0; + next_index_ = 0; + sufficient_skew_stored_ = false; + std::fill(skew_history_.begin(), skew_history_.end(), 0); +} + +rtc::Optional SkewEstimator::GetSkewFromCapture() { + --skew_; + + skew_sum_ += skew_ - skew_history_[next_index_]; + skew_history_[next_index_] = skew_; + if (++next_index_ == skew_history_.size()) { + next_index_ = 0; + sufficient_skew_stored_ = true; + } + + return sufficient_skew_stored_ + ? rtc::Optional(skew_sum_ >> skew_history_size_log2_) + : rtc::nullopt; +} + +} // namespace webrtc diff --git a/modules/audio_processing/aec3/skew_estimator.h b/modules/audio_processing/aec3/skew_estimator.h new file mode 100644 index 0000000000..2afcd76694 --- /dev/null +++ b/modules/audio_processing/aec3/skew_estimator.h @@ -0,0 +1,50 @@ +/* + * 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_SKEW_ESTIMATOR_H_ +#define MODULES_AUDIO_PROCESSING_AEC3_SKEW_ESTIMATOR_H_ + +#include + +#include "api/optional.h" +#include "rtc_base/constructormagic.h" + +namespace webrtc { + +// Estimator of API call skew between render and capture. +class SkewEstimator { + public: + explicit SkewEstimator(size_t skew_history_size_log2); + ~SkewEstimator(); + + // Resets the estimation. + void Reset(); + + // Updates the skew data for a render call. + void LogRenderCall() { ++skew_; } + + // Updates and computes the skew at a capture call. Returns an optional which + // is non-null if a reliable skew has been found. + rtc::Optional GetSkewFromCapture(); + + private: + const int skew_history_size_log2_; + std::vector skew_history_; + int skew_ = 0; + int skew_sum_ = 0; + size_t next_index_ = 0; + bool sufficient_skew_stored_ = false; + + RTC_DISALLOW_COPY_AND_ASSIGN(SkewEstimator); +}; + +} // namespace webrtc + +#endif // MODULES_AUDIO_PROCESSING_AEC3_SKEW_ESTIMATOR_H_ diff --git a/modules/audio_processing/aec3/skew_estimator_unittest.cc b/modules/audio_processing/aec3/skew_estimator_unittest.cc new file mode 100644 index 0000000000..a1a679fe25 --- /dev/null +++ b/modules/audio_processing/aec3/skew_estimator_unittest.cc @@ -0,0 +1,124 @@ +/* + * 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/skew_estimator.h" + +#include "test/gtest.h" + +namespace webrtc { +namespace aec3 { + +// Tests that the skew ends up as it should after a skew change. +TEST(SkewEstimator, SkewChangeAdaptation) { + constexpr int kNumSkewsLog2 = 7; + constexpr int kNumSkews = 1 << kNumSkewsLog2; + + SkewEstimator estimator(kNumSkewsLog2); + + for (int k = 0; k < kNumSkews - 1; ++k) { + estimator.LogRenderCall(); + auto skew = estimator.GetSkewFromCapture(); + EXPECT_FALSE(skew); + } + + estimator.LogRenderCall(); + + rtc::Optional skew; + for (int k = 0; k < kNumSkews; ++k) { + estimator.LogRenderCall(); + skew = estimator.GetSkewFromCapture(); + EXPECT_TRUE(skew); + } + EXPECT_EQ(1, *skew); + + estimator.LogRenderCall(); + + for (int k = 0; k < kNumSkews; ++k) { + estimator.LogRenderCall(); + skew = estimator.GetSkewFromCapture(); + EXPECT_TRUE(skew); + } + EXPECT_EQ(2, *skew); +} + +// Tests that the skew ends up as it should for a surplus of render calls. +TEST(SkewEstimator, SkewForSurplusRender) { + constexpr int kNumSkewsLog2 = 7; + constexpr int kNumSkews = 1 << kNumSkewsLog2; + + SkewEstimator estimator(kNumSkewsLog2); + + for (int k = 0; k < kNumSkews - 1; ++k) { + estimator.LogRenderCall(); + auto skew = estimator.GetSkewFromCapture(); + EXPECT_FALSE(skew); + } + + estimator.LogRenderCall(); + + rtc::Optional skew; + for (int k = 0; k < kNumSkews; ++k) { + estimator.LogRenderCall(); + skew = estimator.GetSkewFromCapture(); + EXPECT_TRUE(skew); + } + EXPECT_EQ(1, *skew); +} + +// Tests that the skew ends up as it should for a surplus of capture calls. +TEST(SkewEstimator, SkewForSurplusCapture) { + constexpr int kNumSkewsLog2 = 7; + constexpr int kNumSkews = 1 << kNumSkewsLog2; + + SkewEstimator estimator(kNumSkewsLog2); + + for (int k = 0; k < kNumSkews - 1; ++k) { + estimator.LogRenderCall(); + auto skew = estimator.GetSkewFromCapture(); + EXPECT_FALSE(skew); + } + + rtc::Optional skew; + skew = estimator.GetSkewFromCapture(); + + for (int k = 0; k < kNumSkews; ++k) { + estimator.LogRenderCall(); + skew = estimator.GetSkewFromCapture(); + EXPECT_TRUE(skew); + } + EXPECT_EQ(-1, *skew); +} + +// Tests that the skew estimator returns a null optional when it should. +TEST(SkewEstimator, NullEstimate) { + constexpr int kNumSkewsLog2 = 4; + constexpr int kNumSkews = 1 << kNumSkewsLog2; + + SkewEstimator estimator(kNumSkewsLog2); + + for (int k = 0; k < kNumSkews - 1; ++k) { + estimator.LogRenderCall(); + auto skew = estimator.GetSkewFromCapture(); + EXPECT_FALSE(skew); + } + + estimator.LogRenderCall(); + auto skew = estimator.GetSkewFromCapture(); + EXPECT_TRUE(skew); + + estimator.Reset(); + for (int k = 0; k < kNumSkews - 1; ++k) { + estimator.LogRenderCall(); + auto skew = estimator.GetSkewFromCapture(); + EXPECT_FALSE(skew); + } +} +} // namespace aec3 +} // namespace webrtc