summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2022-11-13 10:59:26 +0000
committerAidan MacDonald <amachronic@protonmail.com>2022-11-16 06:48:28 -0500
commit2f278af7606039857bb92d659c811403f27ad463 (patch)
treeb71c80ec22cb9373f7f6cb2b6a5a8a9b5ccddf5c
parentea61347a0b10154f0b346ae8de013b13cd19dd8a (diff)
downloadrockbox-2f278af7606039857bb92d659c811403f27ad463.tar.gz
rockbox-2f278af7606039857bb92d659c811403f27ad463.zip
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
-rw-r--r--lib/rbcodec/codecs/alac.c64
1 files changed, 33 insertions, 31 deletions
diff --git a/lib/rbcodec/codecs/alac.c b/lib/rbcodec/codecs/alac.c
index 903ea2850e..052a44cd70 100644
--- a/lib/rbcodec/codecs/alac.c
+++ b/lib/rbcodec/codecs/alac.c
@@ -31,6 +31,12 @@ CODEC_HEADER
31 31
32static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR; 32static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR;
33 33
34static 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/* this is the codec entry point */ 40/* this is the codec entry point */
35enum codec_status codec_main(enum codec_entry_call_reason reason) 41enum codec_status codec_main(enum codec_entry_call_reason reason)
36{ 42{
@@ -50,12 +56,14 @@ enum codec_status codec_run(void)
50 demux_res_t demux_res; 56 demux_res_t demux_res;
51 stream_t input_stream; 57 stream_t input_stream;
52 uint32_t samplesdone; 58 uint32_t samplesdone;
53 uint32_t elapsedtime;
54 int samplesdecoded; 59 int samplesdecoded;
55 unsigned int i; 60 unsigned int i;
56 unsigned char* buffer; 61 unsigned char* buffer;
57 alac_file alac; 62 alac_file alac;
58 intptr_t param; 63 intptr_t param;
64 unsigned long resume_time;
65 uint32_t resume_offset;
66 unsigned int did_resume;
59 67
60 /* Clean and initialize decoder structures */ 68 /* Clean and initialize decoder structures */
61 memset(&demux_res , 0, sizeof(demux_res)); 69 memset(&demux_res , 0, sizeof(demux_res));
@@ -71,10 +79,10 @@ enum codec_status codec_run(void)
71 79
72 stream_create(&input_stream,ci); 80 stream_create(&input_stream,ci);
73 81
74 /* Read resume info before calling qtmovie_read. */ 82 /* Save resume info because qtmovie_read() can modify it. */
75 elapsedtime = ci->id3->elapsed; 83 resume_time = ci->id3->elapsed;
76 samplesdone = ci->id3->offset; 84 resume_offset = ci->id3->offset;
77 85
78 /* if qtmovie_read returns successfully, the stream is up to 86 /* if qtmovie_read returns successfully, the stream is up to
79 * the movie data, which can be used directly by the decoder */ 87 * the movie data, which can be used directly by the decoder */
80 if (!qtmovie_read(&input_stream, &demux_res)) { 88 if (!qtmovie_read(&input_stream, &demux_res)) {
@@ -84,28 +92,24 @@ enum codec_status codec_run(void)
84 92
85 /* initialise the sound converter */ 93 /* initialise the sound converter */
86 alac_set_info(&alac, demux_res.codecdata); 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
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 }
100 95
101 if (!m4a_seek(&demux_res, &input_stream, samplesdone, 96 if (resume_time)
102 &samplesdone, (int*) &i)) { 97 did_resume = m4a_seek(&demux_res, &input_stream,
103 samplesdone = 0; 98 (uint64_t)resume_time * ci->id3->frequency / 1000ULL,
104 } 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;
105
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; 112 set_elapsed_samples(samplesdone);
108 ci->set_elapsed(elapsedtime);
109 113
110 /* The main decoding loop */ 114 /* The main decoding loop */
111 while (i < demux_res.num_sample_byte_sizes) { 115 while (i < demux_res.num_sample_byte_sizes) {
@@ -117,11 +121,10 @@ enum codec_status codec_run(void)
117 /* Deal with any pending seek requests */ 121 /* Deal with any pending seek requests */
118 if (action == CODEC_ACTION_SEEK_TIME) { 122 if (action == CODEC_ACTION_SEEK_TIME) {
119 if (m4a_seek(&demux_res, &input_stream, 123 if (m4a_seek(&demux_res, &input_stream,
120 (param/10) * (ci->id3->frequency/100), 124 (uint64_t)param * ci->id3->frequency / 1000ULL,
121 &samplesdone, (int *)&i)) { 125 &samplesdone, (int *) &i))
122 elapsedtime=samplesdone*1000LL/ci->id3->frequency; 126 set_elapsed_samples(samplesdone);
123 } 127
124 ci->set_elapsed(elapsedtime);
125 ci->seek_complete(); 128 ci->seek_complete();
126 } 129 }
127 130
@@ -140,8 +143,7 @@ enum codec_status codec_run(void)
140 143
141 /* Update the elapsed-time indicator */ 144 /* Update the elapsed-time indicator */
142 samplesdone+=samplesdecoded; 145 samplesdone+=samplesdecoded;
143 elapsedtime=samplesdone*1000LL/ci->id3->frequency; 146 set_elapsed_samples(samplesdone);
144 ci->set_elapsed(elapsedtime);
145 147
146 i++; 148 i++;
147 } 149 }