summaryrefslogtreecommitdiff
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
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
-rw-r--r--apps/FILES1
-rw-r--r--apps/codecs/Makefile13
-rw-r--r--apps/codecs/SOURCES1
-rw-r--r--apps/codecs/alac.c388
-rw-r--r--apps/metadata.c338
-rw-r--r--apps/playback.c5
-rw-r--r--apps/tree.c1
7 files changed, 744 insertions, 3 deletions
diff --git a/apps/FILES b/apps/FILES
index c38ece11b7..07719a4501 100644
--- a/apps/FILES
+++ b/apps/FILES
@@ -54,6 +54,7 @@ codecs/dumb/src/core/*
54codecs/dumb/src/helpers/* 54codecs/dumb/src/helpers/*
55codecs/dumb/src/it/* 55codecs/dumb/src/it/*
56codecs/libmusepack/* 56codecs/libmusepack/*
57codecs/libalac/*
57codecs/lib/*.[ch] 58codecs/lib/*.[ch]
58codecs/lib/Makefile 59codecs/lib/Makefile
59codecs/lib/SOURCES 60codecs/lib/SOURCES
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}
diff --git a/apps/metadata.c b/apps/metadata.c
index 2883b01407..b8fbd65738 100644
--- a/apps/metadata.c
+++ b/apps/metadata.c
@@ -75,6 +75,7 @@ static const struct format_list formats[] =
75 { AFMT_A52, "a52" }, 75 { AFMT_A52, "a52" },
76 { AFMT_A52, "ac3" }, 76 { AFMT_A52, "ac3" },
77 { AFMT_WAVPACK, "wv" }, 77 { AFMT_WAVPACK, "wv" },
78 { AFMT_ALAC, "m4a" },
78}; 79};
79 80
80static const unsigned short a52_bitrates[] = 81static const unsigned short a52_bitrates[] =
@@ -180,6 +181,30 @@ static void convert_endian(void *data, const char *format)
180 } 181 }
181} 182}
182 183
184/* read_uint32be() - read an unsigned integer from a big-endian
185 (e.g. Quicktime) file. This is used by the .m4a parser
186*/
187#ifdef ROCKBOX_BIG_ENDIAN
188#define read_uint32be(fd,buf) read((fd),(buf),4)
189#else
190int read_uint32be(int fd, unsigned int* buf) {
191 char tmp;
192 char* p=(char*)buf;
193 size_t n;
194
195 n=read(fd,tmp,4);
196 if (n==4) {
197 tmp=p[0];
198 p[0]=p[3];
199 p[1]=p[2];
200 p[2]=p[1];
201 p[3]=tmp;
202 }
203
204 return(n);
205}
206#endif
207
183/* Read an unaligned 32-bit little endian long from buffer. */ 208/* Read an unaligned 32-bit little endian long from buffer. */
184static unsigned long get_long(void* buf) 209static unsigned long get_long(void* buf)
185{ 210{
@@ -264,6 +289,37 @@ static void convert_utf8(char* utf8)
264 *dest = 0; 289 *dest = 0;
265} 290}
266 291
292
293/* Read a string tag from an M4A file */
294void read_m4a_tag_string(int fd, int len,char** bufptr,size_t* bytes_remaining, char** dest)
295{
296 int data_length;
297
298 if (bytes_remaining==0) {
299 lseek(fd,len,SEEK_CUR); /* Skip everything */
300 } else {
301 /* Skip the data tag header - maybe we should parse it properly? */
302 lseek(fd,16,SEEK_CUR);
303 len-=16;
304
305 *dest=*bufptr;
306 if ((size_t)len+1 > *bytes_remaining) {
307 read(fd,*bufptr,*bytes_remaining-1);
308 lseek(fd,len-(*bytes_remaining-1),SEEK_CUR);
309 *bufptr+=(*bytes_remaining-1);
310 } else {
311 read(fd,*bufptr,len);
312 *bufptr+=len;
313 }
314 **bufptr=(char)0;
315
316 convert_utf8(*dest);
317 data_length = strlen(*dest)+1;
318 *bufptr=(*dest)+data_length;
319 *bytes_remaining-=data_length;
320 }
321}
322
267/* Parse the tag (the name-value pair) and fill id3 and buffer accordingly. 323/* Parse the tag (the name-value pair) and fill id3 and buffer accordingly.
268 * String values to keep are written to buf. Returns number of bytes written 324 * String values to keep are written to buf. Returns number of bytes written
269 * to buf (including end nil). 325 * to buf (including end nil).
@@ -887,6 +943,280 @@ static bool get_wave_metadata(int fd, struct mp3entry* id3)
887} 943}
888 944
889 945
946
947static bool get_alac_metadata(int fd, struct mp3entry* id3)
948{
949 unsigned char* buf;
950 unsigned long totalsamples;
951 int i,j,k;
952 size_t n;
953 size_t bytes_remaining;
954 char* id3buf;
955 unsigned int compressedsize;
956 unsigned int sample_count;
957 unsigned int sample_duration;
958 int numentries;
959 int entry_size;
960 int size_remaining;
961 int chunk_len;
962 unsigned char chunk_id[4];
963 int sub_chunk_len;
964 unsigned char sub_chunk_id[4];
965
966 /* A simple parser to read vital metadata from an ALAC file.
967 This parser also works for AAC files - they are both stored in
968 a Quicktime M4A container. */
969
970 /* Use the trackname part of the id3 structure as a temporary buffer */
971 buf=id3->path;
972
973 lseek(fd, 0, SEEK_SET);
974
975 totalsamples=0;
976 compressedsize=0;
977 /* read the chunks - we stop when we find the mdat chunk and set compressedsize */
978 while (compressedsize==0) {
979 n=read_uint32be(fd,&chunk_len);
980
981 // This means it was a 64-bit file, so we have problems.
982 if (chunk_len == 1) {
983 logf("need 64bit support\n");
984 return false;
985 }
986
987 n=read(fd,&chunk_id,4);
988 if (memcmp(&chunk_id,"ftyp",4)==0) {
989 /* Check for M4A type */
990 n=read(fd,&chunk_id,4);
991 if (memcmp(&chunk_id,"M4A ",4)!=0) {
992 logf("Not an M4A file, aborting\n");
993 return false;
994 }
995 /* Skip rest of chunk */
996 lseek(fd, chunk_len - 8 - 4, SEEK_CUR); /* FIXME not 8 */
997 } else if (memcmp(&chunk_id,"moov",4)==0) {
998 size_remaining=chunk_len - 8; /* FIXME not 8 */
999
1000 while (size_remaining > 0) {
1001 n=read_uint32be(fd,&sub_chunk_len);
1002 if ((sub_chunk_len < 1) || (sub_chunk_len > size_remaining)) {
1003 logf("Strange sub_chunk_len value inside moov: %d (remaining: %d)\n",sub_chunk_len,size_remaining);
1004 return false;
1005 }
1006 n=read(fd,&sub_chunk_id,4);
1007 size_remaining-=8;
1008
1009 if (memcmp(&sub_chunk_id,"mvhd",4)==0) {
1010 /* We don't need anything from here - skip */
1011 lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */
1012 size_remaining-=(sub_chunk_len-8);
1013 } else if (memcmp(&sub_chunk_id,"udta",4)==0) {
1014 /* The udta chunk contains the metadata - track, artist, album etc.
1015 The format appears to be:
1016 udta
1017 meta
1018 hdlr
1019 ilst
1020 .nam
1021 [rest of tags]
1022 free
1023
1024 NOTE: This code was written by examination of some .m4a files
1025 produced by iTunes v4.9 - it may not therefore be 100%
1026 compliant with all streams. But it should fail gracefully.
1027 */
1028 j=(sub_chunk_len-8);
1029 size_remaining-=j;
1030 n=read_uint32be(fd,&sub_chunk_len);
1031 n=read(fd,&sub_chunk_id,4);
1032 j-=8;
1033 if (memcmp(&sub_chunk_id,"meta",4)==0) {
1034 lseek(fd, 4, SEEK_CUR);
1035 j-=4;
1036 n=read_uint32be(fd,&sub_chunk_len);
1037 n=read(fd,&sub_chunk_id,4);
1038 j-=8;
1039 if (memcmp(&sub_chunk_id,"hdlr",4)==0) {
1040 lseek(fd, sub_chunk_len - 8, SEEK_CUR);
1041 j-=(sub_chunk_len - 8);
1042 n=read_uint32be(fd,&sub_chunk_len);
1043 n=read(fd,&sub_chunk_id,4);
1044 j-=8;
1045 if (memcmp(&sub_chunk_id,"ilst",4)==0) {
1046 /* Here are the actual tags. We use the id3v2 300-byte buffer
1047 to store the string data */
1048 bytes_remaining=sizeof(id3->id3v2buf);
1049 id3->genre=255; /* Not every track is the Blues */
1050 id3buf=id3->id3v2buf;
1051 k=sub_chunk_len-8;
1052 j-=k;
1053 while (k > 0) {
1054 n=read_uint32be(fd,&sub_chunk_len);
1055 n=read(fd,&sub_chunk_id,4);
1056 k-=8;
1057 if (memcmp(sub_chunk_id,"\251nam",4)==0) {
1058 read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->title);
1059 } else if (memcmp(sub_chunk_id,"\251ART",4)==0) {
1060 read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->artist);
1061 } else if (memcmp(sub_chunk_id,"\251alb",4)==0) {
1062 read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->album);
1063 } else if (memcmp(sub_chunk_id,"\251gen",4)==0) {
1064 read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->genre_string);
1065 } else if (memcmp(sub_chunk_id,"\251day",4)==0) {
1066 read_m4a_tag_string(fd,sub_chunk_len-8,&id3buf,&bytes_remaining,&id3->year_string);
1067 } else if (memcmp(sub_chunk_id,"trkn",4)==0) {
1068 if (sub_chunk_len==0x20) {
1069 read(fd,buf,sub_chunk_len-8);
1070 id3->tracknum=buf[19];
1071 } else {
1072 lseek(fd, sub_chunk_len-8,SEEK_CUR);
1073 }
1074 } else {
1075 lseek(fd, sub_chunk_len-8,SEEK_CUR);
1076 }
1077 k-=(sub_chunk_len-8);
1078 }
1079 }
1080 }
1081 }
1082 /* Skip any remaining data in udta chunk */
1083 lseek(fd, j, SEEK_CUR);
1084 } else if (memcmp(&sub_chunk_id,"trak",4)==0) {
1085 /* Format of trak chunk:
1086 tkhd
1087 mdia
1088 mdhd
1089 hdlr
1090 minf
1091 smhd
1092 dinf
1093 stbl
1094 stsd - Samplerate, Samplesize, Numchannels
1095 stts - time_to_sample array - RLE'd table containing duration of each block
1096 stsz - sample_byte_size array - ?Size in bytes of each compressed block
1097 stsc - Seek table related?
1098 stco - Seek table related?
1099 */
1100
1101 /* Skip tkhd - not needed */
1102 n=read_uint32be(fd,&sub_chunk_len);
1103 n=read(fd,&sub_chunk_id,4);
1104 if (memcmp(&sub_chunk_id,"tkhd",4)!=0) {
1105 logf("Expecting tkhd\n");
1106 return false;
1107 }
1108 lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */
1109 size_remaining-=sub_chunk_len;
1110
1111 /* Process mdia */
1112 n=read_uint32be(fd,&sub_chunk_len);
1113 n=read(fd,&sub_chunk_id,4);
1114 if (memcmp(&sub_chunk_id,"mdia",4)!=0) {
1115 logf("Expecting mdia\n");
1116 return false;
1117 }
1118 size_remaining-=sub_chunk_len;
1119 j=sub_chunk_len-8;
1120
1121 while (j > 0) {
1122 n=read_uint32be(fd,&sub_chunk_len);
1123 n=read(fd,&sub_chunk_id,4);
1124 j-=4;
1125 if (memcmp(&sub_chunk_id,"minf",4)==0) {
1126 j=sub_chunk_len-8;
1127 } else if (memcmp(&sub_chunk_id,"stbl",4)==0) {
1128 j=sub_chunk_len-8;
1129 } else if (memcmp(&sub_chunk_id,"stsd",4)==0) {
1130 n=read(fd,buf,sub_chunk_len-8);
1131 j-=sub_chunk_len;
1132 i=0;
1133 /* Skip version and flags */
1134 i+=4;
1135
1136 numentries=(buf[i]<<24)|(buf[i+1]<<16)|(buf[i+2]<<8)|buf[i+3];
1137 i+=4;
1138 if (numentries!=1) {
1139 logf("ERROR: Expecting only one entry in stsd\n");
1140 }
1141
1142 entry_size=(buf[i]<<24)|(buf[i+1]<<16)|(buf[i+2]<<8)|buf[i+3];
1143 i+=4;
1144
1145 /* Check the codec type - 'alac' for ALAC, 'mp4a' for AAC */
1146 if (memcmp(&buf[i],"alac",4)!=0) {
1147 logf("Not an ALAC file\n");
1148 return false;
1149 }
1150
1151 //numchannels=(buf[i+20]<<8)|buf[i+21]; /* Not used - assume Stereo */
1152 //samplesize=(buf[i+22]<<8)|buf[i+23]; /* Not used - assume 16-bit */
1153
1154 /* Samplerate is 32-bit fixed point, but this works for < 65536 Hz */
1155 id3->frequency=(buf[i+28]<<8)|buf[i+29];
1156 } else if (memcmp(&sub_chunk_id,"stts",4)==0) {
1157 j-=sub_chunk_len;
1158 i=8;
1159 n=read(fd,buf,8);
1160 i+=8;
1161 numentries=(buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7];
1162 for (k=0;k<numentries;k++) {
1163 n=read_uint32be(fd,&sample_count);
1164 n=read_uint32be(fd,&sample_duration);
1165 totalsamples+=sample_count*sample_duration;
1166 i+=8;
1167 }
1168 if (i > 0) lseek(fd, sub_chunk_len - i, SEEK_CUR);
1169 } else if (memcmp(&sub_chunk_id,"stsz",4)==0) {
1170 j-=sub_chunk_len;
1171 i=8;
1172 n=read(fd,buf,8);
1173 i+=8;
1174 numentries=(buf[4]<<24)|(buf[5]<<16)|(buf[6]<<8)|buf[7];
1175 for (k=0;k<numentries;k++) {
1176 n=read_uint32be(fd,&sample_count);
1177 n=read_uint32be(fd,&sample_duration);
1178 totalsamples+=sample_count*sample_duration;
1179 i+=8;
1180 }
1181 if (i > 0) lseek(fd, sub_chunk_len - i, SEEK_CUR);
1182 } else {
1183 lseek(fd, sub_chunk_len - 8, SEEK_CUR); /* FIXME not 8 */
1184 j-=sub_chunk_len;
1185 }
1186 }
1187 } else {
1188 logf("Unexpected sub_chunk_id inside moov: %c%c%c%c\n",
1189 sub_chunk_id[0],sub_chunk_id[1],sub_chunk_id[2],sub_chunk_id[3]);
1190 return false;
1191 }
1192 }
1193 } else if (memcmp(&chunk_id,"mdat",4)==0) {
1194 /* once we hit mdat we stop reading and return.
1195 * this is on the assumption that there is no furhter interesting
1196 * stuff in the stream. if there is stuff will fail (:()).
1197 * But we need the read pointer to be at the mdat stuff
1198 * for the decoder. And we don't want to rely on fseek/ftell,
1199 * as they may not always be avilable */
1200 lseek(fd, chunk_len - 8, SEEK_CUR); /* FIXME not 8 */
1201 compressedsize=chunk_len-8;
1202 } else if (memcmp(&chunk_id,"free",4)==0) {
1203 /* these following atoms can be skipped !!!! */
1204 lseek(fd, chunk_len - 8, SEEK_CUR); /* FIXME not 8 */
1205 } else {
1206 logf("(top) unknown chunk id: %c%c%c%c\n", chunk_id[0],chunk_id[1],chunk_id[2],chunk_id[3]);
1207 return false;
1208 }
1209 }
1210
1211 id3->vbr=true; /* All ALAC files are VBR */
1212 id3->filesize=filesize(fd);
1213 id3->samples=totalsamples;
1214 id3->length=(10*totalsamples)/(id3->frequency/100);
1215 id3->bitrate=(compressedsize*8)/id3->length;;
1216
1217 return true;
1218}
1219
890/* Simple file type probing by looking at the filename extension. */ 1220/* Simple file type probing by looking at the filename extension. */
891unsigned int probe_file_format(const char *filename) 1221unsigned int probe_file_format(const char *filename)
892{ 1222{
@@ -1064,6 +1394,14 @@ bool get_metadata(struct track_info* track, int fd, const char* trackname,
1064 track->id3.length = (totalsamples / track->id3.frequency) * 1000; 1394 track->id3.length = (totalsamples / track->id3.frequency) * 1000;
1065 break; 1395 break;
1066 1396
1397 case AFMT_ALAC:
1398 if (!get_alac_metadata(fd, &(track->id3)))
1399 {
1400// return false;
1401 }
1402
1403 break;
1404
1067 /* If we don't know how to read the metadata, just store the filename */ 1405 /* If we don't know how to read the metadata, just store the filename */
1068 default: 1406 default:
1069 break; 1407 break;
diff --git a/apps/playback.c b/apps/playback.c
index f05cb9ef8a..0885bd487a 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -74,6 +74,7 @@ static volatile bool paused;
74#define CODEC_A52 "/.rockbox/codecs/a52.codec" 74#define CODEC_A52 "/.rockbox/codecs/a52.codec"
75#define CODEC_MPC "/.rockbox/codecs/mpc.codec" 75#define CODEC_MPC "/.rockbox/codecs/mpc.codec"
76#define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec" 76#define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec"
77#define CODEC_ALAC "/.rockbox/codecs/alac.codec"
77 78
78#define AUDIO_FILL_CYCLE (1024*256) 79#define AUDIO_FILL_CYCLE (1024*256)
79#define AUDIO_DEFAULT_WATERMARK (1024*512) 80#define AUDIO_DEFAULT_WATERMARK (1024*512)
@@ -881,6 +882,10 @@ bool loadcodec(const char *trackname, bool start_play)
881 logf("Codec: WAVPACK"); 882 logf("Codec: WAVPACK");
882 codec_path = CODEC_WAVPACK; 883 codec_path = CODEC_WAVPACK;
883 break; 884 break;
885 case AFMT_ALAC:
886 logf("Codec: ALAC");
887 codec_path = CODEC_ALAC;
888 break;
884 default: 889 default:
885 logf("Codec: Unsupported"); 890 logf("Codec: Unsupported");
886 snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname); 891 snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname);
diff --git a/apps/tree.c b/apps/tree.c
index e8bf46f1af..a52d453081 100644
--- a/apps/tree.c
+++ b/apps/tree.c
@@ -82,6 +82,7 @@ const struct filetype filetypes[] = {
82 { "a52", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 82 { "a52", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
83 { "mpc", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 83 { "mpc", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
84 { "wv", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA }, 84 { "wv", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
85 { "m4a", TREE_ATTR_MPA, Icon_Audio, VOICE_EXT_MPA },
85#endif 86#endif
86 { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, 87 { "m3u", TREE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
87 { "cfg", TREE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG }, 88 { "cfg", TREE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG },