forked from len0rd/rockbox
New API for buffered PCM playback
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6284 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
14c7900383
commit
267eb077a7
3 changed files with 167 additions and 19 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue