summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-10-31 11:47:37 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-11-01 09:52:34 -0400
commit4e60fb77e07fa417efebb58d673fb0b2156f55db (patch)
tree4e09cf70681a6de29b8218385e82be44dfdcdb3e
parent26ffcd8f9fca77ee0983eb673e37c15738f1f592 (diff)
downloadrockbox-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
-rw-r--r--lib/rbcodec/codecs/mpa.c87
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