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}};