3#include "debugtrace.hpp"
10using rte = std::runtime_error;
11using lock = std::scoped_lock<std::mutex>;
15 if (filter_coefs.n_cols != 1) {
16 throw rte(
"Expected filter coefficients for a single SeriesBiquad as a "
17 "single column with length 6 x n_filters");
20 if (filter_coefs.n_rows % 6 != 0) {
21 cerr <<
"Number of rows given: " << filter_coefs.n_rows << endl;
22 throw rte(
"filter_coefs should be multiple of 6, given: " +
23 std::to_string(filter_coefs.n_rows));
25 us nfilters = filter_coefs.n_rows / 6;
28 state =
dmat(2, nfilters, arma::fill::zeros);
30 sos.resize(6, nfilters);
31 for (
us i = 0; i < nfilters; i++) {
32 sos.col(i) = filter_coefs.subvec(6 * i, 6 * (i + 1) - 1);
36 if (!arma::approx_equal(sos.row(3), arma::rowvec(nfilters, arma::fill::ones),
38 std::cerr <<
"Read row: " << sos.row(3) << endl;
41 "Filter coefficients should have fourth element (a0) equal to 1.0");
48 throw rte(
"Invalid sampling frequency: " + std::to_string(fs) +
" [Hz]");
51 throw rte(
"Invalid cuton frequency: " + std::to_string(cuton_Hz) +
" [Hz]");
53 if (cuton_Hz >= 0.98 * fs / 2) {
55 "Invalid cuton frequency. We limit this to 0.98* fs / 2. Given value" +
56 std::to_string(cuton_Hz) +
" [Hz]");
59 const d tau = 1 / (2 * arma::datum::pi * cuton_Hz);
60 const d facnum = 2 * fs * tau / (1 + 2 * fs * tau);
61 const d facden = (1 - 2 * fs * tau) / (1 + 2 * fs * tau);
85 return std::make_unique<SeriesBiquad>(sos.as_col());
97 for (
us filterno = 0; filterno < sos.n_cols; filterno++) {
98 d b0 = sos(0, filterno);
99 d b1 = sos(1, filterno);
100 d b2 = sos(2, filterno);
101 d a1 = sos(4, filterno);
102 d a2 = sos(5, filterno);
104 d w1 = state(0, filterno);
105 d w2 = state(1, filterno);
107 for (
us sample = 0; sample < inout.size(); sample++) {
108 d w0 = inout(sample) - a1 * w1 - a2 * w2;
109 d yn = b0 * w0 + b1 * w1 + b2 * w2;
115 state(0, filterno) = w1;
116 state(1, filterno) = w2;
128 for (
us i = 0; i < filters.n_cols; i++) {
129 _filters.emplace_back(filters.col(i));
132 if (gains !=
nullptr) {
135 _gains =
vd(_filters.size(), arma::fill::ones);
144 throw rte(
"Invalid number of gain values given.");
152 std::vector<std::future<vd>> futs;
155 vd inout_cpy = inout;
156 for (
us i = 0; i < _filters.size(); i++) {
157 futs.emplace_back(_pool.
submit(
158 [&](
vd inout,
us i) {
159 _filters[i].filter(inout);
168 for (
us i = 0; i < _filters.size(); i++) {
169 inout += futs[i].get() * _gains[i];
173 vd inout_cpy = inout;
175 for (
us i = 0; i < _filters.size(); i++) {
176 vd cpy_again = inout_cpy;
177 _filters[i].filter(cpy_again);
178 inout += cpy_again * _gains(i);
185 for (
auto &f : _filters) {
191 return std::make_unique<BiquadBank>(_filters, _gains);
std::unique_ptr< Filter > clone() const override final
Clone a filter, to generate a copy.
virtual void filter(vd &inout) override final
Filter input, and provides output in same array as input.
void reset() override final
Reset filter state to 0 (history was all-zero).
void setGains(const vd &gains)
Set new gain values for each filter in the BiquadBank.
us nfilters() const
Returns the number of Filters.
BiquadBank(const dmat &filters, const vd *gains=nullptr)
Initialize biquadbank.
std::future< R > submit(F &&task, A &&...args)
Wrapper around BS::thread_pool::submit(...)
A set of Biquad filters in series.
virtual void filter(vd &inout) override final
Filter input, and provides output in same array as input.
static SeriesBiquad firstOrderHighPass(const d fs, const d cuton_Hz)
Create a SeriesBiquad object for a first order high-pass filter.
void reset() override final
Reset filter state to 0 (history was all-zero).
std::unique_ptr< Filter > clone() const override final
Clone a filter, to generate a copy.
SeriesBiquad(const vd &filter_coefs)
Initalize a SeriesBiquad filter.
std::scoped_lock< std::mutex > lock
std::scoped_lock< std::mutex > lck
size_t us
We often use boolean values.