summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2005-04-14 11:51:31 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2005-04-14 11:51:31 +0000
commit267eb077a79b85638598e1a872ffab9e60da7dfe (patch)
treea95638d2e28de3299da5e74c6f5e01b5c58374c2
parent14c7900383bd2082494ce1cfa3e191bc34a44b3a (diff)
downloadrockbox-267eb077a79b85638598e1a872ffab9e60da7dfe.tar.gz
rockbox-267eb077a79b85638598e1a872ffab9e60da7dfe.zip
New API for buffered PCM playback
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6284 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c57
-rw-r--r--firmware/export/pcm_playback.h10
-rw-r--r--firmware/pcm_playback.c119
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
69static unsigned char line = 0; 69static unsigned char line = 0;
70static unsigned char *audio_buffer; 70static 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.. */
96int load_wave(char *filename) 96int 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
155static void test_get_more(unsigned char **ptr, long *size) 163int test_tracknum;
164static 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
169extern int pcmbuf_unplayed_bytes;
170
169bool uda1380_test(void) 171bool 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
22void pcm_init(void); 22void pcm_init(void);
23void pcm_set_frequency(unsigned int frequency); 23void pcm_set_frequency(unsigned int frequency);
24
25/* This is for playing "raw" PCM data */
24void pcm_play_data(const unsigned char* start, int size, 26void 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
26void pcm_play_stop(void); 29void pcm_play_stop(void);
27void pcm_play_pause(bool play); 30void pcm_play_pause(bool play);
28bool pcm_is_playing(void); 31bool pcm_is_playing(void);
29void pcm_set_volume(int volume); 32void pcm_set_volume(int volume);
30 33
34/* These functions are for playing chained buffers of PCM data */
35void pcm_play_init(void);
36void pcm_play_start(void);
37bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void));
38int pcm_play_num_used_buffers(void);
39void 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
215struct 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
222int pcmbuf_read_index;
223int pcmbuf_write_index;
224int pcmbuf_unplayed_bytes;
225int pcmbuf_watermark;
226void (*pcmbuf_watermark_callback)(int bytes_left);
227
228int pcm_play_num_used_buffers(void)
229{
230 return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
231}
232
233void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left))
234{
235 pcmbuf_watermark = numbytes;
236 pcmbuf_watermark_callback = callback;
237}
238
239bool 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
256void 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
264static int last_chunksize = 0;
265
266static 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
313void 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}