filter-chain: use Q value for biquad shelving filters

The current implementation uses the slope variable S to define the
filter slope. Setting S = 1 results in a constant Q value of
sqrt(2)/2, or 0.7071, which is a good default value.

However, calculating alpha from the Q value instead, as done in
RBJ's cookbook [1], the filter shape can be changed which might
be desired for certain applications and provides flexibility.

Since the current implementation always defaulted to using S = 1,
make sure that configurations missing Q uses the same slope value.

[1] = https://www.w3.org/TR/audio-eq-cookbook/
This commit is contained in:
Niklas Carlsson 2024-10-16 15:04:10 +02:00 committed by Wim Taymans
parent 74fed1d208
commit 08af555e90

View file

@ -15,6 +15,9 @@
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
/* S = 1 in Q */
#define BIQUAD_SHELVING_DEFAULT_Q 0.707106781186548
static void set_coefficient(struct biquad *bq, double b0, double b1, double b2, static void set_coefficient(struct biquad *bq, double b0, double b1, double b2,
double a0, double a1, double a2) double a0, double a1, double a2)
{ {
@ -137,7 +140,8 @@ static void biquad_bandpass(struct biquad *bq, double frequency, double Q)
set_coefficient(bq, b0, b1, b2, a0, a1, a2); set_coefficient(bq, b0, b1, b2, a0, a1, a2);
} }
static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain) static void biquad_lowshelf(struct biquad *bq, double frequency, double Q,
double db_gain)
{ {
/* Clip frequencies to between 0 and 1, inclusive. */ /* Clip frequencies to between 0 and 1, inclusive. */
frequency = fmax(0.0, fmin(frequency, 1.0)); frequency = fmax(0.0, fmin(frequency, 1.0));
@ -155,9 +159,12 @@ static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain)
return; return;
} }
/* Set Q to an equivalent value to S = 1 if not specified */
if (Q <= 0)
Q = BIQUAD_SHELVING_DEFAULT_Q;
double w0 = M_PI * frequency; double w0 = M_PI * frequency;
double S = 1; /* filter slope (1 is max value) */ double alpha = sin(w0) / (2 * Q);
double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
double k = cos(w0); double k = cos(w0);
double k2 = 2 * sqrt(A) * alpha; double k2 = 2 * sqrt(A) * alpha;
double a_plus_one = A + 1; double a_plus_one = A + 1;
@ -173,7 +180,7 @@ static void biquad_lowshelf(struct biquad *bq, double frequency, double db_gain)
set_coefficient(bq, b0, b1, b2, a0, a1, a2); set_coefficient(bq, b0, b1, b2, a0, a1, a2);
} }
static void biquad_highshelf(struct biquad *bq, double frequency, static void biquad_highshelf(struct biquad *bq, double frequency, double Q,
double db_gain) double db_gain)
{ {
/* Clip frequencies to between 0 and 1, inclusive. */ /* Clip frequencies to between 0 and 1, inclusive. */
@ -192,9 +199,12 @@ static void biquad_highshelf(struct biquad *bq, double frequency,
return; return;
} }
/* Set Q to an equivalent value to S = 1 if not specified */
if (Q <= 0)
Q = BIQUAD_SHELVING_DEFAULT_Q;
double w0 = M_PI * frequency; double w0 = M_PI * frequency;
double S = 1; /* filter slope (1 is max value) */ double alpha = sin(w0) / (2 * Q);
double alpha = 0.5 * sin(w0) * sqrt((A + 1 / A) * (1 / S - 1) + 2);
double k = cos(w0); double k = cos(w0);
double k2 = 2 * sqrt(A) * alpha; double k2 = 2 * sqrt(A) * alpha;
double a_plus_one = A + 1; double a_plus_one = A + 1;
@ -341,10 +351,10 @@ void biquad_set(struct biquad *bq, enum biquad_type type, double freq, double Q,
biquad_bandpass(bq, freq, Q); biquad_bandpass(bq, freq, Q);
break; break;
case BQ_LOWSHELF: case BQ_LOWSHELF:
biquad_lowshelf(bq, freq, gain); biquad_lowshelf(bq, freq, Q, gain);
break; break;
case BQ_HIGHSHELF: case BQ_HIGHSHELF:
biquad_highshelf(bq, freq, gain); biquad_highshelf(bq, freq, Q, gain);
break; break;
case BQ_PEAKING: case BQ_PEAKING:
biquad_peaking(bq, freq, Q, gain); biquad_peaking(bq, freq, Q, gain);