From 3eb962d13bd4ca8c29ab33c41428a44e644e59ec Mon Sep 17 00:00:00 2001 From: Miika Pekkarinen Date: Thu, 7 Jul 2005 07:15:05 +0000 Subject: PCM buffering fixes. Made a temporary workaround for playback glitch bug (see the patch). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7049 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs.c | 3 ++ apps/codecs/mpa.c | 20 +++++------- apps/playback.c | 49 +++++++++++++++------------- firmware/pcm_playback.c | 87 +++++++++++++++++++++++++++---------------------- 4 files changed, 87 insertions(+), 72 deletions(-) diff --git a/apps/codecs.c b/apps/codecs.c index d3a9d9e9c1..400e7fbfcf 100644 --- a/apps/codecs.c +++ b/apps/codecs.c @@ -277,6 +277,9 @@ int codec_load_file(const char *plugin) int fd; int rc; + /* zero out codec buffer to ensure a properly zeroed bss area */ + memset(codecbuf, 0, CODEC_SIZE); + fd = open(plugin, O_RDONLY); if (fd < 0) { snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin); diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c index 5cf4eb8730..5d6f7d29ad 100644 --- a/apps/codecs/mpa.c +++ b/apps/codecs/mpa.c @@ -39,12 +39,6 @@ void abort(void) { #define INPUT_CHUNK_SIZE 8192 -#define OUTPUT_BUFFER_SIZE 65536 /* Must be an integer multiple of 4. */ - -unsigned char OutputBuffer[OUTPUT_BUFFER_SIZE]; -unsigned char *OutputPtr; -unsigned char *GuardPtr = NULL; -const unsigned char *OutputBufferEnd = OutputBuffer + OUTPUT_BUFFER_SIZE; mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR; unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR; @@ -115,7 +109,6 @@ enum codec_status codec_start(struct codec_api* api) first_frame = false; file_end = 0; - OutputPtr = OutputBuffer; while (!*ci->taginfo_ready) ci->yield(); @@ -195,7 +188,7 @@ enum codec_status codec_start(struct codec_api* api) } else if(MAD_RECOVERABLE(Stream.error)) { - if(Stream.error!=MAD_ERROR_LOSTSYNC || Stream.this_frame!=GuardPtr) + if(Stream.error!=MAD_ERROR_LOSTSYNC) { // rb->splash(HZ*1, true, "Recoverable...!"); } @@ -209,9 +202,9 @@ enum codec_status codec_start(struct codec_api* api) Status=1; break; } + break ; } - if (Stream.next_frame) - ci->advance_buffer_loc((void *)Stream.next_frame); + file_end = false; /* ?? Do we need the timer module? */ // mad_timer_add(&Timer,Frame.header.duration); @@ -222,7 +215,7 @@ enum codec_status codec_start(struct codec_api* api) /* We skip start_skip number of samples here, this should only happen for very first frame in the stream. */ /* TODO: possible for start_skip to exceed one frames worth of samples? */ - + if (MAD_NCHANNELS(&Frame.header) == 2) { if (current_stereo_mode != STEREO_NONINTERLEAVED) { ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_NONINTERLEAVED); @@ -241,6 +234,11 @@ enum codec_status codec_start(struct codec_api* api) } start_skip = 0; /* not very elegant, and might want to keep this value */ + if (Stream.next_frame) + ci->advance_buffer_loc((void *)Stream.next_frame); + else + ci->advance_buffer(size); + samplesdone += Synth.pcm.length; samplecount -= Synth.pcm.length; ci->set_elapsed(samplesdone / (frequency_divider / 10)); diff --git a/apps/playback.c b/apps/playback.c index 13c66a43b5..f9caff1c4f 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -659,12 +659,12 @@ void audio_fill_file_buffer(void) buf_widx -= codecbuflen; i += rc; tracks[track_widx].available += rc; + tracks[track_widx].filerem -= rc; + tracks[track_widx].filepos += rc; codecbufused += rc; fill_bytesleft -= rc; } - tracks[track_widx].filerem -= i; - tracks[track_widx].filepos += i; /*logf("Filled:%d/%d", tracks[track_widx].available, tracks[track_widx].filerem);*/ } @@ -890,26 +890,29 @@ bool audio_load_track(int offset, bool start_play, int peek_offset) /* Starting playback from an offset is only support in MPA at the moment */ if (offset > 0) { - if ((tracks[track_widx].id3.codectype==AFMT_MPA_L2) || - (tracks[track_widx].id3.codectype==AFMT_MPA_L3)) { - lseek(fd, offset, SEEK_SET); - tracks[track_widx].id3.offset = offset; - mp3_set_elapsed(&tracks[track_widx].id3); - tracks[track_widx].filepos = offset; - tracks[track_widx].filerem = tracks[track_widx].filesize - offset; - ci.curpos = offset; - tracks[track_widx].start_pos = offset; - } - else if (tracks[track_widx].id3.codectype==AFMT_WAVPACK) { - lseek(fd, offset, SEEK_SET); - tracks[track_widx].id3.offset = offset; - tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2; - tracks[track_widx].filepos = offset; - tracks[track_widx].filerem = tracks[track_widx].filesize - offset; - ci.curpos = offset; - tracks[track_widx].start_pos = offset; - } - } + switch (tracks[track_widx].id3.codectype) { + case AFMT_MPA_L2: + case AFMT_MPA_L3: + lseek(fd, offset, SEEK_SET); + tracks[track_widx].id3.offset = offset; + mp3_set_elapsed(&tracks[track_widx].id3); + tracks[track_widx].filepos = offset; + tracks[track_widx].filerem = tracks[track_widx].filesize - offset; + ci.curpos = offset; + tracks[track_widx].start_pos = offset; + break; + + case AFMT_WAVPACK: + lseek(fd, offset, SEEK_SET); + tracks[track_widx].id3.offset = offset; + tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2; + tracks[track_widx].filepos = offset; + tracks[track_widx].filerem = tracks[track_widx].filesize - offset; + ci.curpos = offset; + tracks[track_widx].start_pos = offset; + break; + } + } if (start_play) { track_count++; @@ -1795,6 +1798,8 @@ void audio_init(void) track_buffer_callback = NULL; track_unbuffer_callback = NULL; track_changed_callback = NULL; + /* Just to prevent cur_ti never be anything random. */ + cur_ti = &tracks[0]; logf("abuf:%0x", PCMBUF_SIZE); logf("fbuf:%0x", codecbuflen); diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index be7f4e4379..6b9a4bbd5c 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c @@ -146,7 +146,7 @@ static void dma_stop(void) /* Reset the FIFO */ IIS2CONFIG = 0x800; EBU1CONFIG = 0x800; - + pcmbuf_unplayed_bytes = 0; last_chunksize = 0; audiobuffer_pos = 0; @@ -213,7 +213,8 @@ static void pcm_play_callback(unsigned char** start, long* size) if(pcm_play_num_used_buffers()) { /* Play max 64K at a time */ - sz = MIN(desc->size, 32768); + //sz = MIN(desc->size, 32768); + sz = desc->size; *start = desc->addr; *size = sz; @@ -245,11 +246,21 @@ 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. + * Some debug data: + * - This problem will occur only when the first file. + * - First frame will be totally corrupt and the song will begin + * from the next frame. But at the next time (when the bug has + * already happened), the song will start from first frame. + * - Dropping some frames directly from (mpa) codec will also + * prevent the problem from happening. So it's unlikely you can + * find the explanation for this bug from this file. + */ + get_more((unsigned char **)&start, (long *)&size); // REMOVE THIS TO TEST get_more(&next_start, &next_size); dma_start(start, size); - - /* Sleep a while, then unmute audio output */ - sleep(HZ/8); uda1380_mute(false); } @@ -310,7 +321,7 @@ void DMA0(void) if(res & 0x70) { dma_stop(); - logf("DMA Error"); + logf("DMA Error:0x%04x", res); } else { @@ -326,7 +337,7 @@ void DMA0(void) { /* Finished playing */ dma_stop(); - logf("DMA No Data"); + logf("DMA No Data:0x%04x", res); } } @@ -431,7 +442,7 @@ bool pcm_is_lowdata(void) bool pcm_crossfade_init(void) { if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled - || crossfade_active) { + || crossfade_active || crossfade_init) { return false; } logf("pcm_crossfade_init"); @@ -457,21 +468,27 @@ void pcm_flush_audio(void) void pcm_flush_fillpos(void) { - if (audiobuffer_fillpos) { - while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos], - audiobuffer_fillpos, pcm_event_handler)) { + 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); yield(); /* This is a fatal error situation that should never happen. */ - if (!pcm_playing) + if (!pcm_playing) { + logf("pcm_flush_fillpos error"); break ; + } } pcm_event_handler = NULL; - audiobuffer_pos += audiobuffer_fillpos; + audiobuffer_pos += copy_n; if (audiobuffer_pos >= PCMBUF_SIZE) audiobuffer_pos -= PCMBUF_SIZE; - audiobuffer_free -= audiobuffer_fillpos; - audiobuffer_fillpos = 0; + audiobuffer_free -= copy_n; + audiobuffer_fillpos -= copy_n; } } @@ -541,7 +558,7 @@ int crossfade(short *buf, const short *buf2, int length) return size; } -inline static bool prepare_insert(long length) +static bool prepare_insert(long length) { if (crossfade_init) crossfade_start(); @@ -566,9 +583,10 @@ void* pcm_request_buffer(long length, long *realsize) { void *ptr = NULL; - if (!prepare_insert(length)) { - *realsize = 0; - return NULL; + while (audiobuffer_free < length + audiobuffer_fillpos + + CHUNK_SIZE && !crossfade_active) { + pcm_boost(false); + yield(); } if (crossfade_active) { @@ -595,6 +613,8 @@ void pcm_flush_buffer(long length) { int copy_n; char *buf; + + prepare_insert(length); if (crossfade_active) { buf = &guardbuf[0]; @@ -620,14 +640,14 @@ void pcm_flush_buffer(long length) 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; @@ -699,15 +719,9 @@ void pcm_play_init(void) audiobuffer = &audiobuf[(audiobufend - audiobuf) - PCMBUF_SIZE - PCMBUF_GUARD]; guardbuf = &audiobuffer[PCMBUF_SIZE]; - audiobuffer_free = PCMBUF_SIZE; - audiobuffer_pos = 0; - audiobuffer_fillpos = 0; - boost_mode = 0; - pcmbuf_read_index = 0; - pcmbuf_write_index = 0; - pcmbuf_unplayed_bytes = 0; - crossfade_active = false; - crossfade_init = false; + + /* Call dma_stop to initialize everything. */ + dma_stop(); pcm_event_handler = NULL; } @@ -723,9 +737,8 @@ bool pcm_is_crossfade_enabled(void) void pcm_play_start(void) { - struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; - int size; - char *start; + unsigned long size; + unsigned char *start; if (crossfade_enabled) { pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); @@ -736,11 +749,7 @@ void pcm_play_start(void) if(!pcm_is_playing()) { - size = MIN(desc->size, 32768); - start = desc->addr; - last_chunksize = size; - desc->size -= size; - desc->addr += size; + pcm_play_callback(&start, &size); pcm_play_data(start, size, pcm_play_callback); } } -- cgit v1.2.3