rate: add some sanity checking
[sox.git] / src / biquads.c
blob2b7ca73038862dde79e131a75cf05d094e574344
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)
27 * A = 1 - X
28 * B = X
29 * Fc = cutoff freq / sample rate
31 * Mimics an RC low-pass filter:
33 * ---/\/\/\/\----------->
34 * |
35 * --- C
36 * ---
37 * |
38 * |
39 * V
41 * high-pass: output[N] = A0 * input[N] + A1 * input[N-1] + B1 * output[N-1]
42 * X = exp(-2.0 * pi * Fc)
43 * A0 = (1 + X) / 2
44 * A1 = -(1 + X) / 2
45 * B1 = X
46 * Fc = cutoff freq / sample rate
48 * Mimics an RC high-pass filter:
50 * || C
51 * ----||--------->
52 * || |
53 * <
54 * > R
55 * <
56 * |
57 * V
61 #include "biquad.h"
62 #include <assert.h>
63 #include <string.h>
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)
79 ++argv, --argc;
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;
101 int m;
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;
113 p->width = 0.5;
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;
141 (void)argv;
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)
149 size_t i, j;
150 poly[0] = 1;
151 poly[1] = -roots[0];
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) {
165 p->fc = 5283;
166 p->width = 0.4845;
167 p->gain = -9.477;
169 else if (effp->in_signal.rate == 48000) {
170 p->fc = 5356;
171 p->width = 0.479;
172 p->gain = -9.62;
174 else {
175 lsx_fail("sample rate must be 44100 (audio-CD) or 48000 (DAT)");
176 return SOX_EOF;
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));
184 if (w0 > M_PI) {
185 lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)");
186 return SOX_EOF;
189 /* Set defaults: */
190 p->b0 = p->b1 = p->b2 = p->a1 = p->a2 = 0;
191 p->a0 = 1;
193 if (p->width) switch (p->width_type) {
194 case width_slope:
195 alpha = sin(w0)/2 * sqrt((A + 1/A)*(1/p->width - 1) + 2);
196 break;
198 case width_Q:
199 alpha = sin(w0)/(2*p->width);
200 break;
202 case width_bw_oct:
203 alpha = sin(w0)*sinh(log(2.)/2 * p->width * w0/sin(w0));
204 break;
206 case width_bw_Hz:
207 alpha = sin(w0)/(2*p->fc/p->width);
208 break;
210 case width_bw_kHz: assert(0); /* Shouldn't get here */
212 case width_bw_old:
213 alpha = tan(M_PI * p->width / effp->in_signal.rate);
214 break;
216 switch (p->filter_type) {
217 case filter_LPF: /* H(s) = 1 / (s^2 + s/Q + 1) */
218 p->b0 = (1 - cos(w0))/2;
219 p->b1 = 1 - cos(w0);
220 p->b2 = (1 - cos(w0))/2;
221 p->a0 = 1 + alpha;
222 p->a1 = -2*cos(w0);
223 p->a2 = 1 - alpha;
224 break;
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;
230 p->a0 = 1 + alpha;
231 p->a1 = -2*cos(w0);
232 p->a2 = 1 - alpha;
233 break;
235 case filter_BPF_CSG: /* H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q) */
236 p->b0 = sin(w0)/2;
237 p->b1 = 0;
238 p->b2 = -sin(w0)/2;
239 p->a0 = 1 + alpha;
240 p->a1 = -2*cos(w0);
241 p->a2 = 1 - alpha;
242 break;
244 case filter_BPF: /* H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain) */
245 p->b0 = alpha;
246 p->b1 = 0;
247 p->b2 = -alpha;
248 p->a0 = 1 + alpha;
249 p->a1 = -2*cos(w0);
250 p->a2 = 1 - alpha;
251 break;
253 case filter_notch: /* H(s) = (s^2 + 1) / (s^2 + s/Q + 1) */
254 p->b0 = 1;
255 p->b1 = -2*cos(w0);
256 p->b2 = 1;
257 p->a0 = 1 + alpha;
258 p->a1 = -2*cos(w0);
259 p->a2 = 1 - alpha;
260 break;
262 case filter_APF: /* H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1) */
263 p->b0 = 1 - alpha;
264 p->b1 = -2*cos(w0);
265 p->b2 = 1 + alpha;
266 p->a0 = 1 + alpha;
267 p->a1 = -2*cos(w0);
268 p->a2 = 1 - alpha;
269 break;
271 case filter_peakingEQ: /* H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1) */
272 if (A == 1)
273 return SOX_EFF_NULL;
274 p->b0 = 1 + alpha*A;
275 p->b1 = -2*cos(w0);
276 p->b2 = 1 - alpha*A;
277 p->a0 = 1 + alpha/A;
278 p->a1 = -2*cos(w0);
279 p->a2 = 1 - alpha/A;
280 break;
282 case filter_lowShelf: /* H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1) */
283 if (A == 1)
284 return SOX_EFF_NULL;
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;
291 break;
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) */
296 if (!A)
297 return SOX_EFF_NULL;
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;
304 break;
306 case filter_LPF_1: /* single-pole */
307 p->a1 = -exp(-w0);
308 p->b0 = 1 + p->a1;
309 break;
311 case filter_HPF_1: /* single-pole */
312 p->a1 = -exp(-w0);
313 p->b0 = (1 - p->a1)/2;
314 p->b1 = -p->b0;
315 break;
317 case filter_BPF_SPK: case filter_BPF_SPK_N: {
318 double bw_Hz;
319 if (!p->width)
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 */
325 break;
328 case filter_AP1: /* Experimental 1-pole all-pass from Tom Erbe @ UCSD */
329 p->b0 = exp(-w0);
330 p->b1 = -1;
331 p->a1 = -exp(-w0);
332 break;
334 case filter_AP2: /* Experimental 2-pole all-pass from Tom Erbe @ UCSD */
335 p->b0 = 1 - sin(w0);
336 p->b1 = -2 * cos(w0);
337 p->b2 = 1 + sin(w0);
338 p->a0 = 1 + sin(w0);
339 p->a1 = -2 * cos(w0);
340 p->a2 = 1 - sin(w0);
341 break;
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);
374 else {
375 lsx_fail("Sample rate must be 44.1k, 48k, 88.2k, 96k, or 192k");
376 return SOX_EOF;
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));
389 break;
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)\
402 }; \
403 return &handler; \
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)