diff options
Diffstat (limited to 'apps/codecs/wma.c')
-rw-r--r-- | apps/codecs/wma.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/apps/codecs/wma.c b/apps/codecs/wma.c new file mode 100644 index 0000000000..5303eb4287 --- /dev/null +++ b/apps/codecs/wma.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id: flac.c 12830 2007-03-18 09:50:53Z learman $ | ||
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 "codeclib.h" | ||
21 | #include "libwma/asf.h" | ||
22 | #include "libwma/wmadec.h" | ||
23 | |||
24 | CODEC_HEADER | ||
25 | |||
26 | #define MAX_BLOCKSIZE 2048 | ||
27 | |||
28 | /* The output buffers containing the decoded samples (channels 0 and 1) */ | ||
29 | |||
30 | /* NOTE: WMADecodeContext is 142688 bytes (on x86) */ | ||
31 | static WMADecodeContext wmadec; | ||
32 | |||
33 | /* TODO: Check the size of this */ | ||
34 | #define OUTBUF_SIZE 256*1024 | ||
35 | |||
36 | enum asf_error_e { | ||
37 | ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */ | ||
38 | ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */ | ||
39 | ASF_ERROR_EOF = -3, /* unexpected end of file */ | ||
40 | ASF_ERROR_IO = -4, /* error reading or writing to file */ | ||
41 | ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */ | ||
42 | ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */ | ||
43 | ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */ | ||
44 | ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */ | ||
45 | ASF_ERROR_SEEKABLE = -9, /* file not seekable */ | ||
46 | ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */ | ||
47 | }; | ||
48 | |||
49 | /* Read an unaligned 32-bit little endian long from buffer. */ | ||
50 | static unsigned long get_long_le(void* buf) | ||
51 | { | ||
52 | unsigned char* p = (unsigned char*) buf; | ||
53 | |||
54 | return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); | ||
55 | } | ||
56 | |||
57 | /* Read an unaligned 16-bit little endian short from buffer. */ | ||
58 | static unsigned short get_short_le(void* buf) | ||
59 | { | ||
60 | unsigned char* p = (unsigned char*) buf; | ||
61 | |||
62 | return p[0] | (p[1] << 8); | ||
63 | } | ||
64 | |||
65 | #define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits) | ||
66 | |||
67 | #define GETVALUE2b(bits, data) \ | ||
68 | (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \ | ||
69 | 0 : *(data) : get_short_le(data) : get_long_le(data)) | ||
70 | |||
71 | static int asf_read_packet(int* padding, asf_waveformatex_t* wfx) | ||
72 | { | ||
73 | uint8_t tmp8, packet_flags, packet_property; | ||
74 | int ec_length, opaque_data, ec_length_type; | ||
75 | int datalen; | ||
76 | uint8_t data[18]; | ||
77 | uint8_t* datap; | ||
78 | uint32_t length; | ||
79 | uint32_t padding_length; | ||
80 | uint32_t send_time; | ||
81 | uint16_t duration; | ||
82 | uint16_t payload_count; | ||
83 | int payload_length_type; | ||
84 | uint32_t payload_hdrlen; | ||
85 | int payload_datalen; | ||
86 | int multiple; | ||
87 | uint32_t replicated_length; | ||
88 | uint32_t media_object_number; | ||
89 | uint32_t media_object_offset; | ||
90 | uint32_t bytesread = 0; | ||
91 | |||
92 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
93 | return ASF_ERROR_EOF; | ||
94 | } | ||
95 | bytesread++; | ||
96 | |||
97 | /* TODO: We need a better way to detect endofstream */ | ||
98 | if (tmp8 != 0x82) { return -1; } | ||
99 | |||
100 | //DEBUGF("tmp8=0x%02x\n",tmp8); | ||
101 | |||
102 | if (tmp8 & 0x80) { | ||
103 | ec_length = tmp8 & 0x0f; | ||
104 | opaque_data = (tmp8 >> 4) & 0x01; | ||
105 | ec_length_type = (tmp8 >> 5) & 0x03; | ||
106 | |||
107 | if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) { | ||
108 | DEBUGF("incorrect error correction flags\n"); | ||
109 | return ASF_ERROR_INVALID_VALUE; | ||
110 | } | ||
111 | |||
112 | /* Skip ec_data */ | ||
113 | ci->advance_buffer(ec_length); | ||
114 | bytesread += ec_length; | ||
115 | } else { | ||
116 | ec_length = 0; | ||
117 | } | ||
118 | |||
119 | if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; } | ||
120 | if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; } | ||
121 | bytesread += 2; | ||
122 | |||
123 | datalen = GETLEN2b((packet_flags >> 1) & 0x03) + | ||
124 | GETLEN2b((packet_flags >> 3) & 0x03) + | ||
125 | GETLEN2b((packet_flags >> 5) & 0x03) + 6; | ||
126 | |||
127 | #if 0 | ||
128 | if (datalen > sizeof(data)) { | ||
129 | DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); | ||
130 | return ASF_ERROR_OUTOFMEM; | ||
131 | } | ||
132 | #endif | ||
133 | |||
134 | if (ci->read_filebuf(data, datalen) == 0) { | ||
135 | return ASF_ERROR_EOF; | ||
136 | } | ||
137 | |||
138 | bytesread += datalen; | ||
139 | |||
140 | datap = data; | ||
141 | length = GETVALUE2b((packet_flags >> 5) & 0x03, datap); | ||
142 | datap += GETLEN2b((packet_flags >> 5) & 0x03); | ||
143 | /* sequence value is not used */ | ||
144 | GETVALUE2b((packet_flags >> 1) & 0x03, datap); | ||
145 | datap += GETLEN2b((packet_flags >> 1) & 0x03); | ||
146 | padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap); | ||
147 | datap += GETLEN2b((packet_flags >> 3) & 0x03); | ||
148 | send_time = get_long_le(datap); | ||
149 | datap += 4; | ||
150 | duration = get_short_le(datap); | ||
151 | datap += 2; | ||
152 | |||
153 | /* this is really idiotic, packet length can (and often will) be | ||
154 | * undefined and we just have to use the header packet size as the size | ||
155 | * value */ | ||
156 | if (!((packet_flags >> 5) & 0x03)) { | ||
157 | length = wfx->packet_size; | ||
158 | } | ||
159 | |||
160 | /* this is also really idiotic, if packet length is smaller than packet | ||
161 | * size, we need to manually add the additional bytes into padding length | ||
162 | */ | ||
163 | if (length < wfx->packet_size) { | ||
164 | padding_length += wfx->packet_size - length; | ||
165 | length = wfx->packet_size; | ||
166 | } | ||
167 | |||
168 | if (length > wfx->packet_size) { | ||
169 | DEBUGF("packet with too big length value\n"); | ||
170 | return ASF_ERROR_INVALID_LENGTH; | ||
171 | } | ||
172 | |||
173 | /* check if we have multiple payloads */ | ||
174 | if (packet_flags & 0x01) { | ||
175 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
176 | return ASF_ERROR_EOF; | ||
177 | } | ||
178 | payload_count = tmp8 & 0x3f; | ||
179 | payload_length_type = (tmp8 >> 6) & 0x03; | ||
180 | bytesread++; | ||
181 | } else { | ||
182 | payload_count = 1; | ||
183 | payload_length_type = 0x02; /* not used */ | ||
184 | } | ||
185 | |||
186 | if (length < bytesread) { | ||
187 | DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread); | ||
188 | /* FIXME: should this be checked earlier? */ | ||
189 | return ASF_ERROR_INVALID_LENGTH; | ||
190 | } | ||
191 | |||
192 | if (ci->read_filebuf(&tmp8, 1) == 0) { | ||
193 | return ASF_ERROR_EOF; | ||
194 | } | ||
195 | //DEBUGF("stream = %u\n",tmp8&0x7f); | ||
196 | bytesread++; | ||
197 | |||
198 | if ((tmp8 & 0x7f) != wfx->audiostream) { | ||
199 | /* Not interested in this packet, just skip it */ | ||
200 | ci->advance_buffer(length - bytesread); | ||
201 | return 0; | ||
202 | } else { | ||
203 | /* We are now at the data */ | ||
204 | //DEBUGF("Read packet - length=%u, padding_length=%u, send_time=%u, duration=%u, payload_count=%d, bytesread=%d\n",length,padding_length,(int)send_time,duration,payload_count,bytesread); | ||
205 | |||
206 | /* TODO: Loop through all payloads in this packet - or do we | ||
207 | assume that audio streams only have one payload per packet? */ | ||
208 | |||
209 | payload_hdrlen = GETLEN2b(packet_property & 0x03) + | ||
210 | GETLEN2b((packet_property >> 2) & 0x03) + | ||
211 | GETLEN2b((packet_property >> 4) & 0x03); | ||
212 | |||
213 | //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen); | ||
214 | #if 0 | ||
215 | /* TODO */ | ||
216 | if (payload_hdrlen > size) { | ||
217 | return ASF_ERROR_INVALID_LENGTH; | ||
218 | } | ||
219 | #endif | ||
220 | if (payload_hdrlen > sizeof(data)) { | ||
221 | DEBUGF("Unexpectedly long datalen in data - %d\n",datalen); | ||
222 | return ASF_ERROR_OUTOFMEM; | ||
223 | } | ||
224 | |||
225 | if (ci->read_filebuf(data, payload_hdrlen) == 0) { | ||
226 | return ASF_ERROR_EOF; | ||
227 | } | ||
228 | bytesread += payload_hdrlen; | ||
229 | |||
230 | datap = data; | ||
231 | media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap); | ||
232 | datap += GETLEN2b((packet_property >> 4) & 0x03); | ||
233 | media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap); | ||
234 | datap += GETLEN2b((packet_property >> 2) & 0x03); | ||
235 | replicated_length = GETVALUE2b(packet_property & 0x03, datap); | ||
236 | datap += GETLEN2b(packet_property & 0x03); | ||
237 | |||
238 | /* TODO: Validate replicated_length */ | ||
239 | /* TODO: Is the content of this important for us? */ | ||
240 | ci->advance_buffer(replicated_length); | ||
241 | bytesread += replicated_length; | ||
242 | |||
243 | |||
244 | multiple = packet_flags & 0x01; | ||
245 | |||
246 | if (multiple) { | ||
247 | int x; | ||
248 | |||
249 | x = GETLEN2b(payload_length_type); | ||
250 | |||
251 | if (x != 2) { | ||
252 | /* in multiple payloads datalen should be a word */ | ||
253 | return ASF_ERROR_INVALID_VALUE; | ||
254 | } | ||
255 | |||
256 | #if 0 | ||
257 | if (skip + tmp > datalen) { | ||
258 | /* not enough data */ | ||
259 | return ASF_ERROR_INVALID_LENGTH; | ||
260 | } | ||
261 | #endif | ||
262 | if (ci->read_filebuf(&data, x) == 0) { | ||
263 | return ASF_ERROR_EOF; | ||
264 | } | ||
265 | bytesread += x; | ||
266 | payload_datalen = GETVALUE2b(payload_length_type, data); | ||
267 | } else { | ||
268 | payload_datalen = length - bytesread; | ||
269 | } | ||
270 | |||
271 | //DEBUGF("WE HAVE DATA - %d bytes\n", payload_datalen); | ||
272 | // lseek(fd, payload_datalen, SEEK_CUR); | ||
273 | *padding = padding_length; | ||
274 | return payload_datalen; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | /* this is the codec entry point */ | ||
279 | enum codec_status codec_main(void) | ||
280 | { | ||
281 | uint32_t samplesdone; | ||
282 | uint32_t elapsedtime; | ||
283 | int retval; | ||
284 | asf_waveformatex_t wfx; | ||
285 | uint32_t currentframe; | ||
286 | unsigned char* inbuffer; | ||
287 | size_t resume_offset; | ||
288 | size_t n; | ||
289 | int wmares, res, padding, outbufsize; | ||
290 | uint8_t* outbuf; | ||
291 | |||
292 | /* Generic codec initialisation */ | ||
293 | ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512); | ||
294 | ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, 1024*128); | ||
295 | |||
296 | ci->configure(DSP_SET_SAMPLE_DEPTH, 15); | ||
297 | |||
298 | next_track: | ||
299 | |||
300 | /* Wait for the metadata to be read */ | ||
301 | while (!*ci->taginfo_ready && !ci->stop_codec) | ||
302 | ci->sleep(1); | ||
303 | |||
304 | retval = CODEC_OK; | ||
305 | |||
306 | /* Remember the resume position - when the codec is opened, the | ||
307 | playback engine will reset it. */ | ||
308 | resume_offset = ci->id3->offset; | ||
309 | |||
310 | if (codec_init()) { | ||
311 | LOGF("WMA: Error initialising codec\n"); | ||
312 | retval = CODEC_ERROR; | ||
313 | goto exit; | ||
314 | } | ||
315 | |||
316 | outbuf = codec_malloc(OUTBUF_SIZE); | ||
317 | |||
318 | /* Copy the format metadata we've stored in the id3 TOC field. This | ||
319 | saves us from parsing it again here. */ | ||
320 | memcpy(&wfx, ci->id3->toc, sizeof(wfx)); | ||
321 | |||
322 | wma_decode_init(&wmadec,&wfx); | ||
323 | |||
324 | /* Now advance the file position to the first frame */ | ||
325 | ci->seek_buffer(ci->id3->first_frame_offset); | ||
326 | |||
327 | ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate); | ||
328 | ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ? | ||
329 | STEREO_MONO : STEREO_INTERLEAVED); | ||
330 | codec_set_replaygain(ci->id3); | ||
331 | |||
332 | /* The main decoding loop */ | ||
333 | |||
334 | currentframe = 0; | ||
335 | samplesdone = 0; | ||
336 | |||
337 | DEBUGF("**************** IN WMA.C ******************\n"); | ||
338 | wma_decode_init(&wmadec,&wfx); | ||
339 | |||
340 | res = 1; | ||
341 | while (res >= 0) | ||
342 | { | ||
343 | ci->yield(); | ||
344 | if (ci->stop_codec || ci->new_track) { | ||
345 | goto done; | ||
346 | } | ||
347 | |||
348 | /* Deal with any pending seek requests - ignore them */ | ||
349 | if (ci->seek_time) | ||
350 | { | ||
351 | ci->seek_complete(); | ||
352 | } | ||
353 | |||
354 | res = asf_read_packet(&padding, &wfx); | ||
355 | if (res > 0) { | ||
356 | inbuffer = ci->request_buffer(&n, res - padding); | ||
357 | |||
358 | wmares = wma_decode_superframe(&wmadec, | ||
359 | outbuf,&outbufsize, | ||
360 | inbuffer,res - padding); | ||
361 | |||
362 | ci->advance_buffer(res); | ||
363 | |||
364 | if (wmares > 0) { | ||
365 | ci->pcmbuf_insert(outbuf, NULL, outbufsize / (wfx.channels * 2)); | ||
366 | samplesdone += (outbufsize / (wfx.channels * 2)); | ||
367 | DEBUGF("Decoded %d samples\n",(outbufsize / (wfx.channels * 2))); | ||
368 | elapsedtime = (samplesdone*10)/(wfx.rate/100); | ||
369 | ci->set_elapsed(elapsedtime); | ||
370 | } | ||
371 | |||
372 | ci->yield (); | ||
373 | } | ||
374 | } | ||
375 | retval = CODEC_OK; | ||
376 | |||
377 | done: | ||
378 | LOGF("WMA: Decoded %ld samples\n",samplesdone); | ||
379 | |||
380 | if (ci->request_next_track()) | ||
381 | goto next_track; | ||
382 | |||
383 | exit: | ||
384 | return retval; | ||
385 | } | ||