diff options
| author | Dave Chapman <dave@dchapman.com> | 2006-05-20 09:57:55 +0000 |
|---|---|---|
| committer | Dave Chapman <dave@dchapman.com> | 2006-05-20 09:57:55 +0000 |
| commit | fa5caa0b5b2bce6ec56a99d716584405854ede76 (patch) | |
| tree | e4130e1d2092a6d9161afed571f104c88f2ce3b6 /apps/codecs | |
| parent | 965e824923e63b6fd53113ed4c4c2c04692b2fe4 (diff) | |
| download | rockbox-fa5caa0b5b2bce6ec56a99d716584405854ede76.zip rockbox-fa5caa0b5b2bce6ec56a99d716584405854ede76.tar.gz rockbox-fa5caa0b5b2bce6ec56a99d716584405854ede76.tar.bz2 rockbox-fa5caa0b5b2bce6ec56a99d716584405854ede76.tar.xz | |
Patch from bug report #5200 by Mark Arigo - attempt to fix gapless playback after seeking in an MP3 file. It works for me, but needs more testing with a wider range of files before we can close the bug report - please post feedback on the tracker.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9962 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs')
| -rw-r--r-- | apps/codecs/mpa.c | 163 |
1 files changed, 75 insertions, 88 deletions
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c index 4c3784a..81604de 100644 --- a/apps/codecs/mpa.c +++ b/apps/codecs/mpa.c @@ -40,6 +40,7 @@ mad_fixed_t mad_frame_overlap[2][32][18] IBSS_ATTR; unsigned char mad_main_data[MAD_BUFFER_MDLEN] IBSS_ATTR; /* TODO: what latency does layer 1 have? */ int mpeg_latency[3] = { 0, 481, 529 }; +int mpeg_framesize[3] = {384, 1152, 1152}; #ifdef USE_IRAM extern char iramcopy[]; @@ -50,34 +51,13 @@ extern char iend[]; #endif struct codec_api *ci; -int64_t samplecount; -int64_t samplesdone; -int stop_skip, start_skip; -int current_stereo_mode = -1; -unsigned long current_frequency = 0; - -void recalc_samplecount(void) -{ - /* NOTE: currently this doesn't work, the below calculated samples_count - seems to be right, but sometimes we just don't have all the data we - need... */ - if (ci->id3->frame_count) { - /* TODO: 1152 is the frame size in samples for MPEG1 layer 2 and layer 3, - it's probably not correct at all for MPEG2 and layer 1 */ - samplecount = ((int64_t)ci->id3->frame_count) * 1152; - } else { - samplecount = ((int64_t)ci->id3->length) * current_frequency / 1000; - } - - samplecount -= start_skip + stop_skip; -} void init_mad(void) { ci->memset(&stream, 0, sizeof(struct mad_stream)); ci->memset(&frame, 0, sizeof(struct mad_frame)); ci->memset(&synth, 0, sizeof(struct mad_synth)); - + mad_stream_init(&stream); mad_frame_init(&frame); mad_synth_init(&synth); @@ -94,14 +74,14 @@ enum codec_status codec_start(struct codec_api *api) int status; size_t size; int file_end; - int frame_skip; /* samples to skip current frame */ int samples_to_skip; /* samples to skip in total for this file (at start) */ char *inputbuffer; - /* If we know the position isn't exact (i.e., we have seeked to a - * position that isn't the start of the file), we can't reliably do - * end-of-file trimming for gapless playback. - */ - bool exact_position = true; + int64_t samplesdone; + int stop_skip, start_skip; + int current_stereo_mode = -1; + unsigned long current_frequency = 0; + int framelength; + int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */ ci = api; @@ -120,20 +100,17 @@ enum codec_status codec_start(struct codec_api *api) ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE); ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1)); ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); - - /** This label might need to be moved above all the init code, but I don't - * think reiniting the codec is necessary for MPEG. It might even be unwanted - * for gapless playback. - * Reinitializing seems to be necessary to avoid playback quircks when seeking. */ - next_track: + +next_track: status = CODEC_OK; - + + /* Reinitializing seems to be necessary to avoid playback quircks when seeking. */ init_mad(); file_end = 0; while (!*ci->taginfo_ready && !ci->stop_codec) ci->sleep(1); - + ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency); current_frequency = ci->id3->frequency; codec_set_replaygain(ci->id3); @@ -151,35 +128,52 @@ enum codec_status codec_start(struct codec_api *api) start_skip = mpeg_latency[ci->id3->layer]; } + /* Libmad will not decode the last frame without 8 bytes of extra padding + in the buffer. So, we can trick libmad into not decoding the last frame + if we are to skip it entirely and then cut the appropriate samples from + final frame that we did decode. Note, if all tags (ID3, APE) are not + properly stripped from the end of the file, this trick will not work. */ + if (stop_skip >= mpeg_framesize[ci->id3->layer]) { + padding = 0; + stop_skip -= mpeg_framesize[ci->id3->layer]; + } else { + padding = MAD_BUFFER_GUARD; + } + samplesdone = ((int64_t)ci->id3->elapsed) * current_frequency / 1000; - exact_position = samplesdone == 0; - samples_to_skip = start_skip; - recalc_samplecount(); - + + /* Don't skip any samples unless we start at the beginning. */ + if (samplesdone > 0) + samples_to_skip = 0; + else + samples_to_skip = start_skip; + + framelength = 0; + /* This is the decoding loop. */ while (1) { - int framelength; - ci->yield(); if (ci->stop_codec || ci->new_track) break; if (ci->seek_time) { int newpos; - - samplesdone = ((int64_t) (ci->seek_time - 1)) - * current_frequency / 1000; - exact_position = samplesdone == 0; - if (ci->seek_time-1 == 0) + samplesdone = ((int64_t)(ci->seek_time-1))*current_frequency/1000; + + if (ci->seek_time-1 == 0) { newpos = ci->id3->first_frame_offset; - else + samples_to_skip = start_skip; + } else { newpos = ci->mp3_get_filepos(ci->seek_time-1); + samples_to_skip = 0; + } if (!ci->seek_buffer(newpos)) break; ci->seek_complete(); init_mad(); + framelength = 0; } /* Lock buffers */ @@ -187,18 +181,17 @@ enum codec_status codec_start(struct codec_api *api) inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE); if (size == 0 || inputbuffer == NULL) break; - /* size + MAD_BUFFER_GUARD to help mad decode the last frame */ mad_stream_buffer(&stream, (unsigned char *)inputbuffer, - size + MAD_BUFFER_GUARD); + size + padding); } - + if (mad_frame_decode(&frame, &stream)) { if (stream.error == MAD_FLAG_INCOMPLETE || stream.error == MAD_ERROR_BUFLEN) { /* This makes the codec support partially corrupted files */ if (file_end == 30) break; - + /* Fill the buffer */ if (stream.next_frame) ci->advance_buffer_loc((void *)stream.next_frame); @@ -216,39 +209,28 @@ enum codec_status codec_start(struct codec_api *api) } break; } - + file_end = 0; - mad_synth_frame(&synth, &frame); - - /* We need to skip samples_to_skip samples from the start of every file - to properly support LAME style gapless MP3 files. samples_to_skip - might be larger than one frame. */ - if (samples_to_skip < synth.pcm.length) { - /* skip just part of the frame */ - frame_skip = samples_to_skip; + /* Do the pcmbuf insert here. Note, this is the PREVIOUS frame's pcm + data (not the one just decoded above). When we exit the decoding + loop we will need to process the final frame that was decoded. */ + if (framelength > 0) { + /* In case of a mono file, the second array will be ignored. */ + ci->pcmbuf_insert_split(&synth.pcm.samples[0][samples_to_skip], + &synth.pcm.samples[1][samples_to_skip], + framelength * 4); + + /* Only skip samples for the first frame added. */ samples_to_skip = 0; - } else { - /* we need to skip an entire frame */ - frame_skip = synth.pcm.length; - samples_to_skip -= synth.pcm.length; - } - - framelength = synth.pcm.length - frame_skip; - - if (exact_position && (stop_skip > 0)) { - int64_t max = samplecount - samplesdone; - - if (max < 0) max = 0; - if (max < framelength) framelength = (int)max; - if (framelength == 0 && frame_skip == 0) break; } - + + mad_synth_frame(&synth, &frame); + /* Check if sample rate and stereo settings changed in this frame. */ if (frame.header.samplerate != current_frequency) { current_frequency = frame.header.samplerate; ci->configure(DSP_SWITCH_FREQUENCY, (int *)current_frequency); - recalc_samplecount(); } if (MAD_NCHANNELS(&frame.header) == 2) { if (current_stereo_mode != STEREO_NONINTERLEAVED) { @@ -261,27 +243,32 @@ enum codec_status codec_start(struct codec_api *api) current_stereo_mode = STEREO_MONO; } } - - /* Check if we can just skip the entire frame. */ - if (frame_skip < synth.pcm.length) { - /* In case of a mono file, the second array will be ignored. */ - ci->pcmbuf_insert_split(&synth.pcm.samples[0][frame_skip], - &synth.pcm.samples[1][frame_skip], - framelength * 4); - } - + if (stream.next_frame) ci->advance_buffer_loc((void *)stream.next_frame); else ci->advance_buffer(size); + framelength = synth.pcm.length - samples_to_skip; + if (framelength < 0) { + framelength = 0; + samples_to_skip -= synth.pcm.length; + } + samplesdone += framelength; ci->set_elapsed(samplesdone / (current_frequency / 1000)); } + + /* Finish the remaining decoded frame. + Cut the required samples from the end. */ + if (framelength > stop_skip) + ci->pcmbuf_insert_split(synth.pcm.samples[0], synth.pcm.samples[1], + (framelength - stop_skip) * 4); + stream.error = 0; - + if (ci->request_next_track()) goto next_track; - + return status; } |