arduino-audio-tools
Filter.h
1 #pragma once
2 #include "AudioConfig.h"
3 #include <math.h>
4 #ifdef USE_TYPETRAITS
5 #include <type_traits>
6 #endif
7 
20 namespace audio_tools {
21 
27 template <typename T>
28 class Filter {
29  public:
30  // construct without coefs
31  Filter() = default;
32  virtual ~Filter() = default;
33  Filter(Filter const &) = delete;
34  Filter &operator=(Filter const &) = delete;
35 
36  virtual T process(T in) = 0;
37 };
38 
45 template <typename T>
46 class NoFilter : Filter<T> {
47  public:
48  // construct without coefs
49  NoFilter() = default;
50  virtual T process(T in) { return in; }
51 };
52 
62 template <typename T>
63 class FIR : public Filter<T> {
64  public:
65  template <size_t B>
66  FIR(const T (&b)[B], const T factor = 1.0) : lenB(B), factor(factor) {
67  setValues(b);
68  }
69 
70  template <size_t B>
71  void setValues(const T (&b)[B]) {
72  if (x != nullptr) delete x;
73  x = new T[lenB]();
74  coeff_b = new T[2 * lenB - 1];
75  for (uint16_t i = 0; i < 2 * lenB - 1; i++) {
76  coeff_b[i] = b[(2 * lenB - 1 - i) % lenB];
77  }
78  }
79 
80  ~FIR() {
81  delete[] x;
82  delete[] coeff_b;
83  }
84  T process(T value) {
85  x[i_b] = value;
86  T b_terms = 0;
87  T *b_shift = &coeff_b[lenB - i_b - 1];
88  for (uint8_t i = 0; i < lenB; i++) {
89  b_terms += b_shift[i] * x[i];
90  }
91  i_b++;
92  if (i_b == lenB) i_b = 0;
93 
94 #ifdef USE_TYPETRAITS
95  if (!(std::is_same<T, float>::value || std::is_same<T, float>::value)) {
96  b_terms = b_terms / factor;
97  }
98 #else
99  if (factor != 1.0) {
100  b_terms = b_terms / factor;
101  }
102 #endif
103  return b_terms;
104  }
105 
106  private:
107  const uint8_t lenB;
108  uint8_t i_b = 0;
109  T *x = nullptr;
110  T *coeff_b;
111  T factor;
112 };
113 
122 template <typename T>
123 class IIR : public Filter<T> {
124  public:
125  template <size_t B, size_t A>
126  IIR(const T (&b)[B], const T (&_a)[A], T factor = 1.0)
127  : factor(factor), lenB(B), lenA(A - 1) {
128  x = new T[lenB]();
129  y = new T[lenA]();
130  coeff_b = new T[2 * lenB - 1];
131  coeff_a = new T[2 * lenA - 1];
132  T a0 = _a[0];
133  const T *a = &_a[1];
134  for (uint16_t i = 0; i < 2 * lenB - 1; i++) {
135  coeff_b[i] = b[(2 * lenB - 1 - i) % lenB] / a0;
136  }
137  for (uint16_t i = 0; i < 2 * lenA - 1; i++) {
138  coeff_a[i] = a[(2 * lenA - 2 - i) % lenA] / a0;
139  }
140  }
141 
142  ~IIR() {
143  delete[] x;
144  delete[] y;
145  delete[] coeff_a;
146  delete[] coeff_b;
147  }
148 
149  T process(T value) {
150  x[i_b] = value;
151  T b_terms = 0;
152  T *b_shift = &coeff_b[lenB - i_b - 1];
153 
154  T a_terms = 0;
155  T *a_shift = &coeff_a[lenA - i_a - 1];
156 
157  for (uint8_t i = 0; i < lenB; i++) {
158  b_terms += x[i] * b_shift[i];
159  }
160  for (uint8_t i = 0; i < lenA; i++) {
161  a_terms += y[i] * a_shift[i];
162  }
163 
164  T filtered = b_terms - a_terms;
165  y[i_a] = filtered;
166  i_b++;
167  if (i_b == lenB) i_b = 0;
168  i_a++;
169  if (i_a == lenA) i_a = 0;
170 
171 #ifdef USE_TYPETRAITS
172  if (!(std::is_same<T, float>::value || std::is_same<T, float>::value)) {
173  filtered = filtered / factor;
174  }
175 #else
176  if (factor != 1.0) {
177  filtered = filtered / factor;
178  }
179 #endif
180  return filtered;
181  }
182 
183  private:
184  T factor;
185  const uint8_t lenB, lenA;
186  uint8_t i_b = 0, i_a = 0;
187  T *x;
188  T *y;
189  T *coeff_b;
190  T *coeff_a;
191 };
192 
202 template <typename T>
203 class BiQuadDF1 : public Filter<T> {
204  public:
205  BiQuadDF1(const T (&b)[3], const T (&a)[3])
206  : b_0(b[0] / a[0]),
207  b_1(b[1] / a[0]),
208  b_2(b[2] / a[0]),
209  a_1(a[1] / a[0]),
210  a_2(a[2] / a[0]) {}
211  BiQuadDF1(const T (&b)[3], const T (&a)[2])
212  : b_0(b[0]), b_1(b[1]), b_2(b[2]), a_1(a[0]), a_2(a[1]) {}
213  BiQuadDF1(const T (&b)[3], const T (&a)[2], T gain)
214  : b_0(gain * b[0]),
215  b_1(gain * b[1]),
216  b_2(gain * b[2]),
217  a_1(a[0]),
218  a_2(a[1]) {}
219  BiQuadDF1(const T (&b)[3], const T (&a)[3], T gain)
220  : b_0(gain * b[0] / a[0]),
221  b_1(gain * b[1] / a[0]),
222  b_2(gain * b[2] / a[0]),
223  a_1(a[1] / a[0]),
224  a_2(a[2] / a[0]) {}
225 
226  T process(T value) {
227  T x_2 = x_1;
228  x_1 = x_0;
229  x_0 = value;
230  T b_terms = x_0 * b_0 + x_1 * b_1 + x_2 * b_2;
231  T a_terms = y_1 * a_1 + y_2 * a_2;
232  y_2 = y_1;
233  y_1 = b_terms - a_terms;
234  return y_1;
235  }
236 
237  private:
238  T b_0;
239  T b_1;
240  T b_2;
241  T a_1;
242  T a_2;
243 
244  T x_0 = 0;
245  T x_1 = 0;
246  T y_1 = 0;
247  T y_2 = 0;
248 };
249 
260 template <typename T>
261 class BiQuadDF2 : public Filter<T> {
262  public:
263  BiQuadDF2(const T (&b)[3], const T (&a)[3])
264  : b_0(b[0] / a[0]),
265  b_1(b[1] / a[0]),
266  b_2(b[2] / a[0]),
267  a_1(a[1] / a[0]),
268  a_2(a[2] / a[0]) {}
269  BiQuadDF2(const T (&b)[3], const T (&a)[2])
270  : b_0(b[0]), b_1(b[1]), b_2(b[2]), a_1(a[0]), a_2(a[1]) {}
271  BiQuadDF2(const T (&b)[3], const T (&a)[2], T gain)
272  : b_0(gain * b[0]),
273  b_1(gain * b[1]),
274  b_2(gain * b[2]),
275  a_1(a[0]),
276  a_2(a[1]) {}
277  BiQuadDF2(const T (&b)[3], const T (&a)[3], T gain)
278  : b_0(gain * b[0] / a[0]),
279  b_1(gain * b[1] / a[0]),
280  b_2(gain * b[2] / a[0]),
281  a_1(a[1] / a[0]),
282  a_2(a[2] / a[0]) {}
283 
284  T process(T value) {
285  T w_2 = w_1;
286  w_1 = w_0;
287  w_0 = value - a_1 * w_1 - a_2 * w_2;
288  T y = b_0 * w_0 + b_1 * w_1 + b_2 * w_2;
289  return y;
290  }
291 
292  protected:
293  T b_0 = 0;
294  T b_1 = 0;
295  T b_2 = 0;
296  T a_1 = 0;
297  T a_2 = 0;
298 
299  // allow constructor w/o parameter in subclasses
300  BiQuadDF2() = default;
301 
302  T w_0 = 0;
303  T w_1 = 0;
304 };
305 
318 template <typename T>
319 class LowPassFilter : public BiQuadDF2<T> {
320  public:
321  LowPassFilter(float frequency, float sampleRate, float q = 0.7071f)
322  : BiQuadDF2<T>() {
323  begin(frequency, sampleRate, q);
324  }
325  void begin(float frequency, float sampleRate, float q = 0.7071f) {
326  T w0 = frequency * (2.0f * 3.141592654f / sampleRate);
327  T sinW0 = sin(w0);
328  T alpha = sinW0 / ((float)q * 2.0);
329  T cosW0 = cos(w0);
330  T scale = 1.0 / (1.0 + alpha);
331  BiQuadDF2<T>::b_0 = ((1.0 - cosW0) / 2.0) * scale;
332  BiQuadDF2<T>::b_1 = (1.0 - cosW0) * scale;
334  BiQuadDF2<T>::a_1 = (-2.0 * cosW0) * scale;
335  BiQuadDF2<T>::a_2 = (1.0 - alpha) * scale;
336  }
337 };
338 
351 template <typename T>
352 class HighPassFilter : public BiQuadDF2<T> {
353  public:
354  HighPassFilter(float frequency, float sampleRate, float q = 0.7071)
355  : BiQuadDF2<T>() {
356  begin(frequency, sampleRate, q);
357  }
358  void begin(float frequency, float sampleRate, float q = 0.7071) {
359  T w0 = frequency * (2.0f * 3.141592654f / sampleRate);
360  T sinW0 = sin(w0);
361  T alpha = sinW0 / ((float)q * 2.0);
362  T cosW0 = cos(w0);
363  T scale = 1.0 / (1.0 + alpha);
364  BiQuadDF2<T>::b_0 = ((1.0 + cosW0) / 2.0) * scale;
365  BiQuadDF2<T>::b_1 = -(1.0 + cosW0) * scale;
367  BiQuadDF2<T>::a_1 = (-2.0 * cosW0) * scale;
368  BiQuadDF2<T>::a_2 = (1.0 - alpha) * scale;
369  }
370 };
371 
384 template <typename T>
385 class BandPassFilter : public BiQuadDF2<T> {
386  public:
387  BandPassFilter(float frequency, float sampleRate, float q = 1.0)
388  : BiQuadDF2<T>() {
389  begin(frequency, sampleRate, q);
390  }
391  void begin(float frequency, float sampleRate, float q = 1.0) {
392  T w0 = frequency * (2.0f * 3.141592654f / sampleRate);
393  T sinW0 = sin(w0);
394  T alpha = sinW0 / ((T)q * 2.0);
395  T cosW0 = cos(w0);
396  T scale = 1.0 / (1.0 + alpha);
397  BiQuadDF2<T>::b_0 = alpha * scale;
398  BiQuadDF2<T>::b_1 = 0;
399  BiQuadDF2<T>::b_2 = (-alpha) * scale;
400  BiQuadDF2<T>::a_1 = (-2.0 * cosW0) * scale;
401  BiQuadDF2<T>::a_2 = (1.0 - alpha) * scale;
402  }
403 };
404 
417 template <typename T>
418 class NotchFilter : public BiQuadDF2<T> {
419  public:
420  NotchFilter(float frequency, float sampleRate, float q = 1.0)
421  : BiQuadDF2<T>() {
422  begin(frequency, sampleRate, q);
423  }
424 
425  void begin(float frequency, float sampleRate, float q = 1.0) {
426  T w0 = frequency * (2.0f * 3.141592654f / sampleRate);
427  T sinW0 = sin(w0);
428  T alpha = sinW0 / ((float)q * 2.0);
429  T cosW0 = cos(w0);
430  T scale = 1.0 / (1.0 + alpha);
431  BiQuadDF2<T>::b_0 = scale;
432  BiQuadDF2<T>::b_1 = (-2.0 * cosW0) * scale;
434  BiQuadDF2<T>::a_1 = (-2.0 * cosW0) * scale;
435  BiQuadDF2<T>::a_2 = (1.0 - alpha) * scale;
436  }
437 };
438 
451 template <typename T>
452 class LowShelfFilter : public BiQuadDF2<T> {
453  public:
454  LowShelfFilter(float frequency, float sampleRate, float gain,
455  float slope = 1.0f)
456  : BiQuadDF2<T>() {
457  begin(frequency, sampleRate, gain, slope);
458  }
459 
460  void begin(float frequency, float sampleRate, float gain,
461  float slope = 1.0f) {
462  T a = pow(10.0, gain / 40.0f);
463  T w0 = frequency * (2.0f * 3.141592654f / sampleRate);
464  T sinW0 = sin(w0);
465  // float alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
466  T cosW0 = cos(w0);
467  // generate three helper-values (intermediate results):
468  T sinsq = sinW0 *
469  sqrt((pow(a, 2.0) + 1.0) * (1.0 / (float)slope - 1.0) + 2.0 * a);
470  T aMinus = (a - 1.0) * cosW0;
471  T aPlus = (a + 1.0) * cosW0;
472  T scale = 1.0 / ((a + 1.0) + aMinus + sinsq);
473  BiQuadDF2<T>::b_0 = a * ((a + 1.0) - aMinus + sinsq) * scale;
474  BiQuadDF2<T>::b_1 = 2.0 * a * ((a - 1.0) - aPlus) * scale;
475  BiQuadDF2<T>::b_2 = a * ((a + 1.0) - aMinus - sinsq) * scale;
476  BiQuadDF2<T>::a_1 = -2.0 * ((a - 1.0) + aPlus) * scale;
477  BiQuadDF2<T>::a_2 = ((a + 1.0) + aMinus - sinsq) * scale;
478  }
479 };
480 
493 template <typename T>
494 class HighShelfFilter : public BiQuadDF2<T> {
495  public:
496  HighShelfFilter(float frequency, float sampleRate, float gain,
497  float slope = 1.0f)
498  : BiQuadDF2<T>() {
499  begin(frequency, sampleRate, gain, slope);
500  }
501  void begin(float frequency, float sampleRate, float gain,
502  float slope = 1.0f) {
503  T a = pow(10.0, gain / 40.0f);
504  T w0 = frequency * (2.0f * 3.141592654f / sampleRate);
505  T sinW0 = sin(w0);
506  // float alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0;
507  T cosW0 = cos(w0);
508  // generate three helper-values (intermediate results):
509  T sinsq = sinW0 *
510  sqrt((pow(a, 2.0) + 1.0) * (1.0 / (float)slope - 1.0) + 2.0 * a);
511  T aMinus = (a - 1.0) * cosW0;
512  T aPlus = (a + 1.0) * cosW0;
513  T scale = 1.0 / ((a + 1.0) - aMinus + sinsq);
514  BiQuadDF2<T>::b_0 = a * ((a + 1.0) + aMinus + sinsq) * scale;
515  BiQuadDF2<T>::b_1 = -2.0 * a * ((a - 1.0) + aPlus) * scale;
516  BiQuadDF2<T>::b_2 = a * ((a + 1.0) + aMinus - sinsq) * scale;
517  BiQuadDF2<T>::a_1 = 2.0 * ((a - 1.0) - aPlus) * scale;
518  BiQuadDF2<T>::a_2 = ((a + 1.0) - aMinus - sinsq) * scale;
519  }
520 };
521 
532 template <typename T, size_t N>
533 class SOSFilter : public Filter<T> {
534  public:
535  SOSFilter(const T (&b)[N][3], const T (&a)[N][3], const T (&gain)[N]) {
536  for (size_t i = 0; i < N; i++)
537  filters[i] = new BiQuadDF2<T>(b[i], a[i], gain[i]);
538  }
539  SOSFilter(const T (&sos)[N][6], const T (&gain)[N]) {
540  for (size_t i = 0; i < N; i++) {
541  T b[3];
542  T a[3];
543  copy(b, &sos[i][0]);
544  copy(a, &sos[i][3]);
545  filters[i] = new BiQuadDF2<T>(b, a, gain[i]);
546  }
547  }
548  SOSFilter(const T (&b)[N][3], const T (&a)[N][2], const T (&gain)[N]) {
549  for (size_t i = 0; i < N; i++)
550  filters[i] = new BiQuadDF2<T>(b[i], a[i], gain[i]);
551  }
552  SOSFilter(const T (&b)[N][3], const T (&a)[N][2]) {
553  for (size_t i = 0; i < N; i++) filters[i] = new BiQuadDF2<T>(b[i], a[i]);
554  }
555  SOSFilter(const T (&b)[N][3], const T (&a)[N][3]) {
556  for (size_t i = 0; i < N; i++) filters[i] = new BiQuadDF2<T>(b[i], a[i]);
557  }
558  ~SOSFilter() {
559  for (size_t i = 0; i < N; i++) delete filters[i];
560  }
561  T process(T value) {
562  for (Filter<T> *&filter : filters) value = filter->process(value);
563  return value;
564  }
565 
566  private:
567  Filter<T> *filters[N];
568  template <size_t M>
569  void copy(T (&dest)[M], const T *src) {
570  for (size_t i = 0; i < M; i++) dest[i] = src[i];
571  }
572 };
573 
580 template <typename T, size_t N>
581 class FilterChain : public Filter<T> {
582  public:
583  FilterChain(Filter<T> *(&&filters)[N]) {
584  for (size_t i = 0; i < N; i++) {
585  this->filters[i] = filters[i];
586  }
587  }
588 
589  T process(T value) {
590  for (Filter<T> *&filter : filters) {
591  if (filter != nullptr) {
592  value = filter->process(value);
593  }
594  }
595  return value;
596  }
597 
598  private:
599  Filter<T> *filters[N] = {0};
600 };
601 
602 } // namespace audio_tools
Biquad DF2 Band Pass Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:385
Biquad DF1 Filter. converted from https://github.com/tttapa/Filters/blob/master/src/BiQuad....
Definition: Filter.h:203
Biquad DF2 Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:261
FIR Filter Converted from https://github.com/sebnil/FIR-filter-Arduino-Library/tree/master/src You ca...
Definition: Filter.h:63
FilterChain - A Cascade of multiple filters.
Definition: Filter.h:581
Abstract filter interface definition;.
Definition: Filter.h:28
Biquad DF2 High Pass Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:352
Biquad DF2 High Shelf Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:494
IIRFilter Converted from https://github.com/tttapa/Filters/blob/master/src/IIRFilter....
Definition: Filter.h:123
Biquad DF2 Low Pass Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:319
Biquad DF2 Low Shelf Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:452
No change to the input.
Definition: Filter.h:46
Biquad DF2 Notch Filter. When dealing with high-order IIR filters, they can get unstable....
Definition: Filter.h:418
Second Order Filter: Instead of manually cascading BiQuad filters, you can use a Second Order Section...
Definition: Filter.h:533
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10