From 435187d18d8e72b046458fb7fc2c9cd10495e4c7 Mon Sep 17 00:00:00 2001 From: Gustaf Ullberg Date: Thu, 31 May 2018 14:14:20 +0200 Subject: [PATCH] AEC3: CascadedBiQuadFilter can run different filters in cascade MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CascadedBiQuadFilter can run identical filters multiple times. This CL allows the use of different filters in each step. This enables the use of more elaborate filters. The filters are defined by zeros, poles and gains. The 'old' way of initializing CascadedBiQuadFilter with a transfer function and number of filters is left intact. Bug: webrtc:9288,chromium:846615 Change-Id: Ie4a5b98eba044415571cdcac087b20870a0b5d33 Reviewed-on: https://webrtc-review.googlesource.com/80060 Reviewed-by: Per Ã…hgren Commit-Queue: Gustaf Ullberg Cr-Commit-Position: refs/heads/master@{#23473} --- .../aec3/cascaded_biquad_filter.cc | 59 ++++++++++++++----- .../aec3/cascaded_biquad_filter.h | 30 +++++++--- .../aec3/cascaded_biquad_filter_unittest.cc | 28 +++++++++ modules/audio_processing/aec3/decimator.cc | 4 +- 4 files changed, 96 insertions(+), 25 deletions(-) diff --git a/modules/audio_processing/aec3/cascaded_biquad_filter.cc b/modules/audio_processing/aec3/cascaded_biquad_filter.cc index 9a472f516f..5881d60ae3 100644 --- a/modules/audio_processing/aec3/cascaded_biquad_filter.cc +++ b/modules/audio_processing/aec3/cascaded_biquad_filter.cc @@ -13,37 +13,68 @@ namespace webrtc { +CascadedBiQuadFilter::BiQuadParam::BiQuadParam(std::complex zero, + std::complex pole, + float gain) + : zero(zero), pole(pole), gain(gain) {} + +CascadedBiQuadFilter::BiQuad::BiQuad( + const CascadedBiQuadFilter::BiQuadParam& param) + : x(), y() { + float z_r = std::real(param.zero); + float z_i = std::imag(param.zero); + float p_r = std::real(param.pole); + float p_i = std::imag(param.pole); + float gain = param.gain; + + coefficients.b[0] = gain * 1.f; + coefficients.b[1] = gain * -2.f * z_r; + coefficients.b[2] = gain * (z_r * z_r + z_i * z_i); + + coefficients.a[0] = -2.f * p_r; + coefficients.a[1] = p_r * p_r + p_i * p_i; +} + CascadedBiQuadFilter::CascadedBiQuadFilter( const CascadedBiQuadFilter::BiQuadCoefficients& coefficients, size_t num_biquads) - : biquad_states_(num_biquads), coefficients_(coefficients) {} + : biquads_(num_biquads, coefficients) {} + +CascadedBiQuadFilter::CascadedBiQuadFilter( + const std::vector& biquad_params) { + for (const auto& param : biquad_params) { + biquads_.push_back(BiQuad(param)); + } +} CascadedBiQuadFilter::~CascadedBiQuadFilter() = default; void CascadedBiQuadFilter::Process(rtc::ArrayView x, rtc::ArrayView y) { - ApplyBiQuad(x, y, &biquad_states_[0]); - for (size_t k = 1; k < biquad_states_.size(); ++k) { - ApplyBiQuad(y, y, &biquad_states_[k]); + if (biquads_.size() > 0) { + ApplyBiQuad(x, y, &biquads_[0]); + for (size_t k = 1; k < biquads_.size(); ++k) { + ApplyBiQuad(y, y, &biquads_[k]); + } + } else { + std::copy(x.begin(), x.end(), y.begin()); } } void CascadedBiQuadFilter::Process(rtc::ArrayView y) { - for (auto& biquad : biquad_states_) { + for (auto& biquad : biquads_) { ApplyBiQuad(y, y, &biquad); } } -void CascadedBiQuadFilter::ApplyBiQuad( - rtc::ArrayView x, - rtc::ArrayView y, - CascadedBiQuadFilter::BiQuadState* biquad_state) { +void CascadedBiQuadFilter::ApplyBiQuad(rtc::ArrayView x, + rtc::ArrayView y, + CascadedBiQuadFilter::BiQuad* biquad) { RTC_DCHECK_EQ(x.size(), y.size()); - RTC_DCHECK(biquad_state); - const auto* c_b = coefficients_.b; - const auto* c_a = coefficients_.a; - auto* m_x = biquad_state->x; - auto* m_y = biquad_state->y; + const auto* c_b = biquad->coefficients.b; + const auto* c_a = biquad->coefficients.a; + auto* m_x = biquad->x; + auto* m_y = biquad->y; for (size_t k = 0; k < x.size(); ++k) { const float tmp = x[k]; y[k] = c_b[0] * tmp + c_b[1] * m_x[0] + c_b[2] * m_x[1] - c_a[0] * m_y[0] - diff --git a/modules/audio_processing/aec3/cascaded_biquad_filter.h b/modules/audio_processing/aec3/cascaded_biquad_filter.h index aea889ab53..feae68d63d 100644 --- a/modules/audio_processing/aec3/cascaded_biquad_filter.h +++ b/modules/audio_processing/aec3/cascaded_biquad_filter.h @@ -11,6 +11,7 @@ #ifndef MODULES_AUDIO_PROCESSING_AEC3_CASCADED_BIQUAD_FILTER_H_ #define MODULES_AUDIO_PROCESSING_AEC3_CASCADED_BIQUAD_FILTER_H_ +#include #include #include "api/array_view.h" @@ -18,14 +19,15 @@ namespace webrtc { -// Applies a number of identical biquads in a cascaded manner. The filter -// implementation is direct form 1. +// Applies a number of biquads in a cascaded manner. The filter implementation +// is direct form 1. class CascadedBiQuadFilter { public: - struct BiQuadState { - BiQuadState() : x(), y() {} - float x[2]; - float y[2]; + struct BiQuadParam { + BiQuadParam(std::complex zero, std::complex pole, float gain); + std::complex zero; + std::complex pole; + float gain; }; struct BiQuadCoefficients { @@ -33,9 +35,20 @@ class CascadedBiQuadFilter { float a[2]; }; + struct BiQuad { + BiQuad(const BiQuadCoefficients& coefficients) + : coefficients(coefficients), x(), y() {} + BiQuad(const CascadedBiQuadFilter::BiQuadParam& param); + BiQuadCoefficients coefficients; + float x[2]; + float y[2]; + }; + CascadedBiQuadFilter( const CascadedBiQuadFilter::BiQuadCoefficients& coefficients, size_t num_biquads); + CascadedBiQuadFilter( + const std::vector& biquad_params); ~CascadedBiQuadFilter(); // Applies the biquads on the values in x in order to form the output in y. void Process(rtc::ArrayView x, rtc::ArrayView y); @@ -45,10 +58,9 @@ class CascadedBiQuadFilter { private: void ApplyBiQuad(rtc::ArrayView x, rtc::ArrayView y, - CascadedBiQuadFilter::BiQuadState* biquad_state); + CascadedBiQuadFilter::BiQuad* biquad); - std::vector biquad_states_; - const BiQuadCoefficients coefficients_; + std::vector biquads_; RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CascadedBiQuadFilter); }; diff --git a/modules/audio_processing/aec3/cascaded_biquad_filter_unittest.cc b/modules/audio_processing/aec3/cascaded_biquad_filter_unittest.cc index fcb77e1f6e..0f1b0db14b 100644 --- a/modules/audio_processing/aec3/cascaded_biquad_filter_unittest.cc +++ b/modules/audio_processing/aec3/cascaded_biquad_filter_unittest.cc @@ -95,4 +95,32 @@ TEST(CascadedBiquadFilter, InputSizeCheckVerification) { } #endif +// Verifies the conversion from zero, pole, gain to filter coefficients for +// lowpass filter. +TEST(CascadedBiquadFilter, BiQuadParamLowPass) { + CascadedBiQuadFilter::BiQuadParam param( + {-1.0f, 0.0f}, {0.23146901f, 0.39514232f}, 0.1866943331163784f); + CascadedBiQuadFilter::BiQuad filter(param); + const float epsilon = 1e-6f; + EXPECT_NEAR(filter.coefficients.b[0], 0.18669433f, epsilon); + EXPECT_NEAR(filter.coefficients.b[1], 0.37338867f, epsilon); + EXPECT_NEAR(filter.coefficients.b[2], 0.18669433f, epsilon); + EXPECT_NEAR(filter.coefficients.a[0], -0.46293803f, epsilon); + EXPECT_NEAR(filter.coefficients.a[1], 0.20971536f, epsilon); +} + +// Verifies the conversion from zero, pole, gain to filter coefficients for +// highpass filter. +TEST(CascadedBiquadFilter, BiQuadParamHighPass) { + CascadedBiQuadFilter::BiQuadParam param( + {1.0f, 0.0f}, {0.72712179f, 0.21296904f}, 0.75707637533388494f); + CascadedBiQuadFilter::BiQuad filter(param); + const float epsilon = 1e-6f; + EXPECT_NEAR(filter.coefficients.b[0], 0.75707638f, epsilon); + EXPECT_NEAR(filter.coefficients.b[1], -1.51415275f, epsilon); + EXPECT_NEAR(filter.coefficients.b[2], 0.75707638f, epsilon); + EXPECT_NEAR(filter.coefficients.a[0], -1.45424359f, epsilon); + EXPECT_NEAR(filter.coefficients.a[1], 0.57406192f, epsilon); +} + } // namespace webrtc diff --git a/modules/audio_processing/aec3/decimator.cc b/modules/audio_processing/aec3/decimator.cc index 6135db5bc2..8fffc8aba9 100644 --- a/modules/audio_processing/aec3/decimator.cc +++ b/modules/audio_processing/aec3/decimator.cc @@ -21,8 +21,8 @@ const CascadedBiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients2 = { {-0.27666461f, 0.18513647f}}; constexpr int kNumFilters2 = 3; -// b, a = signal.butter(2, 1500/8000.0, 'lowpass', analog=False) which are the -// same as b, a = signal.butter(2, 75/4000.0, 'lowpass', analog=False). +// b, a = signal.butter(2, 750/8000.0, 'lowpass', analog=False) which are the +// same as b, a = signal.butter(2, 375/4000.0, 'lowpass', analog=False). const CascadedBiQuadFilter::BiQuadCoefficients kLowPassFilterCoefficients4 = { {0.0179f, 0.0357f, 0.0179f}, {-1.5879f, 0.6594f}};