Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

codecs: alac: Improve resume accuracy and clean up rounding errors

Resume by offset was obviously inaccurate for ALAC -- it tried
to convert the offset to an elapsed time using the approximate
bitrate, which is going to be wrong for VBR files. This became
a problem since commit 26ffcd8f9f restored the ability to resume
by offset.

It turns out that m4a_seek_raw() has terrible resolution since
it can only seek to chunk boundaries, and lies about the real
sample position; basically the same issue that affected seeking
described in commit 4dd3c2b33e. Resuming by offset is still not
very accurate because of this. Prefer to resume by time first,
which is normally highly accurate (and never worse than offset)
but use the file offset if it's the only thing we have.

There were a couple time calculations still using 32-bit math,
so clean those up too to reduce issues due to rounding errors.

Change-Id: Idd3bccd67505f4e59e784d92e45ea80a273975bb

+32 -30
+32 -30
lib/rbcodec/codecs/alac.c
··· 31 31 32 32 static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR; 33 33 34 + static void set_elapsed_samples(uint32_t samplesdone) 35 + { 36 + uint32_t elapsedtime = (uint64_t)samplesdone * 1000ULL / ci->id3->frequency; 37 + ci->set_elapsed(elapsedtime); 38 + } 39 + 34 40 /* this is the codec entry point */ 35 41 enum codec_status codec_main(enum codec_entry_call_reason reason) 36 42 { ··· 50 56 demux_res_t demux_res; 51 57 stream_t input_stream; 52 58 uint32_t samplesdone; 53 - uint32_t elapsedtime; 54 59 int samplesdecoded; 55 60 unsigned int i; 56 61 unsigned char* buffer; 57 62 alac_file alac; 58 63 intptr_t param; 64 + unsigned long resume_time; 65 + uint32_t resume_offset; 66 + unsigned int did_resume; 59 67 60 68 /* Clean and initialize decoder structures */ 61 69 memset(&demux_res , 0, sizeof(demux_res)); ··· 71 79 72 80 stream_create(&input_stream,ci); 73 81 74 - /* Read resume info before calling qtmovie_read. */ 75 - elapsedtime = ci->id3->elapsed; 76 - samplesdone = ci->id3->offset; 77 - 82 + /* Save resume info because qtmovie_read() can modify it. */ 83 + resume_time = ci->id3->elapsed; 84 + resume_offset = ci->id3->offset; 85 + 78 86 /* if qtmovie_read returns successfully, the stream is up to 79 87 * the movie data, which can be used directly by the decoder */ 80 88 if (!qtmovie_read(&input_stream, &demux_res)) { ··· 84 92 85 93 /* initialise the sound converter */ 86 94 alac_set_info(&alac, demux_res.codecdata); 87 - 88 - /* Set i for first frame, seek to desired sample position for resuming. */ 89 - i=0; 90 95 91 - if (elapsedtime || samplesdone) { 92 - if (samplesdone) { 93 - samplesdone = 94 - (uint32_t)((uint64_t)samplesdone*ci->id3->frequency / 95 - (ci->id3->bitrate*128)); 96 - } 97 - else { 98 - samplesdone = (elapsedtime/10) * (ci->id3->frequency/100); 99 - } 96 + if (resume_time) 97 + did_resume = m4a_seek(&demux_res, &input_stream, 98 + (uint64_t)resume_time * ci->id3->frequency / 1000ULL, 99 + &samplesdone, (int *) &i); 100 + else if (resume_offset) 101 + did_resume = m4a_seek_raw(&demux_res, &input_stream, resume_offset, 102 + &samplesdone, (int *) &i); 103 + else 104 + did_resume = 0; 100 105 101 - if (!m4a_seek(&demux_res, &input_stream, samplesdone, 102 - &samplesdone, (int*) &i)) { 103 - samplesdone = 0; 104 - } 106 + /* Start from the beginning if we did not resume. */ 107 + if (!did_resume) { 108 + i = 0; 109 + samplesdone = 0; 105 110 } 106 111 107 - elapsedtime = samplesdone * 1000LL / ci->id3->frequency; 108 - ci->set_elapsed(elapsedtime); 112 + set_elapsed_samples(samplesdone); 109 113 110 114 /* The main decoding loop */ 111 115 while (i < demux_res.num_sample_byte_sizes) { ··· 117 121 /* Deal with any pending seek requests */ 118 122 if (action == CODEC_ACTION_SEEK_TIME) { 119 123 if (m4a_seek(&demux_res, &input_stream, 120 - (param/10) * (ci->id3->frequency/100), 121 - &samplesdone, (int *)&i)) { 122 - elapsedtime=samplesdone*1000LL/ci->id3->frequency; 123 - } 124 - ci->set_elapsed(elapsedtime); 124 + (uint64_t)param * ci->id3->frequency / 1000ULL, 125 + &samplesdone, (int *) &i)) 126 + set_elapsed_samples(samplesdone); 127 + 125 128 ci->seek_complete(); 126 129 } 127 130 ··· 140 143 141 144 /* Update the elapsed-time indicator */ 142 145 samplesdone+=samplesdecoded; 143 - elapsedtime=samplesdone*1000LL/ci->id3->frequency; 144 - ci->set_elapsed(elapsedtime); 146 + set_elapsed_samples(samplesdone); 145 147 146 148 i++; 147 149 }