blob: 0506c0ca494723b1830ac71caf4ee667dd91d3f2 [file] [log] [blame]
Dave Chapman52027422007-06-05 16:58:29 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Dave Chapman
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "codeclib.h"
21#define ROCKBOX
22#include <codecs/demac/libdemac/demac.h>
23
24CODEC_HEADER
25
26#define BLOCKS_PER_LOOP 4608
27#define MAX_CHANNELS 2
28#define MAX_BYTESPERSAMPLE 3
29
Dave Chapman7b1d90a2007-06-08 22:35:26 +000030/* Monkey's Audio files have one seekpoint per frame. The framesize
31 varies between 73728 and 1179648 samples.
32
33 At the smallest framesize, 30000 frames would be 50155 seconds of
34 audio - almost 14 hours. This should be enough for any file a user
35 would want to play in Rockbox, given the 2GB FAT filesize (and 4GB
36 seektable entry size) limit.
37
38 This means the seektable is 120000 bytes, but we have a lot of
39 spare room in the codec buffer - the APE codec itself is small.
40*/
41
42#define MAX_SEEKPOINTS 30000
43static uint32_t seektablebuf[MAX_SEEKPOINTS];
44
Dave Chapman52027422007-06-05 16:58:29 +000045#define INPUT_CHUNKSIZE (32*1024)
46
47/* 4608*4 = 18432 bytes per channel */
48static int32_t decoded0[BLOCKS_PER_LOOP] IBSS_ATTR;
49static int32_t decoded1[BLOCKS_PER_LOOP] IBSS_ATTR;
50
51#define MAX_SUPPORTED_SEEKTABLE_SIZE 5000
52
Dave Chapman7b1d90a2007-06-08 22:35:26 +000053
54/* Given an ape_ctx and a sample to seek to, return the file position
55 to the frame containing that sample, and the number of samples to
56 skip in that frame.
57*/
58
59bool ape_calc_seekpos(struct ape_ctx_t* ape_ctx,
60 uint32_t new_sample,
61 uint32_t* newframe,
62 uint32_t* filepos,
63 uint32_t* samplestoskip)
64{
65 uint32_t n;
66
67 n = new_sample / ape_ctx->blocksperframe;
68 if (n >= ape_ctx->numseekpoints)
69 {
70 /* We don't have a seekpoint for that frame */
71 return false;
72 }
73
74 *newframe = n;
75 *filepos = ape_ctx->seektable[n];
76 *samplestoskip = new_sample - (n * ape_ctx->blocksperframe);
77
78 return true;
79}
80
81/* The resume offset is a value in bytes - we need to
82 turn it into a frame number and samplestoskip value */
83
84void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset,
85 uint32_t* currentframe, uint32_t* samplesdone,
86 uint32_t* samplestoskip, int* firstbyte)
87{
88 off_t newfilepos;
89 int64_t framesize;
90 int64_t offset;
91
92 *currentframe = 0;
93 *samplesdone = 0;
94 *samplestoskip = 0;
95
96 while ((*currentframe < ape_ctx->totalframes) &&
97 (*currentframe < ape_ctx->numseekpoints) &&
98 (resume_offset > ape_ctx->seektable[*currentframe]))
99 {
100 ++*currentframe;
101 *samplesdone += ape_ctx->blocksperframe;
102 }
103
104 if ((*currentframe > 0) &&
105 (ape_ctx->seektable[*currentframe] > resume_offset)) {
106 --*currentframe;
107 *samplesdone -= ape_ctx->blocksperframe;
108 }
109
110 newfilepos = ape_ctx->seektable[*currentframe];
111
112 /* APE's bytestream is weird... */
113 *firstbyte = 3 - (newfilepos & 3);
114 newfilepos &= ~3;
115
116 ci->seek_buffer(newfilepos);
117
118 /* We estimate where we were in the current frame, based on the
119 byte offset */
120 if (*currentframe < (ape_ctx->totalframes - 1)) {
121 framesize = ape_ctx->seektable[*currentframe+1] - ape_ctx->seektable[*currentframe];
122 offset = resume_offset - ape_ctx->seektable[*currentframe];
123
124 *samplestoskip = (offset * ape_ctx->blocksperframe) / framesize;
125 }
126}
127
Dave Chapman52027422007-06-05 16:58:29 +0000128/* this is the codec entry point */
129enum codec_status codec_main(void)
130{
131 struct ape_ctx_t ape_ctx;
132 uint32_t samplesdone;
133 uint32_t elapsedtime;
134 size_t bytesleft;
135 int retval;
136
137 uint32_t currentframe;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000138 uint32_t newfilepos;
139 uint32_t samplestoskip;
Dave Chapman52027422007-06-05 16:58:29 +0000140 int nblocks;
141 int bytesconsumed;
142 unsigned char* inbuffer;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000143 uint32_t blockstodecode;
Dave Chapman52027422007-06-05 16:58:29 +0000144 int res;
145 int firstbyte;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000146 size_t resume_offset;
Dave Chapman52027422007-06-05 16:58:29 +0000147
148 /* Generic codec initialisation */
149 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
150 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, 1024*128);
151
152 ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1);
153
154 next_track:
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000155
156 retval = CODEC_OK;
157
158 /* Remember the resume position - when the codec is opened, the
159 playback engine will reset it. */
160 resume_offset = ci->id3->offset;
Dave Chapman52027422007-06-05 16:58:29 +0000161
162 if (codec_init()) {
163 LOGF("APE: Error initialising codec\n");
164 retval = CODEC_ERROR;
165 goto exit;
166 }
167
168 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
169
170 /* Read the file headers to populate the ape_ctx struct */
171 if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) {
172 LOGF("APE: Error reading header\n");
173 retval = CODEC_ERROR;
174 goto exit;
175 }
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000176
177 /* Initialise the seektable for this file */
178 ape_ctx.seektable = seektablebuf;
179 ape_ctx.numseekpoints = MIN(MAX_SEEKPOINTS,ape_ctx.numseekpoints);
180
181 ci->advance_buffer(ape_ctx.seektablefilepos);
182
183 /* The seektable may be bigger than the guard buffer (32KB), so we
184 do a read() */
185 ci->read_filebuf(ape_ctx.seektable, ape_ctx.numseekpoints * sizeof(uint32_t));
186
187#ifdef ROCKBOX_BIG_ENDIAN
188 /* Byte-swap the little-endian seekpoints */
189 {
190 uint32_t i;
191
192 for(i = 0; i < ape_ctx.numseekpoints; i++)
193 ape_ctx.seektable[i] = swap32(ape_ctx.seektable[i]);
194 }
195#endif
196
197 /* Now advance the file position to the first frame */
198 ci->advance_buffer(ape_ctx.firstframe -
199 (ape_ctx.seektablefilepos +
200 ape_ctx.numseekpoints * sizeof(uint32_t)));
Dave Chapman52027422007-06-05 16:58:29 +0000201
202 while (!*ci->taginfo_ready && !ci->stop_codec)
203 ci->sleep(1);
204
205 ci->configure(DSP_SWITCH_FREQUENCY, ape_ctx.samplerate);
206 ci->configure(DSP_SET_STEREO_MODE, ape_ctx.channels == 1 ?
207 STEREO_MONO : STEREO_NONINTERLEAVED);
208 codec_set_replaygain(ci->id3);
209
210 /* The main decoding loop */
211
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000212 if (resume_offset) {
213 /* The resume offset is a value in bytes - we need to
214 turn it into a frame number and samplestoskip value */
215
216 ape_resume(&ape_ctx, resume_offset,
217 &currentframe, &samplesdone, &samplestoskip, &firstbyte);
218 } else {
219 currentframe = 0;
220 samplesdone = 0;
221 samplestoskip = 0;
222 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */
223 }
Dave Chapman52027422007-06-05 16:58:29 +0000224
225 /* Initialise the buffer */
226 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
Dave Chapman52027422007-06-05 16:58:29 +0000227
228 /* The main decoding loop - we decode the frames a small chunk at a time */
229 while (currentframe < ape_ctx.totalframes)
230 {
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000231frame_start:
Dave Chapman52027422007-06-05 16:58:29 +0000232 /* Calculate how many blocks there are in this frame */
233 if (currentframe == (ape_ctx.totalframes - 1))
234 nblocks = ape_ctx.finalframeblocks;
235 else
236 nblocks = ape_ctx.blocksperframe;
237
238 ape_ctx.currentframeblocks = nblocks;
239
240 /* Initialise the frame decoder */
241 init_frame_decoder(&ape_ctx, inbuffer, &firstbyte, &bytesconsumed);
242
243 ci->advance_buffer(bytesconsumed);
244 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
245
246 /* Decode the frame a chunk at a time */
247 while (nblocks > 0)
248 {
249 ci->yield();
250 if (ci->stop_codec || ci->new_track) {
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000251 goto done;
252 }
253
254 /* Deal with any pending seek requests */
255 if (ci->seek_time)
256 {
257 if (ape_calc_seekpos(&ape_ctx,
258 ((ci->seek_time-1)/10) * (ci->id3->frequency/100),
259 &currentframe,
260 &newfilepos,
261 &samplestoskip))
262 {
263 samplesdone = currentframe * ape_ctx.blocksperframe;
264
265 /* APE's bytestream is weird... */
266 firstbyte = 3 - (newfilepos & 3);
267 newfilepos &= ~3;
268
269 ci->seek_buffer(newfilepos);
270 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
271
272 ci->seek_complete();
273 goto frame_start; /* Sorry... */
274 }
275 ci->seek_complete();
Dave Chapman52027422007-06-05 16:58:29 +0000276 }
277
278 blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks);
279
280 if ((res = decode_chunk(&ape_ctx, inbuffer, &firstbyte,
281 &bytesconsumed,
282 decoded0, decoded1,
283 blockstodecode)) < 0)
284 {
285 /* Frame decoding error, abort */
286 LOGF("APE: Frame %d, error %d\n",currentframe,res);
287 retval = CODEC_ERROR;
288 goto done;
289 }
290
291 ci->yield();
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000292
293 if (samplestoskip > 0) {
294 if (samplestoskip < blockstodecode) {
295 ci->pcmbuf_insert(decoded0 + samplestoskip,
296 decoded1 + samplestoskip,
297 blockstodecode - samplestoskip);
298 samplestoskip = 0;
299 } else {
300 samplestoskip -= blockstodecode;
301 }
302 } else {
303 ci->pcmbuf_insert(decoded0, decoded1, blockstodecode);
304 }
Dave Chapman52027422007-06-05 16:58:29 +0000305
Dave Chapman52027422007-06-05 16:58:29 +0000306 samplesdone += blockstodecode;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000307
308 if (!samplestoskip) {
309 /* Update the elapsed-time indicator */
310 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
311 ci->set_elapsed(elapsedtime);
312 }
Dave Chapman52027422007-06-05 16:58:29 +0000313
314 ci->advance_buffer(bytesconsumed);
315 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
316
317 /* Decrement the block count */
318 nblocks -= blockstodecode;
319 }
320
321 currentframe++;
322 }
323
324 retval = CODEC_OK;
325
326done:
327 LOGF("APE: Decoded %ld samples\n",samplesdone);
328
329 if (ci->request_next_track())
330 goto next_track;
331
332exit:
333 return retval;
334}