diff options
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs.c | 1 | ||||
-rw-r--r-- | apps/codecs.h | 1 | ||||
-rw-r--r-- | apps/codecs/a52.c | 15 | ||||
-rw-r--r-- | apps/codecs/flac.c | 15 | ||||
-rw-r--r-- | apps/codecs/mpa.c | 303 | ||||
-rw-r--r-- | apps/codecs/vorbis.c | 30 | ||||
-rw-r--r-- | apps/codecs/wav.c | 17 | ||||
-rw-r--r-- | apps/codecs/wavpack.c | 16 | ||||
-rw-r--r-- | apps/dsp.c | 397 | ||||
-rw-r--r-- | apps/dsp.h | 48 | ||||
-rw-r--r-- | apps/playback.c | 131 | ||||
-rw-r--r-- | apps/playback.h | 17 | ||||
-rw-r--r-- | firmware/export/pcm_playback.h | 8 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 133 |
15 files changed, 805 insertions, 328 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index 738533105b..271c2ba48c 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -52,6 +52,7 @@ recorder/recording.c | |||
52 | playback.c | 52 | playback.c |
53 | metadata.c | 53 | metadata.c |
54 | codecs.c | 54 | codecs.c |
55 | dsp.c | ||
55 | #ifndef SIMULATOR | 56 | #ifndef SIMULATOR |
56 | pcm_recording.c | 57 | pcm_recording.c |
57 | #endif | 58 | #endif |
diff --git a/apps/codecs.c b/apps/codecs.c index 053a622841..da4b1f8bff 100644 --- a/apps/codecs.c +++ b/apps/codecs.c | |||
@@ -88,6 +88,7 @@ struct codec_api ci = { | |||
88 | NULL, | 88 | NULL, |
89 | NULL, | 89 | NULL, |
90 | NULL, | 90 | NULL, |
91 | NULL, | ||
91 | 92 | ||
92 | splash, | 93 | splash, |
93 | 94 | ||
diff --git a/apps/codecs.h b/apps/codecs.h index e322d758e1..c373bdb8ba 100644 --- a/apps/codecs.h +++ b/apps/codecs.h | |||
@@ -143,6 +143,7 @@ struct codec_api { | |||
143 | /* Insert PCM data into audio buffer for playback. Playback will start | 143 | /* Insert PCM data into audio buffer for playback. Playback will start |
144 | automatically. */ | 144 | automatically. */ |
145 | bool (*audiobuffer_insert)(char *data, size_t length); | 145 | bool (*audiobuffer_insert)(char *data, size_t length); |
146 | bool (*audiobuffer_insert_split)(void *ch1, void *ch2, size_t length); | ||
146 | /* Set song position in WPS (value in ms). */ | 147 | /* Set song position in WPS (value in ms). */ |
147 | void (*set_elapsed)(unsigned int value); | 148 | void (*set_elapsed)(unsigned int value); |
148 | 149 | ||
diff --git a/apps/codecs/a52.c b/apps/codecs/a52.c index bc711965ec..663e7941ec 100644 --- a/apps/codecs/a52.c +++ b/apps/codecs/a52.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <codecs/liba52/a52.h> | 24 | #include <codecs/liba52/a52.h> |
25 | 25 | ||
26 | #include "playback.h" | 26 | #include "playback.h" |
27 | #include "dsp.h" | ||
27 | #include "lib/codeclib.h" | 28 | #include "lib/codeclib.h" |
28 | 29 | ||
29 | #define BUFFER_SIZE 4096 | 30 | #define BUFFER_SIZE 4096 |
@@ -173,12 +174,26 @@ enum codec_status codec_start(struct codec_api* api) | |||
173 | ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); | 174 | ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); |
174 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); | 175 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); |
175 | 176 | ||
177 | ci->configure(DSP_DITHER, (bool *)false); | ||
178 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
179 | ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); | ||
180 | |||
176 | next_track: | 181 | next_track: |
177 | 182 | ||
178 | if (codec_init(api)) { | 183 | if (codec_init(api)) { |
179 | return CODEC_ERROR; | 184 | return CODEC_ERROR; |
180 | } | 185 | } |
181 | 186 | ||
187 | while (!rb->taginfo_ready) | ||
188 | rb->yield(); | ||
189 | |||
190 | if (rb->id3->frequency != NATIVE_FREQUENCY) { | ||
191 | rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); | ||
192 | rb->configure(CODEC_DSP_ENABLE, (bool *)true); | ||
193 | } else { | ||
194 | rb->configure(CODEC_DSP_ENABLE, (bool *)false); | ||
195 | } | ||
196 | |||
182 | /* Intialise the A52 decoder and check for success */ | 197 | /* Intialise the A52 decoder and check for success */ |
183 | state = a52_init (0); // Parameter is "accel" | 198 | state = a52_init (0); // Parameter is "accel" |
184 | 199 | ||
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c index 07e5b8f566..d7ae037d26 100644 --- a/apps/codecs/flac.c +++ b/apps/codecs/flac.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <codecs/libFLAC/include/FLAC/seekable_stream_decoder.h> | 22 | #include <codecs/libFLAC/include/FLAC/seekable_stream_decoder.h> |
23 | #include "playback.h" | 23 | #include "playback.h" |
24 | #include "lib/codeclib.h" | 24 | #include "lib/codeclib.h" |
25 | #include "dsp.h" | ||
25 | 26 | ||
26 | #define FLAC_MAX_SUPPORTED_BLOCKSIZE 4608 | 27 | #define FLAC_MAX_SUPPORTED_BLOCKSIZE 4608 |
27 | #define FLAC_MAX_SUPPORTED_CHANNELS 2 | 28 | #define FLAC_MAX_SUPPORTED_CHANNELS 2 |
@@ -180,12 +181,26 @@ enum codec_status codec_start(struct codec_api* api) | |||
180 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); | 181 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); |
181 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); | 182 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); |
182 | 183 | ||
184 | ci->configure(DSP_DITHER, (bool *)false); | ||
185 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
186 | ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); | ||
187 | |||
183 | next_track: | 188 | next_track: |
184 | 189 | ||
185 | if (codec_init(api)) { | 190 | if (codec_init(api)) { |
186 | return CODEC_ERROR; | 191 | return CODEC_ERROR; |
187 | } | 192 | } |
188 | 193 | ||
194 | while (!rb->taginfo_ready) | ||
195 | rb->yield(); | ||
196 | |||
197 | if (rb->id3->frequency != NATIVE_FREQUENCY) { | ||
198 | rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); | ||
199 | rb->configure(CODEC_DSP_ENABLE, (bool *)true); | ||
200 | } else { | ||
201 | rb->configure(CODEC_DSP_ENABLE, (bool *)false); | ||
202 | } | ||
203 | |||
189 | /* Create a decoder instance */ | 204 | /* Create a decoder instance */ |
190 | 205 | ||
191 | flacDecoder=FLAC__seekable_stream_decoder_new(); | 206 | flacDecoder=FLAC__seekable_stream_decoder_new(); |
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c index 736eef1ffe..f052b9df88 100644 --- a/apps/codecs/mpa.c +++ b/apps/codecs/mpa.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <codecs/libmad/mad.h> | 22 | #include <codecs/libmad/mad.h> |
23 | 23 | ||
24 | #include "playback.h" | 24 | #include "playback.h" |
25 | #include "dsp.h" | ||
25 | #include "mp3data.h" | 26 | #include "mp3data.h" |
26 | #include "lib/codeclib.h" | 27 | #include "lib/codeclib.h" |
27 | 28 | ||
@@ -29,7 +30,6 @@ struct mad_stream Stream IDATA_ATTR; | |||
29 | struct mad_frame Frame IDATA_ATTR; | 30 | struct mad_frame Frame IDATA_ATTR; |
30 | struct mad_synth Synth IDATA_ATTR; | 31 | struct mad_synth Synth IDATA_ATTR; |
31 | mad_timer_t Timer; | 32 | mad_timer_t Timer; |
32 | struct dither d0, d1; | ||
33 | 33 | ||
34 | /* The following function is used inside libmad - let's hope it's never | 34 | /* The following function is used inside libmad - let's hope it's never |
35 | called. | 35 | called. |
@@ -38,122 +38,6 @@ struct dither d0, d1; | |||
38 | void abort(void) { | 38 | void abort(void) { |
39 | } | 39 | } |
40 | 40 | ||
41 | /* The "dither" code to convert the 24-bit samples produced by libmad was | ||
42 | taken from the coolplayer project - coolplayer.sourceforge.net */ | ||
43 | |||
44 | struct dither { | ||
45 | mad_fixed_t error[3]; | ||
46 | mad_fixed_t random; | ||
47 | }; | ||
48 | |||
49 | # define SAMPLE_DEPTH 16 | ||
50 | # define scale(x, y) dither((x), (y)) | ||
51 | |||
52 | /* | ||
53 | * NAME: prng() | ||
54 | * DESCRIPTION: 32-bit pseudo-random number generator | ||
55 | */ | ||
56 | static __inline | ||
57 | unsigned long prng(unsigned long state) | ||
58 | { | ||
59 | return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * NAME: dither() | ||
64 | * DESCRIPTION: dither and scale sample | ||
65 | */ | ||
66 | inline int dither(mad_fixed_t sample, struct dither *dither) | ||
67 | { | ||
68 | unsigned int scalebits; | ||
69 | mad_fixed_t output, mask, random; | ||
70 | |||
71 | enum { | ||
72 | MIN = -MAD_F_ONE, | ||
73 | MAX = MAD_F_ONE - 1 | ||
74 | }; | ||
75 | |||
76 | /* noise shape */ | ||
77 | sample += dither->error[0] - dither->error[1] + dither->error[2]; | ||
78 | |||
79 | dither->error[2] = dither->error[1]; | ||
80 | dither->error[1] = dither->error[0]/2; | ||
81 | |||
82 | /* bias */ | ||
83 | output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1)); | ||
84 | |||
85 | scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH; | ||
86 | mask = (1L << scalebits) - 1; | ||
87 | |||
88 | /* dither */ | ||
89 | random = prng(dither->random); | ||
90 | output += (random & mask) - (dither->random & mask); | ||
91 | |||
92 | //dither->random = random; | ||
93 | |||
94 | /* clip */ | ||
95 | if (output > MAX) { | ||
96 | output = MAX; | ||
97 | |||
98 | if (sample > MAX) | ||
99 | sample = MAX; | ||
100 | } else if (output < MIN) { | ||
101 | output = MIN; | ||
102 | |||
103 | if (sample < MIN) | ||
104 | sample = MIN; | ||
105 | } | ||
106 | |||
107 | /* quantize */ | ||
108 | output &= ~mask; | ||
109 | |||
110 | /* error feedback */ | ||
111 | dither->error[0] = sample - output; | ||
112 | |||
113 | /* scale */ | ||
114 | return output >> scalebits; | ||
115 | } | ||
116 | |||
117 | inline int detect_silence(mad_fixed_t sample) | ||
118 | { | ||
119 | unsigned int scalebits; | ||
120 | mad_fixed_t output, mask; | ||
121 | |||
122 | enum { | ||
123 | MIN = -MAD_F_ONE, | ||
124 | MAX = MAD_F_ONE - 1 | ||
125 | }; | ||
126 | |||
127 | /* bias */ | ||
128 | output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1)); | ||
129 | |||
130 | scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH; | ||
131 | mask = (1L << scalebits) - 1; | ||
132 | |||
133 | /* clip */ | ||
134 | if (output > MAX) { | ||
135 | output = MAX; | ||
136 | |||
137 | if (sample > MAX) | ||
138 | sample = MAX; | ||
139 | } else if (output < MIN) { | ||
140 | output = MIN; | ||
141 | |||
142 | if (sample < MIN) | ||
143 | sample = MIN; | ||
144 | } | ||
145 | |||
146 | /* quantize */ | ||
147 | output &= ~mask; | ||
148 | |||
149 | /* scale */ | ||
150 | output >>= scalebits + 4; | ||
151 | |||
152 | if (output == 0x00 || output == 0xff) | ||
153 | return 1; | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | 41 | ||
158 | #define INPUT_CHUNK_SIZE 8192 | 42 | #define INPUT_CHUNK_SIZE 8192 |
159 | #define OUTPUT_BUFFER_SIZE 65536 /* Must be an integer multiple of 4. */ | 43 | #define OUTPUT_BUFFER_SIZE 65536 /* Must be an integer multiple of 4. */ |
@@ -162,7 +46,6 @@ unsigned char OutputBuffer[OUTPUT_BUFFER_SIZE]; | |||
162 | unsigned char *OutputPtr; | 46 | unsigned char *OutputPtr; |
163 | unsigned char *GuardPtr = NULL; | 47 | unsigned char *GuardPtr = NULL; |
164 | const unsigned char *OutputBufferEnd = OutputBuffer + OUTPUT_BUFFER_SIZE; | 48 | const unsigned char *OutputBufferEnd = OutputBuffer + OUTPUT_BUFFER_SIZE; |
165 | long resampled_data[2][5000]; /* enough to cope with 11khz upsampling */ | ||
166 | 49 | ||
167 | mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR; | 50 | mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR; |
168 | unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR; | 51 | unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR; |
@@ -174,73 +57,7 @@ extern char iramstart[]; | |||
174 | extern char iramend[]; | 57 | extern char iramend[]; |
175 | #endif | 58 | #endif |
176 | 59 | ||
177 | #undef DEBUG_GAPLESS | 60 | /* |
178 | |||
179 | struct resampler { | ||
180 | long last_sample, phase, delta; | ||
181 | }; | ||
182 | |||
183 | #if CONFIG_CPU==MCF5249 && !defined(SIMULATOR) | ||
184 | |||
185 | #define INIT() asm volatile ("move.l #0xb0, %macsr") /* frac, round, clip */ | ||
186 | #define FRACMUL(x, y) \ | ||
187 | ({ \ | ||
188 | long t; \ | ||
189 | asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \ | ||
190 | "movclr.l %%acc0, %[t]\n\t" \ | ||
191 | : [t] "=r" (t) : [a] "r" (x), [b] "r" (y)); \ | ||
192 | t; \ | ||
193 | }) | ||
194 | |||
195 | #else | ||
196 | |||
197 | #define INIT() | ||
198 | #define FRACMUL(x, y) (long)(((long long)(x)*(long long)(y)) << 1) | ||
199 | #endif | ||
200 | |||
201 | /* linear resampling, introduces one sample delay, because of our inability to | ||
202 | look into the future at the end of a frame */ | ||
203 | long downsample(long *in, long *out, int num, struct resampler *s) | ||
204 | { | ||
205 | long i = 1, pos; | ||
206 | long last = s->last_sample; | ||
207 | |||
208 | INIT(); | ||
209 | pos = s->phase >> 16; | ||
210 | /* check if we need last sample of previous frame for interpolation */ | ||
211 | if (pos > 0) | ||
212 | last = in[pos - 1]; | ||
213 | out[0] = last + FRACMUL((s->phase & 0xffff) << 15, in[pos] - last); | ||
214 | s->phase += s->delta; | ||
215 | while ((pos = s->phase >> 16) < num) { | ||
216 | out[i++] = in[pos - 1] + FRACMUL((s->phase & 0xffff) << 15, in[pos] - in[pos - 1]); | ||
217 | s->phase += s->delta; | ||
218 | } | ||
219 | /* wrap phase accumulator back to start of next frame */ | ||
220 | s->phase -= num << 16; | ||
221 | s->last_sample = in[num - 1]; | ||
222 | return i; | ||
223 | } | ||
224 | |||
225 | long upsample(long *in, long *out, int num, struct resampler *s) | ||
226 | { | ||
227 | long i = 0, pos; | ||
228 | |||
229 | INIT(); | ||
230 | while ((pos = s->phase >> 16) == 0) { | ||
231 | out[i++] = s->last_sample + FRACMUL((s->phase & 0xffff) << 15, in[pos] - s->last_sample); | ||
232 | s->phase += s->delta; | ||
233 | } | ||
234 | while ((pos = s->phase >> 16) < num) { | ||
235 | out[i++] = in[pos - 1] + FRACMUL((s->phase & 0xffff) << 15, in[pos] - in[pos - 1]); | ||
236 | s->phase += s->delta; | ||
237 | } | ||
238 | /* wrap phase accumulator back to start of next frame */ | ||
239 | s->phase -= num << 16; | ||
240 | s->last_sample = in[num - 1]; | ||
241 | return i; | ||
242 | } | ||
243 | |||
244 | long resample(long *in, long *out, int num, struct resampler *s) | 61 | long resample(long *in, long *out, int num, struct resampler *s) |
245 | { | 62 | { |
246 | if (s->delta >= (1 << 16)) | 63 | if (s->delta >= (1 << 16)) |
@@ -248,7 +65,7 @@ long resample(long *in, long *out, int num, struct resampler *s) | |||
248 | else | 65 | else |
249 | return upsample(in, out, num, s); | 66 | return upsample(in, out, num, s); |
250 | } | 67 | } |
251 | 68 | */ | |
252 | /* this is the codec entry point */ | 69 | /* this is the codec entry point */ |
253 | enum codec_status codec_start(struct codec_api* api) | 70 | enum codec_status codec_start(struct codec_api* api) |
254 | { | 71 | { |
@@ -257,20 +74,12 @@ enum codec_status codec_start(struct codec_api* api) | |||
257 | int Status = 0; | 74 | int Status = 0; |
258 | size_t size; | 75 | size_t size; |
259 | int file_end; | 76 | int file_end; |
260 | unsigned short Sample; | ||
261 | char *InputBuffer; | 77 | char *InputBuffer; |
262 | unsigned int samplecount; | 78 | unsigned int samplecount; |
263 | unsigned int samplesdone; | 79 | unsigned int samplesdone; |
264 | bool first_frame; | 80 | bool first_frame; |
265 | #ifdef DEBUG_GAPLESS | ||
266 | bool first = true; | ||
267 | int fd; | ||
268 | #endif | ||
269 | int i; | ||
270 | int yieldcounter = 0; | ||
271 | int stop_skip, start_skip; | 81 | int stop_skip, start_skip; |
272 | struct resampler lr = { 0, 0, 0 }, rr = { 0, 0, 0 }; | 82 | // struct resampler lr = { 0, 0, 0 }, rr = { 0, 0, 0 }; |
273 | long length; | ||
274 | /* Generic codec inititialisation */ | 83 | /* Generic codec inititialisation */ |
275 | 84 | ||
276 | TEST_CODEC_API(api); | 85 | TEST_CODEC_API(api); |
@@ -289,7 +98,13 @@ enum codec_status codec_start(struct codec_api* api) | |||
289 | 98 | ||
290 | ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); | 99 | ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); |
291 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); | 100 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); |
292 | 101 | ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE); | |
102 | ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1)); | ||
103 | ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(MAD_F_FRACBITS)); | ||
104 | ci->configure(DSP_DITHER, (bool *)true); | ||
105 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED); | ||
106 | ci->configure(CODEC_DSP_ENABLE, (bool *)true); | ||
107 | |||
293 | ci->memset(&Stream, 0, sizeof(struct mad_stream)); | 108 | ci->memset(&Stream, 0, sizeof(struct mad_stream)); |
294 | ci->memset(&Frame, 0, sizeof(struct mad_frame)); | 109 | ci->memset(&Frame, 0, sizeof(struct mad_frame)); |
295 | ci->memset(&Synth, 0, sizeof(struct mad_synth)); | 110 | ci->memset(&Synth, 0, sizeof(struct mad_synth)); |
@@ -309,14 +124,6 @@ enum codec_status codec_start(struct codec_api* api) | |||
309 | for gapless playback */ | 124 | for gapless playback */ |
310 | next_track: | 125 | next_track: |
311 | 126 | ||
312 | #ifdef DEBUG_GAPLESS | ||
313 | if (first) | ||
314 | fd = ci->open("/first.pcm", O_WRONLY | O_CREAT); | ||
315 | else | ||
316 | fd = ci->open("/second.pcm", O_WRONLY | O_CREAT); | ||
317 | first = false; | ||
318 | #endif | ||
319 | |||
320 | info = ci->mp3data; | 127 | info = ci->mp3data; |
321 | first_frame = false; | 128 | first_frame = false; |
322 | file_end = 0; | 129 | file_end = 0; |
@@ -325,6 +132,8 @@ enum codec_status codec_start(struct codec_api* api) | |||
325 | while (!*ci->taginfo_ready) | 132 | while (!*ci->taginfo_ready) |
326 | ci->yield(); | 133 | ci->yield(); |
327 | 134 | ||
135 | ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency); | ||
136 | |||
328 | ci->request_buffer(&size, ci->id3->first_frame_offset); | 137 | ci->request_buffer(&size, ci->id3->first_frame_offset); |
329 | ci->advance_buffer(size); | 138 | ci->advance_buffer(size); |
330 | 139 | ||
@@ -350,13 +159,7 @@ enum codec_status codec_start(struct codec_api* api) | |||
350 | samplecount = ci->id3->length * (ci->id3->frequency / 100) / 10; | 159 | samplecount = ci->id3->length * (ci->id3->frequency / 100) / 10; |
351 | samplesdone = ci->id3->elapsed * (ci->id3->frequency / 100) / 10; | 160 | samplesdone = ci->id3->elapsed * (ci->id3->frequency / 100) / 10; |
352 | } | 161 | } |
353 | /* rb->snprintf(buf2, sizeof(buf2), "sc: %d", samplecount); | 162 | |
354 | rb->splash(0, true, buf2); | ||
355 | rb->snprintf(buf2, sizeof(buf2), "length: %d", ci->id3->length); | ||
356 | rb->splash(HZ*5, true, buf2); | ||
357 | rb->snprintf(buf2, sizeof(buf2), "frequency: %d", ci->id3->frequency); | ||
358 | rb->splash(HZ*5, true, buf2); */ | ||
359 | lr.delta = rr.delta = ci->id3->frequency*65536/44100; | ||
360 | /* This is the decoding loop. */ | 163 | /* This is the decoding loop. */ |
361 | while (1) { | 164 | while (1) { |
362 | ci->yield(); | 165 | ci->yield(); |
@@ -387,9 +190,6 @@ enum codec_status codec_start(struct codec_api* api) | |||
387 | mad_stream_buffer(&Stream, InputBuffer, size); | 190 | mad_stream_buffer(&Stream, InputBuffer, size); |
388 | } | 191 | } |
389 | 192 | ||
390 | //if ((int)ci->curpos >= ci->id3->first_frame_offset) | ||
391 | //first_frame = true; | ||
392 | |||
393 | if(mad_frame_decode(&Frame,&Stream)) | 193 | if(mad_frame_decode(&Frame,&Stream)) |
394 | { | 194 | { |
395 | if (Stream.error == MAD_FLAG_INCOMPLETE || Stream.error == MAD_ERROR_BUFLEN) { | 195 | if (Stream.error == MAD_FLAG_INCOMPLETE || Stream.error == MAD_ERROR_BUFLEN) { |
@@ -428,78 +228,23 @@ enum codec_status codec_start(struct codec_api* api) | |||
428 | 228 | ||
429 | mad_synth_frame(&Synth,&Frame); | 229 | mad_synth_frame(&Synth,&Frame); |
430 | 230 | ||
431 | //if (!first_frame) { | ||
432 | //samplecount -= Synth.pcm.length; | ||
433 | //continue ; | ||
434 | //} | ||
435 | |||
436 | /* Convert MAD's numbers to an array of 16-bit LE signed integers */ | 231 | /* Convert MAD's numbers to an array of 16-bit LE signed integers */ |
437 | /* We skip start_skip number of samples here, this should only happen for | 232 | /* We skip start_skip number of samples here, this should only happen for |
438 | very first frame in the stream. */ | 233 | very first frame in the stream. */ |
439 | /* TODO: possible for start_skip to exceed one frames worth of samples? */ | 234 | /* TODO: possible for start_skip to exceed one frames worth of samples? */ |
440 | length = resample((long *)&Synth.pcm.samples[0][start_skip], resampled_data[0], Synth.pcm.length, &lr); | 235 | //length = resample((long *)&Synth.pcm.samples[0][start_skip], resampled_data[0], Synth.pcm.length, &lr); |
441 | if (MAD_NCHANNELS(&Frame.header) == 2) | 236 | //if (MAD_NCHANNELS(&Frame.header) == 2) |
442 | resample((long *)&Synth.pcm.samples[1][start_skip], resampled_data[1], Synth.pcm.length, &rr); | 237 | // resample((long *)&Synth.pcm.samples[1][start_skip], resampled_data[1], Synth.pcm.length, &rr); |
443 | for (i = 0; i < length; i++) | 238 | ci->audiobuffer_insert_split(&Synth.pcm.samples[0][start_skip], |
444 | { | 239 | &Synth.pcm.samples[1][start_skip], |
445 | start_skip = 0; /* not very elegant, and might want to keep this value */ | 240 | (Synth.pcm.length - start_skip) * 4); |
446 | samplesdone++; | 241 | start_skip = 0; /* not very elegant, and might want to keep this value */ |
447 | //if (ci->mp3data->padding > 0) { | 242 | |
448 | // ci->mp3data->padding--; | 243 | samplesdone += Synth.pcm.length; |
449 | // continue ; | 244 | samplecount -= Synth.pcm.length; |
450 | //} | ||
451 | /*if (!first_frame) { | ||
452 | if (detect_silence(Synth.pcm.samples[0][i])) | ||
453 | continue ; | ||
454 | first_frame = true; | ||
455 | }*/ | ||
456 | |||
457 | /* Left channel */ | ||
458 | Sample = scale(resampled_data[0][i], &d0); | ||
459 | *(OutputPtr++) = Sample >> 8; | ||
460 | *(OutputPtr++) = Sample & 0xff; | ||
461 | |||
462 | /* Right channel. If the decoded stream is monophonic then | ||
463 | * the right output channel is the same as the left one. | ||
464 | */ | ||
465 | if (MAD_NCHANNELS(&Frame.header) == 2) | ||
466 | Sample = scale(resampled_data[1][i], &d1); | ||
467 | *(OutputPtr++) = Sample >> 8; | ||
468 | *(OutputPtr++) = Sample & 0xff; | ||
469 | |||
470 | samplecount--; | ||
471 | if (samplecount == 0) { | ||
472 | #ifdef DEBUG_GAPLESS | ||
473 | ci->write(fd, OutputBuffer, (int)OutputPtr - (int)OutputBuffer); | ||
474 | #endif | ||
475 | while (!ci->audiobuffer_insert(OutputBuffer, (int)OutputPtr - (int)OutputBuffer)) | ||
476 | ci->yield(); | ||
477 | goto song_end; | ||
478 | } | ||
479 | |||
480 | if (yieldcounter++ == 200) { | ||
481 | ci->yield(); | ||
482 | yieldcounter = 0; | ||
483 | } | ||
484 | |||
485 | /* Flush the buffer if it is full. */ | ||
486 | if (OutputPtr == OutputBufferEnd) | ||
487 | { | ||
488 | #ifdef DEBUG_GAPLESS | ||
489 | ci->write(fd, OutputBuffer, OUTPUT_BUFFER_SIZE); | ||
490 | #endif | ||
491 | while (!ci->audiobuffer_insert(OutputBuffer, OUTPUT_BUFFER_SIZE)) | ||
492 | ci->yield(); | ||
493 | OutputPtr = OutputBuffer; | ||
494 | } | ||
495 | } | ||
496 | ci->set_elapsed(samplesdone / (ci->id3->frequency/1000)); | 245 | ci->set_elapsed(samplesdone / (ci->id3->frequency/1000)); |
497 | } | 246 | } |
498 | 247 | ||
499 | song_end: | ||
500 | #ifdef DEBUG_GAPLESS | ||
501 | ci->close(fd); | ||
502 | #endif | ||
503 | Stream.error = 0; | 248 | Stream.error = 0; |
504 | 249 | ||
505 | if (ci->request_next_track()) | 250 | if (ci->request_next_track()) |
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c index f2939aa68d..9afeb053e1 100644 --- a/apps/codecs/vorbis.c +++ b/apps/codecs/vorbis.c | |||
@@ -21,6 +21,7 @@ | |||
21 | 21 | ||
22 | #include "Tremor/ivorbisfile.h" | 22 | #include "Tremor/ivorbisfile.h" |
23 | #include "playback.h" | 23 | #include "playback.h" |
24 | #include "dsp.h" | ||
24 | #include "lib/codeclib.h" | 25 | #include "lib/codeclib.h" |
25 | 26 | ||
26 | static struct codec_api* rb; | 27 | static struct codec_api* rb; |
@@ -92,10 +93,6 @@ enum codec_status codec_start(struct codec_api* api) | |||
92 | long n; | 93 | long n; |
93 | int current_section; | 94 | int current_section; |
94 | int eof; | 95 | int eof; |
95 | #if BYTE_ORDER == BIG_ENDIAN | ||
96 | int i; | ||
97 | char x; | ||
98 | #endif | ||
99 | 96 | ||
100 | TEST_CODEC_API(api); | 97 | TEST_CODEC_API(api); |
101 | 98 | ||
@@ -110,15 +107,27 @@ enum codec_status codec_start(struct codec_api* api) | |||
110 | rb->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); | 107 | rb->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*2)); |
111 | rb->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*64)); | 108 | rb->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*64)); |
112 | 109 | ||
113 | /* We need to flush reserver memory every track load. */ | 110 | rb->configure(DSP_DITHER, (bool *)false); |
111 | rb->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
112 | rb->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); | ||
113 | |||
114 | /* We need to flush reserver memory every track load. */ | ||
114 | next_track: | 115 | next_track: |
115 | if (codec_init(rb)) { | 116 | if (codec_init(rb)) { |
116 | return CODEC_ERROR; | 117 | return CODEC_ERROR; |
117 | } | 118 | } |
118 | 119 | ||
119 | 120 | while (!rb->taginfo_ready) | |
121 | rb->yield(); | ||
122 | |||
123 | if (rb->id3->frequency != NATIVE_FREQUENCY) { | ||
124 | rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); | ||
125 | rb->configure(CODEC_DSP_ENABLE, (bool *)true); | ||
126 | } else { | ||
127 | rb->configure(CODEC_DSP_ENABLE, (bool *)false); | ||
128 | } | ||
129 | |||
120 | /* Create a decoder instance */ | 130 | /* Create a decoder instance */ |
121 | |||
122 | callbacks.read_func=read_handler; | 131 | callbacks.read_func=read_handler; |
123 | callbacks.seek_func=seek_handler; | 132 | callbacks.seek_func=seek_handler; |
124 | callbacks.tell_func=tell_handler; | 133 | callbacks.tell_func=tell_handler; |
@@ -148,17 +157,10 @@ enum codec_status codec_start(struct codec_api* api) | |||
148 | if (rb->stop_codec || rb->reload_codec) | 157 | if (rb->stop_codec || rb->reload_codec) |
149 | break ; | 158 | break ; |
150 | 159 | ||
151 | rb->yield(); | ||
152 | while (!rb->audiobuffer_insert(pcmbuf, n)) | 160 | while (!rb->audiobuffer_insert(pcmbuf, n)) |
153 | rb->yield(); | 161 | rb->yield(); |
154 | 162 | ||
155 | rb->set_elapsed(ov_time_tell(&vf)); | 163 | rb->set_elapsed(ov_time_tell(&vf)); |
156 | |||
157 | #if BYTE_ORDER == BIG_ENDIAN | ||
158 | for (i=0;i<n;i+=2) { | ||
159 | x=pcmbuf[i]; pcmbuf[i]=pcmbuf[i+1]; pcmbuf[i+1]=x; | ||
160 | } | ||
161 | #endif | ||
162 | } | 164 | } |
163 | } | 165 | } |
164 | 166 | ||
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c index dfed97d64c..49bd12da1f 100644 --- a/apps/codecs/wav.c +++ b/apps/codecs/wav.c | |||
@@ -20,6 +20,7 @@ | |||
20 | #include "codec.h" | 20 | #include "codec.h" |
21 | #include "playback.h" | 21 | #include "playback.h" |
22 | #include "lib/codeclib.h" | 22 | #include "lib/codeclib.h" |
23 | #include "dsp.h" | ||
23 | 24 | ||
24 | #define BYTESWAP(x) (((x>>8) & 0xff) | ((x<<8) & 0xff00)) | 25 | #define BYTESWAP(x) (((x>>8) & 0xff) | ((x<<8) & 0xff00)) |
25 | 26 | ||
@@ -60,12 +61,26 @@ enum codec_status codec_start(struct codec_api* api) | |||
60 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); | 61 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); |
61 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256)); | 62 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256)); |
62 | 63 | ||
64 | ci->configure(DSP_DITHER, (bool *)false); | ||
65 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
66 | ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); | ||
67 | |||
63 | next_track: | 68 | next_track: |
64 | 69 | ||
65 | if (codec_init(api)) { | 70 | if (codec_init(api)) { |
66 | return CODEC_ERROR; | 71 | return CODEC_ERROR; |
67 | } | 72 | } |
68 | 73 | ||
74 | while (!rb->taginfo_ready) | ||
75 | rb->yield(); | ||
76 | |||
77 | if (rb->id3->frequency != NATIVE_FREQUENCY) { | ||
78 | rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency)); | ||
79 | rb->configure(CODEC_DSP_ENABLE, (bool *)true); | ||
80 | } else { | ||
81 | rb->configure(CODEC_DSP_ENABLE, (bool *)false); | ||
82 | } | ||
83 | |||
69 | /* FIX: Correctly parse WAV header - we assume canonical 44-byte header */ | 84 | /* FIX: Correctly parse WAV header - we assume canonical 44-byte header */ |
70 | 85 | ||
71 | header=ci->request_buffer(&n,44); | 86 | header=ci->request_buffer(&n,44); |
@@ -116,7 +131,7 @@ enum codec_status codec_start(struct codec_api* api) | |||
116 | 131 | ||
117 | /* Byte-swap data */ | 132 | /* Byte-swap data */ |
118 | for (i=0;i<n/2;i++) { | 133 | for (i=0;i<n/2;i++) { |
119 | wavbuf[i]=BYTESWAP(wavbuf[i]); | 134 | wavbuf[i]=SWAB16(wavbuf[i]); |
120 | } | 135 | } |
121 | 136 | ||
122 | samplesdone+=nsamples; | 137 | samplesdone+=nsamples; |
diff --git a/apps/codecs/wavpack.c b/apps/codecs/wavpack.c index 2ea8f052df..275f5f11e4 100644 --- a/apps/codecs/wavpack.c +++ b/apps/codecs/wavpack.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <codecs/libwavpack/wavpack.h> | 22 | #include <codecs/libwavpack/wavpack.h> |
23 | #include "playback.h" | 23 | #include "playback.h" |
24 | #include "lib/codeclib.h" | 24 | #include "lib/codeclib.h" |
25 | #include "dsp.h" | ||
25 | 26 | ||
26 | static struct codec_api *rb; | 27 | static struct codec_api *rb; |
27 | static struct codec_api *ci; | 28 | static struct codec_api *ci; |
@@ -61,14 +62,27 @@ enum codec_status codec_start(struct codec_api* api) | |||
61 | ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*10)); | 62 | ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*10)); |
62 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); | 63 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512)); |
63 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); | 64 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128)); |
65 | |||
66 | ci->configure(DSP_DITHER, (bool *)false); | ||
67 | ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED); | ||
68 | ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16)); | ||
64 | 69 | ||
65 | next_track: | 70 | next_track: |
66 | 71 | ||
67 | if (codec_init(api)) | 72 | if (codec_init(api)) |
68 | return CODEC_ERROR; | 73 | return CODEC_ERROR; |
69 | 74 | ||
75 | while (!rb->taginfo_ready) | ||
76 | ci->yield(); | ||
77 | |||
78 | if (ci->id3->frequency != NATIVE_FREQUENCY) { | ||
79 | ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency)); | ||
80 | ci->configure(CODEC_DSP_ENABLE, (bool *)true); | ||
81 | } else { | ||
82 | ci->configure(CODEC_DSP_ENABLE, (bool *)false); | ||
83 | } | ||
84 | |||
70 | /* Create a decoder instance */ | 85 | /* Create a decoder instance */ |
71 | |||
72 | wpc = WavpackOpenFileInput (read_callback, error); | 86 | wpc = WavpackOpenFileInput (read_callback, error); |
73 | 87 | ||
74 | if (!wpc) | 88 | if (!wpc) |
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 | |||
diff --git a/apps/dsp.h b/apps/dsp.h new file mode 100644 index 0000000000..65cd837c1e --- /dev/null +++ b/apps/dsp.h | |||
@@ -0,0 +1,48 @@ | |||
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 | |||
20 | #ifndef _DSP_H | ||
21 | #define _DSP_H | ||
22 | |||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include <stdbool.h> | ||
26 | |||
27 | #define NATIVE_FREQUENCY 44100 | ||
28 | #define STEREO_INTERLEAVED 0 | ||
29 | #define STEREO_NONINTERLEAVED 1 | ||
30 | /* Not supported yet. */ | ||
31 | #define STEREO_MONO 2 | ||
32 | |||
33 | struct dsp_configuration { | ||
34 | long frequency; | ||
35 | long clip_min, clip_max; | ||
36 | int sample_depth; | ||
37 | bool dither_enabled; | ||
38 | int stereo_mode; | ||
39 | }; | ||
40 | |||
41 | extern struct dsp_configuration dsp_config; | ||
42 | |||
43 | int dsp_process(char *dest, char *src, int samplecount); | ||
44 | bool dsp_configure(int setting, void *value); | ||
45 | |||
46 | #endif | ||
47 | |||
48 | |||
diff --git a/apps/playback.c b/apps/playback.c index 500f44e45b..996e825e13 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #include "playback.h" | 49 | #include "playback.h" |
50 | #include "pcm_playback.h" | 50 | #include "pcm_playback.h" |
51 | #include "buffer.h" | 51 | #include "buffer.h" |
52 | #include "dsp.h" | ||
52 | #ifdef HAVE_LCD_BITMAP | 53 | #ifdef HAVE_LCD_BITMAP |
53 | #include "icons.h" | 54 | #include "icons.h" |
54 | #include "peakmeter.h" | 55 | #include "peakmeter.h" |
@@ -171,7 +172,7 @@ int mp3_get_file_pos(void); | |||
171 | 172 | ||
172 | /* Simulator stubs. */ | 173 | /* Simulator stubs. */ |
173 | #ifdef SIMULATOR | 174 | #ifdef SIMULATOR |
174 | bool audiobuffer_insert(char *buf, size_t length) | 175 | bool pcm_insert_buffer(char *buf, size_t length) |
175 | { | 176 | { |
176 | (void)buf; | 177 | (void)buf; |
177 | (void)length; | 178 | (void)length; |
@@ -179,6 +180,20 @@ bool audiobuffer_insert(char *buf, size_t length) | |||
179 | return true; | 180 | return true; |
180 | } | 181 | } |
181 | 182 | ||
183 | void pcm_flush_buffer(size_t length) | ||
184 | { | ||
185 | (void)length; | ||
186 | } | ||
187 | |||
188 | |||
189 | void* pcm_request_buffer(size_t length, size_t *realsize) | ||
190 | { | ||
191 | (void)length; | ||
192 | (void)realsize; | ||
193 | |||
194 | return NULL; | ||
195 | } | ||
196 | |||
182 | void audiobuffer_add_event(void (*event_handler)(void)) | 197 | void audiobuffer_add_event(void (*event_handler)(void)) |
183 | { | 198 | { |
184 | (void)event_handler; | 199 | (void)event_handler; |
@@ -229,6 +244,92 @@ int ata_sleep(void) | |||
229 | } | 244 | } |
230 | #endif | 245 | #endif |
231 | 246 | ||
247 | bool codec_audiobuffer_insert_callback(char *buf, size_t length) | ||
248 | { | ||
249 | char *dest; | ||
250 | size_t realsize; | ||
251 | int factor; | ||
252 | int next_channel = 0; | ||
253 | int processed_length; | ||
254 | |||
255 | /* If non-interleaved stereo mode. */ | ||
256 | if (dsp_config.stereo_mode == STEREO_NONINTERLEAVED) { | ||
257 | next_channel = length / 2; | ||
258 | } | ||
259 | |||
260 | if (dsp_config.sample_depth > 16) { | ||
261 | length /= 2; | ||
262 | factor = 1; | ||
263 | } else { | ||
264 | factor = 0; | ||
265 | } | ||
266 | |||
267 | while (length > 0) { | ||
268 | /* Request a few extra bytes for resampling. */ | ||
269 | /* FIXME: Required extra bytes SHOULD be calculated. */ | ||
270 | while ((dest = pcm_request_buffer(length+16384, &realsize)) == NULL) | ||
271 | yield(); | ||
272 | |||
273 | if (realsize < 16384) { | ||
274 | pcm_flush_buffer(0); | ||
275 | continue ; | ||
276 | } | ||
277 | |||
278 | realsize -= 16384; | ||
279 | |||
280 | if (next_channel) { | ||
281 | processed_length = dsp_process(dest, buf, realsize / 4) * 2; | ||
282 | dsp_process(dest, buf + next_channel, realsize / 4); | ||
283 | } else { | ||
284 | processed_length = dsp_process(dest, buf, realsize / 2); | ||
285 | } | ||
286 | pcm_flush_buffer(processed_length); | ||
287 | length -= realsize; | ||
288 | buf += realsize << factor; | ||
289 | } | ||
290 | |||
291 | return true; | ||
292 | } | ||
293 | |||
294 | bool codec_audiobuffer_insert_split_callback(void *ch1, void *ch2, | ||
295 | size_t length) | ||
296 | { | ||
297 | char *dest; | ||
298 | size_t realsize; | ||
299 | int factor; | ||
300 | int processed_length; | ||
301 | |||
302 | /* non-interleaved stereo mode. */ | ||
303 | if (dsp_config.sample_depth > 16) { | ||
304 | factor = 0; | ||
305 | } else { | ||
306 | length /= 2; | ||
307 | factor = 1; | ||
308 | } | ||
309 | |||
310 | while (length > 0) { | ||
311 | /* Request a few extra bytes for resampling. */ | ||
312 | while ((dest = pcm_request_buffer(length+4096, &realsize)) == NULL) | ||
313 | yield(); | ||
314 | |||
315 | if (realsize < 4096) { | ||
316 | pcm_flush_buffer(0); | ||
317 | continue ; | ||
318 | } | ||
319 | |||
320 | realsize -= 4096; | ||
321 | |||
322 | processed_length = dsp_process(dest, ch1, realsize / 4) * 2; | ||
323 | dsp_process(dest, ch2, realsize / 4); | ||
324 | pcm_flush_buffer(processed_length); | ||
325 | length -= realsize; | ||
326 | ch1 += realsize >> factor; | ||
327 | ch2 += realsize >> factor; | ||
328 | } | ||
329 | |||
330 | return true; | ||
331 | } | ||
332 | |||
232 | void* get_codec_memory_callback(size_t *size) | 333 | void* get_codec_memory_callback(size_t *size) |
233 | { | 334 | { |
234 | *size = MALLOC_BUFSIZE; | 335 | *size = MALLOC_BUFSIZE; |
@@ -260,7 +361,7 @@ size_t codec_filebuf_callback(void *ptr, size_t size) | |||
260 | 361 | ||
261 | if (ci.stop_codec || !playing) | 362 | if (ci.stop_codec || !playing) |
262 | return 0; | 363 | return 0; |
263 | 364 | ||
264 | copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem); | 365 | copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem); |
265 | 366 | ||
266 | while (copy_n > cur_ti->available) { | 367 | while (copy_n > cur_ti->available) { |
@@ -283,7 +384,7 @@ size_t codec_filebuf_callback(void *ptr, size_t size) | |||
283 | buf_ridx -= codecbuflen; | 384 | buf_ridx -= codecbuflen; |
284 | ci.curpos += copy_n; | 385 | ci.curpos += copy_n; |
285 | cur_ti->available -= copy_n; | 386 | cur_ti->available -= copy_n; |
286 | codecbufused -= copy_n; | 387 | codecbufused -= copy_n; |
287 | 388 | ||
288 | return copy_n; | 389 | return copy_n; |
289 | } | 390 | } |
@@ -427,8 +528,18 @@ void codec_configure_callback(int setting, void *value) | |||
427 | conf_bufferlimit = (unsigned int)value; | 528 | conf_bufferlimit = (unsigned int)value; |
428 | break; | 529 | break; |
429 | 530 | ||
531 | case CODEC_DSP_ENABLE: | ||
532 | if ((bool)value) | ||
533 | ci.audiobuffer_insert = codec_audiobuffer_insert_callback; | ||
534 | else | ||
535 | ci.audiobuffer_insert = pcm_insert_buffer; | ||
536 | break ; | ||
537 | |||
538 | #ifndef SIMULATOR | ||
430 | default: | 539 | default: |
431 | logf("Illegal key: %d", setting); | 540 | if (!dsp_configure(setting, value)) |
541 | logf("Illegal key: %d", setting); | ||
542 | #endif | ||
432 | } | 543 | } |
433 | } | 544 | } |
434 | 545 | ||
@@ -647,6 +758,8 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
647 | conf_bufferlimit = 0; | 758 | conf_bufferlimit = 0; |
648 | conf_watermark = AUDIO_DEFAULT_WATERMARK; | 759 | conf_watermark = AUDIO_DEFAULT_WATERMARK; |
649 | conf_filechunk = AUDIO_DEFAULT_FILECHUNK; | 760 | conf_filechunk = AUDIO_DEFAULT_FILECHUNK; |
761 | dsp_configure(DSP_RESET, 0); | ||
762 | ci.configure(CODEC_DSP_ENABLE, false); | ||
650 | } | 763 | } |
651 | 764 | ||
652 | tracks[track_widx].codecbuf = &codecbuf[buf_widx]; | 765 | tracks[track_widx].codecbuf = &codecbuf[buf_widx]; |
@@ -697,7 +810,7 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) | |||
697 | copy_n = MIN(size - i, copy_n); | 810 | copy_n = MIN(size - i, copy_n); |
698 | copy_n = MIN((int)fill_bytesleft, copy_n); | 811 | copy_n = MIN((int)fill_bytesleft, copy_n); |
699 | rc = read(fd, &codecbuf[buf_widx], copy_n); | 812 | rc = read(fd, &codecbuf[buf_widx], copy_n); |
700 | if (rc < 0) { | 813 | if (rc <= 0) { |
701 | logf("File error!"); | 814 | logf("File error!"); |
702 | close(fd); | 815 | close(fd); |
703 | return false; | 816 | return false; |
@@ -1152,7 +1265,7 @@ struct mp3entry* audio_next_track(void) | |||
1152 | 1265 | ||
1153 | bool audio_has_changed_track(void) | 1266 | bool audio_has_changed_track(void) |
1154 | { | 1267 | { |
1155 | if (track_changed && track_count > 0) { | 1268 | if (track_changed && track_count > 0 && playing) { |
1156 | if (!cur_ti->taginfo_ready) | 1269 | if (!cur_ti->taginfo_ready) |
1157 | return false; | 1270 | return false; |
1158 | track_changed = false; | 1271 | track_changed = false; |
@@ -1384,6 +1497,7 @@ int mp3_get_file_pos(void) | |||
1384 | void audio_set_buffer_margin(int seconds) | 1497 | void audio_set_buffer_margin(int seconds) |
1385 | { | 1498 | { |
1386 | (void)seconds; | 1499 | (void)seconds; |
1500 | logf("bufmargin: %d", seconds); | ||
1387 | } | 1501 | } |
1388 | #endif | 1502 | #endif |
1389 | 1503 | ||
@@ -1395,7 +1509,7 @@ void mpeg_id3_options(bool _v1first) | |||
1395 | void audio_init(void) | 1509 | void audio_init(void) |
1396 | { | 1510 | { |
1397 | logf("audio api init"); | 1511 | logf("audio api init"); |
1398 | codecbuflen = audiobufend - audiobuf - PCMBUF_SIZE | 1512 | codecbuflen = audiobufend - audiobuf - PCMBUF_SIZE - PCMBUF_GUARD |
1399 | - MALLOC_BUFSIZE - GUARD_BUFSIZE; | 1513 | - MALLOC_BUFSIZE - GUARD_BUFSIZE; |
1400 | //codecbuflen = 2*512*1024; | 1514 | //codecbuflen = 2*512*1024; |
1401 | codecbufused = 0; | 1515 | codecbufused = 0; |
@@ -1412,7 +1526,8 @@ void audio_init(void) | |||
1412 | 1526 | ||
1413 | /* Initialize codec api. */ | 1527 | /* Initialize codec api. */ |
1414 | ci.read_filebuf = codec_filebuf_callback; | 1528 | ci.read_filebuf = codec_filebuf_callback; |
1415 | ci.audiobuffer_insert = audiobuffer_insert; | 1529 | ci.audiobuffer_insert = pcm_insert_buffer; |
1530 | ci.audiobuffer_insert_split = codec_audiobuffer_insert_split_callback; | ||
1416 | ci.get_codec_memory = get_codec_memory_callback; | 1531 | ci.get_codec_memory = get_codec_memory_callback; |
1417 | ci.request_buffer = codec_request_buffer_callback; | 1532 | ci.request_buffer = codec_request_buffer_callback; |
1418 | ci.advance_buffer = codec_advance_buffer_callback; | 1533 | ci.advance_buffer = codec_advance_buffer_callback; |
diff --git a/apps/playback.h b/apps/playback.h index 24fc8570e8..672ddae440 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -27,10 +27,19 @@ | |||
27 | #include "id3.h" | 27 | #include "id3.h" |
28 | #include "mp3data.h" | 28 | #include "mp3data.h" |
29 | 29 | ||
30 | /* File buffer configuration keys. */ | 30 | enum { |
31 | #define CODEC_SET_FILEBUF_WATERMARK 1 | 31 | CODEC_SET_FILEBUF_WATERMARK = 1, |
32 | #define CODEC_SET_FILEBUF_CHUNKSIZE 2 | 32 | CODEC_SET_FILEBUF_CHUNKSIZE, |
33 | #define CODEC_SET_FILEBUF_LIMIT 3 | 33 | CODEC_SET_FILEBUF_LIMIT, |
34 | CODEC_DSP_ENABLE, | ||
35 | DSP_SET_FREQUENCY, | ||
36 | DSP_SET_CLIP_MIN, | ||
37 | DSP_SET_CLIP_MAX, | ||
38 | DSP_SET_SAMPLE_DEPTH, | ||
39 | DSP_SET_STEREO_MODE, | ||
40 | DSP_RESET, | ||
41 | DSP_DITHER | ||
42 | }; | ||
34 | 43 | ||
35 | /* Not yet implemented. */ | 44 | /* Not yet implemented. */ |
36 | #define CODEC_SET_AUDIOBUF_WATERMARK 4 | 45 | #define CODEC_SET_AUDIOBUF_WATERMARK 4 |
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index aa29601f70..3fe60670b3 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h | |||
@@ -19,6 +19,10 @@ | |||
19 | #ifndef PCM_PLAYBACK_H | 19 | #ifndef PCM_PLAYBACK_H |
20 | #define PCM_PLAYBACK_H | 20 | #define PCM_PLAYBACK_H |
21 | 21 | ||
22 | /* Guard buffer for crossfader when dsp is enabled. */ | ||
23 | #define PCMBUF_GUARD 32768 | ||
24 | |||
25 | /* PCM audio buffer. */ | ||
22 | #define PCMBUF_SIZE (1*1024*1024) | 26 | #define PCMBUF_SIZE (1*1024*1024) |
23 | 27 | ||
24 | void pcm_init(void); | 28 | void pcm_init(void); |
@@ -44,7 +48,9 @@ bool pcm_is_lowdata(void); | |||
44 | bool pcm_crossfade_init(void); | 48 | bool pcm_crossfade_init(void); |
45 | void audiobuffer_add_event(void (*event_handler)(void)); | 49 | void audiobuffer_add_event(void (*event_handler)(void)); |
46 | unsigned int audiobuffer_get_latency(void); | 50 | unsigned int audiobuffer_get_latency(void); |
47 | bool audiobuffer_insert(char *buf, size_t length); | 51 | bool pcm_insert_buffer(char *buf, size_t length); |
52 | void pcm_flush_buffer(size_t length); | ||
53 | void* pcm_request_buffer(size_t length, size_t *realsize); | ||
48 | bool pcm_is_crossfade_enabled(void); | 54 | bool pcm_is_crossfade_enabled(void); |
49 | void pcm_crossfade_enable(bool on_off); | 55 | void pcm_crossfade_enable(bool on_off); |
50 | 56 | ||
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index 24fc5d4530..12ecfd14e2 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c | |||
@@ -67,6 +67,7 @@ static int crossfade_pos; | |||
67 | static int crossfade_amount; | 67 | static int crossfade_amount; |
68 | static int crossfade_rem; | 68 | static int crossfade_rem; |
69 | 69 | ||
70 | static char *guardbuf; | ||
70 | static void (*pcm_event_handler)(void); | 71 | static void (*pcm_event_handler)(void); |
71 | 72 | ||
72 | static unsigned char *next_start; | 73 | static unsigned char *next_start; |
@@ -258,7 +259,6 @@ void pcm_play_pause(bool play) | |||
258 | IIS2CONFIG = 0x800; | 259 | IIS2CONFIG = 0x800; |
259 | } | 260 | } |
260 | pcm_paused = !play; | 261 | pcm_paused = !play; |
261 | pcm_boost(false); | ||
262 | } | 262 | } |
263 | 263 | ||
264 | bool pcm_is_playing(void) | 264 | bool pcm_is_playing(void) |
@@ -401,15 +401,8 @@ bool pcm_crossfade_init(void) | |||
401 | 401 | ||
402 | } | 402 | } |
403 | 403 | ||
404 | static void crossfade_start(void) | 404 | void pcm_flush_fillpos(void) |
405 | { | 405 | { |
406 | if (!crossfade_init) | ||
407 | return ; | ||
408 | |||
409 | crossfade_init = 0; | ||
410 | if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 6) | ||
411 | return ; | ||
412 | |||
413 | if (audiobuffer_fillpos) { | 406 | if (audiobuffer_fillpos) { |
414 | while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos], | 407 | while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos], |
415 | audiobuffer_fillpos, pcm_event_handler)) { | 408 | audiobuffer_fillpos, pcm_event_handler)) { |
@@ -419,13 +412,26 @@ static void crossfade_start(void) | |||
419 | audiobuffer_pos += audiobuffer_fillpos; | 412 | audiobuffer_pos += audiobuffer_fillpos; |
420 | if (audiobuffer_pos >= PCMBUF_SIZE) | 413 | if (audiobuffer_pos >= PCMBUF_SIZE) |
421 | audiobuffer_pos -= PCMBUF_SIZE; | 414 | audiobuffer_pos -= PCMBUF_SIZE; |
415 | audiobuffer_free -= audiobuffer_fillpos; | ||
416 | audiobuffer_fillpos = 0; | ||
422 | } | 417 | } |
418 | } | ||
419 | |||
420 | static void crossfade_start(void) | ||
421 | { | ||
422 | if (!crossfade_init) | ||
423 | return ; | ||
424 | |||
425 | crossfade_init = 0; | ||
426 | if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 6) | ||
427 | return ; | ||
428 | |||
429 | pcm_flush_fillpos(); | ||
423 | pcm_boost(true); | 430 | pcm_boost(true); |
424 | crossfade_active = true; | 431 | crossfade_active = true; |
425 | crossfade_pos = audiobuffer_pos; | 432 | crossfade_pos = audiobuffer_pos; |
426 | crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; | 433 | crossfade_amount = (PCMBUF_SIZE - audiobuffer_free - (CHUNK_SIZE * 2))/2; |
427 | crossfade_rem = crossfade_amount; | 434 | crossfade_rem = crossfade_amount; |
428 | audiobuffer_fillpos = 0; | ||
429 | 435 | ||
430 | crossfade_pos -= crossfade_amount*2; | 436 | crossfade_pos -= crossfade_amount*2; |
431 | if (crossfade_pos < 0) | 437 | if (crossfade_pos < 0) |
@@ -451,12 +457,11 @@ int crossfade(short *buf, const short *buf2, int length) | |||
451 | return size; | 457 | return size; |
452 | } | 458 | } |
453 | 459 | ||
454 | bool audiobuffer_insert(char *buf, size_t length) | 460 | inline static bool prepare_insert(size_t length) |
455 | { | 461 | { |
456 | size_t copy_n = 0; | ||
457 | |||
458 | crossfade_start(); | 462 | crossfade_start(); |
459 | if (audiobuffer_free < length + CHUNK_SIZE && !crossfade_active) { | 463 | if (audiobuffer_free < length + audiobuffer_fillpos |
464 | + CHUNK_SIZE && !crossfade_active) { | ||
460 | pcm_boost(false); | 465 | pcm_boost(false); |
461 | return false; | 466 | return false; |
462 | } | 467 | } |
@@ -467,7 +472,94 @@ bool audiobuffer_insert(char *buf, size_t length) | |||
467 | if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4) | 472 | if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4) |
468 | pcm_play_start(); | 473 | pcm_play_start(); |
469 | } | 474 | } |
475 | |||
476 | return true; | ||
477 | } | ||
478 | |||
479 | void* pcm_request_buffer(size_t length, size_t *realsize) | ||
480 | { | ||
481 | void *ptr = NULL; | ||
482 | |||
483 | if (!prepare_insert(length)) { | ||
484 | *realsize = 0; | ||
485 | return NULL; | ||
486 | } | ||
487 | |||
488 | if (crossfade_active) { | ||
489 | *realsize = MIN(length, PCMBUF_GUARD); | ||
490 | ptr = &guardbuf[0]; | ||
491 | } else { | ||
492 | *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos | ||
493 | - audiobuffer_fillpos); | ||
494 | if (*realsize < length) { | ||
495 | *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD); | ||
496 | //logf("gbr:%d/%d", *realsize, length); | ||
497 | } | ||
498 | ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos]; | ||
499 | } | ||
500 | |||
501 | return ptr; | ||
502 | } | ||
503 | |||
504 | void pcm_flush_buffer(size_t length) | ||
505 | { | ||
506 | int copy_n; | ||
507 | char *buf; | ||
508 | |||
509 | if (crossfade_active) { | ||
510 | buf = &guardbuf[0]; | ||
511 | length = MIN(length, PCMBUF_GUARD); | ||
512 | while (length > 0 && crossfade_active) { | ||
513 | copy_n = MIN(length, PCMBUF_SIZE - (unsigned int)crossfade_pos); | ||
514 | copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], | ||
515 | (const short *)buf, copy_n/2); | ||
516 | buf += copy_n; | ||
517 | length -= copy_n; | ||
518 | crossfade_pos += copy_n; | ||
519 | if (crossfade_pos >= PCMBUF_SIZE) | ||
520 | crossfade_pos -= PCMBUF_SIZE; | ||
521 | } | ||
522 | |||
523 | if (length > 0) { | ||
524 | memcpy(&audiobuffer[audiobuffer_pos], buf, length); | ||
525 | audiobuffer_fillpos = length; | ||
526 | goto try_flush; | ||
527 | } | ||
528 | } else { | ||
529 | /* if (length == 0) { | ||
530 | pcm_flush_fillpos(); | ||
531 | audiobuffer_pos = 0; | ||
532 | return ; | ||
533 | } */ | ||
534 | |||
535 | audiobuffer_fillpos += length; | ||
536 | |||
537 | try_flush: | ||
538 | if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE | ||
539 | - audiobuffer_pos - audiobuffer_fillpos > 0) | ||
540 | return ; | ||
541 | |||
542 | copy_n = MIN((long)(audiobuffer_fillpos - (PCMBUF_SIZE | ||
543 | - audiobuffer_pos)), PCMBUF_GUARD); | ||
544 | if (copy_n > 0) { | ||
545 | //logf("guard buf used:%d", copy_n); | ||
546 | audiobuffer_fillpos -= copy_n; | ||
547 | pcm_flush_fillpos(); | ||
548 | memcpy(&audiobuffer[0], &guardbuf[0], copy_n); | ||
549 | audiobuffer_fillpos = copy_n; | ||
550 | goto try_flush; | ||
551 | } | ||
552 | pcm_flush_fillpos(); | ||
553 | } | ||
554 | } | ||
470 | 555 | ||
556 | bool pcm_insert_buffer(char *buf, size_t length) | ||
557 | { | ||
558 | size_t copy_n = 0; | ||
559 | |||
560 | if (!prepare_insert(length)) | ||
561 | return false; | ||
562 | |||
471 | while (length > 0) { | 563 | while (length > 0) { |
472 | if (crossfade_active) { | 564 | if (crossfade_active) { |
473 | copy_n = MIN(length, PCMBUF_SIZE - (unsigned int)crossfade_pos); | 565 | copy_n = MIN(length, PCMBUF_SIZE - (unsigned int)crossfade_pos); |
@@ -521,7 +613,8 @@ bool audiobuffer_insert(char *buf, size_t length) | |||
521 | void pcm_play_init(void) | 613 | void pcm_play_init(void) |
522 | { | 614 | { |
523 | audiobuffer = &audiobuf[(audiobufend - audiobuf) - | 615 | audiobuffer = &audiobuf[(audiobufend - audiobuf) - |
524 | PCMBUF_SIZE]; | 616 | PCMBUF_SIZE - PCMBUF_GUARD]; |
617 | guardbuf = &audiobuffer[PCMBUF_SIZE]; | ||
525 | audiobuffer_free = PCMBUF_SIZE; | 618 | audiobuffer_free = PCMBUF_SIZE; |
526 | audiobuffer_pos = 0; | 619 | audiobuffer_pos = 0; |
527 | audiobuffer_fillpos = 0; | 620 | audiobuffer_fillpos = 0; |
@@ -532,11 +625,6 @@ void pcm_play_init(void) | |||
532 | crossfade_active = false; | 625 | crossfade_active = false; |
533 | crossfade_init = false; | 626 | crossfade_init = false; |
534 | pcm_event_handler = NULL; | 627 | pcm_event_handler = NULL; |
535 | if (crossfade_enabled) { | ||
536 | pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); | ||
537 | } else { | ||
538 | pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback); | ||
539 | } | ||
540 | } | 628 | } |
541 | 629 | ||
542 | void pcm_crossfade_enable(bool on_off) | 630 | void pcm_crossfade_enable(bool on_off) |
@@ -555,6 +643,11 @@ void pcm_play_start(void) | |||
555 | int size; | 643 | int size; |
556 | char *start; | 644 | char *start; |
557 | 645 | ||
646 | if (crossfade_enabled) { | ||
647 | pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); | ||
648 | } else { | ||
649 | pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback); | ||
650 | } | ||
558 | crossfade_active = false; | 651 | crossfade_active = false; |
559 | if(!pcm_is_playing()) | 652 | if(!pcm_is_playing()) |
560 | { | 653 | { |