diff options
Diffstat (limited to 'apps/dsp.c')
-rw-r--r-- | apps/dsp.c | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/apps/dsp.c b/apps/dsp.c new file mode 100644 index 0000000000..963e98da2e --- /dev/null +++ b/apps/dsp.c | |||
@@ -0,0 +1,397 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 Miika Pekkarinen | ||
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 | #include "kernel.h" | ||
20 | #include "logf.h" | ||
21 | |||
22 | #include "dsp.h" | ||
23 | #include "playback.h" | ||
24 | #include "system.h" | ||
25 | |||
26 | /* The "dither" code to convert the 24-bit samples produced by libmad was | ||
27 | taken from the coolplayer project - coolplayer.sourceforge.net */ | ||
28 | struct s_dither { | ||
29 | int error[3]; | ||
30 | int random; | ||
31 | }; | ||
32 | |||
33 | static struct s_dither dither[2]; | ||
34 | struct dsp_configuration dsp_config; | ||
35 | static int channel; | ||
36 | static int fracbits; | ||
37 | |||
38 | #define SAMPLE_DEPTH 16 | ||
39 | |||
40 | /* | ||
41 | * NAME: prng() | ||
42 | * DESCRIPTION: 32-bit pseudo-random number generator | ||
43 | */ | ||
44 | static __inline | ||
45 | unsigned long prng(unsigned long state) | ||
46 | { | ||
47 | return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; | ||
48 | } | ||
49 | |||
50 | inline long dsp_noiseshape(long sample) | ||
51 | { | ||
52 | sample += dither[channel].error[0] - dither[channel].error[1] | ||
53 | + dither[channel].error[2]; | ||
54 | dither[channel].error[2] = dither[channel].error[1]; | ||
55 | dither[channel].error[1] = dither[channel].error[0]/2; | ||
56 | |||
57 | return sample; | ||
58 | } | ||
59 | |||
60 | inline long dsp_bias(long sample) | ||
61 | { | ||
62 | sample = sample + (1L << (fracbits - SAMPLE_DEPTH)); | ||
63 | |||
64 | return sample; | ||
65 | } | ||
66 | |||
67 | inline long dsp_dither(long *mask) | ||
68 | { | ||
69 | long random, output; | ||
70 | |||
71 | random = prng(dither[channel].random); | ||
72 | output = (random & *mask) - (dither[channel].random & *mask); | ||
73 | dither[channel].random = random; | ||
74 | |||
75 | return output; | ||
76 | } | ||
77 | |||
78 | inline void dsp_clip(long *sample, long *output) | ||
79 | { | ||
80 | if (*output > dsp_config.clip_max) { | ||
81 | *output = dsp_config.clip_max; | ||
82 | |||
83 | if (*sample > dsp_config.clip_max) | ||
84 | *sample = dsp_config.clip_max; | ||
85 | } else if (*output < dsp_config.clip_min) { | ||
86 | *output = dsp_config.clip_min; | ||
87 | |||
88 | if (*sample < dsp_config.clip_min) | ||
89 | *sample = dsp_config.clip_min; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * NAME: dither() | ||
95 | * DESCRIPTION: dither and scale sample | ||
96 | */ | ||
97 | inline int scale_dither_clip(long sample) | ||
98 | { | ||
99 | unsigned int scalebits; | ||
100 | long output, mask; | ||
101 | |||
102 | /* noise shape */ | ||
103 | sample = dsp_noiseshape(sample); | ||
104 | |||
105 | /* bias */ | ||
106 | output = dsp_bias(sample); | ||
107 | |||
108 | scalebits = fracbits + 1 - SAMPLE_DEPTH; | ||
109 | mask = (1L << scalebits) - 1; | ||
110 | |||
111 | /* dither */ | ||
112 | output += dsp_dither(&mask); | ||
113 | |||
114 | /* clip */ | ||
115 | dsp_clip(&sample, &output); | ||
116 | |||
117 | /* quantize */ | ||
118 | output &= ~mask; | ||
119 | |||
120 | /* error feedback */ | ||
121 | dither->error[0] = sample - output; | ||
122 | |||
123 | /* scale */ | ||
124 | return output >> scalebits; | ||
125 | } | ||
126 | |||
127 | inline int scale_clip(long sample) | ||
128 | { | ||
129 | unsigned int scalebits; | ||
130 | long output, mask; | ||
131 | |||
132 | output = sample; | ||
133 | scalebits = fracbits + 1 - SAMPLE_DEPTH; | ||
134 | mask = (1L << scalebits) - 1; | ||
135 | |||
136 | dsp_clip(&sample, &output); | ||
137 | output &= ~mask; | ||
138 | |||
139 | return output >> scalebits; | ||
140 | } | ||
141 | |||
142 | void dsp_scale_dither_clip(short *dest, long *src, int samplecount) | ||
143 | { | ||
144 | dest += channel; | ||
145 | while (samplecount-- > 0) { | ||
146 | *dest = scale_dither_clip(*src); | ||
147 | src++; | ||
148 | dest += 2; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | void dsp_scale_clip(short *dest, long *src, int samplecount) | ||
153 | { | ||
154 | dest += channel; | ||
155 | while (samplecount-- > 0) { | ||
156 | *dest = scale_clip(*src); | ||
157 | src++; | ||
158 | dest += 2; | ||
159 | } | ||
160 | } | ||
161 | |||
162 | struct resampler { | ||
163 | long last_sample, phase, delta; | ||
164 | }; | ||
165 | |||
166 | static struct resampler resample[2]; | ||
167 | |||
168 | #if CONFIG_CPU==MCF5249 && !defined(SIMULATOR) | ||
169 | |||
170 | #define INIT() asm volatile ("move.l #0xb0, %macsr") /* frac, round, clip */ | ||
171 | #define FRACMUL(x, y) \ | ||
172 | ({ \ | ||
173 | long t; \ | ||
174 | asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \ | ||
175 | "movclr.l %%acc0, %[t]\n\t" \ | ||
176 | : [t] "=r" (t) : [a] "r" (x), [b] "r" (y)); \ | ||
177 | t; \ | ||
178 | }) | ||
179 | |||
180 | #else | ||
181 | |||
182 | #define INIT() | ||
183 | #define FRACMUL(x, y) (long)(((long long)(x)*(long long)(y)) << 1) | ||
184 | #endif | ||
185 | |||
186 | /* linear resampling, introduces one sample delay, because of our inability to | ||
187 | look into the future at the end of a frame */ | ||
188 | long downsample(long *out, long *in, int num, struct resampler *s) | ||
189 | { | ||
190 | long i = 1, pos; | ||
191 | long last = s->last_sample; | ||
192 | |||
193 | INIT(); | ||
194 | pos = s->phase >> 16; | ||
195 | /* check if we need last sample of previous frame for interpolation */ | ||
196 | if (pos > 0) | ||
197 | last = in[pos - 1]; | ||
198 | out[0] = last + FRACMUL((s->phase & 0xffff) << 15, in[pos] - last); | ||
199 | s->phase += s->delta; | ||
200 | while ((pos = s->phase >> 16) < num) { | ||
201 | out[i++] = in[pos - 1] + FRACMUL((s->phase & 0xffff) << 15, in[pos] - in[pos - 1]); | ||
202 | s->phase += s->delta; | ||
203 | } | ||
204 | /* wrap phase accumulator back to start of next frame */ | ||
205 | s->phase -= num << 16; | ||
206 | s->last_sample = in[num - 1]; | ||
207 | return i; | ||
208 | } | ||
209 | |||
210 | long upsample(long *out, long *in, int num, struct resampler *s) | ||
211 | { | ||
212 | long i = 0, pos; | ||
213 | |||
214 | INIT(); | ||
215 | while ((pos = s->phase >> 16) == 0) { | ||
216 | out[i++] = s->last_sample + FRACMUL((s->phase & 0xffff) << 15, in[pos] - s->last_sample); | ||
217 | s->phase += s->delta; | ||
218 | } | ||
219 | while ((pos = s->phase >> 16) < num) { | ||
220 | out[i++] = in[pos - 1] + FRACMUL((s->phase & 0xffff) << 15, in[pos] - in[pos - 1]); | ||
221 | s->phase += s->delta; | ||
222 | } | ||
223 | /* wrap phase accumulator back to start of next frame */ | ||
224 | s->phase -= num << 16; | ||
225 | s->last_sample = in[num - 1]; | ||
226 | return i; | ||
227 | } | ||
228 | |||
229 | #define MAX_CHUNK_SIZE 1024 | ||
230 | static char samplebuf[MAX_CHUNK_SIZE*4]; | ||
231 | /* enough to cope with 11khz upsampling */ | ||
232 | long resampled[MAX_CHUNK_SIZE * 4]; | ||
233 | |||
234 | int process(short *dest, long *src, int samplecount) | ||
235 | { | ||
236 | long *p; | ||
237 | int length = samplecount; | ||
238 | |||
239 | p = resampled; | ||
240 | |||
241 | /* Resample as necessary */ | ||
242 | if (dsp_config.frequency > NATIVE_FREQUENCY) | ||
243 | length = upsample(resampled, src, samplecount, &resample[channel]); | ||
244 | else if (dsp_config.frequency < NATIVE_FREQUENCY) | ||
245 | length = downsample(resampled, src, samplecount, &resample[channel]); | ||
246 | else | ||
247 | p = src; | ||
248 | |||
249 | /* Scale & dither */ | ||
250 | if (dsp_config.dither_enabled) { | ||
251 | dsp_scale_dither_clip(dest, p, length); | ||
252 | } else { | ||
253 | dsp_scale_clip(dest, p, length); | ||
254 | } | ||
255 | |||
256 | return length; | ||
257 | } | ||
258 | |||
259 | void convert_stereo_mode(long *dest, long *src, int samplecount) | ||
260 | { | ||
261 | int i; | ||
262 | |||
263 | samplecount /= 2; | ||
264 | |||
265 | for (i = 0; i < samplecount; i++) { | ||
266 | dest[i] = src[i*2 + 0]; | ||
267 | dest[i+samplecount] = src[i*2 + 1]; | ||
268 | } | ||
269 | } | ||
270 | |||
271 | /* Not yet functional. */ | ||
272 | void scale_up(long *dest, short *src, int samplecount) | ||
273 | { | ||
274 | int i; | ||
275 | |||
276 | for (i = 0; i < samplecount; i++) | ||
277 | dest[i] = (long)(src[i] << 8); | ||
278 | } | ||
279 | |||
280 | void scale_up_convert_stereo_mode(long *dest, short *src, int samplecount) | ||
281 | { | ||
282 | int i; | ||
283 | |||
284 | samplecount /= 2; | ||
285 | |||
286 | for (i = 0; i < samplecount; i++) { | ||
287 | dest[i] = (long)(src[i*2+0] << SAMPLE_DEPTH); | ||
288 | dest[i+samplecount] = (long)(src[i*2+1] << SAMPLE_DEPTH); | ||
289 | //dest[i] = (long)(((src[i*2 + 0] << 8)&0x7fff) | ((1L << 31) & src[i*2+0]<<15)); | ||
290 | //dest[i+samplecount] = (long)(((src[i*2 + 1] << 8)&0x7fff) | ((1L << 31) & src[i*2+1]<<15)); | ||
291 | } | ||
292 | } | ||
293 | |||
294 | int dsp_process(char *dest, char *src, int samplecount) | ||
295 | { | ||
296 | int copy_n, rc; | ||
297 | char *p; | ||
298 | int processed_bytes = 0; | ||
299 | |||
300 | fracbits = dsp_config.sample_depth; | ||
301 | |||
302 | while (samplecount > 0) { | ||
303 | yield(); | ||
304 | copy_n = MIN(MAX_CHUNK_SIZE / 4, samplecount); | ||
305 | |||
306 | p = src; | ||
307 | /* Scale up to 32-bit samples. */ | ||
308 | if (dsp_config.sample_depth <= SAMPLE_DEPTH) { | ||
309 | if (dsp_config.stereo_mode == STEREO_INTERLEAVED) | ||
310 | scale_up_convert_stereo_mode((long *)samplebuf, | ||
311 | (short *)p, copy_n); | ||
312 | else | ||
313 | scale_up((long *)samplebuf, (short *)p, copy_n); | ||
314 | p = samplebuf; | ||
315 | fracbits = 31; | ||
316 | } | ||
317 | |||
318 | /* Convert to non-interleaved stereo. */ | ||
319 | else if (dsp_config.stereo_mode == STEREO_INTERLEAVED) { | ||
320 | convert_stereo_mode((long *)samplebuf, (long *)p, copy_n); | ||
321 | p = samplebuf; | ||
322 | } | ||
323 | |||
324 | /* Apply DSP functions. */ | ||
325 | if (dsp_config.stereo_mode == STEREO_INTERLEAVED) { | ||
326 | channel = 0; | ||
327 | rc = process((short *)dest, (long *)p, copy_n / 2) * 4; | ||
328 | p += copy_n * 2; | ||
329 | channel = 1; | ||
330 | process((short *)dest, (long *)p, copy_n / 2); | ||
331 | dest += rc; | ||
332 | } else { | ||
333 | rc = process((short *)dest, (long *)p, copy_n) * 2; | ||
334 | dest += rc * 2; | ||
335 | } | ||
336 | |||
337 | samplecount -= copy_n; | ||
338 | if (dsp_config.sample_depth <= SAMPLE_DEPTH) | ||
339 | src += copy_n * 2; | ||
340 | else | ||
341 | src += copy_n * 4; | ||
342 | |||
343 | processed_bytes += rc; | ||
344 | } | ||
345 | |||
346 | /* Set stereo channel */ | ||
347 | channel = channel ? 0 : 1; | ||
348 | |||
349 | return processed_bytes; | ||
350 | } | ||
351 | |||
352 | bool dsp_configure(int setting, void *value) | ||
353 | { | ||
354 | switch (setting) { | ||
355 | case DSP_SET_FREQUENCY: | ||
356 | dsp_config.frequency = (int)value; | ||
357 | resample[0].delta = resample[1].delta = | ||
358 | (unsigned long)value*65536/NATIVE_FREQUENCY; | ||
359 | break ; | ||
360 | |||
361 | case DSP_SET_CLIP_MIN: | ||
362 | dsp_config.clip_min = (long)value; | ||
363 | break ; | ||
364 | |||
365 | case DSP_SET_CLIP_MAX: | ||
366 | dsp_config.clip_max = (long)value; | ||
367 | break ; | ||
368 | |||
369 | case DSP_SET_SAMPLE_DEPTH: | ||
370 | dsp_config.sample_depth = (long)value; | ||
371 | break ; | ||
372 | |||
373 | case DSP_SET_STEREO_MODE: | ||
374 | dsp_config.stereo_mode = (long)value; | ||
375 | channel = 0; | ||
376 | break ; | ||
377 | |||
378 | case DSP_RESET: | ||
379 | dsp_config.dither_enabled = false; | ||
380 | dsp_config.clip_max = 0x7fffffff; | ||
381 | dsp_config.clip_min = 0x80000000; | ||
382 | dsp_config.frequency = NATIVE_FREQUENCY; | ||
383 | channel = 0; | ||
384 | break ; | ||
385 | |||
386 | case DSP_DITHER: | ||
387 | dsp_config.dither_enabled = (bool)value; | ||
388 | break ; | ||
389 | |||
390 | default: | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | return 1; | ||
395 | } | ||
396 | |||
397 | |||