Use the correct variable name - fixes warning
[Rockbox.git] / apps / codecs / wma.c
blobb2f837beb51992bf8faa5e3dcc70a68e7f9c7591
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Dave Chapman
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.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "codeclib.h"
21 #include "libwma/asf.h"
22 #include "libwma/wmadec.h"
24 CODEC_HEADER
26 int packet_count=0;
28 /* The output buffer containing the decoded samples (channels 0 and 1)
29 BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2.
32 static uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS];
34 /* NOTE: WMADecodeContext is 120152 bytes (on x86) */
35 static WMADecodeContext wmadec;
37 enum asf_error_e {
38 ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
39 ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */
40 ASF_ERROR_EOF = -3, /* unexpected end of file */
41 ASF_ERROR_IO = -4, /* error reading or writing to file */
42 ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */
43 ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */
44 ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
45 ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
46 ASF_ERROR_SEEKABLE = -9, /* file not seekable */
47 ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */
50 /* Read an unaligned 32-bit little endian long from buffer. */
51 static unsigned long get_long_le(void* buf)
53 unsigned char* p = (unsigned char*) buf;
55 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
58 /* Read an unaligned 16-bit little endian short from buffer. */
59 static unsigned short get_short_le(void* buf)
61 unsigned char* p = (unsigned char*) buf;
63 return p[0] | (p[1] << 8);
66 #define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits)
68 #define GETVALUE2b(bits, data) \
69 (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \
70 0 : *(data) : get_short_le(data) : get_long_le(data))
72 static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx)
74 uint8_t tmp8, packet_flags, packet_property;
75 int stream_id;
76 int ec_length, opaque_data, ec_length_type;
77 int datalen;
78 uint8_t data[18];
79 uint8_t* datap;
80 uint32_t length;
81 uint32_t padding_length;
82 uint32_t send_time;
83 uint16_t duration;
84 uint16_t payload_count;
85 int payload_length_type;
86 uint32_t payload_hdrlen;
87 int payload_datalen;
88 int multiple;
89 uint32_t replicated_length;
90 uint32_t media_object_number;
91 uint32_t media_object_offset;
92 uint32_t bytesread = 0;
93 uint8_t* buf;
94 size_t bufsize;
95 int i;
96 DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);
98 if (ci->read_filebuf(&tmp8, 1) == 0) {
99 return ASF_ERROR_EOF;
101 bytesread++;
103 //DEBUGF("tmp8=0x%02x\n",tmp8);
104 /* TODO: We need a better way to detect endofstream */
105 if (tmp8 != 0x82) {
106 DEBUGF("Read failed: packet did not sync\n");
107 return -1;
111 if (tmp8 & 0x80) {
112 ec_length = tmp8 & 0x0f;
113 opaque_data = (tmp8 >> 4) & 0x01;
114 ec_length_type = (tmp8 >> 5) & 0x03;
116 if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) {
117 DEBUGF("incorrect error correction flags\n");
118 return ASF_ERROR_INVALID_VALUE;
121 /* Skip ec_data */
122 ci->advance_buffer(ec_length);
123 bytesread += ec_length;
124 } else {
125 ec_length = 0;
128 if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; }
129 if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; }
130 bytesread += 2;
132 datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
133 GETLEN2b((packet_flags >> 3) & 0x03) +
134 GETLEN2b((packet_flags >> 5) & 0x03) + 6;
136 #if 0
137 if (datalen > sizeof(data)) {
138 DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
139 return ASF_ERROR_OUTOFMEM;
141 #endif
143 if (ci->read_filebuf(data, datalen) == 0) {
144 return ASF_ERROR_EOF;
147 bytesread += datalen;
149 datap = data;
150 length = GETVALUE2b((packet_flags >> 5) & 0x03, datap);
151 datap += GETLEN2b((packet_flags >> 5) & 0x03);
152 /* sequence value is not used */
153 GETVALUE2b((packet_flags >> 1) & 0x03, datap);
154 datap += GETLEN2b((packet_flags >> 1) & 0x03);
155 padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
156 datap += GETLEN2b((packet_flags >> 3) & 0x03);
157 send_time = get_long_le(datap);
158 datap += 4;
159 duration = get_short_le(datap);
160 datap += 2;
161 DEBUGF("and duration %d ms\n", duration);
163 /* this is really idiotic, packet length can (and often will) be
164 * undefined and we just have to use the header packet size as the size
165 * value */
166 if (!((packet_flags >> 5) & 0x03)) {
167 length = wfx->packet_size;
170 /* this is also really idiotic, if packet length is smaller than packet
171 * size, we need to manually add the additional bytes into padding length
173 if (length < wfx->packet_size) {
174 padding_length += wfx->packet_size - length;
175 length = wfx->packet_size;
178 if (length > wfx->packet_size) {
179 DEBUGF("packet with too big length value\n");
180 return ASF_ERROR_INVALID_LENGTH;
183 /* check if we have multiple payloads */
184 if (packet_flags & 0x01) {
185 if (ci->read_filebuf(&tmp8, 1) == 0) {
186 return ASF_ERROR_EOF;
188 payload_count = tmp8 & 0x3f;
189 payload_length_type = (tmp8 >> 6) & 0x03;
190 bytesread++;
191 } else {
192 payload_count = 1;
193 payload_length_type = 0x02; /* not used */
196 if (length < bytesread) {
197 DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread);
198 /* FIXME: should this be checked earlier? */
199 return ASF_ERROR_INVALID_LENGTH;
203 /* We now parse the individual payloads, and move all payloads
204 belonging to our audio stream to a contiguous block, starting at
205 the location of the first payload.
208 *audiobuf = NULL;
209 *audiobufsize = 0;
210 *packetlength = length - bytesread;
212 buf = ci->request_buffer(&bufsize, length);
213 datap = buf;
215 if (bufsize != length) {
216 /* This should only happen with packets larger than 32KB (the
217 guard buffer size). All the streams I've seen have
218 relatively small packets less than about 8KB), but I don't
219 know what is expected.
221 DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n",
222 (int)length,(int)bufsize,(int)ci->curpos);
223 return -1;
226 for (i=0; i<payload_count; i++) {
227 stream_id = datap[0]&0x7f;
228 datap++;
229 bytesread++;
231 payload_hdrlen = GETLEN2b(packet_property & 0x03) +
232 GETLEN2b((packet_property >> 2) & 0x03) +
233 GETLEN2b((packet_property >> 4) & 0x03);
235 //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen);
236 #if 0
237 /* TODO */
238 if (payload_hdrlen > size) {
239 return ASF_ERROR_INVALID_LENGTH;
241 #endif
242 if (payload_hdrlen > sizeof(data)) {
243 DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
244 return ASF_ERROR_OUTOFMEM;
247 bytesread += payload_hdrlen;
248 media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap);
249 datap += GETLEN2b((packet_property >> 4) & 0x03);
250 media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap);
251 datap += GETLEN2b((packet_property >> 2) & 0x03);
252 replicated_length = GETVALUE2b(packet_property & 0x03, datap);
253 datap += GETLEN2b(packet_property & 0x03);
255 /* TODO: Validate replicated_length */
256 /* TODO: Is the content of this important for us? */
257 datap += replicated_length;
258 bytesread += replicated_length;
260 multiple = packet_flags & 0x01;
263 if (multiple) {
264 int x;
266 x = GETLEN2b(payload_length_type);
268 if (x != 2) {
269 /* in multiple payloads datalen should be a word */
270 return ASF_ERROR_INVALID_VALUE;
273 #if 0
274 if (skip + tmp > datalen) {
275 /* not enough data */
276 return ASF_ERROR_INVALID_LENGTH;
278 #endif
279 payload_datalen = GETVALUE2b(payload_length_type, datap);
280 datap += x;
281 bytesread += x;
282 } else {
283 payload_datalen = length - bytesread - padding_length;
286 if (stream_id == wfx->audiostream)
288 if (*audiobuf == NULL) {
289 /* The first payload can stay where it is */
290 *audiobuf = datap;
291 *audiobufsize = payload_datalen;
292 } else {
293 /* The second and subsequent payloads in this packet
294 that belong to the audio stream need to be moved to be
295 contiguous with the first payload.
297 memmove(*audiobuf + *audiobufsize, datap, payload_datalen);
298 *audiobufsize += payload_datalen;
301 datap += payload_datalen;
302 bytesread += payload_datalen;
305 if (*audiobuf != NULL)
306 return 1;
307 else
308 return 0;
312 static int get_timestamp(int *duration){
314 uint8_t tmp8, packet_flags, packet_property;
315 //int stream_id;
316 int ec_length, opaque_data, ec_length_type;
317 int datalen;
318 uint8_t data[18];
319 uint8_t* datap;
320 uint32_t length;
321 uint32_t padding_length;
322 uint32_t send_time;
324 //uint16_t payload_count;
325 uint32_t bytesread = 0;
326 packet_count++;
327 if (ci->read_filebuf(&tmp8, 1) == 0) {
328 DEBUGF("ASF ERROR (EOF?)\n");
329 return ASF_ERROR_EOF;
331 bytesread++;
333 /* TODO: We need a better way to detect endofstream */
334 if (tmp8 != 0x82) {
335 DEBUGF("Get timestamp: Detected end of stream\n");
337 return ASF_ERROR_EOF; }
340 if (tmp8 & 0x80) {
341 ec_length = tmp8 & 0x0f;
342 opaque_data = (tmp8 >> 4) & 0x01;
343 ec_length_type = (tmp8 >> 5) & 0x03;
345 if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) {
346 DEBUGF("incorrect error correction flags\n");
347 return ASF_ERROR_INVALID_VALUE;
350 /* Skip ec_data */
351 ci->advance_buffer(ec_length);
352 bytesread += ec_length;
353 } else {
354 ec_length = 0;
357 if (ci->read_filebuf(&packet_flags, 1) == 0) { DEBUGF("Detected end of stream 2\n"); return ASF_ERROR_EOF; }
358 if (ci->read_filebuf(&packet_property, 1) == 0) {DEBUGF("Detected end of stream3\n"); return ASF_ERROR_EOF; }
359 bytesread += 2;
361 datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
362 GETLEN2b((packet_flags >> 3) & 0x03) +
363 GETLEN2b((packet_flags >> 5) & 0x03) + 6;
365 if (ci->read_filebuf(data, datalen) == 0) {
366 DEBUGF("Detected end of stream4\n");
367 return ASF_ERROR_EOF;
370 bytesread += datalen;
372 datap = data;
373 length = GETVALUE2b((packet_flags >> 5) & 0x03, datap);
374 datap += GETLEN2b((packet_flags >> 5) & 0x03);
375 /* sequence value is not used */
376 GETVALUE2b((packet_flags >> 1) & 0x03, datap);
377 datap += GETLEN2b((packet_flags >> 1) & 0x03);
378 padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
379 datap += GETLEN2b((packet_flags >> 3) & 0x03);
380 send_time = get_long_le(datap);
381 datap += 4;
382 *duration = get_short_le(datap);
384 return send_time;
387 /*entry point for seeks*/
388 static int seek(int ms, asf_waveformatex_t* wfx){
389 int time, duration, delta, temp, count=0;
391 /*estimate packet number from bitrate*/
392 int initial_packet = ci->curpos/wfx->packet_size;
393 int packet_num = (ms*(wfx->bitrate>>3))/wfx->packet_size/1000;
394 int last_packet = ci->id3->filesize / wfx->packet_size;
396 if(packet_num > last_packet){
397 packet_num = last_packet;
399 /*calculate byte address of the start of that packet*/
400 int packet_offset = packet_num*wfx->packet_size;
402 /*seek to estimated packet*/
403 ci->seek_buffer(ci->id3->first_frame_offset+packet_offset);
404 temp = ms;
405 while(1){
406 /*for very large files it can be difficult and unimportant to find the exact packet*/
407 count++;
409 /*check the time stamp of our packet*/
410 time = get_timestamp(&duration);
411 DEBUGF("seeked to %d ms with duration %d\n", time, duration);
413 if(time < 0){
414 /*unknown error, try to recover*/
415 DEBUGF("UKNOWN SEEK ERROR\n");
416 ci->seek_buffer(ci->id3->first_frame_offset+initial_packet*wfx->packet_size);
417 return ms;
420 if((time+duration>=ms && time<=ms) || count > 10){
421 /*the get_timestamp function advances us 12 bytes past the packet start*/
422 ci->seek_buffer(ci->curpos-12);
423 DEBUGF("Found our packet! Now at %d packet\n", packet_num);
424 return time;
425 }else {
426 /*seek again*/
427 delta = ms-time;
428 /*estimate new packet number from bitrate and our current position*/
429 temp += delta;
430 packet_num = ((temp/1000)*(wfx->bitrate>>3) - (wfx->packet_size>>1))/wfx->packet_size; //round down!
431 packet_offset = packet_num*wfx->packet_size;
432 ci->seek_buffer(ci->id3->first_frame_offset+packet_offset);
439 /* this is the codec entry point */
440 enum codec_status codec_main(void)
442 uint32_t elapsedtime;
443 int retval;
444 asf_waveformatex_t wfx;
445 uint32_t currentframe;
446 size_t resume_offset;
447 int i;
448 int wmares, res;
449 uint8_t* audiobuf;
450 int audiobufsize;
451 int packetlength;
452 int errcount = 0;
454 /* Generic codec initialisation */
455 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
457 ci->configure(DSP_SET_SAMPLE_DEPTH, 30);
459 next_track:
461 /* Wait for the metadata to be read */
462 while (!*ci->taginfo_ready && !ci->stop_codec)
463 ci->sleep(1);
465 retval = CODEC_OK;
467 /* Remember the resume position - when the codec is opened, the
468 playback engine will reset it. */
469 resume_offset = ci->id3->offset;
471 if (codec_init()) {
472 LOGF("WMA: Error initialising codec\n");
473 retval = CODEC_ERROR;
474 goto exit;
477 /* Copy the format metadata we've stored in the id3 TOC field. This
478 saves us from parsing it again here. */
479 memcpy(&wfx, ci->id3->toc, sizeof(wfx));
481 if (wma_decode_init(&wmadec,&wfx) < 0) {
482 LOGF("WMA: Unsupported or corrupt file\n");
483 retval = CODEC_ERROR;
484 goto exit;
487 /* Now advance the file position to the first frame */
488 ci->seek_buffer(ci->id3->first_frame_offset);
490 ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
491 ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
492 STEREO_MONO : STEREO_INTERLEAVED);
493 codec_set_replaygain(ci->id3);
495 /* The main decoding loop */
497 currentframe = 0;
498 elapsedtime = 0;
500 DEBUGF("**************** IN WMA.C ******************\n");
501 wma_decode_init(&wmadec,&wfx);
503 res = 1;
504 while (res >= 0)
506 ci->yield();
507 if (ci->stop_codec || ci->new_track) {
508 goto done;
511 /* Deal with any pending seek requests */
512 if (ci->seek_time){
514 if (ci->seek_time == 1) {
515 ci->seek_complete();
516 goto next_track; /* Pretend you never saw this... */
519 elapsedtime = seek(ci->seek_time, &wfx);
520 if(elapsedtime < 1){
521 ci->seek_complete();
522 goto next_track;
524 DEBUGF("Seek returned %d\n", (int)elapsedtime);
525 ci->set_elapsed(elapsedtime);
527 /*flush the wma decoder state*/
528 wmadec.last_superframe_len = 0;
529 wmadec.last_bitoffset = 0;
530 ci->seek_complete();
532 errcount = 0;
533 new_packet:
534 res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
535 if(res < 0){
537 /* We'll try to recover from a parse error a certain number of
538 * times. If we succeed, the error counter will be reset.
541 errcount++;
542 DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
543 if (errcount > 5) {
544 goto done;
545 } else {
546 ci->advance_buffer(packetlength);
547 goto new_packet;
550 }else if (res > 0) {
551 wma_decode_superframe_init(&wmadec,
552 audiobuf, audiobufsize);
554 for (i=0; i < wmadec.nb_frames; i++)
556 wmares = wma_decode_superframe_frame(&wmadec,
557 decoded,
558 audiobuf, audiobufsize);
560 ci->yield ();
562 if (wmares < 0){
563 /* Do the above, but for errors in decode.
565 errcount++;
566 DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
567 if (errcount > 5) {
568 goto done;
569 } else {
570 ci->advance_buffer(packetlength);
571 goto new_packet;
573 } else if (wmares > 0) {
574 ci->pcmbuf_insert(decoded, NULL, wmares);
575 elapsedtime += (wmares*10)/(wfx.rate/100);
576 ci->set_elapsed(elapsedtime);
578 ci->yield();
582 ci->advance_buffer(packetlength);
584 retval = CODEC_OK;
586 done:
587 LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);
589 if (ci->request_next_track())
590 goto next_track;
591 exit:
592 return retval;