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:
Gustaf Ullberg 2018-05-31 14:14:20 +02:00 committed by Commit Bot
parent bae79db1f6
commit 435187d18d
4 changed files with 96 additions and 25 deletions

View File

@ -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] -

View File

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

View File

@ -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

View File

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