Initial multi-band EQ support for software codec platforms. Now go start
making that user interface! git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8478 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
b0302f0cbb
commit
a8cc6a7454
4 changed files with 305 additions and 0 deletions
|
@ -76,4 +76,8 @@ playback.c
|
||||||
metadata.c
|
metadata.c
|
||||||
codecs.c
|
codecs.c
|
||||||
dsp.c
|
dsp.c
|
||||||
|
eq.c
|
||||||
|
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR)
|
||||||
|
eq_cf.S
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
34
apps/dsp.c
34
apps/dsp.c
|
@ -19,6 +19,7 @@
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "dsp.h"
|
#include "dsp.h"
|
||||||
|
#include "eq.h"
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "system.h"
|
#include "system.h"
|
||||||
|
@ -166,10 +167,21 @@ struct crossfeed_data
|
||||||
int index;
|
int index;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Current setup is one lowshelf filters, three peaking filters and one
|
||||||
|
highshelf filter. Varying the number of shelving filters make no sense,
|
||||||
|
but adding peaking filters are possible. */
|
||||||
|
struct eq_state {
|
||||||
|
char enabled[5]; /* Flags for active filters */
|
||||||
|
struct eqfilter ls;
|
||||||
|
struct eqfilter pk[3];
|
||||||
|
struct eqfilter hs;
|
||||||
|
};
|
||||||
|
|
||||||
static struct dsp_config dsp_conf[2] IBSS_ATTR;
|
static struct dsp_config dsp_conf[2] IBSS_ATTR;
|
||||||
static struct dither_data dither_data[2] IBSS_ATTR;
|
static struct dither_data dither_data[2] IBSS_ATTR;
|
||||||
static struct resample_data resample_data[2] IBSS_ATTR;
|
static struct resample_data resample_data[2] IBSS_ATTR;
|
||||||
struct crossfeed_data crossfeed_data IBSS_ATTR;
|
struct crossfeed_data crossfeed_data IBSS_ATTR;
|
||||||
|
static struct eq_state eq_data;
|
||||||
|
|
||||||
static int pitch_ratio = 1000;
|
static int pitch_ratio = 1000;
|
||||||
|
|
||||||
|
@ -608,6 +620,25 @@ static void apply_crossfeed(long* src[], int count)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Apply EQ filters to those bands that have got it switched on. */
|
||||||
|
void eq_process(long **x, unsigned num)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned int channels = dsp->stereo_mode != STEREO_MONO ? 2 : 1;
|
||||||
|
|
||||||
|
/* filter configuration currently is 1 low shelf filter, 3 band peaking
|
||||||
|
filters and 1 high shelf filter, in that order.
|
||||||
|
*/
|
||||||
|
if (eq_data.enabled[0])
|
||||||
|
eq_filter(x, &eq_data.ls, num, channels, EQ_SHELF_SHIFT);
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
if (eq_data.enabled[1 + i])
|
||||||
|
eq_filter(x, &eq_data.pk[i], num, channels, EQ_PEAK_SHIFT);
|
||||||
|
}
|
||||||
|
if (eq_data.enabled[4])
|
||||||
|
eq_filter(x, &eq_data.hs, num, channels, EQ_SHELF_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
/* Apply a constant gain to the samples (e.g., for ReplayGain). May update
|
/* Apply a constant gain to the samples (e.g., for ReplayGain). May update
|
||||||
* the src array if gain was applied.
|
* the src array if gain was applied.
|
||||||
* Note that this must be called before the resampler.
|
* Note that this must be called before the resampler.
|
||||||
|
@ -713,6 +744,9 @@ long dsp_process(char* dst, char* src[], long size)
|
||||||
samples = resample(tmp, samples);
|
samples = resample(tmp, samples);
|
||||||
if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO)
|
if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO)
|
||||||
apply_crossfeed(tmp, samples);
|
apply_crossfeed(tmp, samples);
|
||||||
|
/* TODO: Might want to wrap this with a generic eq_enabled when the
|
||||||
|
settings are in place */
|
||||||
|
eq_process(tmp, samples);
|
||||||
write_samples((short*) dst, tmp, samples);
|
write_samples((short*) dst, tmp, samples);
|
||||||
written += samples;
|
written += samples;
|
||||||
dst += samples * sizeof(short) * 2;
|
dst += samples * sizeof(short) * 2;
|
||||||
|
|
226
apps/eq.c
Normal file
226
apps/eq.c
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 Thom Johansen
|
||||||
|
*
|
||||||
|
* All files in this archive are subject to the GNU General Public License.
|
||||||
|
* See the file COPYING in the source tree root for full license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "eq.h"
|
||||||
|
|
||||||
|
/* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson.
|
||||||
|
Slightly faster calculation can be done by deriving forms which use tan()
|
||||||
|
instead of cos() and sin(), but the latter are far easier to use when doing
|
||||||
|
fixed point math, and performance is not a big point in the calculation part.
|
||||||
|
We realise the filters as a second order direct form 1 structure. Direct
|
||||||
|
form 1 was chosen because of better numerical properties for fixed point
|
||||||
|
implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y))
|
||||||
|
/* TODO: This macro requires the EMAC unit to be in fractional mode
|
||||||
|
when the coef generator routines are called. If this can be guaranteeed,
|
||||||
|
then remove the "&& 0" below for faster coef calculation on Coldfire.
|
||||||
|
*/
|
||||||
|
#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) && 0
|
||||||
|
#define FRACMUL(x, y) \
|
||||||
|
({ \
|
||||||
|
long t; \
|
||||||
|
asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \
|
||||||
|
"movclr.l %%acc0, %[t]\n\t" \
|
||||||
|
: [t] "=r" (t) : [a] "r" (x), [b] "r" (y)); \
|
||||||
|
t; \
|
||||||
|
})
|
||||||
|
#else
|
||||||
|
#define FRACMUL(x, y) ((long)(((((long long) (x)) * ((long long) (y))) >> 31)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long fcos(unsigned long phase)
|
||||||
|
{
|
||||||
|
return fsin(phase + 0xffffffff/4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixed point square root via Newton-Raphson.
|
||||||
|
* Output is in same fixed point format as input.
|
||||||
|
* fracbits specifies number of fractional bits in argument.
|
||||||
|
*/
|
||||||
|
static long fsqrt(long a, unsigned int fracbits)
|
||||||
|
{
|
||||||
|
long b = a/2 + (1 << fracbits); /* initial approximation */
|
||||||
|
unsigned n;
|
||||||
|
const unsigned iterations = 4;
|
||||||
|
|
||||||
|
for (n = 0; n < iterations; ++n)
|
||||||
|
b = (b + DIV64(a, b, fracbits))/2;
|
||||||
|
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
short dbtoatab[49] = {
|
||||||
|
2058, 2180, 2309, 2446, 2591, 2744, 2907, 3079, 3261, 3455, 3659, 3876,
|
||||||
|
4106, 4349, 4607, 4880, 5169, 5475, 5799, 6143, 6507, 6893, 7301, 7734,
|
||||||
|
8192, 8677, 9192, 9736, 10313, 10924, 11572, 12257, 12983, 13753, 14568,
|
||||||
|
15431, 16345, 17314, 18340, 19426, 20577, 21797, 23088, 24456, 25905, 27440,
|
||||||
|
29066, 30789, 32613
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Function for converting dB to squared amplitude factor (A = 10^(dB/40)).
|
||||||
|
Range is -24 to 24 dB. If gain values outside of this is needed, the above
|
||||||
|
table needs to be extended.
|
||||||
|
Parameter format is s15.16 fixed point. Return format is s2.29 fixed point.
|
||||||
|
*/
|
||||||
|
static long dbtoA(long db)
|
||||||
|
{
|
||||||
|
const unsigned long bias = 24 << 16;
|
||||||
|
unsigned short frac = (db + bias) & 0x0000ffff;
|
||||||
|
unsigned short pos = (db + bias) >> 16;
|
||||||
|
short diff = dbtoatab[pos + 1] - dbtoatab[pos];
|
||||||
|
|
||||||
|
return (dbtoatab[pos] << 16) + frac*diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate second order section peaking filter coefficients.
|
||||||
|
cutoff is a value from 0 to 0xffffffff, where 0 represents 0 hz and
|
||||||
|
0xffffffff represents nyquist (samplerate/2).
|
||||||
|
Q is an unsigned 6.26 fixed point number, lower bound is artificially set
|
||||||
|
at 0.5.
|
||||||
|
db is s15.16 fixed point and describes gain/attenuation at peak freq.
|
||||||
|
c is a pointer where the coefs will be stored.
|
||||||
|
*/
|
||||||
|
void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, long *c)
|
||||||
|
{
|
||||||
|
const long one = 1 << 28; /* s3.28 */
|
||||||
|
const long A = dbtoA(db);
|
||||||
|
const long alpha = DIV64(fsin(cutoff), 2*Q, 25); /* s1.30 */
|
||||||
|
long a0, a1, a2; /* these are all s3.28 format */
|
||||||
|
long 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] */
|
||||||
|
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] */
|
||||||
|
|
||||||
|
c[0] = DIV64(b0, a0, 28);
|
||||||
|
c[1] = DIV64(b1, a0, 28);
|
||||||
|
c[2] = DIV64(b2, a0, 28);
|
||||||
|
c[3] = DIV64(a1, a0, 28);
|
||||||
|
c[4] = DIV64(a2, a0, 28);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate coefficients for lowshelf filter */
|
||||||
|
void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, long *c)
|
||||||
|
{
|
||||||
|
const long one = 1 << 24; /* s7.24 */
|
||||||
|
const long A = dbtoA(db);
|
||||||
|
const long alpha = DIV64(fsin(cutoff), 2*Q, 25); /* 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);
|
||||||
|
long a0, a1, a2; /* these are all s7.24 format */
|
||||||
|
long b0, b1, b2;
|
||||||
|
long cs = fcos(cutoff);
|
||||||
|
|
||||||
|
b0 = FRACMUL(A, ap1 - FRACMUL(am1, cs) + twosqrtalpha) << 2;
|
||||||
|
b1 = FRACMUL(A, am1 - FRACMUL(ap1, cs)) << 3;
|
||||||
|
b2 = FRACMUL(A, ap1 - FRACMUL(am1, cs) - twosqrtalpha) << 2;
|
||||||
|
a0 = ap1 + FRACMUL(am1, cs) + twosqrtalpha;
|
||||||
|
a1 = -2*((am1 + FRACMUL(ap1, cs)));
|
||||||
|
a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha;
|
||||||
|
|
||||||
|
c[0] = DIV64(b0, a0, 24);
|
||||||
|
c[1] = DIV64(b1, a0, 24);
|
||||||
|
c[2] = DIV64(b2, a0, 24);
|
||||||
|
c[3] = DIV64(a1, a0, 24);
|
||||||
|
c[4] = DIV64(a2, a0, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate coefficients for highshelf filter */
|
||||||
|
void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, long *c)
|
||||||
|
{
|
||||||
|
const long one = 1 << 24; /* s7.24 */
|
||||||
|
const long A = dbtoA(db);
|
||||||
|
const long alpha = DIV64(fsin(cutoff), 2*Q, 25); /* 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);
|
||||||
|
long a0, a1, a2; /* these are all s7.24 format */
|
||||||
|
long b0, b1, b2;
|
||||||
|
long cs = fcos(cutoff);
|
||||||
|
|
||||||
|
b0 = FRACMUL(A, ap1 + FRACMUL(am1, cs) + twosqrtalpha) << 2;
|
||||||
|
b1 = -FRACMUL(A, am1 + FRACMUL(ap1, cs)) << 3;
|
||||||
|
b2 = FRACMUL(A, ap1 + FRACMUL(am1, cs) - twosqrtalpha) << 2;
|
||||||
|
a0 = ap1 - FRACMUL(am1, cs) + twosqrtalpha;
|
||||||
|
a1 = 2*((am1 - FRACMUL(ap1, cs)));
|
||||||
|
a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha;
|
||||||
|
|
||||||
|
c[0] = DIV64(b0, a0, 24);
|
||||||
|
c[1] = DIV64(b1, a0, 24);
|
||||||
|
c[2] = DIV64(b2, a0, 24);
|
||||||
|
c[3] = DIV64(a1, a0, 24);
|
||||||
|
c[4] = DIV64(a2, a0, 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(CPU_COLDFIRE) || defined(SIMULATOR)
|
||||||
|
void eq_filter(long **x, struct eqfilter *f, unsigned num,
|
||||||
|
unsigned channels, unsigned shift)
|
||||||
|
{
|
||||||
|
/* TODO: Implement generic filtering routine. */
|
||||||
|
(void)x;
|
||||||
|
(void)f;
|
||||||
|
(void)num;
|
||||||
|
(void)channels;
|
||||||
|
(void)shift;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
41
apps/eq.h
Normal file
41
apps/eq.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/***************************************************************************
|
||||||
|
* __________ __ ___.
|
||||||
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
||||||
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
||||||
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
|
* \/ \/ \/ \/ \/
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006 Thom Johansen
|
||||||
|
*
|
||||||
|
* All files in this archive are subject to the GNU General Public License.
|
||||||
|
* See the file COPYING in the source tree root for full license agreement.
|
||||||
|
*
|
||||||
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||||
|
* KIND, either express or implied.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _EQ_H
|
||||||
|
#define _EQ_H
|
||||||
|
|
||||||
|
/* These depend on the fixed point formats used by the different filter types
|
||||||
|
and need to be changed when they change.
|
||||||
|
*/
|
||||||
|
#define EQ_PEAK_SHIFT 3
|
||||||
|
#define EQ_SHELF_SHIFT 7
|
||||||
|
|
||||||
|
struct eqfilter {
|
||||||
|
long coefs[5]; /* Order is b0, b1, b2, a1, a2 */
|
||||||
|
long history[2][4];
|
||||||
|
};
|
||||||
|
|
||||||
|
void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, long *c);
|
||||||
|
void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, long *c);
|
||||||
|
void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, long *c);
|
||||||
|
void eq_filter(long **x, struct eqfilter *f, unsigned num,
|
||||||
|
unsigned samples, unsigned shift);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue