From e70a50cd9a7d0a3ade17c40ed36011ba159e5e65 Mon Sep 17 00:00:00 2001 From: Thom Johansen Date: Thu, 23 Mar 2006 15:40:59 +0000 Subject: 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 --- apps/eq.c | 134 ++++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 96 insertions(+), 38 deletions(-) (limited to 'apps') 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; -- cgit v1.2.3