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)
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 @@
#include "pcm_playback.h"
#include "buffer.h"
-#define CHUNK_SIZE 44100 /* Transfer CHUNK_SIZE bytes on
- each DMA transfer */
+#define CHUNK_SIZE 0x100000 /* Transfer CHUNK_SIZE bytes on
+ each DMA transfer */
static unsigned char line = 0;
static unsigned char *audio_buffer;
@@ -95,9 +95,9 @@ static void puts(const char *fmt, ...)
/* Very basic WAVE-file support.. Just for testing purposes.. */
int load_wave(char *filename)
{
- int f, i;
+ int f, i, num;
unsigned char buf[32];
- unsigned short *p;
+ unsigned short *p, *end;
puts("Loading %s..", filename);
@@ -136,10 +136,18 @@ int load_wave(char *filename)
close(f);
puts("Changing byte order..");
+ end = (unsigned short *)(audio_buffer + audio_size);
p = (unsigned short *)audio_buffer;
- for (i=0; i<audio_size/2; i++, p++)
+ while(p < end)
{
- *p = SWAB16(*p);
+ /* Swap 128k at a time, to allow the other threads to run */
+ num = MIN(0x20000, (int)(end - p));
+ for(i = 0;i < num;i++)
+ {
+ *p = SWAB16(*p);
+ p++;
+ }
+ yield();
}
return 0;
@@ -152,20 +160,14 @@ int load_wave(char *filename)
*/
-static void test_get_more(unsigned char **ptr, long *size)
+int test_tracknum;
+static void test_trackchange(void)
{
- static long last_chunk_size = 0;
-
- audio_pos += last_chunk_size;
-
- if(audio_pos < audio_size)
- {
- last_chunk_size = MIN(CHUNK_SIZE, (audio_size - audio_pos));
- *ptr = &audio_buffer[audio_pos];
- *size = last_chunk_size;
- }
+ test_tracknum++;
}
+extern int pcmbuf_unplayed_bytes;
+
bool uda1380_test(void)
{
long button;
@@ -173,11 +175,15 @@ bool uda1380_test(void)
bool done = false;
char buf[80];
bool play = true;
+ int sz;
+ char *ptr;
lcd_setmargins(0, 0);
lcd_clear_display();
lcd_update();
+ test_tracknum = 1;
+
line = 0;
if (load_wave("/sample.wav") == -1)
@@ -188,10 +194,19 @@ bool uda1380_test(void)
puts("Playing..");
audio_pos = 0;
+ pcm_play_init();
pcm_set_frequency(44100);
pcm_set_volume(0xff - vol);
- pcm_play_data(audio_buffer, CHUNK_SIZE,
- test_get_more);
+
+ ptr = audio_buffer;
+ for(sz = 0;sz < audio_size;sz += CHUNK_SIZE)
+ {
+ if(!pcm_play_add_chunk(ptr, CHUNK_SIZE, test_trackchange))
+ break;
+ ptr += MIN(CHUNK_SIZE, (audio_size - sz));
+ }
+
+ pcm_play_start();
while(!done)
{
@@ -205,6 +220,10 @@ bool uda1380_test(void)
lcd_puts(0, line+3, buf);
snprintf(buf, sizeof(buf), "DSR0: %02x", DSR0);
lcd_puts(0, line+4, buf);
+ snprintf(buf, sizeof(buf), "Track: %d", test_tracknum);
+ lcd_puts(0, line+5, buf);
+ snprintf(buf, sizeof(buf), "Unplayed: %08x", pcmbuf_unplayed_bytes);
+ lcd_puts(0, line+6, buf);
lcd_update();
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 @@
void pcm_init(void);
void pcm_set_frequency(unsigned int frequency);
+
+/* This is for playing "raw" PCM data */
void pcm_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, long* size));
+
void pcm_play_stop(void);
void pcm_play_pause(bool play);
bool pcm_is_playing(void);
void pcm_set_volume(int volume);
+/* These functions are for playing chained buffers of PCM data */
+void pcm_play_init(void);
+void pcm_play_start(void);
+bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void));
+int pcm_play_num_used_buffers(void);
+void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left));
+
#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)
pcm_set_frequency(44100);
}
+
+
+#define NUM_PCM_BUFFERS 16 /* Must be a power of 2 */
+#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
+
+struct pcmbufdesc
+{
+ void *addr;
+ int size;
+ void (*callback)(void); /* Call this when the buffer has been played */
+} pcmbuffers[NUM_PCM_BUFFERS];
+
+int pcmbuf_read_index;
+int pcmbuf_write_index;
+int pcmbuf_unplayed_bytes;
+int pcmbuf_watermark;
+void (*pcmbuf_watermark_callback)(int bytes_left);
+
+int pcm_play_num_used_buffers(void)
+{
+ return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
+}
+
+void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left))
+{
+ pcmbuf_watermark = numbytes;
+ pcmbuf_watermark_callback = callback;
+}
+
+bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void))
+{
+ /* We don't use the last buffer, since we can't see the difference
+ between the full and empty condition */
+ if(pcm_play_num_used_buffers() < (NUM_PCM_BUFFERS - 1))
+ {
+ pcmbuffers[pcmbuf_write_index].addr = addr;
+ pcmbuffers[pcmbuf_write_index].size = size;
+ pcmbuffers[pcmbuf_write_index].callback = callback;
+ pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK;
+ pcmbuf_unplayed_bytes += size;
+ return true;
+ }
+ else
+ return false;
+}
+
+void pcm_play_init(void)
+{
+ pcmbuf_read_index = 0;
+ pcmbuf_write_index = 0;
+ pcmbuf_unplayed_bytes = 0;
+ pcm_play_set_watermark(0x10000, NULL);
+}
+
+static int last_chunksize = 0;
+
+static void pcm_play_callback(unsigned char** start, long* size)
+{
+ struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
+ int sz;
+
+ pcmbuf_unplayed_bytes -= last_chunksize;
+
+ if(desc->size == 0)
+ {
+ /* The buffer is finished, call the callback function */
+ if(desc->callback)
+ desc->callback();
+
+ /* Advance to the next buffer */
+ pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
+ desc = &pcmbuffers[pcmbuf_read_index];
+ }
+
+ if(pcm_play_num_used_buffers())
+ {
+ /* Play max 64K at a time */
+ sz = MIN(desc->size, 32768);
+ *start = desc->addr;
+ *size = sz;
+
+ /* Update the buffer descriptor */
+ desc->size -= sz;
+ desc->addr += sz;
+
+ last_chunksize = sz;
+ }
+ else
+ {
+ /* No more buffers */
+ *size = 0;
+ }
+#if 0
+ if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
+ {
+ if(pcmbuf_watermark_callback)
+ {
+ pcmbuf_watermark_callback(pcmbuf_unplayed_bytes);
+ }
+ }
+#endif
+}
+
+void pcm_play_start(void)
+{
+ struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
+ int size;
+ char *start;
+
+ if(!pcm_is_playing())
+ {
+ size = MIN(desc->size, 32768);
+ start = desc->addr;
+ pcm_play_data(start, size, pcm_play_callback);
+ last_chunksize = size;
+ desc->size -= size;
+ desc->addr += size;
+ }
+}