diff options
Diffstat (limited to 'apps/eq.c')
-rw-r--r-- | apps/eq.c | 100 |
1 files changed, 47 insertions, 53 deletions
@@ -118,33 +118,42 @@ static long fsincos(unsigned long phase, long *cos) { | |||
118 | return y; | 118 | return y; |
119 | } | 119 | } |
120 | 120 | ||
121 | /** | 121 | /** |
122 | * Calculate first order shelving filter coefficients. | 122 | * Calculate first order shelving filter. Filter is not directly usable by the |
123 | * Note that the filter is not compatible with the eq_filter routine. | 123 | * eq_filter() function. |
124 | * @param cutoff a value from 0 to 0x80000000, where 0 represents 0 Hz and | 124 | * @param cutoff shelf midpoint frequency. See eq_pk_coefs for format. |
125 | * 0x80000000 represents the Nyquist frequency (samplerate/2). | 125 | * @param A decibel value multiplied by ten, describing gain/attenuation of |
126 | * @param ad gain at 0 Hz. s3.27 fixed point. | 126 | * shelf. Max value is 24 dB. |
127 | * @param an gain at Nyquist frequency. s3.27 fixed point. | 127 | * @param low true for low-shelf filter, false for high-shelf filter. |
128 | * @param c pointer to coefficient storage. The coefs are s0.31 format. | 128 | * @param c pointer to coefficient storage. Coefficients are s4.27 format. |
129 | */ | 129 | */ |
130 | void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c) | 130 | void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c) |
131 | { | 131 | { |
132 | const long one = 1 << 27; | 132 | long sin, cos; |
133 | long a0, a1; | 133 | int32_t b0, b1, a0, a1; /* s3.28 */ |
134 | long b0, b1; | 134 | const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */ |
135 | long s, cs; | 135 | |
136 | s = fsincos(cutoff, &cs) >> 4; | 136 | sin = fsincos(cutoff/2, &cos); |
137 | cs = one + (cs >> 4); | 137 | if (low) { |
138 | 138 | const int32_t sin_div_g = DIV64(sin, g, 25); | |
139 | /* For max A = 4 (24 dB) */ | 139 | cos >>= 3; |
140 | b0 = FRACMUL_SHL(ad, s, 4) + FRACMUL_SHL(an, cs, 4); | 140 | b0 = FRACMUL(sin, g) + cos; /* 0.25 .. 4.10 */ |
141 | b1 = FRACMUL_SHL(ad, s, 4) - FRACMUL_SHL(an, cs, 4); | 141 | b1 = FRACMUL(sin, g) - cos; /* -1 .. 3.98 */ |
142 | a0 = s + cs; | 142 | a0 = sin_div_g + cos; /* 0.25 .. 4.10 */ |
143 | a1 = s - cs; | 143 | a1 = sin_div_g - cos; /* -1 .. 3.98 */ |
144 | } else { | ||
145 | const int32_t cos_div_g = DIV64(cos, g, 25); | ||
146 | sin >>= 3; | ||
147 | b0 = sin + FRACMUL(cos, g); /* 0.25 .. 4.10 */ | ||
148 | b1 = sin - FRACMUL(cos, g); /* -3.98 .. 1 */ | ||
149 | a0 = sin + cos_div_g; /* 0.25 .. 4.10 */ | ||
150 | a1 = sin - cos_div_g; /* -3.98 .. 1 */ | ||
151 | } | ||
144 | 152 | ||
145 | c[0] = DIV64(b0, a0, 31); | 153 | const int32_t rcp_a0 = DIV64(1, a0, 57); /* 0.24 .. 3.98, s2.29 */ |
146 | c[1] = DIV64(b1, a0, 31); | 154 | *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */ |
147 | c[2] = -DIV64(a1, a0, 31); | 155 | *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */ |
156 | *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */ | ||
148 | } | 157 | } |
149 | 158 | ||
150 | #ifdef HAVE_SW_TONE_CONTROLS | 159 | #ifdef HAVE_SW_TONE_CONTROLS |
@@ -163,41 +172,26 @@ void filter_shelf_coefs(unsigned long cutoff, long ad, long an, int32_t *c) | |||
163 | void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, | 172 | void filter_bishelf_coefs(unsigned long cutoff_low, unsigned long cutoff_high, |
164 | long A_low, long A_high, long A, int32_t *c) | 173 | long A_low, long A_high, long A, int32_t *c) |
165 | { | 174 | { |
166 | long sin1, cos2; /* s0.31 */ | ||
167 | long cos1, sin2; /* s3.28 */ | ||
168 | int32_t b0, b1, b2, b3; /* s3.28 */ | ||
169 | int32_t a0, a1, a2, a3; | ||
170 | const long gd = get_replaygain_int(A_low*5) << 4; /* 10^(db/40), s3.28 */ | ||
171 | const long gn = get_replaygain_int(A_high*5) << 4; /* 10^(db/40), s3.28 */ | ||
172 | const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */ | 175 | const long g = get_replaygain_int(A*10) << 7; /* 10^(db/20), s0.31 */ |
176 | int32_t c_ls[3], c_hs[3]; | ||
173 | 177 | ||
174 | sin1 = fsincos(cutoff_low/2, &cos1); | 178 | filter_shelf_coefs(cutoff_low, A_low, true, c_ls); |
175 | sin2 = fsincos(cutoff_high/2, &cos2) >> 3; | 179 | filter_shelf_coefs(cutoff_high, A_high, false, c_hs); |
176 | cos1 >>= 3; | 180 | c_ls[0] = FRACMUL(g, c_ls[0]); |
177 | 181 | c_ls[1] = FRACMUL(g, c_ls[1]); | |
178 | /* lowshelf filter, ranges listed are for all possible cutoffs */ | ||
179 | b0 = FRACMUL(sin1, gd) + cos1; /* 0.25 .. 4.10 */ | ||
180 | b1 = FRACMUL(sin1, gd) - cos1; /* -1 .. 3.98 */ | ||
181 | a0 = DIV64(sin1, gd, 25) + cos1; /* 0.25 .. 4.10 */ | ||
182 | a1 = DIV64(sin1, gd, 25) - cos1; /* -1 .. 3.98 */ | ||
183 | |||
184 | /* highshelf filter */ | ||
185 | b2 = sin2 + FRACMUL(cos2, gn); /* 0.25 .. 4.10 */ | ||
186 | b3 = sin2 - FRACMUL(cos2, gn); /* -3.98 .. 1 */ | ||
187 | a2 = sin2 + DIV64(cos2, gn, 25); /* 0.25 .. 4.10 */ | ||
188 | a3 = sin2 - DIV64(cos2, gn, 25); /* -3.98 .. 1 */ | ||
189 | 182 | ||
190 | /* now we cascade the two first order filters to one second order filter | 183 | /* now we cascade the two first order filters to one second order filter |
191 | * which can be used by eq_filter(). these resulting coefficients have a | 184 | * which can be used by eq_filter(). these resulting coefficients have a |
192 | * really wide numerical range, so we use a fixed point format which will | 185 | * really wide numerical range, so we use a fixed point format which will |
193 | * work for the selected cutoff frequencies (in dsp.c) only. | 186 | * work for the selected cutoff frequencies (in dsp.c) only. |
194 | */ | 187 | */ |
195 | const int32_t rcp_a0 = DIV64(1, FRACMUL(a0, a2), 53); /* s3.28 */ | 188 | const int32_t b0 = c_ls[0], b1 = c_ls[1], b2 = c_hs[0], b3 = c_hs[1]; |
196 | *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b0, b2), rcp_a0, 5)); | 189 | const int32_t a0 = c_ls[2], a1 = c_hs[2]; |
197 | *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b0, b3) + FRACMUL(b1, b2), rcp_a0, 5)); | 190 | *c++ = FRACMUL_SHL(b0, b2, 4); |
198 | *c++ = FRACMUL(g, FRACMUL_SHL(FRACMUL(b1, b3), rcp_a0, 5)); | 191 | *c++ = FRACMUL_SHL(b0, b3, 4) + FRACMUL_SHL(b1, b2, 4); |
199 | *c++ = -FRACMUL_SHL(FRACMUL(a0, a3) + FRACMUL(a1, a2), rcp_a0, 5); | 192 | *c++ = FRACMUL_SHL(b1, b3, 4); |
200 | *c++ = -FRACMUL_SHL(FRACMUL(a1, a3), rcp_a0, 5); | 193 | *c++ = a0 + a1; |
194 | *c++ = -FRACMUL_SHL(a0, a1, 4); | ||
201 | } | 195 | } |
202 | #endif | 196 | #endif |
203 | 197 | ||
@@ -276,7 +270,7 @@ void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
276 | a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha; | 270 | a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha; |
277 | 271 | ||
278 | /* [0.1 .. 1.99] */ | 272 | /* [0.1 .. 1.99] */ |
279 | const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ | 273 | const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ |
280 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */ | 274 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */ |
281 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */ | 275 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */ |
282 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */ | 276 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */ |
@@ -315,7 +309,7 @@ void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
315 | a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha; | 309 | a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha; |
316 | 310 | ||
317 | /* [0.1 .. 1.99] */ | 311 | /* [0.1 .. 1.99] */ |
318 | const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ | 312 | const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ |
319 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */ | 313 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */ |
320 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */ | 314 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */ |
321 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */ | 315 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */ |