diff options
author | Thom Johansen <thomj@rockbox.org> | 2006-01-29 02:10:28 +0000 |
---|---|---|
committer | Thom Johansen <thomj@rockbox.org> | 2006-01-29 02:10:28 +0000 |
commit | a8cc6a74547e6485f3aa9fe597f5d1dc176cca8a (patch) | |
tree | 4606e1a8470f699cefab39357f7f6d38984eff20 | |
parent | b0302f0cbb4674d9ff1584b87628f2bfaafa7a0a (diff) | |
download | rockbox-a8cc6a74547e6485f3aa9fe597f5d1dc176cca8a.tar.gz rockbox-a8cc6a74547e6485f3aa9fe597f5d1dc176cca8a.zip |
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
-rw-r--r-- | apps/SOURCES | 4 | ||||
-rw-r--r-- | apps/dsp.c | 34 | ||||
-rw-r--r-- | apps/eq.c | 226 | ||||
-rw-r--r-- | apps/eq.h | 41 |
4 files changed, 305 insertions, 0 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index cf17bbc27c..94c081ec15 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -76,4 +76,8 @@ playback.c | |||
76 | metadata.c | 76 | metadata.c |
77 | codecs.c | 77 | codecs.c |
78 | dsp.c | 78 | dsp.c |
79 | eq.c | ||
80 | #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) | ||
81 | eq_cf.S | ||
82 | #endif | ||
79 | #endif | 83 | #endif |
diff --git a/apps/dsp.c b/apps/dsp.c index 19cb669a06..b2fc0ce7a2 100644 --- a/apps/dsp.c +++ b/apps/dsp.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <inttypes.h> | 19 | #include <inttypes.h> |
20 | #include <string.h> | 20 | #include <string.h> |
21 | #include "dsp.h" | 21 | #include "dsp.h" |
22 | #include "eq.h" | ||
22 | #include "kernel.h" | 23 | #include "kernel.h" |
23 | #include "playback.h" | 24 | #include "playback.h" |
24 | #include "system.h" | 25 | #include "system.h" |
@@ -166,10 +167,21 @@ struct crossfeed_data | |||
166 | int index; | 167 | int index; |
167 | }; | 168 | }; |
168 | 169 | ||
170 | /* Current setup is one lowshelf filters, three peaking filters and one | ||
171 | highshelf filter. Varying the number of shelving filters make no sense, | ||
172 | but adding peaking filters are possible. */ | ||
173 | struct eq_state { | ||
174 | char enabled[5]; /* Flags for active filters */ | ||
175 | struct eqfilter ls; | ||
176 | struct eqfilter pk[3]; | ||
177 | struct eqfilter hs; | ||
178 | }; | ||
179 | |||
169 | static struct dsp_config dsp_conf[2] IBSS_ATTR; | 180 | static struct dsp_config dsp_conf[2] IBSS_ATTR; |
170 | static struct dither_data dither_data[2] IBSS_ATTR; | 181 | static struct dither_data dither_data[2] IBSS_ATTR; |
171 | static struct resample_data resample_data[2] IBSS_ATTR; | 182 | static struct resample_data resample_data[2] IBSS_ATTR; |
172 | struct crossfeed_data crossfeed_data IBSS_ATTR; | 183 | struct crossfeed_data crossfeed_data IBSS_ATTR; |
184 | static struct eq_state eq_data; | ||
173 | 185 | ||
174 | static int pitch_ratio = 1000; | 186 | static int pitch_ratio = 1000; |
175 | 187 | ||
@@ -608,6 +620,25 @@ static void apply_crossfeed(long* src[], int count) | |||
608 | } | 620 | } |
609 | #endif | 621 | #endif |
610 | 622 | ||
623 | /* Apply EQ filters to those bands that have got it switched on. */ | ||
624 | void eq_process(long **x, unsigned num) | ||
625 | { | ||
626 | int i; | ||
627 | unsigned int channels = dsp->stereo_mode != STEREO_MONO ? 2 : 1; | ||
628 | |||
629 | /* filter configuration currently is 1 low shelf filter, 3 band peaking | ||
630 | filters and 1 high shelf filter, in that order. | ||
631 | */ | ||
632 | if (eq_data.enabled[0]) | ||
633 | eq_filter(x, &eq_data.ls, num, channels, EQ_SHELF_SHIFT); | ||
634 | for (i = 0; i < 3; i++) { | ||
635 | if (eq_data.enabled[1 + i]) | ||
636 | eq_filter(x, &eq_data.pk[i], num, channels, EQ_PEAK_SHIFT); | ||
637 | } | ||
638 | if (eq_data.enabled[4]) | ||
639 | eq_filter(x, &eq_data.hs, num, channels, EQ_SHELF_SHIFT); | ||
640 | } | ||
641 | |||
611 | /* Apply a constant gain to the samples (e.g., for ReplayGain). May update | 642 | /* Apply a constant gain to the samples (e.g., for ReplayGain). May update |
612 | * the src array if gain was applied. | 643 | * the src array if gain was applied. |
613 | * Note that this must be called before the resampler. | 644 | * Note that this must be called before the resampler. |
@@ -713,6 +744,9 @@ long dsp_process(char* dst, char* src[], long size) | |||
713 | samples = resample(tmp, samples); | 744 | samples = resample(tmp, samples); |
714 | if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO) | 745 | if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO) |
715 | apply_crossfeed(tmp, samples); | 746 | apply_crossfeed(tmp, samples); |
747 | /* TODO: Might want to wrap this with a generic eq_enabled when the | ||
748 | settings are in place */ | ||
749 | eq_process(tmp, samples); | ||
716 | write_samples((short*) dst, tmp, samples); | 750 | write_samples((short*) dst, tmp, samples); |
717 | written += samples; | 751 | written += samples; |
718 | dst += samples * sizeof(short) * 2; | 752 | dst += samples * sizeof(short) * 2; |
diff --git a/apps/eq.c b/apps/eq.c new file mode 100644 index 0000000000..8ad886fc0c --- /dev/null +++ b/apps/eq.c | |||
@@ -0,0 +1,226 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Thom Johansen | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #include "config.h" | ||
21 | #include "eq.h" | ||
22 | |||
23 | /* Coef calculation taken from Audio-EQ-Cookbook.txt by Robert Bristow-Johnson. | ||
24 | Slightly faster calculation can be done by deriving forms which use tan() | ||
25 | instead of cos() and sin(), but the latter are far easier to use when doing | ||
26 | fixed point math, and performance is not a big point in the calculation part. | ||
27 | We realise the filters as a second order direct form 1 structure. Direct | ||
28 | form 1 was chosen because of better numerical properties for fixed point | ||
29 | implementations. | ||
30 | */ | ||
31 | |||
32 | #define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y)) | ||
33 | /* TODO: This macro requires the EMAC unit to be in fractional mode | ||
34 | when the coef generator routines are called. If this can be guaranteeed, | ||
35 | then remove the "&& 0" below for faster coef calculation on Coldfire. | ||
36 | */ | ||
37 | #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) && 0 | ||
38 | #define FRACMUL(x, y) \ | ||
39 | ({ \ | ||
40 | long t; \ | ||
41 | asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \ | ||
42 | "movclr.l %%acc0, %[t]\n\t" \ | ||
43 | : [t] "=r" (t) : [a] "r" (x), [b] "r" (y)); \ | ||
44 | t; \ | ||
45 | }) | ||
46 | #else | ||
47 | #define FRACMUL(x, y) ((long)(((((long long) (x)) * ((long long) (y))) >> 31))) | ||
48 | #endif | ||
49 | |||
50 | /* TODO: replaygain.c has some fixed point routines. perhaps we could reuse | ||
51 | them? */ | ||
52 | |||
53 | /* 128 sixteen bit sine samples + guard point */ | ||
54 | short sinetab[] = { | ||
55 | 0, 1607, 3211, 4807, 6392, 7961, 9511, 11038, 12539, 14009, 15446, 16845, | ||
56 | 18204, 19519, 20787, 22004, 23169, 24278, 25329, 26318, 27244, 28105, 28897, | ||
57 | 29621, 30272, 30851, 31356, 31785, 32137, 32412, 32609,32727, 32767, 32727, | ||
58 | 32609, 32412, 32137, 31785, 31356, 30851, 30272, 29621, 28897, 28105, 27244, | ||
59 | 26318, 25329, 24278, 23169, 22004, 20787, 19519, 18204, 16845, 15446, 14009, | ||
60 | 12539, 11038, 9511, 7961, 6392, 4807, 3211, 1607, 0, -1607, -3211, -4807, | ||
61 | -6392, -7961, -9511, -11038, -12539, -14009, -15446, -16845, -18204, -19519, | ||
62 | -20787, -22004, -23169, -24278, -25329, -26318, -27244, -28105, -28897, | ||
63 | -29621, -30272, -30851, -31356, -31785, -32137, -32412, -32609, -32727, | ||
64 | -32767, -32727, -32609, -32412, -32137, -31785, -31356, -30851, -30272, | ||
65 | -29621, -28897, -28105, -27244, -26318, -25329, -24278, -23169, -22004, | ||
66 | -20787, -19519, -18204, -16845, -15446, -14009, -12539, -11038, -9511, | ||
67 | -7961, -6392, -4807, -3211, -1607, 0 | ||
68 | }; | ||
69 | |||
70 | /* Good quality sine calculated by linearly interpolating | ||
71 | * a 128 sample sine table. First harmonic has amplitude of about -84 dB. | ||
72 | * phase has range from 0 to 0xffffffff, representing 0 and | ||
73 | * 2*pi respectively. | ||
74 | * Return value is a signed value from LONG_MIN to LONG_MAX, representing | ||
75 | * -1 and 1 respectively. | ||
76 | */ | ||
77 | static long fsin(unsigned long phase) | ||
78 | { | ||
79 | unsigned int pos = phase >> 25; | ||
80 | unsigned short frac = (phase & 0x01ffffff) >> 9; | ||
81 | short diff = sinetab[pos + 1] - sinetab[pos]; | ||
82 | |||
83 | return (sinetab[pos] << 16) + frac*diff; | ||
84 | } | ||
85 | |||
86 | static inline long fcos(unsigned long phase) | ||
87 | { | ||
88 | return fsin(phase + 0xffffffff/4); | ||
89 | } | ||
90 | |||
91 | /* Fixed point square root via Newton-Raphson. | ||
92 | * Output is in same fixed point format as input. | ||
93 | * fracbits specifies number of fractional bits in argument. | ||
94 | */ | ||
95 | static long fsqrt(long a, unsigned int fracbits) | ||
96 | { | ||
97 | long b = a/2 + (1 << fracbits); /* initial approximation */ | ||
98 | unsigned n; | ||
99 | const unsigned iterations = 4; | ||
100 | |||
101 | for (n = 0; n < iterations; ++n) | ||
102 | b = (b + DIV64(a, b, fracbits))/2; | ||
103 | |||
104 | return b; | ||
105 | } | ||
106 | |||
107 | short dbtoatab[49] = { | ||
108 | 2058, 2180, 2309, 2446, 2591, 2744, 2907, 3079, 3261, 3455, 3659, 3876, | ||
109 | 4106, 4349, 4607, 4880, 5169, 5475, 5799, 6143, 6507, 6893, 7301, 7734, | ||
110 | 8192, 8677, 9192, 9736, 10313, 10924, 11572, 12257, 12983, 13753, 14568, | ||
111 | 15431, 16345, 17314, 18340, 19426, 20577, 21797, 23088, 24456, 25905, 27440, | ||
112 | 29066, 30789, 32613 | ||
113 | }; | ||
114 | |||
115 | /* Function for converting dB to squared amplitude factor (A = 10^(dB/40)). | ||
116 | Range is -24 to 24 dB. If gain values outside of this is needed, the above | ||
117 | table needs to be extended. | ||
118 | Parameter format is s15.16 fixed point. Return format is s2.29 fixed point. | ||
119 | */ | ||
120 | static long dbtoA(long db) | ||
121 | { | ||
122 | const unsigned long bias = 24 << 16; | ||
123 | unsigned short frac = (db + bias) & 0x0000ffff; | ||
124 | unsigned short pos = (db + bias) >> 16; | ||
125 | short diff = dbtoatab[pos + 1] - dbtoatab[pos]; | ||
126 | |||
127 | return (dbtoatab[pos] << 16) + frac*diff; | ||
128 | } | ||
129 | |||
130 | /* Calculate second order section peaking filter coefficients. | ||
131 | cutoff is a value from 0 to 0xffffffff, where 0 represents 0 hz and | ||
132 | 0xffffffff represents nyquist (samplerate/2). | ||
133 | Q is an unsigned 6.26 fixed point number, lower bound is artificially set | ||
134 | at 0.5. | ||
135 | db is s15.16 fixed point and describes gain/attenuation at peak freq. | ||
136 | c is a pointer where the coefs will be stored. | ||
137 | */ | ||
138 | void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, long *c) | ||
139 | { | ||
140 | const long one = 1 << 28; /* s3.28 */ | ||
141 | const long A = dbtoA(db); | ||
142 | const long alpha = DIV64(fsin(cutoff), 2*Q, 25); /* s1.30 */ | ||
143 | long a0, a1, a2; /* these are all s3.28 format */ | ||
144 | long b0, b1, b2; | ||
145 | |||
146 | /* possible numerical ranges listed after each coef */ | ||
147 | b0 = one + FRACMUL(alpha, A); /* [1.25..5] */ | ||
148 | b1 = a1 = -2*(fcos(cutoff) >> 3); /* [-2..2] */ | ||
149 | b2 = one - FRACMUL(alpha, A); /* [-3..0.75] */ | ||
150 | a0 = one + DIV64(alpha, A, 27); /* [1.25..5] */ | ||
151 | a2 = one - DIV64(alpha, A, 27); /* [-3..0.75] */ | ||
152 | |||
153 | c[0] = DIV64(b0, a0, 28); | ||
154 | c[1] = DIV64(b1, a0, 28); | ||
155 | c[2] = DIV64(b2, a0, 28); | ||
156 | c[3] = DIV64(a1, a0, 28); | ||
157 | c[4] = DIV64(a2, a0, 28); | ||
158 | } | ||
159 | |||
160 | /* Calculate coefficients for lowshelf filter */ | ||
161 | void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, long *c) | ||
162 | { | ||
163 | const long one = 1 << 24; /* s7.24 */ | ||
164 | const long A = dbtoA(db); | ||
165 | const long alpha = DIV64(fsin(cutoff), 2*Q, 25); /* s1.30 */ | ||
166 | const long ap1 = (A >> 5) + one; | ||
167 | const long am1 = (A >> 5) - one; | ||
168 | const long twosqrtalpha = 2*(FRACMUL(fsqrt(A >> 5, 24), alpha) << 1); | ||
169 | long a0, a1, a2; /* these are all s7.24 format */ | ||
170 | long b0, b1, b2; | ||
171 | long cs = fcos(cutoff); | ||
172 | |||
173 | b0 = FRACMUL(A, ap1 - FRACMUL(am1, cs) + twosqrtalpha) << 2; | ||
174 | b1 = FRACMUL(A, am1 - FRACMUL(ap1, cs)) << 3; | ||
175 | b2 = FRACMUL(A, ap1 - FRACMUL(am1, cs) - twosqrtalpha) << 2; | ||
176 | a0 = ap1 + FRACMUL(am1, cs) + twosqrtalpha; | ||
177 | a1 = -2*((am1 + FRACMUL(ap1, cs))); | ||
178 | a2 = ap1 + FRACMUL(am1, cs) - twosqrtalpha; | ||
179 | |||
180 | c[0] = DIV64(b0, a0, 24); | ||
181 | c[1] = DIV64(b1, a0, 24); | ||
182 | c[2] = DIV64(b2, a0, 24); | ||
183 | c[3] = DIV64(a1, a0, 24); | ||
184 | c[4] = DIV64(a2, a0, 24); | ||
185 | } | ||
186 | |||
187 | /* Calculate coefficients for highshelf filter */ | ||
188 | void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, long *c) | ||
189 | { | ||
190 | const long one = 1 << 24; /* s7.24 */ | ||
191 | const long A = dbtoA(db); | ||
192 | const long alpha = DIV64(fsin(cutoff), 2*Q, 25); /* s1.30 */ | ||
193 | const long ap1 = (A >> 5) + one; | ||
194 | const long am1 = (A >> 5) - one; | ||
195 | const long twosqrtalpha = 2*(FRACMUL(fsqrt(A >> 5, 24), alpha) << 1); | ||
196 | long a0, a1, a2; /* these are all s7.24 format */ | ||
197 | long b0, b1, b2; | ||
198 | long cs = fcos(cutoff); | ||
199 | |||
200 | b0 = FRACMUL(A, ap1 + FRACMUL(am1, cs) + twosqrtalpha) << 2; | ||
201 | b1 = -FRACMUL(A, am1 + FRACMUL(ap1, cs)) << 3; | ||
202 | b2 = FRACMUL(A, ap1 + FRACMUL(am1, cs) - twosqrtalpha) << 2; | ||
203 | a0 = ap1 - FRACMUL(am1, cs) + twosqrtalpha; | ||
204 | a1 = 2*((am1 - FRACMUL(ap1, cs))); | ||
205 | a2 = ap1 - FRACMUL(am1, cs) - twosqrtalpha; | ||
206 | |||
207 | c[0] = DIV64(b0, a0, 24); | ||
208 | c[1] = DIV64(b1, a0, 24); | ||
209 | c[2] = DIV64(b2, a0, 24); | ||
210 | c[3] = DIV64(a1, a0, 24); | ||
211 | c[4] = DIV64(a2, a0, 24); | ||
212 | } | ||
213 | |||
214 | #if !defined(CPU_COLDFIRE) || defined(SIMULATOR) | ||
215 | void eq_filter(long **x, struct eqfilter *f, unsigned num, | ||
216 | unsigned channels, unsigned shift) | ||
217 | { | ||
218 | /* TODO: Implement generic filtering routine. */ | ||
219 | (void)x; | ||
220 | (void)f; | ||
221 | (void)num; | ||
222 | (void)channels; | ||
223 | (void)shift; | ||
224 | } | ||
225 | #endif | ||
226 | |||
diff --git a/apps/eq.h b/apps/eq.h new file mode 100644 index 0000000000..f7616387b4 --- /dev/null +++ b/apps/eq.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 Thom Johansen | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef _EQ_H | ||
21 | #define _EQ_H | ||
22 | |||
23 | /* These depend on the fixed point formats used by the different filter types | ||
24 | and need to be changed when they change. | ||
25 | */ | ||
26 | #define EQ_PEAK_SHIFT 3 | ||
27 | #define EQ_SHELF_SHIFT 7 | ||
28 | |||
29 | struct eqfilter { | ||
30 | long coefs[5]; /* Order is b0, b1, b2, a1, a2 */ | ||
31 | long history[2][4]; | ||
32 | }; | ||
33 | |||
34 | void eq_pk_coefs(unsigned long cutoff, unsigned long Q, long db, long *c); | ||
35 | void eq_ls_coefs(unsigned long cutoff, unsigned long Q, long db, long *c); | ||
36 | void eq_hs_coefs(unsigned long cutoff, unsigned long Q, long db, long *c); | ||
37 | void eq_filter(long **x, struct eqfilter *f, unsigned num, | ||
38 | unsigned samples, unsigned shift); | ||
39 | |||
40 | #endif | ||
41 | |||