diff options
author | Miika Pekkarinen <miipekk@ihme.org> | 2005-07-13 12:48:22 +0000 |
---|---|---|
committer | Miika Pekkarinen <miipekk@ihme.org> | 2005-07-13 12:48:22 +0000 |
commit | 20b3897474cfa680e33f7d03feb6644390fd2b20 (patch) | |
tree | 6f0cb7bde0b73317e62ad9131a7c81aeff00942a /firmware | |
parent | 10a680556aa690d8643516f74db471e5da8db930 (diff) | |
download | rockbox-20b3897474cfa680e33f7d03feb6644390fd2b20.tar.gz rockbox-20b3897474cfa680e33f7d03feb6644390fd2b20.zip |
Separated buffering stuff from pcm_playback to pcmbuf. Renamed some
function calls (audiobuffer -> pcmbuf etc.).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7131 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/pcm_playback.h | 30 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 526 |
2 files changed, 13 insertions, 543 deletions
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h index c29cd2a03e..3f0b5eee86 100644 --- a/firmware/export/pcm_playback.h +++ b/firmware/export/pcm_playback.h @@ -19,41 +19,15 @@ #ifndef PCM_PLAYBACK_H #define PCM_PLAYBACK_H -/* Guard buffer for crossfader when dsp is enabled. */ -#define PCMBUF_GUARD 32768 - -/* PCM audio buffer. */ -#define PCMBUF_SIZE (1*1024*1024) - 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_data(void (*get_more)(unsigned char** start, long* size)); void pcm_play_stop(void); void pcm_play_pause(bool play); +bool pcm_is_paused(void); bool pcm_is_playing(void); -bool pcm_is_crossfade_active(void); - -/* 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)); - -void pcm_set_boost_mode(bool state); -bool pcm_is_lowdata(void); -void pcm_flush_audio(void); -bool pcm_crossfade_init(void); -void audiobuffer_add_event(void (*event_handler)(void)); -unsigned int audiobuffer_get_latency(void); -bool pcm_insert_buffer(char *buf, long length); -void pcm_flush_buffer(long length); -void* pcm_request_buffer(long length, long *realsize); -bool pcm_is_crossfade_enabled(void); -void pcm_crossfade_enable(bool on_off); #endif diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index af8ce18af4..7682ce03c8 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c @@ -43,61 +43,13 @@ #ifdef HAVE_UDA1380 -#define CHUNK_SIZE 32768 -/* Must be a power of 2 */ -#define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE) -#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1) -#define PCM_WATERMARK (CHUNK_SIZE * 10) -#define PCM_CF_WATERMARK (PCMBUF_SIZE - CHUNK_SIZE*8) - static bool pcm_playing; static bool pcm_paused; static int pcm_freq = 0x6; /* 44.1 is default */ -static char *audiobuffer; -static long audiobuffer_pos; -long audiobuffer_free; -static long audiobuffer_fillpos; -static bool boost_mode; - -/* Crossfade modes. If CFM_CROSSFADE is selected, normal - * crossfader will activate. Selecting CFM_FLUSH is a special - * operation that only overwrites the pcm buffer without crossfading. - */ -enum { - CFM_CROSSFADE, - CFM_FLUSH -}; - -static int crossfade_mode; -static bool crossfade_enabled; -static bool crossfade_active; -static bool crossfade_init; -static int crossfade_pos; -static int crossfade_amount; -static int crossfade_rem; - -static char *guardbuf; -static void (*pcm_event_handler)(void); - static unsigned char *next_start; static long next_size; -static int last_chunksize = 0; - -struct pcmbufdesc -{ - void *addr; - int size; - void (*callback)(void); /* Call this when the buffer has been played */ -} pcmbuffers[NUM_PCM_BUFFERS]; - -volatile int pcmbuf_read_index; -volatile int pcmbuf_write_index; -int pcmbuf_unplayed_bytes; -int pcmbuf_watermark; -void (*pcmbuf_watermark_callback)(int bytes_left); - /* Set up the DMA transfer that kicks in when the audio FIFO gets empty */ static void dma_start(const void *addr, long size) { @@ -122,21 +74,6 @@ static void dma_start(const void *addr, long size) DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_SINC | DMA_START; } -void pcm_boost(bool state) -{ - static bool boost_state = false; - - if (crossfade_init || crossfade_active || boost_mode) - return ; - - if (state != boost_state) { -#ifdef HAVE_ADJUSTABLE_CPU_FREQ - cpu_boost(state); -#endif - boost_state = state; - } -} - /* Stops the DMA transfer and interrupt */ static void dma_stop(void) { @@ -147,17 +84,8 @@ static void dma_stop(void) IIS2CONFIG = 0x800; EBU1CONFIG = 0x800; - pcmbuf_unplayed_bytes = 0; - last_chunksize = 0; - audiobuffer_pos = 0; - audiobuffer_fillpos = 0; - audiobuffer_free = PCMBUF_SIZE; - pcmbuf_read_index = 0; - pcmbuf_write_index = 0; next_start = NULL; next_size = 0; - crossfade_init = 0; - crossfade_active = 0; pcm_paused = false; } @@ -185,67 +113,13 @@ void pcm_set_frequency(unsigned int frequency) /* the registered callback function to ask for more mp3 data */ static void (*callback_for_more)(unsigned char**, long*) = NULL; -int pcm_play_num_used_buffers(void) -{ - return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK; -} - -static void pcm_play_callback(unsigned char** start, long* size) +void pcm_play_data(void (*get_more)(unsigned char** start, long* size)) { - struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; - int sz; - - pcmbuf_unplayed_bytes -= last_chunksize; - audiobuffer_free += 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]; - } + unsigned char *start; + long size; - if(pcm_play_num_used_buffers()) - { - /* Play max 64K at a time */ - //sz = MIN(desc->size, 32768); - sz = desc->size; - *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 (pcm_event_handler) - pcm_event_handler(); - } -#if 1 - if(pcmbuf_unplayed_bytes <= pcmbuf_watermark) - { - if(pcmbuf_watermark_callback) - { - pcmbuf_watermark_callback(pcmbuf_unplayed_bytes); - } - } -#endif -} - -void pcm_play_data(const unsigned char* start, int size, - void (*get_more)(unsigned char** start, long* size)) -{ callback_for_more = get_more; + /** FIXME: This is a temporary fix to prevent playback glitches when * playing the first file. We will just drop the first frame to prevent * that problem from occurring. @@ -259,6 +133,7 @@ void pcm_play_data(const unsigned char* start, int size, * find the explanation for this bug from this file. */ get_more((unsigned char **)&start, (long *)&size); // REMOVE THIS TO TEST + get_more((unsigned char **)&start, (long *)&size); get_more(&next_start, &next_size); dma_start(start, size); uda1380_mute(false); @@ -270,13 +145,11 @@ void pcm_play_stop(void) uda1380_mute(true); dma_stop(); } - pcm_set_boost_mode(false); - pcm_boost(false); } void pcm_play_pause(bool play) { - if(pcm_paused && play && pcmbuf_unplayed_bytes) + if(pcm_paused && play && next_size) { logf("unpause"); /* Reset chunk size so dma has enough data to fill the fifo. */ @@ -303,6 +176,11 @@ void pcm_play_pause(bool play) pcm_paused = !play; } +bool pcm_is_paused(void) +{ + return pcm_paused; +} + bool pcm_is_playing(void) { return pcm_playing; @@ -370,390 +248,8 @@ void pcm_init(void) sleep(HZ/4); uda1380_enable_output(true); - pcm_play_init(); -} - -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 - 2)) - { - 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_watermark_callback(int bytes_left) -{ - /* Fill audio buffer by boosting cpu */ - pcm_boost(true); - if (bytes_left <= CHUNK_SIZE * 2) - crossfade_active = false; -} - -void pcm_set_boost_mode(bool state) -{ - if (state) - pcm_boost(true); - boost_mode = state; -} - -void audiobuffer_add_event(void (*event_handler)(void)) -{ - pcm_event_handler = event_handler; -} - -unsigned int audiobuffer_get_latency(void) -{ - int latency; - - /* This has to be done better. */ - latency = (PCMBUF_SIZE - audiobuffer_free - CHUNK_SIZE)/4 / (44100/1000); - if (latency < 0) - latency = 0; - - return latency; -} - -bool pcm_is_lowdata(void) -{ - if (!pcm_is_playing() || pcm_paused || crossfade_init || crossfade_active) - return false; - - if (pcmbuf_unplayed_bytes < PCM_WATERMARK) - return true; - - return false; -} - -bool pcm_crossfade_init(void) -{ - if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled - || crossfade_active || crossfade_init) { - return false; - } - logf("pcm_crossfade_init"); - pcm_boost(true); - crossfade_mode = CFM_CROSSFADE; - crossfade_init = true; - - return true; - -} - -/** Initialize a track switch so that audio playback will not stop but - * the switch to next track would happen as soon as possible. - */ -void pcm_flush_audio(void) -{ - if (crossfade_init || crossfade_active || !pcm_playing) { - pcm_play_stop(); - return ; - } - - crossfade_mode = CFM_FLUSH; - crossfade_init = true; -} - -void pcm_flush_fillpos(void) -{ - int copy_n; - - copy_n = MIN(audiobuffer_fillpos, CHUNK_SIZE); - - if (copy_n) { - while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos], - copy_n, pcm_event_handler)) { - pcm_boost(false); - sleep(1); - /* This is a fatal error situation that should never happen. */ - if (!pcm_playing) { - logf("pcm_flush_fillpos error"); - break ; - } - } - pcm_event_handler = NULL; - audiobuffer_pos += copy_n; - if (audiobuffer_pos >= PCMBUF_SIZE) - audiobuffer_pos -= PCMBUF_SIZE; - audiobuffer_free -= copy_n; - audiobuffer_fillpos -= copy_n; - } -} - -static void crossfade_start(void) -{ - int bytesleft = pcmbuf_unplayed_bytes; - - crossfade_init = 0; - if (bytesleft < CHUNK_SIZE * 3) { - logf("crossfade rejected"); - pcm_play_stop(); - return ; - } - - logf("crossfade_start"); - pcm_flush_fillpos(); - pcm_boost(true); - crossfade_active = true; - crossfade_pos = audiobuffer_pos; - - switch (crossfade_mode) { - case CFM_CROSSFADE: - crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2; - crossfade_rem = crossfade_amount; - break ; - - case CFM_FLUSH: - crossfade_amount = (bytesleft - (CHUNK_SIZE * 2))/2; - crossfade_rem = crossfade_amount; - break ; - } - - crossfade_pos -= crossfade_amount*2; - if (crossfade_pos < 0) - crossfade_pos += PCMBUF_SIZE; -} - -static __inline -int crossfade(short *buf, const short *buf2, int length) -{ - int size, i; - int val1, val2; - - size = MIN(length, crossfade_rem); - switch (crossfade_mode) { - case CFM_CROSSFADE: - val1 = (crossfade_rem<<10)/crossfade_amount; - val2 = ((crossfade_amount-crossfade_rem)<<10)/crossfade_amount; - - for (i = 0; i < size; i++) { - buf[i] = ((buf[i] * val1) + (buf2[i] * val2)) >> 10; - } - break ; - - case CFM_FLUSH: - for (i = 0; i < size; i++) { - buf[i] = buf2[i]; - } - //memcpy((char *)buf, (char *)buf2, size*2); - break ; - } - - crossfade_rem -= size; - if (crossfade_rem <= 0) - crossfade_active = false; - - return size; -} - -static bool prepare_insert(long length) -{ - if (crossfade_init) - crossfade_start(); - - if (audiobuffer_free < length + audiobuffer_fillpos - + CHUNK_SIZE && !crossfade_active) { - pcm_boost(false); - return false; - } - - if (!pcm_is_playing()) { - pcm_boost(true); - crossfade_active = false; - if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*4) - pcm_play_start(); - } - - return true; -} - -void* pcm_request_buffer(long length, long *realsize) -{ - void *ptr = NULL; - - while (audiobuffer_free < length + audiobuffer_fillpos - + CHUNK_SIZE && !crossfade_active) { - pcm_boost(false); - sleep(1); - } - - if (crossfade_active) { - *realsize = MIN(length, PCMBUF_GUARD); - ptr = &guardbuf[0]; - } else { - *realsize = MIN(length, PCMBUF_SIZE - audiobuffer_pos - - audiobuffer_fillpos); - if (*realsize < length) { - *realsize += MIN((long)(length - *realsize), PCMBUF_GUARD); - } - ptr = &audiobuffer[audiobuffer_pos + audiobuffer_fillpos]; - } - - return ptr; -} - -bool pcm_is_crossfade_active(void) -{ - return crossfade_active || crossfade_init; -} - -void pcm_flush_buffer(long length) -{ - int copy_n; - char *buf; - - prepare_insert(length); - - if (crossfade_active) { - buf = &guardbuf[0]; - length = MIN(length, PCMBUF_GUARD); - while (length > 0 && crossfade_active) { - copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos); - copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], - (const short *)buf, copy_n/2); - buf += copy_n; - length -= copy_n; - crossfade_pos += copy_n; - if (crossfade_pos >= PCMBUF_SIZE) - crossfade_pos -= PCMBUF_SIZE; - } - - while (length > 0) { - copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos); - memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); - audiobuffer_fillpos = copy_n; - buf += copy_n; - length -= copy_n; - if (length > 0) - pcm_flush_fillpos(); - } - } - - audiobuffer_fillpos += length; - - try_flush: - if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE - - audiobuffer_pos - audiobuffer_fillpos > 0) - return ; - - copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); - if (copy_n > 0) { - audiobuffer_fillpos -= copy_n; - pcm_flush_fillpos(); - copy_n = MIN(copy_n, PCMBUF_GUARD); - memcpy(&audiobuffer[0], &guardbuf[0], copy_n); - audiobuffer_fillpos = copy_n; - goto try_flush; - } - pcm_flush_fillpos(); -} - -bool pcm_insert_buffer(char *buf, long length) -{ - long copy_n = 0; - - if (!prepare_insert(length)) - return false; - - - if (crossfade_active) { - while (length > 0 && crossfade_active) { - copy_n = MIN(length, PCMBUF_SIZE - crossfade_pos); - - copy_n = 2 * crossfade((short *)&audiobuffer[crossfade_pos], - (const short *)buf, copy_n/2); - buf += copy_n; - length -= copy_n; - crossfade_pos += copy_n; - if (crossfade_pos >= PCMBUF_SIZE) - crossfade_pos -= PCMBUF_SIZE; - } - - while (length > 0) { - copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos); - memcpy(&audiobuffer[audiobuffer_pos], buf, copy_n); - audiobuffer_fillpos = copy_n; - buf += copy_n; - length -= copy_n; - if (length > 0) - pcm_flush_fillpos(); - } - } - - while (length > 0) { - copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos - - audiobuffer_fillpos); - copy_n = MIN(CHUNK_SIZE - audiobuffer_fillpos, copy_n); - - memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos], - buf, copy_n); - buf += copy_n; - audiobuffer_fillpos += copy_n; - length -= copy_n; - - /* Pre-buffer to meet CHUNK_SIZE requirement */ - if (audiobuffer_fillpos < CHUNK_SIZE && length == 0) { - return true; - } - - pcm_flush_fillpos(); - } - - return true; -} - -void pcm_play_init(void) -{ - audiobuffer = &audiobuf[(audiobufend - audiobuf) - - PCMBUF_SIZE - PCMBUF_GUARD]; - guardbuf = &audiobuffer[PCMBUF_SIZE]; - /* Call dma_stop to initialize everything. */ dma_stop(); - pcm_event_handler = NULL; -} - -void pcm_crossfade_enable(bool on_off) -{ - crossfade_enabled = on_off; -} - -bool pcm_is_crossfade_enabled(void) -{ - return crossfade_enabled; -} - -void pcm_play_start(void) -{ - unsigned long size; - unsigned char *start; - - if (crossfade_enabled) { - pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); - } else { - pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback); - } - crossfade_active = false; - - if(!pcm_is_playing()) - { - pcm_play_callback(&start, &size); - pcm_play_data(start, size, pcm_play_callback); - } } #endif /* HAVE_UDA1380 */ |