summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThom Johansen <thomj@rockbox.org>2006-03-23 15:40:59 +0000
committerThom Johansen <thomj@rockbox.org>2006-03-23 15:40:59 +0000
commite70a50cd9a7d0a3ade17c40ed36011ba159e5e65 (patch)
tree375c259f0ba1f5fe7b6a09eec303d827f600a1df
parent55e67fc1dc96e0c709acda0f056a8965008c8e57 (diff)
downloadrockbox-e70a50cd9a7d0a3ade17c40ed36011ba159e5e65.tar.gz
rockbox-e70a50cd9a7d0a3ade17c40ed36011ba159e5e65.zip
Fix the inaccurate frequency setting problems of the EQ due to
inaccuracies in the sin/cos functioncs. A million thanks to safetydan for fixing my crappy trig functions! git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9209 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/eq.c134
1 files changed, 96 insertions, 38 deletions
diff --git a/apps/eq.c b/apps/eq.c
index 192f198944..6e3e1e2126 100644
--- a/apps/eq.c
+++ b/apps/eq.c
@@ -53,42 +53,99 @@
/* TODO: replaygain.c has some fixed point routines. perhaps we could reuse
them? */
-/* 128 sixteen bit sine samples + guard point */
-short sinetab[] = {
- 0, 1607, 3211, 4807, 6392, 7961, 9511, 11038, 12539, 14009, 15446, 16845,
- 18204, 19519, 20787, 22004, 23169, 24278, 25329, 26318, 27244, 28105, 28897,
- 29621, 30272, 30851, 31356, 31785, 32137, 32412, 32609,32727, 32767, 32727,
- 32609, 32412, 32137, 31785, 31356, 30851, 30272, 29621, 28897, 28105, 27244,
- 26318, 25329, 24278, 23169, 22004, 20787, 19519, 18204, 16845, 15446, 14009,
- 12539, 11038, 9511, 7961, 6392, 4807, 3211, 1607, 0, -1607, -3211, -4807,
- -6392, -7961, -9511, -11038, -12539, -14009, -15446, -16845, -18204, -19519,
- -20787, -22004, -23169, -24278, -25329, -26318, -27244, -28105, -28897,
- -29621, -30272, -30851, -31356, -31785, -32137, -32412, -32609, -32727,
- -32767, -32727, -32609, -32412, -32137, -31785, -31356, -30851, -30272,
- -29621, -28897, -28105, -27244, -26318, -25329, -24278, -23169, -22004,
- -20787, -19519, -18204, -16845, -15446, -14009, -12539, -11038, -9511,
- -7961, -6392, -4807, -3211, -1607, 0
+/* Inverse gain of circular cordic rotation in s0.31 format. */
+static const long cordic_circular_gain = 0xb2458939; /* 0.607252929 */
+
+/* Table of values of atan(2^-i) in 0.32 format fractions of pi where pi = 0xffffffff / 2 */
+static const unsigned long atan_table[] = {
+ 0x1fffffff, /* +0.785398163 (or pi/4) */
+ 0x12e4051d, /* +0.463647609 */
+ 0x09fb385b, /* +0.244978663 */
+ 0x051111d4, /* +0.124354995 */
+ 0x028b0d43, /* +0.062418810 */
+ 0x0145d7e1, /* +0.031239833 */
+ 0x00a2f61e, /* +0.015623729 */
+ 0x00517c55, /* +0.007812341 */
+ 0x0028be53, /* +0.003906230 */
+ 0x00145f2e, /* +0.001953123 */
+ 0x000a2f98, /* +0.000976562 */
+ 0x000517cc, /* +0.000488281 */
+ 0x00028be6, /* +0.000244141 */
+ 0x000145f3, /* +0.000122070 */
+ 0x0000a2f9, /* +0.000061035 */
+ 0x0000517c, /* +0.000030518 */
+ 0x000028be, /* +0.000015259 */
+ 0x0000145f, /* +0.000007629 */
+ 0x00000a2f, /* +0.000003815 */
+ 0x00000517, /* +0.000001907 */
+ 0x0000028b, /* +0.000000954 */
+ 0x00000145, /* +0.000000477 */
+ 0x000000a2, /* +0.000000238 */
+ 0x00000051, /* +0.000000119 */
+ 0x00000028, /* +0.000000060 */
+ 0x00000014, /* +0.000000030 */
+ 0x0000000a, /* +0.000000015 */
+ 0x00000005, /* +0.000000007 */
+ 0x00000002, /* +0.000000004 */
+ 0x00000001, /* +0.000000002 */
+ 0x00000000, /* +0.000000001 */
+ 0x00000000, /* +0.000000000 */
};
-/* Good quality sine calculated by linearly interpolating
- * a 128 sample sine table. First harmonic has amplitude of about -84 dB.
- * phase has range from 0 to 0xffffffff, representing 0 and
- * 2*pi respectively.
- * Return value is a signed value from LONG_MIN to LONG_MAX, representing
- * -1 and 1 respectively.
+/**
+ * Implements sin and cos using CORDIC rotation.
+ *
+ * @param phase has range from 0 to 0xffffffff, representing 0 and
+ * 2*pi respectively.
+ * @param cos return address for cos
+ * @return sin of phase, value is a signed value from LONG_MIN to LONG_MAX,
+ * representing -1 and 1 respectively.
*/
-static long fsin(unsigned long phase)
-{
- unsigned int pos = phase >> 25;
- unsigned short frac = (phase & 0x01ffffff) >> 9;
- short diff = sinetab[pos + 1] - sinetab[pos];
-
- return (sinetab[pos] << 16) + frac*diff;
-}
+long fsincos(unsigned long phase, long *cos) {
+ long x, x1, y, y1;
+ unsigned long z, z1;
+ int i;
-static inline long fcos(unsigned long phase)
-{
- return fsin(phase + 0xffffffff/4);
+ /* Setup initial vector */
+ x = cordic_circular_gain;
+ y = 0;
+ z = phase;
+
+ /* The phase has to be somewhere between 0..pi for this to work right */
+ if (z < 0xffffffff / 4) {
+ /* z in first quadrant, z += pi/2 to correct */
+ x = -x;
+ z += 0xffffffff / 4;
+ } else if (z < 3 * (0xffffffff / 4)) {
+ /* z in third quadrant, z -= pi/2 to correct */
+ z -= 0xffffffff / 4;
+ } else {
+ /* z in fourth quadrant, z -= 3pi/2 to correct */
+ x = -x;
+ z -= 3 * (0xffffffff / 4);
+ }
+
+ /* Each iteration adds roughly 1-bit of extra precision */
+ for (i = 0; i < 31; i++) {
+ x1 = x >> i;
+ y1 = y >> i;
+ z1 = atan_table[i];
+
+ /* Decided which direction to rotate vector. Pivot point is pi/2 */
+ if (z >= 0xffffffff / 4) {
+ x -= y1;
+ y += x1;
+ z -= z1;
+ } else {
+ x += y1;
+ y -= x1;
+ z += z1;
+ }
+ }
+
+ *cos = x;
+
+ return y;
}
/* Fixed point square root via Newton-Raphson.
@@ -140,15 +197,16 @@ static long dbtoA(long db)
*/
void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
{
+ long cc;
const long one = 1 << 28; /* s3.28 */
const long A = dbtoA(db);
- const long alpha = DIV64(fsin(cutoff), 2*Q, 15); /* s1.30 */
+ const long alpha = DIV64(fsincos(cutoff, &cc), 2*Q, 15); /* s1.30 */
int32_t a0, a1, a2; /* these are all s3.28 format */
int32_t b0, b1, b2;
/* possible numerical ranges listed after each coef */
b0 = one + FRACMUL(alpha, A); /* [1.25..5] */
- b1 = a1 = -2*(fcos(cutoff) >> 3); /* [-2..2] */
+ b1 = a1 = -2*(cc >> 3); /* [-2..2] */
b2 = one - FRACMUL(alpha, A); /* [-3..0.75] */
a0 = one + DIV64(alpha, A, 27); /* [1.25..5] */
a2 = one - DIV64(alpha, A, 27); /* [-3..0.75] */
@@ -163,15 +221,15 @@ void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
/* Calculate coefficients for lowshelf filter */
void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
{
+ long cs;
const long one = 1 << 24; /* s7.24 */
const long A = dbtoA(db);
- const long alpha = DIV64(fsin(cutoff), 2*Q, 15); /* s1.30 */
+ const long alpha = DIV64(fsincos(cutoff, &cs), 2*Q, 15); /* s1.30 */
const long ap1 = (A >> 5) + one;
const long am1 = (A >> 5) - one;
const long twosqrtalpha = 2*(FRACMUL(fsqrt(A >> 5, 24), alpha) << 1);
int32_t a0, a1, a2; /* these are all s7.24 format */
int32_t b0, b1, b2;
- long cs = fcos(cutoff);
b0 = FRACMUL(A, ap1 - FRACMUL(am1, cs) + twosqrtalpha) << 2;
b1 = FRACMUL(A, am1 - FRACMUL(ap1, cs)) << 3;
@@ -190,15 +248,15 @@ void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
/* Calculate coefficients for highshelf filter */
void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, int32_t *c)
{
+ long cs;
const long one = 1 << 24; /* s7.24 */
const long A = dbtoA(db);
- const long alpha = DIV64(fsin(cutoff), 2*Q, 15); /* s1.30 */
+ const long alpha = DIV64(fsincos(cutoff, &cs), 2*Q, 15); /* s1.30 */
const long ap1 = (A >> 5) + one;
const long am1 = (A >> 5) - one;
const long twosqrtalpha = 2*(FRACMUL(fsqrt(A >> 5, 24), alpha) << 1);
int32_t a0, a1, a2; /* these are all s7.24 format */
int32_t b0, b1, b2;
- long cs = fcos(cutoff);
b0 = FRACMUL(A, ap1 + FRACMUL(am1, cs) + twosqrtalpha) << 2;
b1 = -FRACMUL(A, am1 + FRACMUL(ap1, cs)) << 3;