summaryrefslogtreecommitdiff
path: root/apps/codecs
diff options
context:
space:
mode:
authorDave Chapman <dave@dchapman.com>2005-09-22 21:55:37 +0000
committerDave Chapman <dave@dchapman.com>2005-09-22 21:55:37 +0000
commit139c1cb82491886f600ef5014b79acb49f2c510c (patch)
tree4d65d0037983b78007919268fe04b9c56438458e /apps/codecs
parent8026f0fe05cafc2adfb75b90e9bd65b9ea1f02cf (diff)
downloadrockbox-139c1cb82491886f600ef5014b79acb49f2c510c.tar.gz
rockbox-139c1cb82491886f600ef5014b79acb49f2c510c.zip
First version of ALAC (Apple Lossless) decoder
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7547 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs')
-rw-r--r--apps/codecs/Makefile13
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/alac.c388
3 files changed, 399 insertions, 3 deletions
diff --git a/apps/codecs/Makefile b/apps/codecs/Makefile
index abd108fe28..8f869b350c 100644
--- a/apps/codecs/Makefile
+++ b/apps/codecs/Makefile
@@ -17,7 +17,7 @@ ifdef APPEXTRA
17endif 17endif
18 18
19ifdef SOFTWARECODECS 19ifdef SOFTWARECODECS
20 CODECLIBS = -lmad -la52 -lFLAC -lTremor -lwavpack -lmusepack 20 CODECLIBS = -lmad -la52 -lFLAC -lTremor -lwavpack -lmusepack -lalac
21endif 21endif
22 22
23# we "borrow" the plugin LDS file 23# we "borrow" the plugin LDS file
@@ -39,7 +39,7 @@ DIRS = .
39 39
40CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a 40CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a
41 41
42.PHONY: libmad liba52 libFLAC libTremor libwavpack dumb libmusepack 42.PHONY: libmad liba52 libFLAC libTremor libwavpack dumb libmusepack libalac
43 43
44OUTPUT = $(SOFTWARECODECS) 44OUTPUT = $(SOFTWARECODECS)
45 45
@@ -60,6 +60,7 @@ $(OBJDIR)/vorbis.elf: $(OBJDIR)/vorbis.o $(CODECDEPS) $(BUILDDIR)/libTremor.a
60$(OBJDIR)/mpc.elf: $(OBJDIR)/mpc.o $(CODECDEPS) $(BUILDDIR)/libmusepack.a 60$(OBJDIR)/mpc.elf: $(OBJDIR)/mpc.o $(CODECDEPS) $(BUILDDIR)/libmusepack.a
61$(OBJDIR)/wav.elf: $(OBJDIR)/wav.o $(CODECDEPS) 61$(OBJDIR)/wav.elf: $(OBJDIR)/wav.o $(CODECDEPS)
62$(OBJDIR)/wavpack.elf: $(OBJDIR)/wavpack.o $(CODECDEPS) $(BUILDDIR)/libwavpack.a 62$(OBJDIR)/wavpack.elf: $(OBJDIR)/wavpack.o $(CODECDEPS) $(BUILDDIR)/libwavpack.a
63$(OBJDIR)/alac.elf: $(OBJDIR)/alac.o $(CODECDEPS) $(BUILDDIR)/libalac.a
63 64
64$(OBJDIR)/%.elf: $(OBJDIR)/%.o $(CODECDEPS) 65$(OBJDIR)/%.elf: $(OBJDIR)/%.o $(CODECDEPS)
65 $(ELFIT) 66 $(ELFIT)
@@ -152,14 +153,20 @@ libmusepack:
152 @mkdir -p $(OBJDIR)/libmusepack 153 @mkdir -p $(OBJDIR)/libmusepack
153 @$(MAKE) -C libmusepack OBJDIR=$(OBJDIR)/libmusepack OUTPUT=$(BUILDDIR)/libmusepack.a 154 @$(MAKE) -C libmusepack OBJDIR=$(OBJDIR)/libmusepack OUTPUT=$(BUILDDIR)/libmusepack.a
154 155
156libalac:
157 @echo "MAKE in libalac"
158 @mkdir -p $(OBJDIR)/libalac
159 @$(MAKE) -C libalac OBJDIR=$(OBJDIR)/libalac OUTPUT=$(BUILDDIR)/libalac.a
160
155clean: 161clean:
156 @echo "cleaning codecs" 162 @echo "cleaning codecs"
157 $(SILENT)rm -fr $(OBJDIR)/libmad $(BUILDDIR)/libmad.a $(OBJDIR)/liba52 $(OBJDIR)/libFLAC $(OBJDIR)/Tremor $(OBJDIR)/libwavpack $(OBJDIR)/dumb $(BUILDDIR)/libdumb.a $(BUILDDIR)/libdumbd.a $(OBJDIR)/libmusepack $(BUILDDIR)/libmusepack.a 163 $(SILENT)rm -fr $(OBJDIR)/libmad $(BUILDDIR)/libmad.a $(OBJDIR)/liba52 $(OBJDIR)/libFLAC $(OBJDIR)/Tremor $(OBJDIR)/libwavpack $(OBJDIR)/dumb $(BUILDDIR)/libdumb.a $(BUILDDIR)/libdumbd.a $(OBJDIR)/libmusepack $(BUILDDIR)/libmusepack.a $(OBJDIR)/libalac $(BUILDDIR)/libalac.a
158 @$(MAKE) -C libmad clean OBJDIR=$(OBJDIR)/libmad 164 @$(MAKE) -C libmad clean OBJDIR=$(OBJDIR)/libmad
159 @$(MAKE) -C liba52 clean OBJDIR=$(OBJDIR)/liba52 165 @$(MAKE) -C liba52 clean OBJDIR=$(OBJDIR)/liba52
160 @$(MAKE) -C libFLAC clean OBJDIR=$(OBJDIR)/libFLAC 166 @$(MAKE) -C libFLAC clean OBJDIR=$(OBJDIR)/libFLAC
161 @$(MAKE) -C Tremor clean OBJDIR=$(OBJDIR)/Tremor 167 @$(MAKE) -C Tremor clean OBJDIR=$(OBJDIR)/Tremor
162 @$(MAKE) -C libwavpack clean OBJDIR=$(OBJDIR)/libwavpack 168 @$(MAKE) -C libwavpack clean OBJDIR=$(OBJDIR)/libwavpack
163 @$(MAKE) -C libmusepack clean OBJDIR=$(OBJDIR)/libmusepack 169 @$(MAKE) -C libmusepack clean OBJDIR=$(OBJDIR)/libmusepack
170 @$(MAKE) -C libalac clean OBJDIR=$(OBJDIR)/libalac
164 @$(MAKE) -C dumb clean OBJDIR=$(OBJDIR)/dumb 171 @$(MAKE) -C dumb clean OBJDIR=$(OBJDIR)/dumb
165 @$(MAKE) -C lib clean OBJDIR=$(OBJDIR)/lib 172 @$(MAKE) -C lib clean OBJDIR=$(OBJDIR)/lib
diff --git a/apps/codecs/SOURCES b/apps/codecs/SOURCES
index 14a053bd69..14cf847d10 100644
--- a/apps/codecs/SOURCES
+++ b/apps/codecs/SOURCES
@@ -6,4 +6,5 @@ wav.c
6a52.c 6a52.c
7mpc.c 7mpc.c
8wavpack.c 8wavpack.c
9alac.c
9#endif 10#endif
diff --git a/apps/codecs/alac.c b/apps/codecs/alac.c
new file mode 100644
index 0000000000..f00ae979bf
--- /dev/null
+++ b/apps/codecs/alac.c
@@ -0,0 +1,388 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 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 "codec.h"
21
22#include <codecs/libalac/demux.h>
23#include <codecs/libalac/decomp.h>
24#include <codecs/libalac/stream.h>
25
26#include "playback.h"
27#include "dsp.h"
28#include "lib/codeclib.h"
29
30#ifndef SIMULATOR
31extern char iramcopy[];
32extern char iramstart[];
33extern char iramend[];
34#endif
35
36#define destBufferSize (1024*16)
37
38char inputBuffer[1024*32]; /* Input buffer */
39size_t mdat_offset;
40struct codec_api* rb;
41struct codec_api* ci;
42
43/* Implementation of the stream.h functions used by libalac */
44
45#define _Swap32(v) do { \
46 v = (((v) & 0x000000FF) << 0x18) | \
47 (((v) & 0x0000FF00) << 0x08) | \
48 (((v) & 0x00FF0000) >> 0x08) | \
49 (((v) & 0xFF000000) >> 0x18); } while(0)
50
51#define _Swap16(v) do { \
52 v = (((v) & 0x00FF) << 0x08) | \
53 (((v) & 0xFF00) >> 0x08); } while (0)
54
55/* A normal read without any byte-swapping */
56void stream_read(stream_t *stream, size_t size, void *buf)
57{
58 ci->read_filebuf(buf,size);
59 if (ci->curpos >= ci->filesize) { stream->eof=1; }
60}
61
62int32_t stream_read_int32(stream_t *stream)
63{
64 int32_t v;
65 stream_read(stream, 4, &v);
66#ifdef ROCKBOX_LITTLE_ENDIAN
67 _Swap32(v);
68#endif
69 return v;
70}
71
72uint32_t stream_read_uint32(stream_t *stream)
73{
74 uint32_t v;
75 stream_read(stream, 4, &v);
76#ifdef ROCKBOX_LITTLE_ENDIAN
77 _Swap32(v);
78#endif
79 return v;
80}
81
82int16_t stream_read_int16(stream_t *stream)
83{
84 int16_t v;
85 stream_read(stream, 2, &v);
86#ifdef ROCKBOX_LITTLE_ENDIAN
87 _Swap16(v);
88#endif
89 return v;
90}
91
92uint16_t stream_read_uint16(stream_t *stream)
93{
94 uint16_t v;
95 stream_read(stream, 2, &v);
96#ifdef ROCKBOX_LITTLE_ENDIAN
97 _Swap16(v);
98#endif
99 return v;
100}
101
102int8_t stream_read_int8(stream_t *stream)
103{
104 int8_t v;
105 stream_read(stream, 1, &v);
106 return v;
107}
108
109uint8_t stream_read_uint8(stream_t *stream)
110{
111 uint8_t v;
112 stream_read(stream, 1, &v);
113 return v;
114}
115
116void stream_skip(stream_t *stream, size_t skip)
117{
118 (void)stream;
119 ci->advance_buffer(skip);
120}
121
122int stream_eof(stream_t *stream)
123{
124 return stream->eof;
125}
126
127void stream_create(stream_t *stream)
128{
129 stream->eof=0;
130}
131
132/* This function was part of the original alac decoder implementation */
133
134static int get_sample_info(demux_res_t *demux_res, uint32_t samplenum,
135 uint32_t *sample_duration,
136 uint32_t *sample_byte_size)
137{
138 unsigned int duration_index_accum = 0;
139 unsigned int duration_cur_index = 0;
140
141 if (samplenum >= demux_res->num_sample_byte_sizes) {
142 return 0;
143 }
144
145 if (!demux_res->num_time_to_samples) {
146 return 0;
147 }
148
149 while ((demux_res->time_to_sample[duration_cur_index].sample_count
150 + duration_index_accum) <= samplenum) {
151 duration_index_accum +=
152 demux_res->time_to_sample[duration_cur_index].sample_count;
153
154 duration_cur_index++;
155 if (duration_cur_index >= demux_res->num_time_to_samples) {
156 return 0;
157 }
158 }
159
160 *sample_duration =
161 demux_res->time_to_sample[duration_cur_index].sample_duration;
162 *sample_byte_size = demux_res->sample_byte_size[samplenum];
163
164 return 1;
165}
166
167/* Seek to sample_loc (or close to it). Return 1 on success (and
168 modify samplesdone and currentblock), 0 if failed
169
170 Seeking uses the following two arrays:
171
172 1) the sample_byte_size array contains the length in bytes of
173 each block ("sample" in Applespeak).
174
175 2) the time_to_sample array contains the duration (in samples) of
176 each block of data.
177
178 So we just find the block number we are going to seek to (using
179 time_to_sample) and then find the offset in the file (using
180 sample_byte_size).
181
182 Each ALAC block seems to be independent of all the others.
183 */
184
185static unsigned int alac_seek (demux_res_t* demux_res,
186 unsigned int sample_loc,
187 size_t* samplesdone, int* currentblock)
188{
189 int flag;
190 unsigned int i,j;
191 unsigned int newblock;
192 unsigned int newsample;
193 unsigned int newpos;
194
195 /* First check we have the appropriate metadata - we should always
196 have it. */
197 if ((demux_res->num_time_to_samples==0) ||
198 (demux_res->num_sample_byte_sizes==0)) { return 0; }
199
200 /* Find the destination block from time_to_sample array */
201 i=0;
202 newblock=0;
203 newsample=0;
204 flag=0;
205
206 while ((i<demux_res->num_time_to_samples) && (flag==0) &&
207 (newsample < sample_loc)) {
208 j=(sample_loc-newsample) /
209 demux_res->time_to_sample[i].sample_duration;
210
211 if (j <= demux_res->time_to_sample[i].sample_count) {
212 newblock+=j;
213 newsample+=j*demux_res->time_to_sample[i].sample_duration;
214 flag=1;
215 } else {
216 newsample+=(demux_res->time_to_sample[i].sample_duration
217 * demux_res->time_to_sample[i].sample_count);
218 newblock+=demux_res->time_to_sample[i].sample_count;
219 i++;
220 }
221 }
222
223 /* We know the new block, now calculate the file position */
224 newpos=mdat_offset;
225 for (i=0;i<newblock;i++) {
226 newpos+=demux_res->sample_byte_size[i];
227 }
228
229 /* We know the new file position, so let's try to seek to it */
230 if (ci->seek_buffer(newpos)) {
231 *samplesdone=newsample;
232 *currentblock=newblock;
233 return 1;
234 } else {
235 return 0;
236 }
237}
238
239/* this is the codec entry point */
240enum codec_status codec_start(struct codec_api* api)
241{
242 size_t n;
243 demux_res_t demux_res;
244 static stream_t input_stream;
245 uint32_t samplesdone;
246 uint32_t elapsedtime;
247 uint32_t sample_duration;
248 uint32_t sample_byte_size;
249 int outputBytes;
250 unsigned int i;
251 unsigned char* buffer;
252 alac_file alac;
253 int16_t* pDestBuffer;
254
255 /* Generic codec initialisation */
256 TEST_CODEC_API(api);
257
258 rb = api;
259 ci = (struct codec_api*)api;
260
261#ifndef SIMULATOR
262 rb->memcpy(iramstart, iramcopy, iramend-iramstart);
263#endif
264
265 ci->configure(CODEC_SET_FILEBUF_LIMIT, (int *)(1024*1024*10));
266 ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(1024*512));
267 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*128));
268
269 ci->configure(DSP_DITHER, (bool *)false);
270 ci->configure(DSP_SET_STEREO_MODE, (int *)STEREO_INTERLEAVED);
271 ci->configure(DSP_SET_SAMPLE_DEPTH, (int *)(16));
272
273 next_track:
274
275 if (codec_init(api)) {
276 LOGF("ALAC: Error initialising codec\n");
277 return CODEC_ERROR;
278 }
279
280 while (!rb->taginfo_ready)
281 rb->yield();
282
283 if (rb->id3->frequency != NATIVE_FREQUENCY) {
284 rb->configure(DSP_SET_FREQUENCY, (long *)(rb->id3->frequency));
285 rb->configure(CODEC_DSP_ENABLE, (bool *)true);
286 } else {
287 rb->configure(CODEC_DSP_ENABLE, (bool *)false);
288 }
289
290 stream_create(&input_stream);
291
292 /* if qtmovie_read returns successfully, the stream is up to
293 * the movie data, which can be used directly by the decoder */
294 if (!qtmovie_read(&input_stream, &demux_res)) {
295 LOGF("ALAC: Error initialising file\n");
296 return CODEC_ERROR;
297 }
298
299 /* Keep track of start of stream in file - used for seeking */
300 mdat_offset=ci->curpos;
301
302 /* initialise the sound converter */
303 create_alac(demux_res.sample_size, demux_res.num_channels,&alac);
304 alac_set_info(&alac, demux_res.codecdata);
305
306 i=0;
307 samplesdone=0;
308 /* The main decoding loop */
309 while (i < demux_res.num_sample_byte_sizes) {
310 rb->yield();
311 if (ci->stop_codec || ci->reload_codec) {
312 break;
313 }
314
315 /* Deal with any pending seek requests */
316 if (ci->seek_time) {
317 if (alac_seek(&demux_res,
318 (ci->seek_time/10) * (ci->id3->frequency/100),
319 &samplesdone, &i)) {
320 elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
321 ci->set_elapsed(elapsedtime);
322 }
323 ci->seek_time = 0;
324 }
325
326 /* Lookup the length (in samples and bytes) of block i */
327 if (!get_sample_info(&demux_res, i, &sample_duration,
328 &sample_byte_size)) {
329 LOGF("ALAC: Error in get_sample_info\n");
330 return CODEC_ERROR;
331 }
332
333 /* Request the required number of bytes from the input buffer */
334
335 buffer=ci->request_buffer(&n,sample_byte_size);
336 if (n!=sample_byte_size) {
337 /* The decode_frame function requires the whole frame, so if we
338 can't get it contiguously from the buffer, then we need to
339 copy it via a read - i.e. we are at the buffer wraparound
340 point */
341
342 /* Check we estimated the maximum buffer size correctly */
343 if (sample_byte_size > sizeof(inputBuffer)) {
344 LOGF("ALAC: Input buffer < %d bytes\n",sample_byte_size);
345 return CODEC_ERROR;
346 }
347
348 n=ci->read_filebuf(inputBuffer,sample_byte_size);
349 if (n!=sample_byte_size) {
350 LOGF("ALAC: Error reading data\n");
351 return CODEC_ERROR;
352 }
353 buffer=inputBuffer;
354 }
355
356 /* Decode one block - returned samples will be host-endian */
357 outputBytes = destBufferSize;
358 rb->yield();
359 pDestBuffer=decode_frame(&alac, buffer, &outputBytes);
360
361 /* Advance codec buffer - unless we did a read */
362 if ((char*)buffer!=(char*)inputBuffer) {
363 ci->advance_buffer(n);
364 }
365
366 /* Output the audio */
367 rb->yield();
368 while(!ci->pcmbuf_insert((char*)pDestBuffer,outputBytes))
369 rb->yield();
370
371 /* Update the elapsed-time indicator */
372 samplesdone+=sample_duration;
373 elapsedtime=(samplesdone*10)/(ci->id3->frequency/100);
374 ci->set_elapsed(elapsedtime);
375
376 /* Keep track of current position - for resuming */
377 ci->set_offset(elapsedtime);
378
379 i++;
380 }
381
382 LOGF("ALAC: Decoded %d samples\n",samplesdone);
383
384 if (ci->request_next_track())
385 goto next_track;
386
387 return CODEC_OK;
388}