1 /* libSoX Biquad filter effects (c) 2006-8 robs@users.sourceforge.net
3 * This library is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU Lesser General Public License as published by
5 * the Free Software Foundation; either version 2.1 of the License, or (at
6 * your option) any later version.
8 * This library is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
11 * General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this library; if not, write to the Free Software Foundation,
15 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 * 2-pole filters designed by Robert Bristow-Johnson <rbj@audioimagination.com>
19 * see https://webaudio.github.io/Audio-EQ-Cookbook/audio-eq-cookbook.html
21 * 1-pole filters based on code (c) 2000 Chris Bagwell <cbagwell@sprynet.com>
22 * Algorithms: Recursive single pole low/high pass filter
23 * Reference: The Scientist and Engineer's Guide to Digital Signal Processing
25 * low-pass: output[N] = input[N] * A + output[N-1] * B
26 * X = exp(-2.0 * pi * Fc)
29 * Fc = cutoff freq / sample rate
31 * Mimics an RC low-pass filter:
33 * ---/\/\/\/\----------->
41 * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
42 * X = exp(-2.0 * pi * Fc)
46 * Fc = cutoff freq / sample rate
48 * Mimics an RC high-pass filter:
65 typedef biquad_t priv_t
;
68 static int hilo1_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
69 return lsx_biquad_getopts(effp
, argc
, argv
, 1, 1, 0, 1, 2, "",
70 *effp
->handler
.name
== 'l'? filter_LPF_1
: filter_HPF_1
);
74 static int hilo2_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
75 priv_t
* p
= (priv_t
*)effp
->priv
;
76 if (argc
> 1 && strcmp(argv
[1], "-1") == 0)
77 return hilo1_getopts(effp
, argc
- 1, argv
+ 1);
78 if (argc
> 1 && strcmp(argv
[1], "-2") == 0)
80 p
->width
= sqrt(0.5); /* Default to Butterworth */
81 return lsx_biquad_getopts(effp
, argc
, argv
, 1, 2, 0, 1, 2, "qohk",
82 *effp
->handler
.name
== 'l'? filter_LPF
: filter_HPF
);
86 static int bandpass_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
87 filter_t type
= filter_BPF
;
88 if (argc
> 1 && strcmp(argv
[1], "-c") == 0)
89 ++argv
, --argc
, type
= filter_BPF_CSG
;
90 return lsx_biquad_getopts(effp
, argc
, argv
, 2, 2, 0, 1, 2, "hkqob", type
);
94 static int bandrej_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
95 return lsx_biquad_getopts(effp
, argc
, argv
, 2, 2, 0, 1, 2, "hkqob", filter_notch
);
99 static int allpass_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
100 filter_t type
= filter_APF
;
102 if (argc
> 1 && strcmp(argv
[1], "-1") == 0)
103 ++argv
, --argc
, type
= filter_AP1
;
104 else if (argc
> 1 && strcmp(argv
[1], "-2") == 0)
105 ++argv
, --argc
, type
= filter_AP2
;
106 m
= 1 + (type
== filter_APF
);
107 return lsx_biquad_getopts(effp
, argc
, argv
, m
, m
, 0, 1, 2, "hkqo", type
);
111 static int tone_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
112 priv_t
* p
= (priv_t
*)effp
->priv
;
114 p
->fc
= *effp
->handler
.name
== 'b'? 100 : 3000;
115 return lsx_biquad_getopts(effp
, argc
, argv
, 1, 3, 1, 2, 0, "shkqo",
116 *effp
->handler
.name
== 'b'? filter_lowShelf
: filter_highShelf
);
120 static int equalizer_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
121 return lsx_biquad_getopts(effp
, argc
, argv
, 3, 3, 0, 1, 2, "qohk", filter_peakingEQ
);
125 static int band_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
126 filter_t type
= filter_BPF_SPK
;
127 if (argc
> 1 && strcmp(argv
[1], "-n") == 0)
128 ++argv
, --argc
, type
= filter_BPF_SPK_N
;
129 return lsx_biquad_getopts(effp
, argc
, argv
, 1, 2, 0, 1, 2, "hkqo", type
);
133 static int deemph_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
134 return lsx_biquad_getopts(effp
, argc
, argv
, 0, 0, 0, 1, 2, "s", filter_deemph
);
138 static int riaa_getopts(sox_effect_t
* effp
, int argc
, char **argv
) {
139 priv_t
* p
= (priv_t
*)effp
->priv
;
140 p
->filter_type
= filter_riaa
;
142 return --argc
? lsx_usage(effp
) : SOX_SUCCESS
;
146 static void make_poly_from_roots(
147 double const * roots
, size_t num_roots
, double * poly
)
152 memset(poly
+ 2, 0, (num_roots
+ 1 - 2) * sizeof(*poly
));
153 for (i
= 1; i
< num_roots
; ++i
)
154 for (j
= num_roots
; j
> 0; --j
)
155 poly
[j
] -= poly
[j
- 1] * roots
[i
];
158 static int start(sox_effect_t
* effp
)
160 priv_t
* p
= (priv_t
*)effp
->priv
;
161 double w0
, A
, alpha
, mult
;
163 if (p
->filter_type
== filter_deemph
) { /* See deemph.plt for documentation */
164 if (effp
->in_signal
.rate
== 44100) {
169 else if (effp
->in_signal
.rate
== 48000) {
175 lsx_fail("sample rate must be 44100 (audio-CD) or 48000 (DAT)");
180 w0
= 2 * M_PI
* p
->fc
/ effp
->in_signal
.rate
;
181 A
= exp(p
->gain
/ 40 * log(10.));
182 alpha
= 0, mult
= dB_to_linear(max(p
->gain
, 0));
185 lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)");
190 p
->b0
= p
->b1
= p
->b2
= p
->a1
= p
->a2
= 0;
193 if (p
->width
) switch (p
->width_type
) {
195 alpha
= sin(w0
)/2 * sqrt((A
+ 1/A
)*(1/p
->width
- 1) + 2);
199 alpha
= sin(w0
)/(2*p
->width
);
203 alpha
= sin(w0
)*sinh(log(2.)/2 * p
->width
* w0
/sin(w0
));
207 alpha
= sin(w0
)/(2*p
->fc
/p
->width
);
210 case width_bw_kHz
: assert(0); /* Shouldn't get here */
213 alpha
= tan(M_PI
* p
->width
/ effp
->in_signal
.rate
);
216 switch (p
->filter_type
) {
217 case filter_LPF
: /* H(s) = 1 / (s^2 + s/Q + 1) */
218 p
->b0
= (1 - cos(w0
))/2;
220 p
->b2
= (1 - cos(w0
))/2;
226 case filter_HPF
: /* H(s) = s^2 / (s^2 + s/Q + 1) */
227 p
->b0
= (1 + cos(w0
))/2;
228 p
->b1
= -(1 + cos(w0
));
229 p
->b2
= (1 + cos(w0
))/2;
235 case filter_BPF_CSG
: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */
244 case filter_BPF
: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */
253 case filter_notch
: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */
262 case filter_APF
: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */
271 case filter_peakingEQ
: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */
282 case filter_lowShelf
: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */
285 p
->b0
= A
*( (A
+1) - (A
-1)*cos(w0
) + 2*sqrt(A
)*alpha
);
286 p
->b1
= 2*A
*( (A
-1) - (A
+1)*cos(w0
) );
287 p
->b2
= A
*( (A
+1) - (A
-1)*cos(w0
) - 2*sqrt(A
)*alpha
);
288 p
->a0
= (A
+1) + (A
-1)*cos(w0
) + 2*sqrt(A
)*alpha
;
289 p
->a1
= -2*( (A
-1) + (A
+1)*cos(w0
) );
290 p
->a2
= (A
+1) + (A
-1)*cos(w0
) - 2*sqrt(A
)*alpha
;
293 case filter_deemph
: /* Falls through to high-shelf... */
295 case filter_highShelf
: /* H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A) */
298 p
->b0
= A
*( (A
+1) + (A
-1)*cos(w0
) + 2*sqrt(A
)*alpha
);
299 p
->b1
= -2*A
*( (A
-1) + (A
+1)*cos(w0
) );
300 p
->b2
= A
*( (A
+1) + (A
-1)*cos(w0
) - 2*sqrt(A
)*alpha
);
301 p
->a0
= (A
+1) - (A
-1)*cos(w0
) + 2*sqrt(A
)*alpha
;
302 p
->a1
= 2*( (A
-1) - (A
+1)*cos(w0
) );
303 p
->a2
= (A
+1) - (A
-1)*cos(w0
) - 2*sqrt(A
)*alpha
;
306 case filter_LPF_1
: /* single-pole */
311 case filter_HPF_1
: /* single-pole */
313 p
->b0
= (1 - p
->a1
)/2;
317 case filter_BPF_SPK
: case filter_BPF_SPK_N
: {
320 p
->width
= p
->fc
/ 2;
321 bw_Hz
= p
->width_type
== width_Q
? p
->fc
/ p
->width
:
322 p
->width_type
== width_bw_Hz
? p
->width
:
323 p
->fc
* (pow(2., p
->width
) - 1) * pow(2., -0.5 * p
->width
); /* bw_oct */
324 #include "band.h" /* Has different licence */
328 case filter_AP1
: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */
334 case filter_AP2
: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */
336 p
->b1
= -2 * cos(w0
);
339 p
->a1
= -2 * cos(w0
);
343 case filter_riaa
: /* http://www.dsprelated.com/showmessage/73300/3.php */
344 if (effp
->in_signal
.rate
== 44100) {
345 static const double zeros
[] = {-0.2014898, 0.9233820};
346 static const double poles
[] = {0.7083149, 0.9924091};
347 make_poly_from_roots(zeros
, (size_t)2, &p
->b0
);
348 make_poly_from_roots(poles
, (size_t)2, &p
->a0
);
350 else if (effp
->in_signal
.rate
== 48000) {
351 static const double zeros
[] = {-0.1766069, 0.9321590};
352 static const double poles
[] = {0.7396325, 0.9931330};
353 make_poly_from_roots(zeros
, (size_t)2, &p
->b0
);
354 make_poly_from_roots(poles
, (size_t)2, &p
->a0
);
356 else if (effp
->in_signal
.rate
== 88200) {
357 static const double zeros
[] = {-0.1168735, 0.9648312};
358 static const double poles
[] = {0.8590646, 0.9964002};
359 make_poly_from_roots(zeros
, (size_t)2, &p
->b0
);
360 make_poly_from_roots(poles
, (size_t)2, &p
->a0
);
362 else if (effp
->in_signal
.rate
== 96000) {
363 static const double zeros
[] = {-0.1141486, 0.9676817};
364 static const double poles
[] = {0.8699137, 0.9966946};
365 make_poly_from_roots(zeros
, (size_t)2, &p
->b0
);
366 make_poly_from_roots(poles
, (size_t)2, &p
->a0
);
368 else if (effp
->in_signal
.rate
== 192000) {
369 static const double zeros
[] = {-0.1040610965, 0.9837523263};
370 static const double poles
[] = {0.9328992971, 0.9983633125};
371 make_poly_from_roots(zeros
, (size_t)2, &p
->b0
);
372 make_poly_from_roots(poles
, (size_t)2, &p
->a0
);
375 lsx_fail("Sample rate must be 44.1k, 48k, 88.2k, 96k, or 192k");
378 { /* Normalise to 0dB at 1kHz (Thanks to Glenn Davis) */
379 double y
= 2 * M_PI
* 1000 / effp
->in_signal
.rate
;
380 double b_re
= p
->b0
+ p
->b1
* cos(-y
) + p
->b2
* cos(-2 * y
);
381 double a_re
= p
->a0
+ p
->a1
* cos(-y
) + p
->a2
* cos(-2 * y
);
382 double b_im
= p
->b1
* sin(-y
) + p
->b2
* sin(-2 * y
);
383 double a_im
= p
->a1
* sin(-y
) + p
->a2
* sin(-2 * y
);
384 double g
= 1 / sqrt((sqr(b_re
) + sqr(b_im
)) / (sqr(a_re
) + sqr(a_im
)));
385 p
->b0
*= g
; p
->b1
*= g
; p
->b2
*= g
;
387 mult
= (p
->b0
+ p
->b1
+ p
->b2
) / (p
->a0
+ p
->a1
+ p
->a2
);
388 lsx_debug("gain=%f", linear_to_dB(mult
));
391 if (effp
->in_signal
.mult
)
392 *effp
->in_signal
.mult
/= mult
;
393 return lsx_biquad_start(effp
);
397 #define BIQUAD_EFFECT(name,group,usage,flags) \
398 sox_effect_handler_t const * lsx_##name##_effect_fn(void) { \
399 static sox_effect_handler_t handler = { \
400 #name, usage, flags, \
401 group##_getopts, start, lsx_biquad_flow, 0, 0, 0, sizeof(biquad_t)\
406 BIQUAD_EFFECT(highpass
, hilo2
, "[-1|-2] frequency [width[q|o|h|k](0.707q)]", 0)
407 BIQUAD_EFFECT(lowpass
, hilo2
, "[-1|-2] frequency [width[q|o|h|k]](0.707q)", 0)
408 BIQUAD_EFFECT(bandpass
, bandpass
, "[-c] frequency width[h|k|q|o]", 0)
409 BIQUAD_EFFECT(bandreject
,bandrej
, "frequency width[h|k|q|o]", 0)
410 BIQUAD_EFFECT(allpass
, allpass
, "frequency width[h|k|q|o]", 0)
411 BIQUAD_EFFECT(bass
, tone
, "gain [frequency(100) [width[s|h|k|q|o]](0.5s)]", 0)
412 BIQUAD_EFFECT(treble
, tone
, "gain [frequency(3000) [width[s|h|k|q|o]](0.5s)]", 0)
413 BIQUAD_EFFECT(equalizer
, equalizer
,"frequency width[q|o|h|k] gain", 0)
414 BIQUAD_EFFECT(band
, band
, "[-n] center [width[h|k|q|o]]", 0)
415 BIQUAD_EFFECT(deemph
, deemph
, NULL
, 0)
416 BIQUAD_EFFECT(riaa
, riaa
, NULL
, 0)