AEC3: CascadedBiQuadFilter can run different filters in cascade
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 <peah@webrtc.org> Commit-Queue: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23473}
This commit is contained in:
parent
bae79db1f6
commit
435187d18d
@ -13,37 +13,68 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
CascadedBiQuadFilter::BiQuadParam::BiQuadParam(std::complex<float> zero,
|
||||
std::complex<float> 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<CascadedBiQuadFilter::BiQuadParam>& biquad_params) {
|
||||
for (const auto& param : biquad_params) {
|
||||
biquads_.push_back(BiQuad(param));
|
||||
}
|
||||
}
|
||||
|
||||
CascadedBiQuadFilter::~CascadedBiQuadFilter() = default;
|
||||
|
||||
void CascadedBiQuadFilter::Process(rtc::ArrayView<const float> x,
|
||||
rtc::ArrayView<float> 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<float> y) {
|
||||
for (auto& biquad : biquad_states_) {
|
||||
for (auto& biquad : biquads_) {
|
||||
ApplyBiQuad(y, y, &biquad);
|
||||
}
|
||||
}
|
||||
|
||||
void CascadedBiQuadFilter::ApplyBiQuad(
|
||||
rtc::ArrayView<const float> x,
|
||||
rtc::ArrayView<float> y,
|
||||
CascadedBiQuadFilter::BiQuadState* biquad_state) {
|
||||
void CascadedBiQuadFilter::ApplyBiQuad(rtc::ArrayView<const float> x,
|
||||
rtc::ArrayView<float> 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] -
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#ifndef MODULES_AUDIO_PROCESSING_AEC3_CASCADED_BIQUAD_FILTER_H_
|
||||
#define MODULES_AUDIO_PROCESSING_AEC3_CASCADED_BIQUAD_FILTER_H_
|
||||
|
||||
#include <complex>
|
||||
#include <vector>
|
||||
|
||||
#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<float> zero, std::complex<float> pole, float gain);
|
||||
std::complex<float> zero;
|
||||
std::complex<float> 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<CascadedBiQuadFilter::BiQuadParam>& biquad_params);
|
||||
~CascadedBiQuadFilter();
|
||||
// Applies the biquads on the values in x in order to form the output in y.
|
||||
void Process(rtc::ArrayView<const float> x, rtc::ArrayView<float> y);
|
||||
@ -45,10 +58,9 @@ class CascadedBiQuadFilter {
|
||||
private:
|
||||
void ApplyBiQuad(rtc::ArrayView<const float> x,
|
||||
rtc::ArrayView<float> y,
|
||||
CascadedBiQuadFilter::BiQuadState* biquad_state);
|
||||
CascadedBiQuadFilter::BiQuad* biquad);
|
||||
|
||||
std::vector<BiQuadState> biquad_states_;
|
||||
const BiQuadCoefficients coefficients_;
|
||||
std::vector<BiQuad> biquads_;
|
||||
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CascadedBiQuadFilter);
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user