diff options
-rw-r--r-- | apps/debug_menu.c | 57 | ||||
-rw-r--r-- | firmware/export/pcm_playback.h | 10 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 119 |
3 files changed, 167 insertions, 19 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index 3969972ea8..f1402950a9 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -63,8 +63,8 @@ | |||
63 | #include "pcm_playback.h" | 63 | #include "pcm_playback.h" |
64 | #include "buffer.h" | 64 | #include "buffer.h" |
65 | 65 | ||
66 | #define CHUNK_SIZE 44100 /* Transfer CHUNK_SIZE bytes on | 66 | #define CHUNK_SIZE 0x100000 /* Transfer CHUNK_SIZE bytes on |
67 | each DMA transfer */ | 67 | each DMA transfer */ |
68 | 68 | ||
69 | static unsigned char line = 0; | 69 | static unsigned char line = 0; |
70 | static unsigned char *audio_buffer; | 70 | static unsigned char *audio_buffer; |
@@ -95,9 +95,9 @@ static void puts(const char *fmt, ...) | |||
95 | /* Very basic WAVE-file support.. Just for testing purposes.. */ | 95 | /* Very basic WAVE-file support.. Just for testing purposes.. */ |
96 | int load_wave(char *filename) | 96 | int load_wave(char *filename) |
97 | { | 97 | { |
98 | int f, i; | 98 | int f, i, num; |
99 | unsigned char buf[32]; | 99 | unsigned char buf[32]; |
100 | unsigned short *p; | 100 | unsigned short *p, *end; |
101 | 101 | ||
102 | puts("Loading %s..", filename); | 102 | puts("Loading %s..", filename); |
103 | 103 | ||
@@ -136,10 +136,18 @@ int load_wave(char *filename) | |||
136 | close(f); | 136 | close(f); |
137 | 137 | ||
138 | puts("Changing byte order.."); | 138 | puts("Changing byte order.."); |
139 | end = (unsigned short *)(audio_buffer + audio_size); | ||
139 | p = (unsigned short *)audio_buffer; | 140 | p = (unsigned short *)audio_buffer; |
140 | for (i=0; i<audio_size/2; i++, p++) | 141 | while(p < end) |
141 | { | 142 | { |
142 | *p = SWAB16(*p); | 143 | /* Swap 128k at a time, to allow the other threads to run */ |
144 | num = MIN(0x20000, (int)(end - p)); | ||
145 | for(i = 0;i < num;i++) | ||
146 | { | ||
147 | *p = SWAB16(*p); | ||
148 | p++; | ||
149 | } | ||
150 | yield(); | ||
143 | } | 151 | } |
144 | 152 | ||
145 | return 0; | 153 | return 0; |
@@ -152,20 +160,14 @@ int load_wave(char *filename) | |||
152 | 160 | ||
153 | */ | 161 | */ |
154 | 162 | ||
155 | static void test_get_more(unsigned char **ptr, long *size) | 163 | int test_tracknum; |
164 | static void test_trackchange(void) | ||
156 | { | 165 | { |
157 | static long last_chunk_size = 0; | 166 | test_tracknum++; |
158 | |||
159 | audio_pos += last_chunk_size; | ||
160 | |||
161 | if(audio_pos < audio_size) | ||
162 | { | ||
163 | last_chunk_size = MIN(CHUNK_SIZE, (audio_size - audio_pos)); | ||
164 | *ptr = &audio_buffer[audio_pos]; | ||
165 | *size = last_chunk_size; | ||
166 | } | ||
167 | } | 167 | } |
168 | 168 | ||
169 | extern int pcmbuf_unplayed_bytes; | ||
170 | |||
169 | bool uda1380_test(void) | 171 | bool uda1380_test(void) |
170 | { | 172 | { |
171 | long button; | 173 | long button; |
@@ -173,11 +175,15 @@ bool uda1380_test(void) | |||
173 | bool done = false; | 175 | bool done = false; |
174 | char buf[80]; | 176 | char buf[80]; |
175 | bool play = true; | 177 | bool play = true; |
178 | int sz; | ||
179 | char *ptr; | ||
176 | 180 | ||
177 | lcd_setmargins(0, 0); | 181 | lcd_setmargins(0, 0); |
178 | lcd_clear_display(); | 182 | lcd_clear_display(); |
179 | lcd_update(); | 183 | lcd_update(); |
180 | 184 | ||
185 | test_tracknum = 1; | ||
186 | |||
181 | line = 0; | 187 | line = 0; |
182 | 188 | ||
183 | if (load_wave("/sample.wav") == -1) | 189 | if (load_wave("/sample.wav") == -1) |
@@ -188,10 +194,19 @@ bool uda1380_test(void) | |||
188 | puts("Playing.."); | 194 | puts("Playing.."); |
189 | 195 | ||
190 | audio_pos = 0; | 196 | audio_pos = 0; |
197 | pcm_play_init(); | ||
191 | pcm_set_frequency(44100); | 198 | pcm_set_frequency(44100); |
192 | pcm_set_volume(0xff - vol); | 199 | pcm_set_volume(0xff - vol); |
193 | pcm_play_data(audio_buffer, CHUNK_SIZE, | 200 | |
194 | test_get_more); | 201 | ptr = audio_buffer; |
202 | for(sz = 0;sz < audio_size;sz += CHUNK_SIZE) | ||
203 | { | ||
204 | if(!pcm_play_add_chunk(ptr, CHUNK_SIZE, test_trackchange)) | ||
205 | break; | ||
206 | ptr += MIN(CHUNK_SIZE, (audio_size - sz)); | ||
207 | } | ||
208 | |||
209 | pcm_play_start(); | ||
195 | 210 | ||
196 | while(!done) | 211 | while(!done) |
197 | { | 212 | { |
@@ -205,6 +220,10 @@ bool uda1380_test(void) | |||
205 | lcd_puts(0, line+3, buf); | 220 | lcd_puts(0, line+3, buf); |
206 | snprintf(buf, sizeof(buf), "DSR0: %02x", DSR0); | 221 | snprintf(buf, sizeof(buf), "DSR0: %02x", DSR0); |
207 | lcd_puts(0, line+4, buf); | 222 | lcd_puts(0, line+4, buf); |
223 | snprintf(buf, sizeof(buf), "Track: %d", test_tracknum); | ||
224 | lcd_puts(0, line+5, buf); | ||
225 | snprintf(buf, sizeof(buf), "Unplayed: %08x", pcmbuf_unplayed_bytes); | ||
226 | lcd_puts(0, line+6, buf); | ||
208 | lcd_update(); | 227 | lcd_update(); |
209 | 228 | ||
210 | button = button_get_w_tmo(HZ/2); | 229 | button = button_get_w_tmo(HZ/2); |
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index f6612095e0..23ec1feee9 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h | |||
@@ -21,11 +21,21 @@ | |||
21 | 21 | ||
22 | void pcm_init(void); | 22 | void pcm_init(void); |
23 | void pcm_set_frequency(unsigned int frequency); | 23 | void pcm_set_frequency(unsigned int frequency); |
24 | |||
25 | /* This is for playing "raw" PCM data */ | ||
24 | void pcm_play_data(const unsigned char* start, int size, | 26 | void pcm_play_data(const unsigned char* start, int size, |
25 | void (*get_more)(unsigned char** start, long* size)); | 27 | void (*get_more)(unsigned char** start, long* size)); |
28 | |||
26 | void pcm_play_stop(void); | 29 | void pcm_play_stop(void); |
27 | void pcm_play_pause(bool play); | 30 | void pcm_play_pause(bool play); |
28 | bool pcm_is_playing(void); | 31 | bool pcm_is_playing(void); |
29 | void pcm_set_volume(int volume); | 32 | void pcm_set_volume(int volume); |
30 | 33 | ||
34 | /* These functions are for playing chained buffers of PCM data */ | ||
35 | void pcm_play_init(void); | ||
36 | void pcm_play_start(void); | ||
37 | bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void)); | ||
38 | int pcm_play_num_used_buffers(void); | ||
39 | void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)); | ||
40 | |||
31 | #endif | 41 | #endif |
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index e1afea949d..f5fc5e7391 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c | |||
@@ -207,3 +207,122 @@ void pcm_init(void) | |||
207 | 207 | ||
208 | pcm_set_frequency(44100); | 208 | pcm_set_frequency(44100); |
209 | } | 209 | } |
210 | |||
211 | |||
212 | #define NUM_PCM_BUFFERS 16 /* Must be a power of 2 */ | ||
213 | #define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) | ||
214 | |||
215 | struct pcmbufdesc | ||
216 | { | ||
217 | void *addr; | ||
218 | int size; | ||
219 | void (*callback)(void); /* Call this when the buffer has been played */ | ||
220 | } pcmbuffers[NUM_PCM_BUFFERS]; | ||
221 | |||
222 | int pcmbuf_read_index; | ||
223 | int pcmbuf_write_index; | ||
224 | int pcmbuf_unplayed_bytes; | ||
225 | int pcmbuf_watermark; | ||
226 | void (*pcmbuf_watermark_callback)(int bytes_left); | ||
227 | |||
228 | int pcm_play_num_used_buffers(void) | ||
229 | { | ||
230 | return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK; | ||
231 | } | ||
232 | |||
233 | void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)) | ||
234 | { | ||
235 | pcmbuf_watermark = numbytes; | ||
236 | pcmbuf_watermark_callback = callback; | ||
237 | } | ||
238 | |||
239 | bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void)) | ||
240 | { | ||
241 | /* We don't use the last buffer, since we can't see the difference | ||
242 | between the full and empty condition */ | ||
243 | if(pcm_play_num_used_buffers() < (NUM_PCM_BUFFERS - 1)) | ||
244 | { | ||
245 | pcmbuffers[pcmbuf_write_index].addr = addr; | ||
246 | pcmbuffers[pcmbuf_write_index].size = size; | ||
247 | pcmbuffers[pcmbuf_write_index].callback = callback; | ||
248 | pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK; | ||
249 | pcmbuf_unplayed_bytes += size; | ||
250 | return true; | ||
251 | } | ||
252 | else | ||
253 | return false; | ||
254 | } | ||
255 | |||
256 | void pcm_play_init(void) | ||
257 | { | ||
258 | pcmbuf_read_index = 0; | ||
259 | pcmbuf_write_index = 0; | ||
260 | pcmbuf_unplayed_bytes = 0; | ||
261 | pcm_play_set_watermark(0x10000, NULL); | ||
262 | } | ||
263 | |||
264 | static int last_chunksize = 0; | ||
265 | |||
266 | static void pcm_play_callback(unsigned char** start, long* size) | ||
267 | { | ||
268 | struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; | ||
269 | int sz; | ||
270 | |||
271 | pcmbuf_unplayed_bytes -= last_chunksize; | ||
272 | |||
273 | if(desc->size == 0) | ||
274 | { | ||
275 | /* The buffer is finished, call the callback function */ | ||
276 | if(desc->callback) | ||
277 | desc->callback(); | ||
278 | |||
279 | /* Advance to the next buffer */ | ||
280 | pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK; | ||
281 | desc = &pcmbuffers[pcmbuf_read_index]; | ||
282 | } | ||
283 | |||
284 | if(pcm_play_num_used_buffers()) | ||
285 | { | ||
286 | /* Play max 64K at a time */ | ||
287 | sz = MIN(desc->size, 32768); | ||
288 | *start = desc->addr; | ||
289 | *size = sz; | ||
290 | |||
291 | /* Update the buffer descriptor */ | ||
292 | desc->size -= sz; | ||
293 | desc->addr += sz; | ||
294 | |||
295 | last_chunksize = sz; | ||
296 | } | ||
297 | else | ||
298 | { | ||
299 | /* No more buffers */ | ||
300 | *size = 0; | ||
301 | } | ||
302 | #if 0 | ||
303 | if(pcmbuf_unplayed_bytes <= pcmbuf_watermark) | ||
304 | { | ||
305 | if(pcmbuf_watermark_callback) | ||
306 | { | ||
307 | pcmbuf_watermark_callback(pcmbuf_unplayed_bytes); | ||
308 | } | ||
309 | } | ||
310 | #endif | ||
311 | } | ||
312 | |||
313 | void pcm_play_start(void) | ||
314 | { | ||
315 | struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; | ||
316 | int size; | ||
317 | char *start; | ||
318 | |||
319 | if(!pcm_is_playing()) | ||
320 | { | ||
321 | size = MIN(desc->size, 32768); | ||
322 | start = desc->addr; | ||
323 | pcm_play_data(start, size, pcm_play_callback); | ||
324 | last_chunksize = size; | ||
325 | desc->size -= size; | ||
326 | desc->addr += size; | ||
327 | } | ||
328 | } | ||