Reload the current playlist after reboot even if it has ended. (FS#11644)
[kugel-rb.git] / apps / eq.c
blob122a46a4c5addc1c2cab30356987e9ad5b08d061
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2006-2007 Thom Johansen
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <inttypes.h>
23 #include "config.h"
24 #include "fixedpoint.h"
25 #include "fracmul.h"
26 #include "eq.h"
27 #include "replaygain.h"
29 /**
30 * Calculate first order shelving filter. Filter is not directly usable by the
31 * eq_filter() function.
32 * @param cutoff shelf midpoint frequency. See eq_pk_coefs for format.
33 * @param A decibel value multiplied by ten, describing gain/attenuation of
34 * shelf. Max value is 24 dB.
35 * @param low true for low-shelf filter, false for high-shelf filter.
36 * @param c pointer to coefficient storage. Coefficients are s4.27 format.
38 void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c)
40 long sin, cos;
41 int32_t b0, b1, a0, a1; /* s3.28 */
42 const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */
44 sin = fp_sincos(cutoff/2, &cos);
45 if (low) {
46 const int32_t sin_div_g = fp_div(sin, g, 25);
47 const int32_t sin_g = FRACMUL(sin, g);
48 cos >>= 3;
49 b0 = sin_g + cos; /* 0.25 .. 4.10 */
50 b1 = sin_g - cos; /* -1 .. 3.98 */
51 a0 = sin_div_g + cos; /* 0.25 .. 4.10 */
52 a1 = sin_div_g - cos; /* -1 .. 3.98 */
53 } else {
54 const int32_t cos_div_g = fp_div(cos, g, 25);
55 const int32_t cos_g = FRACMUL(cos, g);
56 sin >>= 3;
57 b0 = sin + cos_g; /* 0.25 .. 4.10 */
58 b1 = sin - cos_g; /* -3.98 .. 1 */
59 a0 = sin + cos_div_g; /* 0.25 .. 4.10 */
60 a1 = sin - cos_div_g; /* -3.98 .. 1 */
63 const int32_t rcp_a0 = fp_div(1, a0, 57); /* 0.24 .. 3.98, s2.29 */
64 *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */
65 *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */
66 *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */
69 #ifdef HAVE_SW_TONE_CONTROLS
70 /**
71 * Calculate second order section filter consisting of one low-shelf and one
72 * high-shelf section.
73 * @param cutoff_low low-shelf midpoint frequency. See eq_pk_coefs for format.
74 * @param cutoff_high high-shelf midpoint frequency.
75 * @param A_low decibel value multiplied by ten, describing gain/attenuation of
76 * low-shelf part. Max value is 24 dB.
77 * @param A_high decibel value multiplied by ten, describing gain/attenuation of
78 * high-shelf part. Max value is 24 dB.
79 * @param A decibel value multiplied by ten, describing additional overall gain.
80 * @param c pointer to coefficient storage. Coefficients are s4.27 format.
82 void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high,
83 long A_low, long A_high, long A, int32_t *c)
85 const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */
86 int32_t c_ls[3], c_hs[3];
88 filter_shelf_coefs(cutoff_low, A_low, true, c_ls);
89 filter_shelf_coefs(cutoff_high, A_high, false, c_hs);
90 c_ls[0] = FRACMUL(g, c_ls[0]);
91 c_ls[1] = FRACMUL(g, c_ls[1]);
93 /* now we cascade the two first order filters to one second order filter
94 * which can be used by eq_filter(). these resulting coefficients have a
95 * really wide numerical range, so we use a fixed point format which will
96 * work for the selected cutoff frequencies (in dsp.c) only.
98 const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1];
99 const int32_t a0 = c_ls[2], a1 = c_hs[2];
100 *c++ = FRACMUL_SHL(b0, b2, 4);
101 *c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4);
102 *c++ = FRACMUL_SHL(b1, b3, 4);
103 *c++ = a0 + a1;
104 *c++ = -FRACMUL_SHL(a0, a1, 4);
106 #endif
108 /* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
109 * Slightly faster calculation can be done by deriving forms which use tan()
110 * instead of cos() and sin(), but the latter are far easier to use when doing
111 * fixed point math, and performance is not a big point in the calculation part.
112 * All the 'a' filter coefficients are negated so we can use only additions
113 * in the filtering equation.
116 /**
117 * Calculate second order section peaking filter coefficients.
118 * @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and
119 * 0x80000000 represents the Nyquist frequency (samplerate/2).
120 * @param Q Q factor value multiplied by ten. Lower bound is artificially set
121 * at 0.5.
122 * @param db decibel value multiplied by ten, describing gain/attenuation at
123 * peak freq. Max value is 24 dB.
124 * @param c pointer to coefficient storage. Coefficients are s3.28 format.
126 void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
128 long cs;
129 const long one = 1 << 28; /* s3.28 */
130 const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */
131 const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
132 int32_t a0, a1, a2; /* these are all s3.28 format */
133 int32_t b0, b1, b2;
134 const long alphadivA = fp_div(alpha, A, 27);
135 const long alphaA = FRACMUL(alpha, A);
137 /* possible numerical ranges are in comments by each coef */
138 b0 = one + alphaA; /* [1 .. 5] */
139 b1 = a1 = -2*(cs >> 3); /* [-2 .. 2] */
140 b2 = one - alphaA; /* [-3 .. 1] */
141 a0 = one + alphadivA; /* [1 .. 5] */
142 a2 = one - alphadivA; /* [-3 .. 1] */
144 /* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */
145 const long rcp_a0 = fp_div(1, a0, 59); /* s0.31 */
146 *c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */
147 *c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */
148 *c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */
149 *c++ = FRACMUL(-a1, rcp_a0); /* [-2 .. 2] */
150 *c++ = FRACMUL(-a2, rcp_a0); /* [-0.6 .. 1] */
154 * Calculate coefficients for lowshelf filter. Parameters are as for
155 * eq_pk_coefs, but the coefficient format is s5.26 fixed point.
157 void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
159 long cs;
160 const long one = 1 << 25; /* s6.25 */
161 const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
162 const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
163 const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
164 const long ap1 = (A >> 4) + one;
165 const long am1 = (A >> 4) - one;
166 const long ap1_cs = FRACMUL(ap1, cs);
167 const long am1_cs = FRACMUL(am1, cs);
168 const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
169 int32_t a0, a1, a2; /* these are all s6.25 format */
170 int32_t b0, b1, b2;
172 /* [0.1 .. 40] */
173 b0 = FRACMUL_SHL(A, ap1 - am1_cs + twosqrtalpha, 2);
174 /* [-16 .. 63.4] */
175 b1 = FRACMUL_SHL(A, am1 - ap1_cs, 3);
176 /* [0 .. 31.7] */
177 b2 = FRACMUL_SHL(A, ap1 - am1_cs - twosqrtalpha, 2);
178 /* [0.5 .. 10] */
179 a0 = ap1 + am1_cs + twosqrtalpha;
180 /* [-16 .. 4] */
181 a1 = -2*(am1 + ap1_cs);
182 /* [0 .. 8] */
183 a2 = ap1 + am1_cs - twosqrtalpha;
185 /* [0.1 .. 1.99] */
186 const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
187 *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */
188 *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */
189 *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */
190 *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
191 *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
195 * Calculate coefficients for highshelf filter. Parameters are as for
196 * eq_pk_coefs, but the coefficient format is s5.26 fixed point.
198 void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
200 long cs;
201 const long one = 1 << 25; /* s6.25 */
202 const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */
203 const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */
204 const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */
205 const long ap1 = (A >> 4) + one;
206 const long am1 = (A >> 4) - one;
207 const long ap1_cs = FRACMUL(ap1, cs);
208 const long am1_cs = FRACMUL(am1, cs);
209 const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha);
210 int32_t a0, a1, a2; /* these are all s6.25 format */
211 int32_t b0, b1, b2;
213 /* [0.1 .. 40] */
214 b0 = FRACMUL_SHL(A, ap1 + am1_cs + twosqrtalpha, 2);
215 /* [-63.5 .. 16] */
216 b1 = -FRACMUL_SHL(A, am1 + ap1_cs, 3);
217 /* [0 .. 32] */
218 b2 = FRACMUL_SHL(A, ap1 + am1_cs - twosqrtalpha, 2);
219 /* [0.5 .. 10] */
220 a0 = ap1 - am1_cs + twosqrtalpha;
221 /* [-4 .. 16] */
222 a1 = 2*(am1 - ap1_cs);
223 /* [0 .. 8] */
224 a2 = ap1 - am1_cs - twosqrtalpha;
226 /* [0.1 .. 1.99] */
227 const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */
228 *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */
229 *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */
230 *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */
231 *c++ = FRACMUL_SHL(-a1, rcp_a0, 2); /* [-2 .. 2] */
232 *c++ = FRACMUL_SHL(-a2, rcp_a0, 2); /* [0 .. 1] */
235 /* We realise the filters as a second order direct form 1 structure. Direct
236 * form 1 was chosen because of better numerical properties for fixed point
237 * implementations.
240 #if (!defined(CPU_COLDFIRE) && !defined(CPU_ARM))
241 void eq_filter(int32_t **x, struct eqfilter *f, unsigned num,
242 unsigned channels, unsigned shift)
244 unsigned c, i;
245 long long acc;
247 /* Direct form 1 filtering code.
248 y[n] = b0*x[i] + b1*x[i - 1] + b2*x[i - 2] + a1*y[i - 1] + a2*y[i - 2],
249 where y[] is output and x[] is input.
252 for (c = 0; c < channels; c++) {
253 for (i = 0; i < num; i++) {
254 acc = (long long) x[c][i] * f->coefs[0];
255 acc += (long long) f->history[c][0] * f->coefs[1];
256 acc += (long long) f->history[c][1] * f->coefs[2];
257 acc += (long long) f->history[c][2] * f->coefs[3];
258 acc += (long long) f->history[c][3] * f->coefs[4];
259 f->history[c][1] = f->history[c][0];
260 f->history[c][0] = x[c][i];
261 f->history[c][3] = f->history[c][2];
262 x[c][i] = (acc << shift) >> 32;
263 f->history[c][2] = x[c][i];
267 #endif