forked from len0rd/rockbox
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
This commit is contained in:
parent
55e67fc1dc
commit
e70a50cd9a
1 changed files with 96 additions and 38 deletions
134
apps/eq.c
134
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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue