From 7785f61677b184ed2ca40424d752e94524cb02b6 Mon Sep 17 00:00:00 2001 From: Niklas Carlsson Date: Fri, 6 Dec 2024 12:57:34 +0100 Subject: [PATCH] audioconvert: align low-/highpass calculations The current biquad calculations are based on RBJ's cookbook [1], except for low-/highpass. Since the filter configuration is also based on using the definition of Q, it makes sense to also align the remaining calculations to use the same filter cookbook instead of using resonance which doesn't result in the same coefficients as when using Q. [1] = https://www.w3.org/TR/audio-eq-cookbook/ --- spa/plugins/audioconvert/biquad.c | 72 +++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/spa/plugins/audioconvert/biquad.c b/spa/plugins/audioconvert/biquad.c index 68a6d8775..d9bb7e3dd 100644 --- a/spa/plugins/audioconvert/biquad.c +++ b/spa/plugins/audioconvert/biquad.c @@ -15,8 +15,8 @@ #define M_PI 3.14159265358979323846 #endif -/* S = 1 in Q */ -#define BIQUAD_SHELVING_DEFAULT_Q 0.707106781186548 +/* Q = 1 / sqrt(2), also resulting Q value when S = 1 */ +#define BIQUAD_DEFAULT_Q 0.707106781186548 static void set_coefficient(struct biquad *bq, double b0, double b1, double b2, double a0, double a1, double a2) @@ -29,7 +29,7 @@ static void set_coefficient(struct biquad *bq, double b0, double b1, double b2, bq->a2 = (float)(a2 * a0_inv); } -static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance) +static void biquad_lowpass(struct biquad *bq, double cutoff, double Q) { /* Limit cutoff to 0 to 1. */ cutoff = fmax(0.0, fmin(cutoff, 1.0)); @@ -43,27 +43,27 @@ static void biquad_lowpass(struct biquad *bq, double cutoff, double resonance) return; } + /* Set Q to a sane default value if not set */ + if (Q <= 0) + Q = BIQUAD_DEFAULT_Q; + /* Compute biquad coefficients for lowpass filter */ - resonance = fmax(0.0, resonance); /* can't go negative */ - double g = pow(10.0, 0.05 * resonance); - double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); + /* H(s) = 1 / (s^2 + s/Q + 1) */ + double w0 = M_PI * cutoff; + double alpha = sin(w0) / (2 * Q); + double k = cos(w0); - double theta = M_PI * cutoff; - double sn = 0.5 * d * sin(theta); - double beta = 0.5 * (1 - sn) / (1 + sn); - double gamma = (0.5 + beta) * cos(theta); - double alpha = 0.25 * (0.5 + beta - gamma); + double b0 = (1 - k) / 2; + double b1 = 1 - k; + double b2 = (1 - k) / 2; + double a0 = 1 + alpha; + double a1 = -2 * k; + double a2 = 1 - alpha; - double b0 = 2 * alpha; - double b1 = 2 * 2 * alpha; - double b2 = 2 * alpha; - double a1 = 2 * -gamma; - double a2 = 2 * beta; - - set_coefficient(bq, b0, b1, b2, 1, a1, a2); + set_coefficient(bq, b0, b1, b2, a0, a1, a2); } -static void biquad_highpass(struct biquad *bq, double cutoff, double resonance) +static void biquad_highpass(struct biquad *bq, double cutoff, double Q) { /* Limit cutoff to 0 to 1. */ cutoff = fmax(0.0, fmin(cutoff, 1.0)); @@ -79,24 +79,24 @@ static void biquad_highpass(struct biquad *bq, double cutoff, double resonance) return; } + /* Set Q to a sane default value if not set */ + if (Q <= 0) + Q = BIQUAD_DEFAULT_Q; + /* Compute biquad coefficients for highpass filter */ - resonance = fmax(0.0, resonance); /* can't go negative */ - double g = pow(10.0, 0.05 * resonance); - double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); + /* H(s) = s^2 / (s^2 + s/Q + 1) */ + double w0 = M_PI * cutoff; + double alpha = sin(w0) / (2 * Q); + double k = cos(w0); - double theta = M_PI * cutoff; - double sn = 0.5 * d * sin(theta); - double beta = 0.5 * (1 - sn) / (1 + sn); - double gamma = (0.5 + beta) * cos(theta); - double alpha = 0.25 * (0.5 + beta + gamma); + double b0 = (1 + k) / 2; + double b1 = -(1 + k); + double b2 = (1 + k) / 2; + double a0 = 1 + alpha; + double a1 = -2 * k; + double a2 = 1 - alpha; - double b0 = 2 * alpha; - double b1 = 2 * -2 * alpha; - double b2 = 2 * alpha; - double a1 = 2 * -gamma; - double a2 = 2 * beta; - - set_coefficient(bq, b0, b1, b2, 1, a1, a2); + set_coefficient(bq, b0, b1, b2, a0, a1, a2); } static void biquad_bandpass(struct biquad *bq, double frequency, double Q) @@ -161,7 +161,7 @@ static void biquad_lowshelf(struct biquad *bq, double frequency, double Q, /* Set Q to an equivalent value to S = 1 if not specified */ if (Q <= 0) - Q = BIQUAD_SHELVING_DEFAULT_Q; + Q = BIQUAD_DEFAULT_Q; double w0 = M_PI * frequency; double alpha = sin(w0) / (2 * Q); @@ -201,7 +201,7 @@ static void biquad_highshelf(struct biquad *bq, double frequency, double Q, /* Set Q to an equivalent value to S = 1 if not specified */ if (Q <= 0) - Q = BIQUAD_SHELVING_DEFAULT_Q; + Q = BIQUAD_DEFAULT_Q; double w0 = M_PI * frequency; double alpha = sin(w0) / (2 * Q);