|
DSPark 1.6.1
Header-only audio DSP framework in pure C++20 — zero dependencies
|
Biquad filter with Transposed Direct Form II, Audio EQ Cookbook coefficients, and thread-safe updates. More...
#include <algorithm>#include <array>#include <cmath>#include <numbers>#include <span>#include <atomic>#include <cassert>#include "AudioBuffer.h"#include "DenormalGuard.h"

Go to the source code of this file.
Classes | |
| struct | dspark::BiquadCoeffs |
| Stores normalised biquad coefficients (b0, b1, b2, a1, a2), always double. More... | |
| class | dspark::Biquad< T, MaxChannels > |
| Biquad filter using Transposed Direct Form II (TDF-II) with thread-safe updates. More... | |
Namespaces | |
| namespace | dspark |
| Main namespace for the DSPark framework. | |
Biquad filter with Transposed Direct Form II, Audio EQ Cookbook coefficients, and thread-safe updates.
Provides two classes:
Numerical design (why the filter core is double regardless of the sample type): a biquad whose corner sits far below the sample rate parks its poles a hair inside the unit circle - 1 - |z| is about pi*fc/(Q*fs), i.e. 5.8e-5 at 10 Hz / 768 kHz - so the denominator evaluated at DC, 1 + a1 + a2 = (2 - 2*cos w0)/a0, is a cancellation of terms of size 2 landing on 6.7e-9, far under the float resolution of the terms it is made of (1.2e-7). Realised in float that sum is no longer resolvable (measured 5.96e-08 against a true 2.68e-08 at 20 Hz / 768 kHz, Q = 2.5628), the recursion stops being contractive and its rounding error is integrated instead of decaying, which turns a high-pass into a DC SOURCE and, steep enough, into an oscillator. Measured on the float core this replaces, on a DC-free 1 kHz tone at -6 dBFS through FilterEngine: a 10 Hz 12 dB/oct corner published -1.46e-01 of sustained DC at 384 kHz with the peak inflated from 0.5068 to 0.6838 (+2.60 dB), a 20 Hz corner +6.62e-03 at 768 kHz (peak 0.5130 -> 0.5821, +1.10 dB), and the realised DC gain - the sum of the impulse response, which must be zero for a high-pass - reached +0.577 at 20 Hz / 768 kHz and -0.962 at 50 Hz / 768 kHz, i.e. the "high-pass" passed DC at unity. The impulse response says it plainly: 20 Hz at 768 kHz, peak |h| per half second, 24 dB/oct went 1.0 -> 4.1e-04 -> 4.8e-06 and then STOPPED decaying (2.7e-06, 1.6e-06, 8.6e-07: a pole on the circle), and 48 dB/oct went 1.0 -> 1.8e+19 -> 4.2e+37 -> non-finite - an unstable filter, reached from setHighPass(20, 0.707, 48) at a rate a host hits by oversampling 48 kHz by 16. In double the same cancellation carries an absolute error of about 2e-16, so the poles land within 1e-8 (relative) of the design; the same points now decay 1.0 -> 3.8e-04 -> 5.3e-15 -> 9.7e-26 and 1.0 -> 6.7e-04 -> 5.4e-10 -> 2.0e-15. Scalar double throughput matches float on x86-64 and ARM64, so coefficients, history and recursion are all double and only the buffer I/O is T; processSampleCore() exists so a cascade can keep the intermediate signal in the core precision instead of re-quantising it to T between stages.
Dependencies: C++20 standard library (<algorithm>, <array>, <atomic>, <cassert>, <cmath>, <numbers>, ).
Definition in file Biquad.h.