diff options
Diffstat (limited to 'apps/eq.c')
-rw-r--r-- | apps/eq.c | 120 |
1 files changed, 13 insertions, 107 deletions
@@ -21,105 +21,11 @@ | |||
21 | 21 | ||
22 | #include <inttypes.h> | 22 | #include <inttypes.h> |
23 | #include "config.h" | 23 | #include "config.h" |
24 | #include "dsp.h" | 24 | #include "fixedpoint.h" |
25 | #include "fracmul.h" | ||
25 | #include "eq.h" | 26 | #include "eq.h" |
26 | #include "replaygain.h" | 27 | #include "replaygain.h" |
27 | 28 | ||
28 | /* Inverse gain of circular cordic rotation in s0.31 format. */ | ||
29 | static const long cordic_circular_gain = 0xb2458939; /* 0.607252929 */ | ||
30 | |||
31 | /* Table of values of atan(2^-i) in 0.32 format fractions of pi where pi = 0xffffffff / 2 */ | ||
32 | static const unsigned long atan_table[] = { | ||
33 | 0x1fffffff, /* +0.785398163 (or pi/4) */ | ||
34 | 0x12e4051d, /* +0.463647609 */ | ||
35 | 0x09fb385b, /* +0.244978663 */ | ||
36 | 0x051111d4, /* +0.124354995 */ | ||
37 | 0x028b0d43, /* +0.062418810 */ | ||
38 | 0x0145d7e1, /* +0.031239833 */ | ||
39 | 0x00a2f61e, /* +0.015623729 */ | ||
40 | 0x00517c55, /* +0.007812341 */ | ||
41 | 0x0028be53, /* +0.003906230 */ | ||
42 | 0x00145f2e, /* +0.001953123 */ | ||
43 | 0x000a2f98, /* +0.000976562 */ | ||
44 | 0x000517cc, /* +0.000488281 */ | ||
45 | 0x00028be6, /* +0.000244141 */ | ||
46 | 0x000145f3, /* +0.000122070 */ | ||
47 | 0x0000a2f9, /* +0.000061035 */ | ||
48 | 0x0000517c, /* +0.000030518 */ | ||
49 | 0x000028be, /* +0.000015259 */ | ||
50 | 0x0000145f, /* +0.000007629 */ | ||
51 | 0x00000a2f, /* +0.000003815 */ | ||
52 | 0x00000517, /* +0.000001907 */ | ||
53 | 0x0000028b, /* +0.000000954 */ | ||
54 | 0x00000145, /* +0.000000477 */ | ||
55 | 0x000000a2, /* +0.000000238 */ | ||
56 | 0x00000051, /* +0.000000119 */ | ||
57 | 0x00000028, /* +0.000000060 */ | ||
58 | 0x00000014, /* +0.000000030 */ | ||
59 | 0x0000000a, /* +0.000000015 */ | ||
60 | 0x00000005, /* +0.000000007 */ | ||
61 | 0x00000002, /* +0.000000004 */ | ||
62 | 0x00000001, /* +0.000000002 */ | ||
63 | 0x00000000, /* +0.000000001 */ | ||
64 | 0x00000000, /* +0.000000000 */ | ||
65 | }; | ||
66 | |||
67 | /** | ||
68 | * Implements sin and cos using CORDIC rotation. | ||
69 | * | ||
70 | * @param phase has range from 0 to 0xffffffff, representing 0 and | ||
71 | * 2*pi respectively. | ||
72 | * @param cos return address for cos | ||
73 | * @return sin of phase, value is a signed value from LONG_MIN to LONG_MAX, | ||
74 | * representing -1 and 1 respectively. | ||
75 | */ | ||
76 | static long fsincos(unsigned long phase, long *cos) { | ||
77 | int32_t x, x1, y, y1; | ||
78 | unsigned long z, z1; | ||
79 | int i; | ||
80 | |||
81 | /* Setup initial vector */ | ||
82 | x = cordic_circular_gain; | ||
83 | y = 0; | ||
84 | z = phase; | ||
85 | |||
86 | /* The phase has to be somewhere between 0..pi for this to work right */ | ||
87 | if (z < 0xffffffff / 4) { | ||
88 | /* z in first quadrant, z += pi/2 to correct */ | ||
89 | x = -x; | ||
90 | z += 0xffffffff / 4; | ||
91 | } else if (z < 3 * (0xffffffff / 4)) { | ||
92 | /* z in third quadrant, z -= pi/2 to correct */ | ||
93 | z -= 0xffffffff / 4; | ||
94 | } else { | ||
95 | /* z in fourth quadrant, z -= 3pi/2 to correct */ | ||
96 | x = -x; | ||
97 | z -= 3 * (0xffffffff / 4); | ||
98 | } | ||
99 | |||
100 | /* Each iteration adds roughly 1-bit of extra precision */ | ||
101 | for (i = 0; i < 31; i++) { | ||
102 | x1 = x >> i; | ||
103 | y1 = y >> i; | ||
104 | z1 = atan_table[i]; | ||
105 | |||
106 | /* Decided which direction to rotate vector. Pivot point is pi/2 */ | ||
107 | if (z >= 0xffffffff / 4) { | ||
108 | x -= y1; | ||
109 | y += x1; | ||
110 | z -= z1; | ||
111 | } else { | ||
112 | x += y1; | ||
113 | y -= x1; | ||
114 | z += z1; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | *cos = x; | ||
119 | |||
120 | return y; | ||
121 | } | ||
122 | |||
123 | /** | 29 | /** |
124 | * Calculate first order shelving filter. Filter is not directly usable by the | 30 | * Calculate first order shelving filter. Filter is not directly usable by the |
125 | * eq_filter() function. | 31 | * eq_filter() function. |
@@ -135,16 +41,16 @@ void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c) | |||
135 | int32_t b0, b1, a0, a1; /* s3.28 */ | 41 | int32_t b0, b1, a0, a1; /* s3.28 */ |
136 | const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */ | 42 | const long g = get_replaygain_int(A*5) << 4; /* 10^(db/40), s3.28 */ |
137 | 43 | ||
138 | sin = fsincos(cutoff/2, &cos); | 44 | sin = fp_sincos(cutoff/2, &cos); |
139 | if (low) { | 45 | if (low) { |
140 | const int32_t sin_div_g = DIV64(sin, g, 25); | 46 | const int32_t sin_div_g = fp_div(sin, g, 25); |
141 | cos >>= 3; | 47 | cos >>= 3; |
142 | b0 = FRACMUL(sin, g) + cos; /* 0.25 .. 4.10 */ | 48 | b0 = FRACMUL(sin, g) + cos; /* 0.25 .. 4.10 */ |
143 | b1 = FRACMUL(sin, g) - cos; /* -1 .. 3.98 */ | 49 | b1 = FRACMUL(sin, g) - cos; /* -1 .. 3.98 */ |
144 | a0 = sin_div_g + cos; /* 0.25 .. 4.10 */ | 50 | a0 = sin_div_g + cos; /* 0.25 .. 4.10 */ |
145 | a1 = sin_div_g - cos; /* -1 .. 3.98 */ | 51 | a1 = sin_div_g - cos; /* -1 .. 3.98 */ |
146 | } else { | 52 | } else { |
147 | const int32_t cos_div_g = DIV64(cos, g, 25); | 53 | const int32_t cos_div_g = fp_div(cos, g, 25); |
148 | sin >>= 3; | 54 | sin >>= 3; |
149 | b0 = sin + FRACMUL(cos, g); /* 0.25 .. 4.10 */ | 55 | b0 = sin + FRACMUL(cos, g); /* 0.25 .. 4.10 */ |
150 | b1 = sin - FRACMUL(cos, g); /* -3.98 .. 1 */ | 56 | b1 = sin - FRACMUL(cos, g); /* -3.98 .. 1 */ |
@@ -152,7 +58,7 @@ void filter_shelf_coefs(unsigned long cutoff, long A, bool low, int32_t *c) | |||
152 | a1 = sin - cos_div_g; /* -3.98 .. 1 */ | 58 | a1 = sin - cos_div_g; /* -3.98 .. 1 */ |
153 | } | 59 | } |
154 | 60 | ||
155 | const int32_t rcp_a0 = DIV64(1, a0, 57); /* 0.24 .. 3.98, s2.29 */ | 61 | const int32_t rcp_a0 = fp_div(1, a0, 57); /* 0.24 .. 3.98, s2.29 */ |
156 | *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */ | 62 | *c++ = FRACMUL_SHL(b0, rcp_a0, 1); /* 0.063 .. 15.85 */ |
157 | *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */ | 63 | *c++ = FRACMUL_SHL(b1, rcp_a0, 1); /* -15.85 .. 15.85 */ |
158 | *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */ | 64 | *c++ = -FRACMUL_SHL(a1, rcp_a0, 1); /* -1 .. 1 */ |
@@ -220,10 +126,10 @@ void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
220 | long cs; | 126 | long cs; |
221 | const long one = 1 << 28; /* s3.28 */ | 127 | const long one = 1 << 28; /* s3.28 */ |
222 | const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */ | 128 | const long A = get_replaygain_int(db*5) << 5; /* 10^(db/40), s2.29 */ |
223 | const long alpha = fsincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ | 129 | const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ |
224 | int32_t a0, a1, a2; /* these are all s3.28 format */ | 130 | int32_t a0, a1, a2; /* these are all s3.28 format */ |
225 | int32_t b0, b1, b2; | 131 | int32_t b0, b1, b2; |
226 | const long alphadivA = DIV64(alpha, A, 27); | 132 | const long alphadivA = fp_div(alpha, A, 27); |
227 | 133 | ||
228 | /* possible numerical ranges are in comments by each coef */ | 134 | /* possible numerical ranges are in comments by each coef */ |
229 | b0 = one + FRACMUL(alpha, A); /* [1 .. 5] */ | 135 | b0 = one + FRACMUL(alpha, A); /* [1 .. 5] */ |
@@ -233,7 +139,7 @@ void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
233 | a2 = one - alphadivA; /* [-3 .. 1] */ | 139 | a2 = one - alphadivA; /* [-3 .. 1] */ |
234 | 140 | ||
235 | /* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */ | 141 | /* range of this is roughly [0.2 .. 1], but we'll never hit 1 completely */ |
236 | const long rcp_a0 = DIV64(1, a0, 59); /* s0.31 */ | 142 | const long rcp_a0 = fp_div(1, a0, 59); /* s0.31 */ |
237 | *c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */ | 143 | *c++ = FRACMUL(b0, rcp_a0); /* [0.25 .. 4] */ |
238 | *c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */ | 144 | *c++ = FRACMUL(b1, rcp_a0); /* [-2 .. 2] */ |
239 | *c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */ | 145 | *c++ = FRACMUL(b2, rcp_a0); /* [-2.4 .. 1] */ |
@@ -251,7 +157,7 @@ void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
251 | const long one = 1 << 25; /* s6.25 */ | 157 | const long one = 1 << 25; /* s6.25 */ |
252 | const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */ | 158 | const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */ |
253 | const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */ | 159 | const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */ |
254 | const long alpha = fsincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ | 160 | const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ |
255 | const long ap1 = (A >> 4) + one; | 161 | const long ap1 = (A >> 4) + one; |
256 | const long am1 = (A >> 4) - one; | 162 | const long am1 = (A >> 4) - one; |
257 | const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha); | 163 | const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha); |
@@ -272,7 +178,7 @@ void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
272 | a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha; | 178 | a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha; |
273 | 179 | ||
274 | /* [0.1 .. 1.99] */ | 180 | /* [0.1 .. 1.99] */ |
275 | const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ | 181 | const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */ |
276 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */ | 182 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0.06 .. 15.9] */ |
277 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */ | 183 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-2 .. 31.7] */ |
278 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */ | 184 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 15.9] */ |
@@ -290,7 +196,7 @@ void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
290 | const long one = 1 << 25; /* s6.25 */ | 196 | const long one = 1 << 25; /* s6.25 */ |
291 | const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */ | 197 | const long sqrtA = get_replaygain_int(db*5/2) << 2; /* 10^(db/80), s5.26 */ |
292 | const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */ | 198 | const long A = FRACMUL_SHL(sqrtA, sqrtA, 8); /* s2.29 */ |
293 | const long alpha = fsincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ | 199 | const long alpha = fp_sincos(cutoff, &cs)/(2*Q)*10 >> 1; /* s1.30 */ |
294 | const long ap1 = (A >> 4) + one; | 200 | const long ap1 = (A >> 4) + one; |
295 | const long am1 = (A >> 4) - one; | 201 | const long am1 = (A >> 4) - one; |
296 | const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha); | 202 | const long twosqrtalpha = 2*FRACMUL(sqrtA, alpha); |
@@ -311,7 +217,7 @@ void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c) | |||
311 | a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha; | 217 | a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha; |
312 | 218 | ||
313 | /* [0.1 .. 1.99] */ | 219 | /* [0.1 .. 1.99] */ |
314 | const long rcp_a0 = DIV64(1, a0, 55); /* s1.30 */ | 220 | const long rcp_a0 = fp_div(1, a0, 55); /* s1.30 */ |
315 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */ | 221 | *c++ = FRACMUL_SHL(b0, rcp_a0, 2); /* [0 .. 16] */ |
316 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */ | 222 | *c++ = FRACMUL_SHL(b1, rcp_a0, 2); /* [-31.7 .. 2] */ |
317 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */ | 223 | *c++ = FRACMUL_SHL(b2, rcp_a0, 2); /* [0 .. 16] */ |