blob: cbbed6cdd68553b3f195abde58ce32764d58eb62 [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 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
Dave Chapman52027422007-06-05 16:58:29 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "codeclib.h"
23#define ROCKBOX
24#include <codecs/demac/libdemac/demac.h>
25
26CODEC_HEADER
27
28#define BLOCKS_PER_LOOP 4608
29#define MAX_CHANNELS 2
30#define MAX_BYTESPERSAMPLE 3
31
Dave Chapman7b1d90a2007-06-08 22:35:26 +000032/* Monkey's Audio files have one seekpoint per frame. The framesize
33 varies between 73728 and 1179648 samples.
34
35 At the smallest framesize, 30000 frames would be 50155 seconds of
36 audio - almost 14 hours. This should be enough for any file a user
37 would want to play in Rockbox, given the 2GB FAT filesize (and 4GB
38 seektable entry size) limit.
39
40 This means the seektable is 120000 bytes, but we have a lot of
41 spare room in the codec buffer - the APE codec itself is small.
42*/
43
44#define MAX_SEEKPOINTS 30000
45static uint32_t seektablebuf[MAX_SEEKPOINTS];
46
Dave Chapman52027422007-06-05 16:58:29 +000047#define INPUT_CHUNKSIZE (32*1024)
48
49/* 4608*4 = 18432 bytes per channel */
50static int32_t decoded0[BLOCKS_PER_LOOP] IBSS_ATTR;
51static int32_t decoded1[BLOCKS_PER_LOOP] IBSS_ATTR;
52
53#define MAX_SUPPORTED_SEEKTABLE_SIZE 5000
54
Dave Chapman7b1d90a2007-06-08 22:35:26 +000055
56/* Given an ape_ctx and a sample to seek to, return the file position
57 to the frame containing that sample, and the number of samples to
58 skip in that frame.
59*/
60
61bool ape_calc_seekpos(struct ape_ctx_t* ape_ctx,
62 uint32_t new_sample,
63 uint32_t* newframe,
64 uint32_t* filepos,
65 uint32_t* samplestoskip)
66{
67 uint32_t n;
68
69 n = new_sample / ape_ctx->blocksperframe;
70 if (n >= ape_ctx->numseekpoints)
71 {
72 /* We don't have a seekpoint for that frame */
73 return false;
74 }
75
76 *newframe = n;
77 *filepos = ape_ctx->seektable[n];
78 *samplestoskip = new_sample - (n * ape_ctx->blocksperframe);
79
80 return true;
81}
82
83/* The resume offset is a value in bytes - we need to
84 turn it into a frame number and samplestoskip value */
85
86void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset,
87 uint32_t* currentframe, uint32_t* samplesdone,
88 uint32_t* samplestoskip, int* firstbyte)
89{
90 off_t newfilepos;
91 int64_t framesize;
92 int64_t offset;
93
94 *currentframe = 0;
95 *samplesdone = 0;
96 *samplestoskip = 0;
97
98 while ((*currentframe < ape_ctx->totalframes) &&
99 (*currentframe < ape_ctx->numseekpoints) &&
100 (resume_offset > ape_ctx->seektable[*currentframe]))
101 {
102 ++*currentframe;
103 *samplesdone += ape_ctx->blocksperframe;
104 }
105
106 if ((*currentframe > 0) &&
107 (ape_ctx->seektable[*currentframe] > resume_offset)) {
108 --*currentframe;
109 *samplesdone -= ape_ctx->blocksperframe;
110 }
111
112 newfilepos = ape_ctx->seektable[*currentframe];
113
114 /* APE's bytestream is weird... */
115 *firstbyte = 3 - (newfilepos & 3);
116 newfilepos &= ~3;
117
118 ci->seek_buffer(newfilepos);
119
120 /* We estimate where we were in the current frame, based on the
121 byte offset */
122 if (*currentframe < (ape_ctx->totalframes - 1)) {
123 framesize = ape_ctx->seektable[*currentframe+1] - ape_ctx->seektable[*currentframe];
124 offset = resume_offset - ape_ctx->seektable[*currentframe];
125
126 *samplestoskip = (offset * ape_ctx->blocksperframe) / framesize;
127 }
128}
129
Dave Chapman52027422007-06-05 16:58:29 +0000130/* this is the codec entry point */
131enum codec_status codec_main(void)
132{
133 struct ape_ctx_t ape_ctx;
134 uint32_t samplesdone;
135 uint32_t elapsedtime;
136 size_t bytesleft;
137 int retval;
138
139 uint32_t currentframe;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000140 uint32_t newfilepos;
141 uint32_t samplestoskip;
Dave Chapman52027422007-06-05 16:58:29 +0000142 int nblocks;
143 int bytesconsumed;
144 unsigned char* inbuffer;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000145 uint32_t blockstodecode;
Dave Chapman52027422007-06-05 16:58:29 +0000146 int res;
147 int firstbyte;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000148 size_t resume_offset;
Dave Chapman52027422007-06-05 16:58:29 +0000149
150 /* Generic codec initialisation */
151 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
Dave Chapman52027422007-06-05 16:58:29 +0000152
153 ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1);
154
155 next_track:
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000156
157 retval = CODEC_OK;
158
159 /* Remember the resume position - when the codec is opened, the
160 playback engine will reset it. */
161 resume_offset = ci->id3->offset;
Dave Chapman52027422007-06-05 16:58:29 +0000162
163 if (codec_init()) {
164 LOGF("APE: Error initialising codec\n");
165 retval = CODEC_ERROR;
166 goto exit;
167 }
168
Magnus Holmgren87842ca2008-02-24 19:12:15 +0000169 while (!*ci->taginfo_ready && !ci->stop_codec)
170 ci->sleep(1);
171
Dave Chapman52027422007-06-05 16:58:29 +0000172 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
173
174 /* Read the file headers to populate the ape_ctx struct */
175 if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) {
176 LOGF("APE: Error reading header\n");
177 retval = CODEC_ERROR;
178 goto exit;
179 }
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000180
181 /* Initialise the seektable for this file */
182 ape_ctx.seektable = seektablebuf;
183 ape_ctx.numseekpoints = MIN(MAX_SEEKPOINTS,ape_ctx.numseekpoints);
184
185 ci->advance_buffer(ape_ctx.seektablefilepos);
186
187 /* The seektable may be bigger than the guard buffer (32KB), so we
188 do a read() */
189 ci->read_filebuf(ape_ctx.seektable, ape_ctx.numseekpoints * sizeof(uint32_t));
190
191#ifdef ROCKBOX_BIG_ENDIAN
192 /* Byte-swap the little-endian seekpoints */
193 {
194 uint32_t i;
195
196 for(i = 0; i < ape_ctx.numseekpoints; i++)
197 ape_ctx.seektable[i] = swap32(ape_ctx.seektable[i]);
198 }
199#endif
200
201 /* Now advance the file position to the first frame */
202 ci->advance_buffer(ape_ctx.firstframe -
203 (ape_ctx.seektablefilepos +
204 ape_ctx.numseekpoints * sizeof(uint32_t)));
Dave Chapman52027422007-06-05 16:58:29 +0000205
Dave Chapman52027422007-06-05 16:58:29 +0000206 ci->configure(DSP_SWITCH_FREQUENCY, ape_ctx.samplerate);
207 ci->configure(DSP_SET_STEREO_MODE, ape_ctx.channels == 1 ?
208 STEREO_MONO : STEREO_NONINTERLEAVED);
209 codec_set_replaygain(ci->id3);
210
211 /* The main decoding loop */
212
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000213 if (resume_offset) {
214 /* The resume offset is a value in bytes - we need to
215 turn it into a frame number and samplestoskip value */
216
217 ape_resume(&ape_ctx, resume_offset,
218 &currentframe, &samplesdone, &samplestoskip, &firstbyte);
219 } else {
220 currentframe = 0;
221 samplesdone = 0;
222 samplestoskip = 0;
223 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */
224 }
Dave Chapman52027422007-06-05 16:58:29 +0000225
226 /* Initialise the buffer */
227 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
Dave Chapman52027422007-06-05 16:58:29 +0000228
229 /* The main decoding loop - we decode the frames a small chunk at a time */
230 while (currentframe < ape_ctx.totalframes)
231 {
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000232frame_start:
Dave Chapman52027422007-06-05 16:58:29 +0000233 /* Calculate how many blocks there are in this frame */
234 if (currentframe == (ape_ctx.totalframes - 1))
235 nblocks = ape_ctx.finalframeblocks;
236 else
237 nblocks = ape_ctx.blocksperframe;
238
239 ape_ctx.currentframeblocks = nblocks;
240
241 /* Initialise the frame decoder */
242 init_frame_decoder(&ape_ctx, inbuffer, &firstbyte, &bytesconsumed);
243
244 ci->advance_buffer(bytesconsumed);
245 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
246
247 /* Decode the frame a chunk at a time */
248 while (nblocks > 0)
249 {
250 ci->yield();
251 if (ci->stop_codec || ci->new_track) {
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000252 goto done;
253 }
254
255 /* Deal with any pending seek requests */
256 if (ci->seek_time)
257 {
258 if (ape_calc_seekpos(&ape_ctx,
259 ((ci->seek_time-1)/10) * (ci->id3->frequency/100),
260 &currentframe,
261 &newfilepos,
262 &samplestoskip))
263 {
264 samplesdone = currentframe * ape_ctx.blocksperframe;
265
266 /* APE's bytestream is weird... */
267 firstbyte = 3 - (newfilepos & 3);
268 newfilepos &= ~3;
269
270 ci->seek_buffer(newfilepos);
271 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
272
273 ci->seek_complete();
274 goto frame_start; /* Sorry... */
275 }
276 ci->seek_complete();
Dave Chapman52027422007-06-05 16:58:29 +0000277 }
278
279 blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks);
280
281 if ((res = decode_chunk(&ape_ctx, inbuffer, &firstbyte,
282 &bytesconsumed,
283 decoded0, decoded1,
284 blockstodecode)) < 0)
285 {
286 /* Frame decoding error, abort */
287 LOGF("APE: Frame %d, error %d\n",currentframe,res);
288 retval = CODEC_ERROR;
289 goto done;
290 }
291
292 ci->yield();
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000293
294 if (samplestoskip > 0) {
295 if (samplestoskip < blockstodecode) {
296 ci->pcmbuf_insert(decoded0 + samplestoskip,
297 decoded1 + samplestoskip,
298 blockstodecode - samplestoskip);
299 samplestoskip = 0;
300 } else {
301 samplestoskip -= blockstodecode;
302 }
303 } else {
304 ci->pcmbuf_insert(decoded0, decoded1, blockstodecode);
305 }
Dave Chapman52027422007-06-05 16:58:29 +0000306
Dave Chapman52027422007-06-05 16:58:29 +0000307 samplesdone += blockstodecode;
Dave Chapman7b1d90a2007-06-08 22:35:26 +0000308
309 if (!samplestoskip) {
310 /* Update the elapsed-time indicator */
311 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
312 ci->set_elapsed(elapsedtime);
313 }
Dave Chapman52027422007-06-05 16:58:29 +0000314
315 ci->advance_buffer(bytesconsumed);
316 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
317
318 /* Decrement the block count */
319 nblocks -= blockstodecode;
320 }
321
322 currentframe++;
323 }
324
325 retval = CODEC_OK;
326
327done:
328 LOGF("APE: Decoded %ld samples\n",samplesdone);
329
330 if (ci->request_next_track())
331 goto next_track;
332
333exit:
334 return retval;
335}