diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2022-10-31 11:47:37 +0000 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2022-11-01 09:52:34 -0400 |
commit | 4e60fb77e07fa417efebb58d673fb0b2156f55db (patch) | |
tree | 4e09cf70681a6de29b8218385e82be44dfdcdb3e /lib/rbcodec/codecs | |
parent | 26ffcd8f9fca77ee0983eb673e37c15738f1f592 (diff) | |
download | rockbox-4e60fb77e07fa417efebb58d673fb0b2156f55db.tar.gz rockbox-4e60fb77e07fa417efebb58d673fb0b2156f55db.zip |
codecs: mpa: Improve seek & resume accuracy for VBR files
The codec used 32-bit math for elapsed time <-> file position
calculations. The rounding errors seem to be the cause of poor
seek/resume accuracy on long VBR files; switching to 64-bit math
makes things much better.
Change-Id: Iba638d9e031a891022510c31c141cc4541e3f149
Diffstat (limited to 'lib/rbcodec/codecs')
-rw-r--r-- | lib/rbcodec/codecs/mpa.c | 87 |
1 files changed, 32 insertions, 55 deletions
diff --git a/lib/rbcodec/codecs/mpa.c b/lib/rbcodec/codecs/mpa.c index d374a5229a..bafed3970c 100644 --- a/lib/rbcodec/codecs/mpa.c +++ b/lib/rbcodec/codecs/mpa.c | |||
@@ -176,38 +176,27 @@ static int get_file_pos(int newtime) | |||
176 | struct mp3entry *id3 = ci->id3; | 176 | struct mp3entry *id3 = ci->id3; |
177 | 177 | ||
178 | if (id3->vbr) { | 178 | if (id3->vbr) { |
179 | /* Convert newtime and id3->length to seconds to | ||
180 | * avoid overflow */ | ||
181 | unsigned int newtime_s = newtime/1000; | ||
182 | unsigned int length_s = id3->length/1000; | ||
183 | |||
184 | if (id3->has_toc) { | 179 | if (id3->has_toc) { |
185 | /* Use the TOC to find the new position */ | 180 | /* Use the TOC to find the new position */ |
186 | unsigned int percent, remainder; | 181 | unsigned int percent = ((uint64_t)newtime * 100) / id3->length; |
187 | int curtoc, nexttoc, plen; | ||
188 | |||
189 | percent = (newtime_s*100) / length_s; | ||
190 | if (percent > 99) | 182 | if (percent > 99) |
191 | percent = 99; | 183 | percent = 99; |
192 | 184 | ||
193 | curtoc = id3->toc[percent]; | 185 | unsigned int pct_timestep = id3->length / 100; |
194 | 186 | unsigned int toc_sizestep = id3->filesize / 256; | |
195 | if (percent < 99) { | 187 | unsigned int cur_toc = id3->toc[percent]; |
196 | nexttoc = id3->toc[percent+1]; | 188 | unsigned int next_toc = percent < 99 ? id3->toc[percent+1] : 256; |
197 | } else { | 189 | unsigned int plength = (next_toc - cur_toc) * toc_sizestep; |
198 | nexttoc = 256; | ||
199 | } | ||
200 | 190 | ||
201 | pos = (id3->filesize/256)*curtoc; | 191 | /* Seek to TOC mark */ |
192 | pos = cur_toc * toc_sizestep; | ||
202 | 193 | ||
203 | /* Use the remainder to get a more accurate position */ | 194 | /* Interpolate between this TOC mark and the next TOC mark */ |
204 | remainder = (newtime_s*100) % length_s; | 195 | newtime -= percent * pct_timestep; |
205 | remainder = (remainder*100) / length_s; | 196 | pos += (uint64_t)plength * newtime / pct_timestep; |
206 | plen = (nexttoc - curtoc)*(id3->filesize/256); | ||
207 | pos += (plen/100)*remainder; | ||
208 | } else { | 197 | } else { |
209 | /* No TOC exists, estimate the new position */ | 198 | /* No TOC exists, estimate the new position */ |
210 | pos = (id3->filesize / length_s) * newtime_s; | 199 | pos = (uint64_t)newtime * id3->filesize / id3->length; |
211 | } | 200 | } |
212 | } else if (id3->bitrate) { | 201 | } else if (id3->bitrate) { |
213 | pos = newtime * (id3->bitrate / 8); | 202 | pos = newtime * (id3->bitrate / 8); |
@@ -234,43 +223,31 @@ static void set_elapsed(struct mp3entry* id3) | |||
234 | 223 | ||
235 | if ( id3->vbr ) { | 224 | if ( id3->vbr ) { |
236 | if ( id3->has_toc ) { | 225 | if ( id3->has_toc ) { |
237 | /* calculate elapsed time using TOC */ | 226 | unsigned int pct_timestep = id3->length / 100; |
238 | int i; | 227 | unsigned int toc_sizestep = id3->filesize / 256; |
239 | unsigned int remainder, plen, relpos, nextpos; | ||
240 | 228 | ||
241 | /* find wich percent we're at */ | 229 | int percent; |
242 | for (i=0; i<100; i++ ) | 230 | for (percent = 0; percent < 100; ++percent) |
243 | if ( offset < id3->toc[i] * (id3->filesize / 256) ) | 231 | if (offset < id3->toc[percent] * toc_sizestep) |
244 | break; | 232 | break; |
233 | if (percent > 0) | ||
234 | --percent; | ||
245 | 235 | ||
246 | i--; | 236 | unsigned int cur_toc = id3->toc[percent]; |
247 | if (i < 0) | 237 | unsigned int next_toc = percent < 99 ? id3->toc[percent+1] : 256; |
248 | i = 0; | 238 | unsigned int plength = (next_toc - cur_toc) * toc_sizestep; |
249 | |||
250 | relpos = id3->toc[i]; | ||
251 | 239 | ||
252 | if (i < 99) | 240 | /* Set elapsed time to the TOC mark */ |
253 | nextpos = id3->toc[i+1]; | 241 | elapsed = percent * pct_timestep; |
254 | else | ||
255 | nextpos = 256; | ||
256 | |||
257 | remainder = offset - (relpos * (id3->filesize / 256)); | ||
258 | |||
259 | /* set time for this percent (divide before multiply to prevent | ||
260 | overflow on long files. loss of precision is negligible on | ||
261 | short files) */ | ||
262 | elapsed = i * (id3->length / 100); | ||
263 | 242 | ||
264 | /* calculate remainder time */ | 243 | /* Interpolate between this TOC mark and the next TOC mark */ |
265 | plen = (nextpos - relpos) * (id3->filesize / 256); | 244 | offset -= cur_toc * toc_sizestep; |
266 | elapsed += (((remainder * 100) / plen) * (id3->length / 10000)); | 245 | elapsed += (uint64_t)pct_timestep * offset / plength; |
267 | } | 246 | } else { |
268 | else { | 247 | /* No TOC, use an approximation (this'll be wildly inaccurate) */ |
269 | /* no TOC exists. set a rough estimate using average bitrate */ | 248 | uint64_t data_size = id3->filesize - |
270 | int tpk = id3->length / | 249 | id3->first_frame_offset - id3->id3v1len; |
271 | ((id3->filesize - id3->first_frame_offset - id3->id3v1len) / | 250 | elapsed = (uint64_t)id3->length * offset / data_size; |
272 | 1024); | ||
273 | elapsed = offset / 1024 * tpk; | ||
274 | } | 251 | } |
275 | } | 252 | } |
276 | else | 253 | else |