diff options
Diffstat (limited to 'lib/rbcodec/codecs/adx.c')
-rw-r--r-- | lib/rbcodec/codecs/adx.c | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/adx.c b/lib/rbcodec/codecs/adx.c new file mode 100644 index 0000000000..0c67fc8d6e --- /dev/null +++ b/lib/rbcodec/codecs/adx.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2006-2008 Adam Gashlin (hcs) | ||
10 | * Copyright (C) 2006 Jens Arnold | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <limits.h> | ||
23 | #include "codeclib.h" | ||
24 | #include "inttypes.h" | ||
25 | #include "math.h" | ||
26 | #include "lib/fixedpoint.h" | ||
27 | |||
28 | CODEC_HEADER | ||
29 | |||
30 | /* Maximum number of bytes to process in one iteration */ | ||
31 | #define WAV_CHUNK_SIZE (1024*2) | ||
32 | |||
33 | /* Number of times to loop looped tracks when repeat is disabled */ | ||
34 | #define LOOP_TIMES 2 | ||
35 | |||
36 | /* Length of fade-out for looped tracks (milliseconds) */ | ||
37 | #define FADE_LENGTH 10000L | ||
38 | |||
39 | /* Default high pass filter cutoff frequency is 500 Hz. | ||
40 | * Others can be set, but the default is nearly always used, | ||
41 | * and there is no way to determine if another was used, anyway. | ||
42 | */ | ||
43 | static const long cutoff = 500; | ||
44 | |||
45 | static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR; | ||
46 | |||
47 | /* this is the codec entry point */ | ||
48 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
49 | { | ||
50 | if (reason == CODEC_LOAD) { | ||
51 | /* Generic codec initialisation */ | ||
52 | /* we only render 16 bits */ | ||
53 | ci->configure(DSP_SET_SAMPLE_DEPTH, 16); | ||
54 | } | ||
55 | |||
56 | return CODEC_OK; | ||
57 | } | ||
58 | |||
59 | /* this is called for each file to process */ | ||
60 | enum codec_status codec_run(void) | ||
61 | { | ||
62 | int channels; | ||
63 | int sampleswritten, i; | ||
64 | uint8_t *buf; | ||
65 | int32_t ch1_1, ch1_2, ch2_1, ch2_2; /* ADPCM history */ | ||
66 | size_t n; | ||
67 | int endofstream; /* end of stream flag */ | ||
68 | uint32_t avgbytespersec; | ||
69 | int looping; /* looping flag */ | ||
70 | int loop_count; /* number of loops done so far */ | ||
71 | int fade_count; /* countdown for fadeout */ | ||
72 | int fade_frames; /* length of fade in frames */ | ||
73 | off_t start_adr, end_adr; /* loop points */ | ||
74 | off_t chanstart, bufoff; | ||
75 | /*long coef1=0x7298L,coef2=-0x3350L;*/ | ||
76 | long coef1, coef2; | ||
77 | intptr_t param; | ||
78 | |||
79 | DEBUGF("ADX: next_track\n"); | ||
80 | if (codec_init()) { | ||
81 | return CODEC_ERROR; | ||
82 | } | ||
83 | DEBUGF("ADX: after init\n"); | ||
84 | |||
85 | /* init history */ | ||
86 | ch1_1=ch1_2=ch2_1=ch2_2=0; | ||
87 | |||
88 | codec_set_replaygain(ci->id3); | ||
89 | |||
90 | /* Get header */ | ||
91 | DEBUGF("ADX: request initial buffer\n"); | ||
92 | ci->seek_buffer(0); | ||
93 | buf = ci->request_buffer(&n, 0x38); | ||
94 | if (!buf || n < 0x38) { | ||
95 | return CODEC_ERROR; | ||
96 | } | ||
97 | bufoff = 0; | ||
98 | DEBUGF("ADX: read size = %lx\n",(unsigned long)n); | ||
99 | |||
100 | /* Get file header for starting offset, channel count */ | ||
101 | |||
102 | chanstart = ((buf[2] << 8) | buf[3]) + 4; | ||
103 | channels = buf[7]; | ||
104 | |||
105 | /* useful for seeking and reporting current playback position */ | ||
106 | avgbytespersec = ci->id3->frequency * 18 * channels / 32; | ||
107 | DEBUGF("avgbytespersec=%ld\n",(unsigned long)avgbytespersec); | ||
108 | |||
109 | /* calculate filter coefficients */ | ||
110 | |||
111 | /** | ||
112 | * A simple table of these coefficients would be nice, but | ||
113 | * some very odd frequencies are used and if I'm going to | ||
114 | * interpolate I might as well just go all the way and | ||
115 | * calclate them precisely. | ||
116 | * Speed is not an issue as this only needs to be done once per file. | ||
117 | */ | ||
118 | { | ||
119 | const int64_t big28 = 0x10000000LL; | ||
120 | const int64_t big32 = 0x100000000LL; | ||
121 | int64_t frequency = ci->id3->frequency; | ||
122 | int64_t phasemultiple = cutoff*big32/frequency; | ||
123 | |||
124 | long z; | ||
125 | int64_t a; | ||
126 | const int64_t b = (M_SQRT2*big28)-big28; | ||
127 | int64_t c; | ||
128 | int64_t d; | ||
129 | |||
130 | fp_sincos((unsigned long)phasemultiple,&z); | ||
131 | |||
132 | a = (M_SQRT2*big28) - (z >> 3); | ||
133 | |||
134 | /** | ||
135 | * In the long passed to fsqrt there are only 4 nonfractional bits, | ||
136 | * which is sufficient here, but this is the only reason why I don't | ||
137 | * use 32 fractional bits everywhere. | ||
138 | */ | ||
139 | d = fp_sqrt((a+b)*(a-b)/big28,28); | ||
140 | c = (a-d)*big28/b; | ||
141 | |||
142 | coef1 = (c*8192) >> 28; | ||
143 | coef2 = (c*c/big28*-4096) >> 28; | ||
144 | DEBUGF("ADX: samprate=%ld ",(long)frequency); | ||
145 | DEBUGF("coef1 %04x ",(unsigned int)(coef1*4)); | ||
146 | DEBUGF("coef2 %04x\n",(unsigned int)(coef2*-4)); | ||
147 | } | ||
148 | |||
149 | /* Get loop data */ | ||
150 | |||
151 | looping = 0; start_adr = 0; end_adr = 0; | ||
152 | if (!memcmp(buf+0x10,"\x01\xF4\x03",3)) { | ||
153 | /* Soul Calibur 2 style (type 03) */ | ||
154 | DEBUGF("ADX: type 03 found\n"); | ||
155 | /* check if header is too small for loop data */ | ||
156 | if (chanstart-6 < 0x2c) looping=0; | ||
157 | else { | ||
158 | looping = (buf[0x18]) || | ||
159 | (buf[0x19]) || | ||
160 | (buf[0x1a]) || | ||
161 | (buf[0x1b]); | ||
162 | end_adr = (buf[0x28]<<24) | | ||
163 | (buf[0x29]<<16) | | ||
164 | (buf[0x2a]<<8) | | ||
165 | (buf[0x2b]); | ||
166 | |||
167 | start_adr = ( | ||
168 | (buf[0x1c]<<24) | | ||
169 | (buf[0x1d]<<16) | | ||
170 | (buf[0x1e]<<8) | | ||
171 | (buf[0x1f]) | ||
172 | )/32*channels*18+chanstart; | ||
173 | } | ||
174 | } else if (!memcmp(buf+0x10,"\x01\xF4\x04",3)) { | ||
175 | /* Standard (type 04) */ | ||
176 | DEBUGF("ADX: type 04 found\n"); | ||
177 | /* check if header is too small for loop data */ | ||
178 | if (chanstart-6 < 0x38) looping=0; | ||
179 | else { | ||
180 | looping = (buf[0x24]) || | ||
181 | (buf[0x25]) || | ||
182 | (buf[0x26]) || | ||
183 | (buf[0x27]); | ||
184 | end_adr = (buf[0x34]<<24) | | ||
185 | (buf[0x35]<<16) | | ||
186 | (buf[0x36]<<8) | | ||
187 | buf[0x37]; | ||
188 | start_adr = ( | ||
189 | (buf[0x28]<<24) | | ||
190 | (buf[0x29]<<16) | | ||
191 | (buf[0x2a]<<8) | | ||
192 | (buf[0x2b]) | ||
193 | )/32*channels*18+chanstart; | ||
194 | } | ||
195 | } else { | ||
196 | DEBUGF("ADX: error, couldn't determine ADX type\n"); | ||
197 | return CODEC_ERROR; | ||
198 | } | ||
199 | |||
200 | /* is file using encryption */ | ||
201 | if (buf[0x13]==0x08) { | ||
202 | DEBUGF("ADX: error, encrypted ADX not supported\n"); | ||
203 | return false; | ||
204 | } | ||
205 | |||
206 | if (looping) { | ||
207 | DEBUGF("ADX: looped, start: %lx end: %lx\n",start_adr,end_adr); | ||
208 | } else { | ||
209 | DEBUGF("ADX: not looped\n"); | ||
210 | } | ||
211 | |||
212 | /* advance to first frame */ | ||
213 | DEBUGF("ADX: first frame at %lx\n",chanstart); | ||
214 | bufoff = chanstart; | ||
215 | |||
216 | /* get in position */ | ||
217 | ci->seek_buffer(bufoff); | ||
218 | ci->set_elapsed(0); | ||
219 | |||
220 | /* setup pcm buffer format */ | ||
221 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | ||
222 | if (channels == 2) { | ||
223 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); | ||
224 | } else if (channels == 1) { | ||
225 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
226 | } else { | ||
227 | DEBUGF("ADX CODEC_ERROR: more than 2 channels\n"); | ||
228 | return CODEC_ERROR; | ||
229 | } | ||
230 | |||
231 | endofstream = 0; | ||
232 | loop_count = 0; | ||
233 | fade_count = -1; /* disable fade */ | ||
234 | fade_frames = 1; | ||
235 | |||
236 | /* The main decoder loop */ | ||
237 | |||
238 | while (!endofstream) { | ||
239 | enum codec_command_action action = ci->get_command(¶m); | ||
240 | |||
241 | if (action == CODEC_ACTION_HALT) | ||
242 | break; | ||
243 | |||
244 | /* do we need to loop? */ | ||
245 | if (bufoff > end_adr-18*channels && looping) { | ||
246 | DEBUGF("ADX: loop!\n"); | ||
247 | /* check for endless looping */ | ||
248 | if (ci->loop_track()) { | ||
249 | loop_count=0; | ||
250 | fade_count = -1; /* disable fade */ | ||
251 | } else { | ||
252 | /* otherwise start fade after LOOP_TIMES loops */ | ||
253 | loop_count++; | ||
254 | if (loop_count >= LOOP_TIMES && fade_count < 0) { | ||
255 | /* frames to fade over */ | ||
256 | fade_frames = FADE_LENGTH*ci->id3->frequency/32/1000; | ||
257 | /* volume relative to fade_frames */ | ||
258 | fade_count = fade_frames; | ||
259 | DEBUGF("ADX: fade_frames = %d\n",fade_frames); | ||
260 | } | ||
261 | } | ||
262 | bufoff = start_adr; | ||
263 | ci->seek_buffer(bufoff); | ||
264 | } | ||
265 | |||
266 | /* do we need to seek? */ | ||
267 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
268 | uint32_t newpos; | ||
269 | |||
270 | DEBUGF("ADX: seek to %ldms\n", (long)param); | ||
271 | |||
272 | endofstream = 0; | ||
273 | loop_count = 0; | ||
274 | fade_count = -1; /* disable fade */ | ||
275 | fade_frames = 1; | ||
276 | |||
277 | newpos = (((uint64_t)avgbytespersec*param) | ||
278 | / (1000LL*18*channels))*(18*channels); | ||
279 | bufoff = chanstart + newpos; | ||
280 | while (bufoff > end_adr-18*channels) { | ||
281 | bufoff-=end_adr-start_adr; | ||
282 | loop_count++; | ||
283 | } | ||
284 | ci->seek_buffer(bufoff); | ||
285 | |||
286 | ci->set_elapsed( | ||
287 | ((end_adr-start_adr)*loop_count + bufoff-chanstart)* | ||
288 | 1000LL/avgbytespersec); | ||
289 | |||
290 | ci->seek_complete(); | ||
291 | } | ||
292 | |||
293 | if (bufoff>ci->filesize-channels*18) break; /* End of stream */ | ||
294 | |||
295 | sampleswritten=0; | ||
296 | |||
297 | while ( | ||
298 | /* Is there data left in the file? */ | ||
299 | (bufoff <= ci->filesize-(18*channels)) && | ||
300 | /* Is there space in the output buffer? */ | ||
301 | (sampleswritten <= WAV_CHUNK_SIZE-(32*channels)) && | ||
302 | /* Should we be looping? */ | ||
303 | ((!looping) || bufoff <= end_adr-18*channels)) | ||
304 | { | ||
305 | /* decode first/only channel */ | ||
306 | int32_t scale; | ||
307 | int32_t ch1_0, d; | ||
308 | |||
309 | /* fetch a frame */ | ||
310 | buf = ci->request_buffer(&n, 18); | ||
311 | |||
312 | if (!buf || n!=18) { | ||
313 | DEBUGF("ADX: couldn't get buffer at %lx\n", | ||
314 | bufoff); | ||
315 | return CODEC_ERROR; | ||
316 | } | ||
317 | |||
318 | scale = ((buf[0] << 8) | (buf[1])) +1; | ||
319 | |||
320 | for (i = 2; i < 18; i++) | ||
321 | { | ||
322 | d = (buf[i] >> 4) & 15; | ||
323 | if (d & 8) d-= 16; | ||
324 | ch1_0 = d*scale + ((coef1*ch1_1 + coef2*ch1_2) >> 12); | ||
325 | if (ch1_0 > 32767) ch1_0 = 32767; | ||
326 | else if (ch1_0 < -32768) ch1_0 = -32768; | ||
327 | samples[sampleswritten] = ch1_0; | ||
328 | sampleswritten+=channels; | ||
329 | ch1_2 = ch1_1; ch1_1 = ch1_0; | ||
330 | |||
331 | d = buf[i] & 15; | ||
332 | if (d & 8) d -= 16; | ||
333 | ch1_0 = d*scale + ((coef1*ch1_1 + coef2*ch1_2) >> 12); | ||
334 | if (ch1_0 > 32767) ch1_0 = 32767; | ||
335 | else if (ch1_0 < -32768) ch1_0 = -32768; | ||
336 | samples[sampleswritten] = ch1_0; | ||
337 | sampleswritten+=channels; | ||
338 | ch1_2 = ch1_1; ch1_1 = ch1_0; | ||
339 | } | ||
340 | bufoff+=18; | ||
341 | ci->advance_buffer(18); | ||
342 | |||
343 | if (channels == 2) { | ||
344 | /* decode second channel */ | ||
345 | int32_t scale; | ||
346 | int32_t ch2_0, d; | ||
347 | |||
348 | buf = ci->request_buffer(&n, 18); | ||
349 | |||
350 | if (!buf || n!=18) { | ||
351 | DEBUGF("ADX: couldn't get buffer at %lx\n", | ||
352 | bufoff); | ||
353 | return CODEC_ERROR; | ||
354 | } | ||
355 | |||
356 | scale = ((buf[0] << 8)|(buf[1]))+1; | ||
357 | |||
358 | sampleswritten-=63; | ||
359 | |||
360 | for (i = 2; i < 18; i++) | ||
361 | { | ||
362 | d = (buf[i] >> 4) & 15; | ||
363 | if (d & 8) d-= 16; | ||
364 | ch2_0 = d*scale + ((coef1*ch2_1 + coef2*ch2_2) >> 12); | ||
365 | if (ch2_0 > 32767) ch2_0 = 32767; | ||
366 | else if (ch2_0 < -32768) ch2_0 = -32768; | ||
367 | samples[sampleswritten] = ch2_0; | ||
368 | sampleswritten+=2; | ||
369 | ch2_2 = ch2_1; ch2_1 = ch2_0; | ||
370 | |||
371 | d = buf[i] & 15; | ||
372 | if (d & 8) d -= 16; | ||
373 | ch2_0 = d*scale + ((coef1*ch2_1 + coef2*ch2_2) >> 12); | ||
374 | if (ch2_0 > 32767) ch2_0 = 32767; | ||
375 | else if (ch2_0 < -32768) ch2_0 = -32768; | ||
376 | samples[sampleswritten] = ch2_0; | ||
377 | sampleswritten+=2; | ||
378 | ch2_2 = ch2_1; ch2_1 = ch2_0; | ||
379 | } | ||
380 | bufoff+=18; | ||
381 | ci->advance_buffer(18); | ||
382 | sampleswritten--; /* go back to first channel's next sample */ | ||
383 | } | ||
384 | |||
385 | if (fade_count>0) { | ||
386 | fade_count--; | ||
387 | for (i=0;i<(channels==1?32:64);i++) samples[sampleswritten-i-1]= | ||
388 | ((int32_t)samples[sampleswritten-i-1])*fade_count/fade_frames; | ||
389 | if (fade_count==0) {endofstream=1; break;} | ||
390 | } | ||
391 | } | ||
392 | |||
393 | if (channels == 2) | ||
394 | sampleswritten >>= 1; /* make samples/channel */ | ||
395 | |||
396 | ci->pcmbuf_insert(samples, NULL, sampleswritten); | ||
397 | |||
398 | ci->set_elapsed( | ||
399 | ((end_adr-start_adr)*loop_count + bufoff-chanstart)* | ||
400 | 1000LL/avgbytespersec); | ||
401 | } | ||
402 | |||
403 | return CODEC_OK; | ||
404 | } | ||