diff options
author | Rani Hod <raenye@gmail.com> | 2006-09-25 16:35:03 +0000 |
---|---|---|
committer | Rani Hod <raenye@gmail.com> | 2006-09-25 16:35:03 +0000 |
commit | d5429307d519bd4a44e021e940c38f2d43fdd130 (patch) | |
tree | fb2823bd9351e31d18efbdfeb05464c5df6b88b0 /apps | |
parent | a875ed5d57ce33663fbf49331d2992d2e4f296d5 (diff) | |
download | rockbox-d5429307d519bd4a44e021e940c38f2d43fdd130.tar.gz rockbox-d5429307d519bd4a44e021e940c38f2d43fdd130.zip |
oops, forgot to 'cvs add' it [thanks for reminding me, linuxstb]
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11047 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/codecs/adx.c | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/apps/codecs/adx.c b/apps/codecs/adx.c new file mode 100644 index 0000000000..99c5f4bef7 --- /dev/null +++ b/apps/codecs/adx.c | |||
@@ -0,0 +1,343 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2006 Adam Gashlin (hcs) | ||
10 | * | ||
11 | * All files in this archive are subject to the GNU General Public License. | ||
12 | * See the file COPYING in the source tree root for full license agreement. | ||
13 | * | ||
14 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
15 | * KIND, either express or implied. | ||
16 | * | ||
17 | ****************************************************************************/ | ||
18 | |||
19 | #include "codeclib.h" | ||
20 | #include "inttypes.h" | ||
21 | |||
22 | CODEC_HEADER | ||
23 | |||
24 | struct codec_api *rb; | ||
25 | |||
26 | /* Maximum number of bytes to process in one iteration */ | ||
27 | #define WAV_CHUNK_SIZE (1024*2) | ||
28 | |||
29 | /* Volume for ADX decoder */ | ||
30 | #define BASE_VOL 0x2000 | ||
31 | |||
32 | /* Number of times to loop looped tracks when repeat is disabled */ | ||
33 | #define LOOP_TIMES 2 | ||
34 | |||
35 | /* Length of fade-out for looped tracks (milliseconds) */ | ||
36 | #define FADE_LENGTH 10000L | ||
37 | |||
38 | static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR; | ||
39 | |||
40 | /* this is the codec entry point */ | ||
41 | enum codec_status codec_start(struct codec_api *api) | ||
42 | { | ||
43 | struct codec_api *ci; | ||
44 | int channels; | ||
45 | int sampleswritten, i; | ||
46 | uint8_t *buf; | ||
47 | int32_t ch1_1, ch1_2, ch2_1, ch2_2; /* ADPCM history */ | ||
48 | size_t n, bufsize; | ||
49 | int endofstream; /* end of stream flag */ | ||
50 | uint32_t avgbytespersec; | ||
51 | int looping; /* looping flag */ | ||
52 | int loop_count; /* number of loops done so far */ | ||
53 | int fade_count; /* countdown for fadeout */ | ||
54 | int fade_frames; /* length of fade in frames */ | ||
55 | off_t start_adr, end_adr; /* loop points */ | ||
56 | off_t chanstart, bufoff; | ||
57 | |||
58 | /* Generic codec initialisation */ | ||
59 | rb = api; | ||
60 | ci = api; | ||
61 | |||
62 | /* we only render 16 bits */ | ||
63 | ci->configure(DSP_SET_SAMPLE_DEPTH, (long *)16); | ||
64 | /*ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, (int *)(1024*256));*/ | ||
65 | ci->configure(DSP_DITHER, (bool *)false); | ||
66 | |||
67 | next_track: | ||
68 | DEBUGF("ADX: next_track\n"); | ||
69 | if (codec_init(api)) { | ||
70 | return CODEC_ERROR; | ||
71 | } | ||
72 | DEBUGF("ADX: after init\n"); | ||
73 | |||
74 | /* init history */ | ||
75 | ch1_1=ch1_2=ch2_1=ch2_2=0; | ||
76 | |||
77 | /* wait for track info to load */ | ||
78 | while (!*ci->taginfo_ready && !ci->stop_codec) | ||
79 | ci->sleep(1); | ||
80 | |||
81 | /* Read the entire file (or as much as possible) */ | ||
82 | DEBUGF("ADX: request initial buffer\n"); | ||
83 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, (int *)(ci->filesize)); | ||
84 | ci->seek_buffer(0); | ||
85 | buf = ci->request_buffer(&n, ci->filesize); | ||
86 | if (!buf || n < 0x38) { | ||
87 | return CODEC_ERROR; | ||
88 | } | ||
89 | bufsize = n; | ||
90 | bufoff = 0; | ||
91 | DEBUGF("ADX: read size = %x\n",bufsize); | ||
92 | |||
93 | /* Get file header for starting offset, channel count */ | ||
94 | |||
95 | chanstart = ((buf[2] << 8) | buf[3]) + 4; | ||
96 | channels = buf[7]; | ||
97 | |||
98 | /* useful for seeking and reporting current playback position */ | ||
99 | avgbytespersec = ci->id3->frequency * 18 * channels / 32; | ||
100 | DEBUGF("avgbytespersec=%d\n",avgbytespersec); | ||
101 | |||
102 | /* Get loop data */ | ||
103 | |||
104 | looping = 0; start_adr = 0; end_adr = 0; | ||
105 | if (!memcmp(buf+0x10,"\x01\xF4\x03\x00",4)) { | ||
106 | /* Soul Calibur 2 style (type 03) */ | ||
107 | DEBUGF("ADX: type 03 found\n"); | ||
108 | /* check if header is too small for loop data */ | ||
109 | if (chanstart-6 < 0x2c) looping=0; | ||
110 | else { | ||
111 | looping = (buf[0x18]) || | ||
112 | (buf[0x19]) || | ||
113 | (buf[0x1a]) || | ||
114 | (buf[0x1b]); | ||
115 | end_adr = (buf[0x28]<<24) | | ||
116 | (buf[0x29]<<16) | | ||
117 | (buf[0x2a]<<8) | | ||
118 | (buf[0x2b]); | ||
119 | |||
120 | start_adr = ( | ||
121 | (buf[0x1c]<<24) | | ||
122 | (buf[0x1d]<<16) | | ||
123 | (buf[0x1e]<<8) | | ||
124 | (buf[0x1f]) | ||
125 | )/32*channels*18+chanstart; | ||
126 | } | ||
127 | } else if (!memcmp(buf+0x10,"\x01\xF4\x04\x00",4)) { | ||
128 | /* Standard (type 04) */ | ||
129 | DEBUGF("ADX: type 04 found\n"); | ||
130 | /* check if header is too small for loop data */ | ||
131 | if (chanstart-6 < 0x38) looping=0; | ||
132 | else { | ||
133 | looping = (buf[0x24]) || | ||
134 | (buf[0x25]) || | ||
135 | (buf[0x26]) || | ||
136 | (buf[0x27]); | ||
137 | end_adr = (buf[0x34]<<24) | | ||
138 | (buf[0x35]<<16) | | ||
139 | (buf[0x36]<<8) | | ||
140 | buf[0x37]; | ||
141 | start_adr = ( | ||
142 | (buf[0x28]<<24) | | ||
143 | (buf[0x29]<<16) | | ||
144 | (buf[0x2a]<<8) | | ||
145 | (buf[0x2b]) | ||
146 | )/32*channels*18+chanstart; | ||
147 | } | ||
148 | } else { | ||
149 | DEBUGF("ADX: error, couldn't determine ADX type\n"); | ||
150 | return CODEC_ERROR; | ||
151 | } | ||
152 | |||
153 | if (looping) { | ||
154 | DEBUGF("ADX: looped, start: %x end: %x\n",start_adr,end_adr); | ||
155 | } else { | ||
156 | DEBUGF("ADX: not looped\n"); | ||
157 | } | ||
158 | |||
159 | /* advance to first frame */ | ||
160 | /*ci->seek_buffer(chanstart);*/ | ||
161 | DEBUGF("ADX: first frame at %x\n",chanstart); | ||
162 | bufoff = chanstart; | ||
163 | |||
164 | /* setup pcm buffer format */ | ||
165 | ci->configure(DSP_SET_FREQUENCY, (long *)(ci->id3->frequency)); | ||
166 | if (channels == 2) { | ||
167 | ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_INTERLEAVED); | ||
168 | } else if (channels == 1) { | ||
169 | ci->configure(DSP_SET_STEREO_MODE, (long *)STEREO_MONO); | ||
170 | } else { | ||
171 | DEBUGF("ADX CODEC_ERROR: more than 2 channels\n"); | ||
172 | return CODEC_ERROR; | ||
173 | } | ||
174 | |||
175 | endofstream = 0; | ||
176 | loop_count = 0; | ||
177 | fade_count = -1; /* disable fade */ | ||
178 | fade_frames = 1; | ||
179 | |||
180 | /* The main decoder loop */ | ||
181 | |||
182 | while (!endofstream) { | ||
183 | ci->yield(); | ||
184 | if (ci->stop_codec || ci->new_track) { | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | /* do we need to loop? */ | ||
189 | if (bufoff >= end_adr-18*channels && looping) { | ||
190 | DEBUGF("ADX: loop!\n"); | ||
191 | /* check for endless looping */ | ||
192 | if (ci->global_settings->repeat_mode==REPEAT_ONE) { | ||
193 | loop_count=0; | ||
194 | fade_count = -1; /* disable fade */ | ||
195 | } else { | ||
196 | /* otherwise start fade after LOOP_TIMES loops */ | ||
197 | loop_count++; | ||
198 | if (loop_count >= LOOP_TIMES && fade_count < 0) { | ||
199 | /* frames to fade over */ | ||
200 | fade_frames = FADE_LENGTH*ci->id3->frequency/32/1000; | ||
201 | /* volume relative to fade_frames */ | ||
202 | fade_count = fade_frames; | ||
203 | DEBUGF("ADX: fade_frames = %d\n",fade_frames); | ||
204 | } | ||
205 | } | ||
206 | bufoff = start_adr; | ||
207 | } | ||
208 | |||
209 | /* do we need to seek? */ | ||
210 | if (ci->seek_time) { | ||
211 | uint32_t newpos; | ||
212 | |||
213 | DEBUGF("ADX: seek to %dms\n",ci->seek_time); | ||
214 | |||
215 | endofstream = 0; | ||
216 | loop_count = 0; | ||
217 | fade_count = -1; /* disable fade */ | ||
218 | fade_frames = 1; | ||
219 | |||
220 | newpos = (((uint64_t)avgbytespersec*(ci->seek_time - 1)) | ||
221 | / (1000LL*18*channels))*(18*channels); | ||
222 | bufoff = chanstart + newpos; | ||
223 | while (bufoff > end_adr-18*channels) { | ||
224 | bufoff-=end_adr-start_adr; | ||
225 | loop_count++; | ||
226 | } | ||
227 | ci->seek_complete(); | ||
228 | } | ||
229 | |||
230 | if (bufoff>ci->filesize-channels*18) break; /* End of stream */ | ||
231 | |||
232 | /* dance with the devil in the pale moonlight */ | ||
233 | if ((bufoff > ci->curpos + (off_t)bufsize - channels*18) || | ||
234 | bufoff < ci->curpos) { | ||
235 | DEBUGF("ADX: requesting another buffer at %x size %x\n", | ||
236 | bufoff,ci->filesize-bufoff); | ||
237 | ci->seek_buffer(bufoff); | ||
238 | buf = ci->request_buffer(&n, ci->filesize-bufoff); | ||
239 | bufsize = n; | ||
240 | DEBUGF("ADX: read size = %x\n",bufsize); | ||
241 | if ((off_t)bufsize < channels*18) { | ||
242 | /* if we can't get a full frame, just request a single | ||
243 | frame (should be able to fit it in the guard buffer) */ | ||
244 | DEBUGF("ADX: requesting single frame at %x\n",bufoff); | ||
245 | buf = ci->request_buffer(&n, channels*18); | ||
246 | bufsize=n; | ||
247 | DEBUGF("ADX: read size = %x\n",bufsize); | ||
248 | } | ||
249 | if (!buf) { | ||
250 | DEBUGF("ADX: couldn't get buffer at %x size %x\n", | ||
251 | bufoff,ci->filesize-bufoff); | ||
252 | return CODEC_ERROR; | ||
253 | } | ||
254 | buf-=bufoff; | ||
255 | } | ||
256 | |||
257 | if (bufsize == 0) break; /* End of stream */ | ||
258 | |||
259 | sampleswritten=0; | ||
260 | |||
261 | while ( | ||
262 | /* Is there data in the file buffer? */ | ||
263 | ((size_t)bufoff <= ci->curpos+bufsize-(18*channels)) && | ||
264 | /* Is there space in the output buffer? */ | ||
265 | (sampleswritten <= WAV_CHUNK_SIZE-(32*channels)) && | ||
266 | /* Should we be looping? */ | ||
267 | ((!looping) || bufoff < end_adr-18*channels)) { | ||
268 | /* decode 18 bytes to 32 samples (from bero) */ | ||
269 | int32_t scale = ((buf[bufoff] << 8) | (buf[bufoff+1])) * BASE_VOL; | ||
270 | |||
271 | int32_t ch1_0, d; | ||
272 | |||
273 | for (i = 2; i < 18; i++) | ||
274 | { | ||
275 | d = (buf[bufoff+i] >> 4) & 15; | ||
276 | if (d & 8) d-= 16; | ||
277 | ch1_0 = (d*scale + 0x7298L*ch1_1 - 0x3350L*ch1_2) >> 14; | ||
278 | if (ch1_0 > 32767) ch1_0 = 32767; | ||
279 | else if (ch1_0 < -32768) ch1_0 = -32768; | ||
280 | samples[sampleswritten] = ch1_0; | ||
281 | sampleswritten+=channels; | ||
282 | ch1_2 = ch1_1; ch1_1 = ch1_0; | ||
283 | d = buf[bufoff+i] & 15; | ||
284 | if (d & 8) d -= 16; | ||
285 | ch1_0 = (d*scale + 0x7298L*ch1_1 - 0x3350L*ch1_2) >> 14; | ||
286 | if (ch1_0 > 32767) ch1_0 = 32767; | ||
287 | else if (ch1_0 < -32768) ch1_0 = -32768; | ||
288 | samples[sampleswritten] = ch1_0; | ||
289 | sampleswritten+=channels; | ||
290 | ch1_2 = ch1_1; ch1_1 = ch1_0; | ||
291 | } | ||
292 | bufoff+=18; | ||
293 | |||
294 | if (channels == 2) { | ||
295 | int32_t scale = ((buf[bufoff] << 8)|(buf[bufoff+1]))*BASE_VOL; | ||
296 | |||
297 | int32_t ch2_0, d; | ||
298 | sampleswritten-=63; | ||
299 | |||
300 | for (i = 2; i < 18; i++) | ||
301 | { | ||
302 | d = (buf[bufoff+i] >> 4) & 15; | ||
303 | if (d & 8) d-= 16; | ||
304 | ch2_0 = (d*scale + 0x7298L*ch2_1 - 0x3350L*ch2_2) >> 14; | ||
305 | if (ch2_0 > 32767) ch2_0 = 32767; | ||
306 | else if (ch2_0 < -32768) ch2_0 = -32768; | ||
307 | samples[sampleswritten] = ch2_0; | ||
308 | sampleswritten+=2; | ||
309 | ch2_2 = ch2_1; ch2_1 = ch2_0; | ||
310 | d = buf[bufoff+i] & 15; | ||
311 | if (d & 8) d -= 16; | ||
312 | ch2_0 = (d*scale + 0x7298L*ch2_1 - 0x3350L*ch2_2) >> 14; | ||
313 | if (ch2_0 > 32767) ch2_0 = 32767; | ||
314 | else if (ch2_0 < -32768) ch2_0 = -32768; | ||
315 | samples[sampleswritten] = ch2_0; | ||
316 | sampleswritten+=2; | ||
317 | ch2_2 = ch2_1; ch2_1 = ch2_0; | ||
318 | } | ||
319 | bufoff+=18; | ||
320 | sampleswritten--; /* go back to first channel's next sample */ | ||
321 | } | ||
322 | if (fade_count>0) { | ||
323 | fade_count--; | ||
324 | for (i=0;i<(channels==1?32:64);i++) samples[sampleswritten-i-1]= | ||
325 | ((int32_t)samples[sampleswritten-i-1])*fade_count/fade_frames; | ||
326 | if (fade_count==0) {endofstream=1; break;} | ||
327 | } | ||
328 | } | ||
329 | |||
330 | /* 2 bytes per sample */ | ||
331 | while (!ci->pcmbuf_insert((char *)samples, sampleswritten*2)) | ||
332 | ci->yield(); | ||
333 | |||
334 | ci->set_elapsed( | ||
335 | ((end_adr-start_adr)*loop_count + bufoff-chanstart)* | ||
336 | 1000LL/avgbytespersec); | ||
337 | } | ||
338 | |||
339 | if (ci->request_next_track()) | ||
340 | goto next_track; | ||
341 | |||
342 | return CODEC_OK; | ||
343 | } | ||