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 | |
parent | 965e824923e63b6fd53113ed4c4c2c04692b2fe4 (diff) | |
download | rockbox-fa5caa0b5b2bce6ec56a99d716584405854ede76.tar.gz rockbox-fa5caa0b5b2bce6ec56a99d716584405854ede76.zip |
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')
-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 4c3784a5f7..81604de08a 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; | |||
40 | unsigned char mad_main_data[MAD_BUFFER_MDLEN] IBSS_ATTR; | 40 | unsigned char mad_main_data[MAD_BUFFER_MDLEN] IBSS_ATTR; |
41 | /* TODO: what latency does layer 1 have? */ | 41 | /* TODO: what latency does layer 1 have? */ |
42 | int mpeg_latency[3] = { 0, 481, 529 }; | 42 | int mpeg_latency[3] = { 0, 481, 529 }; |
43 | int mpeg_framesize[3] = {384, 1152, 1152}; | ||
43 | 44 | ||
44 | #ifdef USE_IRAM | 45 | #ifdef USE_IRAM |
45 | extern char iramcopy[]; | 46 | extern char iramcopy[]; |
@@ -50,34 +51,13 @@ extern char iend[]; | |||
50 | #endif | 51 | #endif |
51 | 52 | ||
52 | struct codec_api *ci; | 53 | struct codec_api *ci; |
53 | int64_t samplecount; | ||
54 | int64_t samplesdone; | ||
55 | int stop_skip, start_skip; | ||
56 | int current_stereo_mode = -1; | ||
57 | unsigned long current_frequency = 0; | ||
58 | |||
59 | void recalc_samplecount(void) | ||
60 | { | ||
61 | /* NOTE: currently this doesn't work, the below calculated samples_count | ||
62 | seems to be right, but sometimes we just don't have all the data we | ||
63 | need... */ | ||
64 | if (ci->id3->frame_count) { | ||
65 | /* TODO: 1152 is the frame size in samples for MPEG1 layer 2 and layer 3, | ||
66 | it's probably not correct at all for MPEG2 and layer 1 */ | ||
67 | samplecount = ((int64_t)ci->id3->frame_count) * 1152; | ||
68 | } else { | ||
69 | samplecount = ((int64_t)ci->id3->length) * current_frequency / 1000; | ||
70 | } | ||
71 | |||
72 | samplecount -= start_skip + stop_skip; | ||
73 | } | ||
74 | 54 | ||
75 | void init_mad(void) | 55 | void init_mad(void) |
76 | { | 56 | { |
77 | ci->memset(&stream, 0, sizeof(struct mad_stream)); | 57 | ci->memset(&stream, 0, sizeof(struct mad_stream)); |
78 | ci->memset(&frame, 0, sizeof(struct mad_frame)); | 58 | ci->memset(&frame, 0, sizeof(struct mad_frame)); |
79 | ci->memset(&synth, 0, sizeof(struct mad_synth)); | 59 | ci->memset(&synth, 0, sizeof(struct mad_synth)); |
80 | 60 | ||
81 | mad_stream_init(&stream); | 61 | mad_stream_init(&stream); |
82 | mad_frame_init(&frame); | 62 | mad_frame_init(&frame); |
83 | mad_synth_init(&synth); | 63 | mad_synth_init(&synth); |
@@ -94,14 +74,14 @@ enum codec_status codec_start(struct codec_api *api) | |||
94 | int status; | 74 | int status; |
95 | size_t size; | 75 | size_t size; |
96 | int file_end; | 76 | int file_end; |
97 | int frame_skip; /* samples to skip current frame */ | ||
98 | int samples_to_skip; /* samples to skip in total for this file (at start) */ | 77 | int samples_to_skip; /* samples to skip in total for this file (at start) */ |
99 | char *inputbuffer; | 78 | char *inputbuffer; |
100 | /* If we know the position isn't exact (i.e., we have seeked to a | 79 | int64_t samplesdone; |
101 | * position that isn't the start of the file), we can't reliably do | 80 | int stop_skip, start_skip; |
102 | * end-of-file trimming for gapless playback. | 81 | int current_stereo_mode = -1; |
103 | */ | 82 | unsigned long current_frequency = 0; |
104 | bool exact_position = true; | 83 | int framelength; |
84 | int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */ | ||
105 | 85 | ||
106 | ci = api; | 86 | ci = api; |
107 | 87 | ||
@@ -120,20 +100,17 @@ enum codec_status codec_start(struct codec_api *api) | |||
120 | ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE); | 100 | ci->configure(DSP_SET_CLIP_MIN, (int *)-MAD_F_ONE); |
121 | ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1)); | 101 | ci->configure(DSP_SET_CLIP_MAX, (int *)(MAD_F_ONE - 1)); |
122 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); | 102 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*16)); |
123 | 103 | ||
124 | /** This label might need to be moved above all the init code, but I don't | 104 | next_track: |
125 | * think reiniting the codec is necessary for MPEG. It might even be unwanted | ||
126 | * for gapless playback. | ||
127 | * Reinitializing seems to be necessary to avoid playback quircks when seeking. */ | ||
128 | next_track: | ||
129 | status = CODEC_OK; | 105 | status = CODEC_OK; |
130 | 106 | ||
107 | /* Reinitializing seems to be necessary to avoid playback quircks when seeking. */ | ||
131 | init_mad(); | 108 | init_mad(); |
132 | 109 | ||
133 | file_end = 0; | 110 | file_end = 0; |
134 | while (!*ci->taginfo_ready && !ci->stop_codec) | 111 | while (!*ci->taginfo_ready && !ci->stop_codec) |
135 | ci->sleep(1); | 112 | ci->sleep(1); |
136 | 113 | ||
137 | ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency); | 114 | ci->configure(DSP_SET_FREQUENCY, (int *)ci->id3->frequency); |
138 | current_frequency = ci->id3->frequency; | 115 | current_frequency = ci->id3->frequency; |
139 | codec_set_replaygain(ci->id3); | 116 | codec_set_replaygain(ci->id3); |
@@ -151,35 +128,52 @@ enum codec_status codec_start(struct codec_api *api) | |||
151 | start_skip = mpeg_latency[ci->id3->layer]; | 128 | start_skip = mpeg_latency[ci->id3->layer]; |
152 | } | 129 | } |
153 | 130 | ||
131 | /* Libmad will not decode the last frame without 8 bytes of extra padding | ||
132 | in the buffer. So, we can trick libmad into not decoding the last frame | ||
133 | if we are to skip it entirely and then cut the appropriate samples from | ||
134 | final frame that we did decode. Note, if all tags (ID3, APE) are not | ||
135 | properly stripped from the end of the file, this trick will not work. */ | ||
136 | if (stop_skip >= mpeg_framesize[ci->id3->layer]) { | ||
137 | padding = 0; | ||
138 | stop_skip -= mpeg_framesize[ci->id3->layer]; | ||
139 | } else { | ||
140 | padding = MAD_BUFFER_GUARD; | ||
141 | } | ||
142 | |||
154 | samplesdone = ((int64_t)ci->id3->elapsed) * current_frequency / 1000; | 143 | samplesdone = ((int64_t)ci->id3->elapsed) * current_frequency / 1000; |
155 | exact_position = samplesdone == 0; | 144 | |
156 | samples_to_skip = start_skip; | 145 | /* Don't skip any samples unless we start at the beginning. */ |
157 | recalc_samplecount(); | 146 | if (samplesdone > 0) |
158 | 147 | samples_to_skip = 0; | |
148 | else | ||
149 | samples_to_skip = start_skip; | ||
150 | |||
151 | framelength = 0; | ||
152 | |||
159 | /* This is the decoding loop. */ | 153 | /* This is the decoding loop. */ |
160 | while (1) { | 154 | while (1) { |
161 | int framelength; | ||
162 | |||
163 | ci->yield(); | 155 | ci->yield(); |
164 | if (ci->stop_codec || ci->new_track) | 156 | if (ci->stop_codec || ci->new_track) |
165 | break; | 157 | break; |
166 | 158 | ||
167 | if (ci->seek_time) { | 159 | if (ci->seek_time) { |
168 | int newpos; | 160 | int newpos; |
169 | |||
170 | samplesdone = ((int64_t) (ci->seek_time - 1)) | ||
171 | * current_frequency / 1000; | ||
172 | exact_position = samplesdone == 0; | ||
173 | 161 | ||
174 | if (ci->seek_time-1 == 0) | 162 | samplesdone = ((int64_t)(ci->seek_time-1))*current_frequency/1000; |
163 | |||
164 | if (ci->seek_time-1 == 0) { | ||
175 | newpos = ci->id3->first_frame_offset; | 165 | newpos = ci->id3->first_frame_offset; |
176 | else | 166 | samples_to_skip = start_skip; |
167 | } else { | ||
177 | newpos = ci->mp3_get_filepos(ci->seek_time-1); | 168 | newpos = ci->mp3_get_filepos(ci->seek_time-1); |
169 | samples_to_skip = 0; | ||
170 | } | ||
178 | 171 | ||
179 | if (!ci->seek_buffer(newpos)) | 172 | if (!ci->seek_buffer(newpos)) |
180 | break; | 173 | break; |
181 | ci->seek_complete(); | 174 | ci->seek_complete(); |
182 | init_mad(); | 175 | init_mad(); |
176 | framelength = 0; | ||
183 | } | 177 | } |
184 | 178 | ||
185 | /* Lock buffers */ | 179 | /* Lock buffers */ |
@@ -187,18 +181,17 @@ enum codec_status codec_start(struct codec_api *api) | |||
187 | inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE); | 181 | inputbuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE); |
188 | if (size == 0 || inputbuffer == NULL) | 182 | if (size == 0 || inputbuffer == NULL) |
189 | break; | 183 | break; |
190 | /* size + MAD_BUFFER_GUARD to help mad decode the last frame */ | ||
191 | mad_stream_buffer(&stream, (unsigned char *)inputbuffer, | 184 | mad_stream_buffer(&stream, (unsigned char *)inputbuffer, |
192 | size + MAD_BUFFER_GUARD); | 185 | size + padding); |
193 | } | 186 | } |
194 | 187 | ||
195 | if (mad_frame_decode(&frame, &stream)) { | 188 | if (mad_frame_decode(&frame, &stream)) { |
196 | if (stream.error == MAD_FLAG_INCOMPLETE | 189 | if (stream.error == MAD_FLAG_INCOMPLETE |
197 | || stream.error == MAD_ERROR_BUFLEN) { | 190 | || stream.error == MAD_ERROR_BUFLEN) { |
198 | /* This makes the codec support partially corrupted files */ | 191 | /* This makes the codec support partially corrupted files */ |
199 | if (file_end == 30) | 192 | if (file_end == 30) |
200 | break; | 193 | break; |
201 | 194 | ||
202 | /* Fill the buffer */ | 195 | /* Fill the buffer */ |
203 | if (stream.next_frame) | 196 | if (stream.next_frame) |
204 | ci->advance_buffer_loc((void *)stream.next_frame); | 197 | ci->advance_buffer_loc((void *)stream.next_frame); |
@@ -216,39 +209,28 @@ enum codec_status codec_start(struct codec_api *api) | |||
216 | } | 209 | } |
217 | break; | 210 | break; |
218 | } | 211 | } |
219 | 212 | ||
220 | file_end = 0; | 213 | file_end = 0; |
221 | 214 | ||
222 | mad_synth_frame(&synth, &frame); | 215 | /* Do the pcmbuf insert here. Note, this is the PREVIOUS frame's pcm |
223 | 216 | data (not the one just decoded above). When we exit the decoding | |
224 | /* We need to skip samples_to_skip samples from the start of every file | 217 | loop we will need to process the final frame that was decoded. */ |
225 | to properly support LAME style gapless MP3 files. samples_to_skip | 218 | if (framelength > 0) { |
226 | might be larger than one frame. */ | 219 | /* In case of a mono file, the second array will be ignored. */ |
227 | if (samples_to_skip < synth.pcm.length) { | 220 | ci->pcmbuf_insert_split(&synth.pcm.samples[0][samples_to_skip], |
228 | /* skip just part of the frame */ | 221 | &synth.pcm.samples[1][samples_to_skip], |
229 | frame_skip = samples_to_skip; | 222 | framelength * 4); |
223 | |||
224 | /* Only skip samples for the first frame added. */ | ||
230 | samples_to_skip = 0; | 225 | samples_to_skip = 0; |
231 | } else { | ||
232 | /* we need to skip an entire frame */ | ||
233 | frame_skip = synth.pcm.length; | ||
234 | samples_to_skip -= synth.pcm.length; | ||
235 | } | ||
236 | |||
237 | framelength = synth.pcm.length - frame_skip; | ||
238 | |||
239 | if (exact_position && (stop_skip > 0)) { | ||
240 | int64_t max = samplecount - samplesdone; | ||
241 | |||
242 | if (max < 0) max = 0; | ||
243 | if (max < framelength) framelength = (int)max; | ||
244 | if (framelength == 0 && frame_skip == 0) break; | ||
245 | } | 226 | } |
246 | 227 | ||
228 | mad_synth_frame(&synth, &frame); | ||
229 | |||
247 | /* Check if sample rate and stereo settings changed in this frame. */ | 230 | /* Check if sample rate and stereo settings changed in this frame. */ |
248 | if (frame.header.samplerate != current_frequency) { | 231 | if (frame.header.samplerate != current_frequency) { |
249 | current_frequency = frame.header.samplerate; | 232 | current_frequency = frame.header.samplerate; |
250 | ci->configure(DSP_SWITCH_FREQUENCY, (int *)current_frequency); | 233 | ci->configure(DSP_SWITCH_FREQUENCY, (int *)current_frequency); |
251 | recalc_samplecount(); | ||
252 | } | 234 | } |
253 | if (MAD_NCHANNELS(&frame.header) == 2) { | 235 | if (MAD_NCHANNELS(&frame.header) == 2) { |
254 | if (current_stereo_mode != STEREO_NONINTERLEAVED) { | 236 | if (current_stereo_mode != STEREO_NONINTERLEAVED) { |
@@ -261,27 +243,32 @@ enum codec_status codec_start(struct codec_api *api) | |||
261 | current_stereo_mode = STEREO_MONO; | 243 | current_stereo_mode = STEREO_MONO; |
262 | } | 244 | } |
263 | } | 245 | } |
264 | 246 | ||
265 | /* Check if we can just skip the entire frame. */ | ||
266 | if (frame_skip < synth.pcm.length) { | ||
267 | /* In case of a mono file, the second array will be ignored. */ | ||
268 | ci->pcmbuf_insert_split(&synth.pcm.samples[0][frame_skip], | ||
269 | &synth.pcm.samples[1][frame_skip], | ||
270 | framelength * 4); | ||
271 | } | ||
272 | |||
273 | if (stream.next_frame) | 247 | if (stream.next_frame) |
274 | ci->advance_buffer_loc((void *)stream.next_frame); | 248 | ci->advance_buffer_loc((void *)stream.next_frame); |
275 | else | 249 | else |
276 | ci->advance_buffer(size); | 250 | ci->advance_buffer(size); |
277 | 251 | ||
252 | framelength = synth.pcm.length - samples_to_skip; | ||
253 | if (framelength < 0) { | ||
254 | framelength = 0; | ||
255 | samples_to_skip -= synth.pcm.length; | ||
256 | } | ||
257 | |||
278 | samplesdone += framelength; | 258 | samplesdone += framelength; |
279 | ci->set_elapsed(samplesdone / (current_frequency / 1000)); | 259 | ci->set_elapsed(samplesdone / (current_frequency / 1000)); |
280 | } | 260 | } |
261 | |||
262 | /* Finish the remaining decoded frame. | ||
263 | Cut the required samples from the end. */ | ||
264 | if (framelength > stop_skip) | ||
265 | ci->pcmbuf_insert_split(synth.pcm.samples[0], synth.pcm.samples[1], | ||
266 | (framelength - stop_skip) * 4); | ||
267 | |||
281 | stream.error = 0; | 268 | stream.error = 0; |
282 | 269 | ||
283 | if (ci->request_next_track()) | 270 | if (ci->request_next_track()) |
284 | goto next_track; | 271 | goto next_track; |
285 | 272 | ||
286 | return status; | 273 | return status; |
287 | } | 274 | } |