diff options
-rw-r--r-- | apps/FILES | 1 | ||||
-rw-r--r-- | apps/codecs/Makefile | 13 | ||||
-rw-r--r-- | apps/codecs/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs/alac.c | 388 | ||||
-rw-r--r-- | apps/metadata.c | 338 | ||||
-rw-r--r-- | apps/playback.c | 5 | ||||
-rw-r--r-- | apps/tree.c | 1 |
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/* | |||
54 | codecs/dumb/src/helpers/* | 54 | codecs/dumb/src/helpers/* |
55 | codecs/dumb/src/it/* | 55 | codecs/dumb/src/it/* |
56 | codecs/libmusepack/* | 56 | codecs/libmusepack/* |
57 | codecs/libalac/* | ||
57 | codecs/lib/*.[ch] | 58 | codecs/lib/*.[ch] |
58 | codecs/lib/Makefile | 59 | codecs/lib/Makefile |
59 | codecs/lib/SOURCES | 60 | codecs/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 | |||
17 | endif | 17 | endif |
18 | 18 | ||
19 | ifdef SOFTWARECODECS | 19 | ifdef SOFTWARECODECS |
20 | CODECLIBS = -lmad -la52 -lFLAC -lTremor -lwavpack -lmusepack | 20 | CODECLIBS = -lmad -la52 -lFLAC -lTremor -lwavpack -lmusepack -lalac |
21 | endif | 21 | endif |
22 | 22 | ||
23 | # we "borrow" the plugin LDS file | 23 | # we "borrow" the plugin LDS file |
@@ -39,7 +39,7 @@ DIRS = . | |||
39 | 39 | ||
40 | CODECDEPS = $(LINKCODEC) $(BUILDDIR)/libcodec.a | 40 | CODECDEPS = $(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 | ||
44 | OUTPUT = $(SOFTWARECODECS) | 44 | OUTPUT = $(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 | ||
156 | libalac: | ||
157 | @echo "MAKE in libalac" | ||
158 | @mkdir -p $(OBJDIR)/libalac | ||
159 | @$(MAKE) -C libalac OBJDIR=$(OBJDIR)/libalac OUTPUT=$(BUILDDIR)/libalac.a | ||
160 | |||
155 | clean: | 161 | clean: |
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 | |||
6 | a52.c | 6 | a52.c |
7 | mpc.c | 7 | mpc.c |
8 | wavpack.c | 8 | wavpack.c |
9 | alac.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 | ||
31 | extern char iramcopy[]; | ||
32 | extern char iramstart[]; | ||
33 | extern char iramend[]; | ||
34 | #endif | ||
35 | |||
36 | #define destBufferSize (1024*16) | ||
37 | |||
38 | char inputBuffer[1024*32]; /* Input buffer */ | ||
39 | size_t mdat_offset; | ||
40 | struct codec_api* rb; | ||
41 | struct 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 */ | ||
56 | void 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 | |||
62 | int32_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 | |||
72 | uint32_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 | |||
82 | int16_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 | |||
92 | uint16_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 | |||
102 | int8_t stream_read_int8(stream_t *stream) | ||
103 | { | ||
104 | int8_t v; | ||
105 | stream_read(stream, 1, &v); | ||
106 | return v; | ||
107 | } | ||
108 | |||
109 | uint8_t stream_read_uint8(stream_t *stream) | ||
110 | { | ||
111 | uint8_t v; | ||
112 | stream_read(stream, 1, &v); | ||
113 | return v; | ||
114 | } | ||
115 | |||
116 | void stream_skip(stream_t *stream, size_t skip) | ||
117 | { | ||
118 | (void)stream; | ||
119 | ci->advance_buffer(skip); | ||
120 | } | ||
121 | |||
122 | int stream_eof(stream_t *stream) | ||
123 | { | ||
124 | return stream->eof; | ||
125 | } | ||
126 | |||
127 | void stream_create(stream_t *stream) | ||
128 | { | ||
129 | stream->eof=0; | ||
130 | } | ||
131 | |||
132 | /* This function was part of the original alac decoder implementation */ | ||
133 | |||
134 | static 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 | |||
185 | static 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 */ | ||
240 | enum 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 | ||
80 | static const unsigned short a52_bitrates[] = | 81 | static 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 | ||
190 | int 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. */ |
184 | static unsigned long get_long(void* buf) | 209 | static 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 */ | ||
294 | void 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 | |||
947 | static 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. */ |
891 | unsigned int probe_file_format(const char *filename) | 1221 | unsigned 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 }, |