LASP 1.0
Library for Acoustic Signal Processing
Loading...
Searching...
No Matches
lasp_biquadbank.cpp
Go to the documentation of this file.
1/* #define DEBUGTRACE_ENABLED */
2#include "lasp_biquadbank.h"
3#include "debugtrace.hpp"
4#include "lasp_thread.h"
5#include <cassert>
6#include <vector>
7
8using std::cerr;
9using std::endl;
10using rte = std::runtime_error;
11using lock = std::scoped_lock<std::mutex>;
12
13SeriesBiquad::SeriesBiquad(const vd &filter_coefs) {
14 DEBUGTRACE_ENTER;
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");
18 }
19
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));
24 }
25 us nfilters = filter_coefs.n_rows / 6;
26
28 state = dmat(2, nfilters, arma::fill::zeros);
29
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);
33 }
34
36 if (!arma::approx_equal(sos.row(3), arma::rowvec(nfilters, arma::fill::ones),
37 "absdiff", 1e-9)) {
38 std::cerr << "Read row: " << sos.row(3) << endl;
39
40 throw rte(
41 "Filter coefficients should have fourth element (a0) equal to 1.0");
42 }
43}
44
45SeriesBiquad SeriesBiquad::firstOrderHighPass(const d fs, const d cuton_Hz) {
46
47 if (fs <= 0) {
48 throw rte("Invalid sampling frequency: " + std::to_string(fs) + " [Hz]");
49 }
50 if (cuton_Hz <= 0) {
51 throw rte("Invalid cuton frequency: " + std::to_string(cuton_Hz) + " [Hz]");
52 }
53 if (cuton_Hz >= 0.98 * fs / 2) {
54 throw rte(
55 "Invalid cuton frequency. We limit this to 0.98* fs / 2. Given value" +
56 std::to_string(cuton_Hz) + " [Hz]");
57 }
58
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);
62
63 vd coefs(6);
64 // b0
65 coefs(0) = facnum;
66 // b1
67 coefs(1) = -facnum;
68 // b2
69 coefs(2) = 0;
70
71 // a0
72 coefs(3) = 1;
73
74 // a1
75 coefs(4) = facden;
76
77 // a2
78 coefs(5) = 0;
79
80 return SeriesBiquad(coefs);
81}
82
83std::unique_ptr<Filter> SeriesBiquad::clone() const {
84 // sos.as_col() concatenates all columns, exactly what we want.
85 return std::make_unique<SeriesBiquad>(sos.as_col());
86}
88 DEBUGTRACE_ENTER;
89 state.zeros();
90}
92
93 DEBUGTRACE_ENTER;
94
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);
103
104 d w1 = state(0, filterno);
105 d w2 = state(1, filterno);
106
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;
110 w2 = w1;
111 w1 = w0;
112 inout(sample) = yn;
113 }
114
115 state(0, filterno) = w1;
116 state(1, filterno) = w2;
117 }
118}
119
120BiquadBank::BiquadBank(const dmat &filters, const vd *gains) {
121 DEBUGTRACE_ENTER;
126 lock lck(_mtx);
127
128 for (us i = 0; i < filters.n_cols; i++) {
129 _filters.emplace_back(filters.col(i));
130 }
131
132 if (gains != nullptr) {
133 setGains(*gains);
134 } else {
135 _gains = vd(_filters.size(), arma::fill::ones);
136 }
137}
138void BiquadBank::setGains(const vd &gains) {
139 DEBUGTRACE_ENTER;
140 lock lck(_mtx);
141
142 const us nfilters = _filters.size();
143 if (gains.size() != nfilters) {
144 throw rte("Invalid number of gain values given.");
145 }
146 _gains = gains;
147}
148
149void BiquadBank::filter(vd &inout) {
150
151 lock lck(_mtx);
152 std::vector<std::future<vd>> futs;
153
154#if 1
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);
160 return inout;
161 }, // Launch a task to filter.
162 inout_cpy, i // Column i as argument to the lambda function above.
163 ));
164 }
165
166 // Zero-out in-out and sum-up the filtered values
167 inout.zeros();
168 for (us i = 0; i < _filters.size(); i++) {
169 inout += futs[i].get() * _gains[i];
170 }
171#else
173 vd inout_cpy = inout;
174 inout.zeros();
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);
179 }
180#endif
181}
183 DEBUGTRACE_ENTER;
184 lock lck(_mtx);
185 for (auto &f : _filters) {
186 f.reset();
187 }
188}
189std::unique_ptr<Filter> BiquadBank::clone() const {
190 lock lck(_mtx);
191 return std::make_unique<BiquadBank>(_filters, _gains);
192}
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(...)
Definition lasp_thread.h:31
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::runtime_error rte
Definition lasp_daq.cpp:16
std::scoped_lock< std::mutex > lock
arma::Col< d > vd
arma::Mat< d > dmat
std::scoped_lock< std::mutex > lck
size_t us
We often use boolean values.
Definition lasp_types.h:29