diff options
Diffstat (limited to 'apps/codecs')
-rw-r--r-- | apps/codecs/Makefile | 13 | ||||
-rw-r--r-- | apps/codecs/SOURCES | 1 | ||||
-rw-r--r-- | apps/codecs/alac.c | 388 |
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 | |||
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 | } | ||