summaryrefslogtreecommitdiffstats
path: root/lib/rbcodec/codecs/mpa.c
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 /lib/rbcodec/codecs/mpa.c
parent26ffcd8f9fca77ee0983eb673e37c15738f1f592 (diff)
downloadrockbox-4e60fb77e0.tar.gz
rockbox-4e60fb77e0.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/mpa.c')
-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)
struct mp3entry *id3 = ci->id3;
if (id3->vbr) {
- /* Convert newtime and id3->length to seconds to
- * avoid overflow */
- unsigned int newtime_s = newtime/1000;
- unsigned int length_s = id3->length/1000;
-
if (id3->has_toc) {
/* Use the TOC to find the new position */
- unsigned int percent, remainder;
- int curtoc, nexttoc, plen;
-
- percent = (newtime_s*100) / length_s;
+ unsigned int percent = ((uint64_t)newtime * 100) / id3->length;
if (percent > 99)
percent = 99;
- curtoc = id3->toc[percent];
-
- if (percent < 99) {
- nexttoc = id3->toc[percent+1];
- } else {
- nexttoc = 256;
- }
+ unsigned int pct_timestep = id3->length / 100;
+ unsigned int toc_sizestep = id3->filesize / 256;
+ unsigned int cur_toc = id3->toc[percent];
+ unsigned int next_toc = percent < 99 ? id3->toc[percent+1] : 256;
+ unsigned int plength = (next_toc - cur_toc) * toc_sizestep;
- pos = (id3->filesize/256)*curtoc;
+ /* Seek to TOC mark */
+ pos = cur_toc * toc_sizestep;
- /* Use the remainder to get a more accurate position */
- remainder = (newtime_s*100) % length_s;
- remainder = (remainder*100) / length_s;
- plen = (nexttoc - curtoc)*(id3->filesize/256);
- pos += (plen/100)*remainder;
+ /* Interpolate between this TOC mark and the next TOC mark */
+ newtime -= percent * pct_timestep;
+ pos += (uint64_t)plength * newtime / pct_timestep;
} else {
/* No TOC exists, estimate the new position */
- pos = (id3->filesize / length_s) * newtime_s;
+ pos = (uint64_t)newtime * id3->filesize / id3->length;
}
} else if (id3->bitrate) {
pos = newtime * (id3->bitrate / 8);
@@ -234,43 +223,31 @@ static void set_elapsed(struct mp3entry* id3)
if ( id3->vbr ) {
if ( id3->has_toc ) {
- /* calculate elapsed time using TOC */
- int i;
- unsigned int remainder, plen, relpos, nextpos;
+ unsigned int pct_timestep = id3->length / 100;
+ unsigned int toc_sizestep = id3->filesize / 256;
- /* find wich percent we're at */
- for (i=0; i<100; i++ )
- if ( offset < id3->toc[i] * (id3->filesize / 256) )
+ int percent;
+ for (percent = 0; percent < 100; ++percent)
+ if (offset < id3->toc[percent] * toc_sizestep)
break;
+ if (percent > 0)
+ --percent;
- i--;
- if (i < 0)
- i = 0;
-
- relpos = id3->toc[i];
+ unsigned int cur_toc = id3->toc[percent];
+ unsigned int next_toc = percent < 99 ? id3->toc[percent+1] : 256;
+ unsigned int plength = (next_toc - cur_toc) * toc_sizestep;
- if (i < 99)
- nextpos = id3->toc[i+1];
- else
- nextpos = 256;
-
- remainder = offset - (relpos * (id3->filesize / 256));
-
- /* set time for this percent (divide before multiply to prevent
- overflow on long files. loss of precision is negligible on
- short files) */
- elapsed = i * (id3->length / 100);
+ /* Set elapsed time to the TOC mark */
+ elapsed = percent * pct_timestep;
- /* calculate remainder time */
- plen = (nextpos - relpos) * (id3->filesize / 256);
- elapsed += (((remainder * 100) / plen) * (id3->length / 10000));
- }
- else {
- /* no TOC exists. set a rough estimate using average bitrate */
- int tpk = id3->length /
- ((id3->filesize - id3->first_frame_offset - id3->id3v1len) /
- 1024);
- elapsed = offset / 1024 * tpk;
+ /* Interpolate between this TOC mark and the next TOC mark */
+ offset -= cur_toc * toc_sizestep;
+ elapsed += (uint64_t)pct_timestep * offset / plength;
+ } else {
+ /* No TOC, use an approximation (this'll be wildly inaccurate) */
+ uint64_t data_size = id3->filesize -
+ id3->first_frame_offset - id3->id3v1len;
+ elapsed = (uint64_t)id3->length * offset / data_size;
}
}
else