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
This commit is contained in:
Miika Pekkarinen 2005-07-07 07:15:05 +00:00
parent 8d3855eb53
commit 3eb962d13b
4 changed files with 87 additions and 72 deletions

View file

@ -277,6 +277,9 @@ int codec_load_file(const char *plugin)
int fd; int fd;
int rc; int rc;
/* zero out codec buffer to ensure a properly zeroed bss area */
memset(codecbuf, 0, CODEC_SIZE);
fd = open(plugin, O_RDONLY); fd = open(plugin, O_RDONLY);
if (fd < 0) { if (fd < 0) {
snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin); snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin);

View file

@ -39,12 +39,6 @@ void abort(void) {
#define INPUT_CHUNK_SIZE 8192 #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; mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR;
unsigned char mad_main_data[MAD_BUFFER_MDLEN] 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; first_frame = false;
file_end = 0; file_end = 0;
OutputPtr = OutputBuffer;
while (!*ci->taginfo_ready) while (!*ci->taginfo_ready)
ci->yield(); ci->yield();
@ -195,7 +188,7 @@ enum codec_status codec_start(struct codec_api* api)
} }
else if(MAD_RECOVERABLE(Stream.error)) 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...!"); // rb->splash(HZ*1, true, "Recoverable...!");
} }
@ -209,9 +202,9 @@ enum codec_status codec_start(struct codec_api* api)
Status=1; Status=1;
break; break;
} }
break ;
} }
if (Stream.next_frame)
ci->advance_buffer_loc((void *)Stream.next_frame);
file_end = false; file_end = false;
/* ?? Do we need the timer module? */ /* ?? Do we need the timer module? */
// mad_timer_add(&Timer,Frame.header.duration); // 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 /* We skip start_skip number of samples here, this should only happen for
very first frame in the stream. */ very first frame in the stream. */
/* TODO: possible for start_skip to exceed one frames worth of samples? */ /* TODO: possible for start_skip to exceed one frames worth of samples? */
if (MAD_NCHANNELS(&Frame.header) == 2) { if (MAD_NCHANNELS(&Frame.header) == 2) {
if (current_stereo_mode != STEREO_NONINTERLEAVED) { if (current_stereo_mode != STEREO_NONINTERLEAVED) {
ci->configure(DSP_SET_STEREO_MODE, (int *)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 */ 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; samplesdone += Synth.pcm.length;
samplecount -= Synth.pcm.length; samplecount -= Synth.pcm.length;
ci->set_elapsed(samplesdone / (frequency_divider / 10)); ci->set_elapsed(samplesdone / (frequency_divider / 10));

View file

@ -659,12 +659,12 @@ void audio_fill_file_buffer(void)
buf_widx -= codecbuflen; buf_widx -= codecbuflen;
i += rc; i += rc;
tracks[track_widx].available += rc; tracks[track_widx].available += rc;
tracks[track_widx].filerem -= rc;
tracks[track_widx].filepos += rc;
codecbufused += rc; codecbufused += rc;
fill_bytesleft -= rc; fill_bytesleft -= rc;
} }
tracks[track_widx].filerem -= i;
tracks[track_widx].filepos += i;
/*logf("Filled:%d/%d", tracks[track_widx].available, /*logf("Filled:%d/%d", tracks[track_widx].available,
tracks[track_widx].filerem);*/ 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 */ /* Starting playback from an offset is only support in MPA at the moment */
if (offset > 0) { if (offset > 0) {
if ((tracks[track_widx].id3.codectype==AFMT_MPA_L2) || switch (tracks[track_widx].id3.codectype) {
(tracks[track_widx].id3.codectype==AFMT_MPA_L3)) { case AFMT_MPA_L2:
lseek(fd, offset, SEEK_SET); case AFMT_MPA_L3:
tracks[track_widx].id3.offset = offset; lseek(fd, offset, SEEK_SET);
mp3_set_elapsed(&tracks[track_widx].id3); tracks[track_widx].id3.offset = offset;
tracks[track_widx].filepos = offset; mp3_set_elapsed(&tracks[track_widx].id3);
tracks[track_widx].filerem = tracks[track_widx].filesize - offset; tracks[track_widx].filepos = offset;
ci.curpos = offset; tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
tracks[track_widx].start_pos = offset; ci.curpos = offset;
} tracks[track_widx].start_pos = offset;
else if (tracks[track_widx].id3.codectype==AFMT_WAVPACK) { break;
lseek(fd, offset, SEEK_SET);
tracks[track_widx].id3.offset = offset; case AFMT_WAVPACK:
tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2; lseek(fd, offset, SEEK_SET);
tracks[track_widx].filepos = offset; tracks[track_widx].id3.offset = offset;
tracks[track_widx].filerem = tracks[track_widx].filesize - offset; tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2;
ci.curpos = offset; tracks[track_widx].filepos = offset;
tracks[track_widx].start_pos = offset; tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
} ci.curpos = offset;
} tracks[track_widx].start_pos = offset;
break;
}
}
if (start_play) { if (start_play) {
track_count++; track_count++;
@ -1795,6 +1798,8 @@ void audio_init(void)
track_buffer_callback = NULL; track_buffer_callback = NULL;
track_unbuffer_callback = NULL; track_unbuffer_callback = NULL;
track_changed_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("abuf:%0x", PCMBUF_SIZE);
logf("fbuf:%0x", codecbuflen); logf("fbuf:%0x", codecbuflen);

View file

@ -146,7 +146,7 @@ static void dma_stop(void)
/* Reset the FIFO */ /* Reset the FIFO */
IIS2CONFIG = 0x800; IIS2CONFIG = 0x800;
EBU1CONFIG = 0x800; EBU1CONFIG = 0x800;
pcmbuf_unplayed_bytes = 0; pcmbuf_unplayed_bytes = 0;
last_chunksize = 0; last_chunksize = 0;
audiobuffer_pos = 0; audiobuffer_pos = 0;
@ -213,7 +213,8 @@ static void pcm_play_callback(unsigned char** start, long* size)
if(pcm_play_num_used_buffers()) if(pcm_play_num_used_buffers())
{ {
/* Play max 64K at a time */ /* Play max 64K at a time */
sz = MIN(desc->size, 32768); //sz = MIN(desc->size, 32768);
sz = desc->size;
*start = desc->addr; *start = desc->addr;
*size = sz; *size = sz;
@ -245,11 +246,21 @@ void pcm_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, long* size)) void (*get_more)(unsigned char** start, long* size))
{ {
callback_for_more = get_more; 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); get_more(&next_start, &next_size);
dma_start(start, size); dma_start(start, size);
/* Sleep a while, then unmute audio output */
sleep(HZ/8);
uda1380_mute(false); uda1380_mute(false);
} }
@ -310,7 +321,7 @@ void DMA0(void)
if(res & 0x70) if(res & 0x70)
{ {
dma_stop(); dma_stop();
logf("DMA Error"); logf("DMA Error:0x%04x", res);
} }
else else
{ {
@ -326,7 +337,7 @@ void DMA0(void)
{ {
/* Finished playing */ /* Finished playing */
dma_stop(); 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) bool pcm_crossfade_init(void)
{ {
if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled if (PCMBUF_SIZE - audiobuffer_free < CHUNK_SIZE * 8 || !crossfade_enabled
|| crossfade_active) { || crossfade_active || crossfade_init) {
return false; return false;
} }
logf("pcm_crossfade_init"); logf("pcm_crossfade_init");
@ -457,21 +468,27 @@ void pcm_flush_audio(void)
void pcm_flush_fillpos(void) void pcm_flush_fillpos(void)
{ {
if (audiobuffer_fillpos) { int copy_n;
while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos],
audiobuffer_fillpos, pcm_event_handler)) { 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); pcm_boost(false);
yield(); yield();
/* This is a fatal error situation that should never happen. */ /* This is a fatal error situation that should never happen. */
if (!pcm_playing) if (!pcm_playing) {
logf("pcm_flush_fillpos error");
break ; break ;
}
} }
pcm_event_handler = NULL; pcm_event_handler = NULL;
audiobuffer_pos += audiobuffer_fillpos; audiobuffer_pos += copy_n;
if (audiobuffer_pos >= PCMBUF_SIZE) if (audiobuffer_pos >= PCMBUF_SIZE)
audiobuffer_pos -= PCMBUF_SIZE; audiobuffer_pos -= PCMBUF_SIZE;
audiobuffer_free -= audiobuffer_fillpos; audiobuffer_free -= copy_n;
audiobuffer_fillpos = 0; audiobuffer_fillpos -= copy_n;
} }
} }
@ -541,7 +558,7 @@ int crossfade(short *buf, const short *buf2, int length)
return size; return size;
} }
inline static bool prepare_insert(long length) static bool prepare_insert(long length)
{ {
if (crossfade_init) if (crossfade_init)
crossfade_start(); crossfade_start();
@ -566,9 +583,10 @@ void* pcm_request_buffer(long length, long *realsize)
{ {
void *ptr = NULL; void *ptr = NULL;
if (!prepare_insert(length)) { while (audiobuffer_free < length + audiobuffer_fillpos
*realsize = 0; + CHUNK_SIZE && !crossfade_active) {
return NULL; pcm_boost(false);
yield();
} }
if (crossfade_active) { if (crossfade_active) {
@ -595,6 +613,8 @@ void pcm_flush_buffer(long length)
{ {
int copy_n; int copy_n;
char *buf; char *buf;
prepare_insert(length);
if (crossfade_active) { if (crossfade_active) {
buf = &guardbuf[0]; buf = &guardbuf[0];
@ -620,14 +640,14 @@ void pcm_flush_buffer(long length)
pcm_flush_fillpos(); pcm_flush_fillpos();
} }
} }
audiobuffer_fillpos += length; audiobuffer_fillpos += length;
try_flush: try_flush:
if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE if (audiobuffer_fillpos < CHUNK_SIZE && PCMBUF_SIZE
- audiobuffer_pos - audiobuffer_fillpos > 0) - audiobuffer_pos - audiobuffer_fillpos > 0)
return ; return ;
copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos); copy_n = audiobuffer_fillpos - (PCMBUF_SIZE - audiobuffer_pos);
if (copy_n > 0) { if (copy_n > 0) {
audiobuffer_fillpos -= copy_n; audiobuffer_fillpos -= copy_n;
@ -699,15 +719,9 @@ void pcm_play_init(void)
audiobuffer = &audiobuf[(audiobufend - audiobuf) - audiobuffer = &audiobuf[(audiobufend - audiobuf) -
PCMBUF_SIZE - PCMBUF_GUARD]; PCMBUF_SIZE - PCMBUF_GUARD];
guardbuf = &audiobuffer[PCMBUF_SIZE]; guardbuf = &audiobuffer[PCMBUF_SIZE];
audiobuffer_free = PCMBUF_SIZE;
audiobuffer_pos = 0; /* Call dma_stop to initialize everything. */
audiobuffer_fillpos = 0; dma_stop();
boost_mode = 0;
pcmbuf_read_index = 0;
pcmbuf_write_index = 0;
pcmbuf_unplayed_bytes = 0;
crossfade_active = false;
crossfade_init = false;
pcm_event_handler = NULL; pcm_event_handler = NULL;
} }
@ -723,9 +737,8 @@ bool pcm_is_crossfade_enabled(void)
void pcm_play_start(void) void pcm_play_start(void)
{ {
struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; unsigned long size;
int size; unsigned char *start;
char *start;
if (crossfade_enabled) { if (crossfade_enabled) {
pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback); pcm_play_set_watermark(PCM_CF_WATERMARK, pcm_watermark_callback);
@ -736,11 +749,7 @@ void pcm_play_start(void)
if(!pcm_is_playing()) if(!pcm_is_playing())
{ {
size = MIN(desc->size, 32768); pcm_play_callback(&start, &size);
start = desc->addr;
last_chunksize = size;
desc->size -= size;
desc->addr += size;
pcm_play_data(start, size, pcm_play_callback); pcm_play_data(start, size, pcm_play_callback);
} }
} }