diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2005-04-14 11:51:31 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2005-04-14 11:51:31 +0000 |
commit | 267eb077a79b85638598e1a872ffab9e60da7dfe (patch) | |
tree | a95638d2e28de3299da5e74c6f5e01b5c58374c2 | |
parent | 14c7900383bd2082494ce1cfa3e191bc34a44b3a (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.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 @@ #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; + } +} |