summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/ape.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/ape.c')
-rw-r--r--lib/rbcodec/codecs/ape.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/ape.c b/lib/rbcodec/codecs/ape.c
new file mode 100644
index 0000000000..ed6ea21685
--- /dev/null
+++ b/lib/rbcodec/codecs/ape.c
@@ -0,0 +1,330 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 Dave Chapman
11 *
12 * 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.
16 *
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#include <codecs/demac/libdemac/demac.h>
24
25CODEC_HEADER
26
27#define BLOCKS_PER_LOOP 1024
28#define MAX_CHANNELS 2
29#define MAX_BYTESPERSAMPLE 3
30
31/* Monkey's Audio files have one seekpoint per frame. The framesize
32 varies between 73728 and 1179648 samples.
33
34 At the smallest framesize, 30000 frames would be 50155 seconds of
35 audio - almost 14 hours. This should be enough for any file a user
36 would want to play in Rockbox, given the 2GB FAT filesize (and 4GB
37 seektable entry size) limit.
38
39 This means the seektable is 120000 bytes, but we have a lot of
40 spare room in the codec buffer - the APE codec itself is small.
41*/
42
43#define MAX_SEEKPOINTS 30000
44static uint32_t seektablebuf[MAX_SEEKPOINTS];
45
46#define INPUT_CHUNKSIZE (32*1024)
47
48/* 1024*4 = 4096 bytes per channel */
49static int32_t decoded0[BLOCKS_PER_LOOP] IBSS_ATTR;
50static int32_t decoded1[BLOCKS_PER_LOOP] IBSS_ATTR;
51
52#define MAX_SUPPORTED_SEEKTABLE_SIZE 5000
53
54
55/* Given an ape_ctx and a sample to seek to, return the file position
56 to the frame containing that sample, and the number of samples to
57 skip in that frame.
58*/
59
60static bool ape_calc_seekpos(struct ape_ctx_t* ape_ctx,
61 uint32_t new_sample,
62 uint32_t* newframe,
63 uint32_t* filepos,
64 uint32_t* samplestoskip)
65{
66 uint32_t n;
67
68 n = new_sample / ape_ctx->blocksperframe;
69 if (n >= ape_ctx->numseekpoints)
70 {
71 /* We don't have a seekpoint for that frame */
72 return false;
73 }
74
75 *newframe = n;
76 *filepos = ape_ctx->seektable[n];
77 *samplestoskip = new_sample - (n * ape_ctx->blocksperframe);
78
79 return true;
80}
81
82/* The resume offset is a value in bytes - we need to
83 turn it into a frame number and samplestoskip value */
84
85static void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset,
86 uint32_t* currentframe, uint32_t* samplesdone,
87 uint32_t* samplestoskip, int* firstbyte)
88{
89 off_t newfilepos;
90 int64_t framesize;
91 int64_t offset;
92
93 *currentframe = 0;
94 *samplesdone = 0;
95 *samplestoskip = 0;
96
97 while ((*currentframe < ape_ctx->totalframes) &&
98 (*currentframe < ape_ctx->numseekpoints) &&
99 (resume_offset > ape_ctx->seektable[*currentframe]))
100 {
101 ++*currentframe;
102 *samplesdone += ape_ctx->blocksperframe;
103 }
104
105 if ((*currentframe > 0) &&
106 (ape_ctx->seektable[*currentframe] > resume_offset)) {
107 --*currentframe;
108 *samplesdone -= ape_ctx->blocksperframe;
109 }
110
111 newfilepos = ape_ctx->seektable[*currentframe];
112
113 /* APE's bytestream is weird... */
114 *firstbyte = 3 - (newfilepos & 3);
115 newfilepos &= ~3;
116
117 ci->seek_buffer(newfilepos);
118
119 /* We estimate where we were in the current frame, based on the
120 byte offset */
121 if (*currentframe < (ape_ctx->totalframes - 1)) {
122 framesize = ape_ctx->seektable[*currentframe+1] - ape_ctx->seektable[*currentframe];
123 offset = resume_offset - ape_ctx->seektable[*currentframe];
124
125 *samplestoskip = (offset * ape_ctx->blocksperframe) / framesize;
126 }
127}
128
129/* this is the codec entry point */
130enum codec_status codec_main(enum codec_entry_call_reason reason)
131{
132 if (reason == CODEC_LOAD) {
133 /* Generic codec initialisation */
134 ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1);
135 }
136
137 return CODEC_OK;
138}
139
140/* this is called for each file to process */
141enum codec_status codec_run(void)
142{
143 struct ape_ctx_t ape_ctx;
144 uint32_t samplesdone;
145 uint32_t elapsedtime;
146 size_t bytesleft;
147
148 uint32_t currentframe;
149 uint32_t newfilepos;
150 uint32_t samplestoskip;
151 int nblocks;
152 int bytesconsumed;
153 unsigned char* inbuffer;
154 uint32_t blockstodecode;
155 int res;
156 int firstbyte;
157 size_t resume_offset;
158 intptr_t param;
159
160 if (codec_init()) {
161 LOGF("APE: Error initialising codec\n");
162 return CODEC_ERROR;
163 }
164
165 /* Remember the resume position - when the codec is opened, the
166 playback engine will reset it. */
167 resume_offset = ci->id3->offset;
168
169 ci->seek_buffer(0);
170 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
171
172 /* Read the file headers to populate the ape_ctx struct */
173 if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) {
174 LOGF("APE: Error reading header\n");
175 return CODEC_ERROR;
176 }
177
178 /* Initialise the seektable for this file */
179 ape_ctx.seektable = seektablebuf;
180 ape_ctx.numseekpoints = MIN(MAX_SEEKPOINTS,ape_ctx.numseekpoints);
181
182 ci->advance_buffer(ape_ctx.seektablefilepos);
183
184 /* The seektable may be bigger than the guard buffer (32KB), so we
185 do a read() */
186 ci->read_filebuf(ape_ctx.seektable, ape_ctx.numseekpoints * sizeof(uint32_t));
187
188#ifdef ROCKBOX_BIG_ENDIAN
189 /* Byte-swap the little-endian seekpoints */
190 {
191 uint32_t i;
192
193 for(i = 0; i < ape_ctx.numseekpoints; i++)
194 ape_ctx.seektable[i] = swap32(ape_ctx.seektable[i]);
195 }
196#endif
197
198 /* Now advance the file position to the first frame */
199 ci->advance_buffer(ape_ctx.firstframe -
200 (ape_ctx.seektablefilepos +
201 ape_ctx.numseekpoints * sizeof(uint32_t)));
202
203 ci->configure(DSP_SWITCH_FREQUENCY, ape_ctx.samplerate);
204 ci->configure(DSP_SET_STEREO_MODE, ape_ctx.channels == 1 ?
205 STEREO_MONO : STEREO_NONINTERLEAVED);
206 codec_set_replaygain(ci->id3);
207
208 /* The main decoding loop */
209
210 if (resume_offset) {
211 /* The resume offset is a value in bytes - we need to
212 turn it into a frame number and samplestoskip value */
213
214 ape_resume(&ape_ctx, resume_offset,
215 &currentframe, &samplesdone, &samplestoskip, &firstbyte);
216 } else {
217 currentframe = 0;
218 samplesdone = 0;
219 samplestoskip = 0;
220 firstbyte = 3; /* Take account of the little-endian 32-bit byte ordering */
221 }
222
223 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
224 ci->set_elapsed(elapsedtime);
225
226 /* Initialise the buffer */
227 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
228
229 /* The main decoding loop - we decode the frames a small chunk at a time */
230 while (currentframe < ape_ctx.totalframes)
231 {
232frame_start:
233 /* 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 enum codec_command_action action = ci->get_command(&param);
251
252 if (action == CODEC_ACTION_HALT)
253 goto done;
254
255 /* Deal with any pending seek requests */
256 if (action == CODEC_ACTION_SEEK_TIME)
257 {
258 if (ape_calc_seekpos(&ape_ctx,
259 (param/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 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
274 ci->set_elapsed(elapsedtime);
275 ci->seek_complete();
276 goto frame_start; /* Sorry... */
277 }
278
279 ci->seek_complete();
280 }
281
282 blockstodecode = MIN(BLOCKS_PER_LOOP, nblocks);
283
284 if ((res = decode_chunk(&ape_ctx, inbuffer, &firstbyte,
285 &bytesconsumed,
286 decoded0, decoded1,
287 blockstodecode)) < 0)
288 {
289 /* Frame decoding error, abort */
290 LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res);
291 return CODEC_ERROR;
292 }
293
294 ci->yield();
295
296 if (samplestoskip > 0) {
297 if (samplestoskip < blockstodecode) {
298 ci->pcmbuf_insert(decoded0 + samplestoskip,
299 decoded1 + samplestoskip,
300 blockstodecode - samplestoskip);
301 samplestoskip = 0;
302 } else {
303 samplestoskip -= blockstodecode;
304 }
305 } else {
306 ci->pcmbuf_insert(decoded0, decoded1, blockstodecode);
307 }
308
309 samplesdone += blockstodecode;
310
311 if (!samplestoskip) {
312 /* Update the elapsed-time indicator */
313 elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100);
314 ci->set_elapsed(elapsedtime);
315 }
316
317 ci->advance_buffer(bytesconsumed);
318 inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE);
319
320 /* Decrement the block count */
321 nblocks -= blockstodecode;
322 }
323
324 currentframe++;
325 }
326
327done:
328 LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone);
329 return CODEC_OK;
330}