blob: a4a78c29cef85ebde806e49044d7ca6aa1091a9b [file] [log] [blame]
Dave Chapmanc7282472007-07-03 09:25:36 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
Dave Chapman9cb40ea2007-07-03 10:21:23 +00008 * $Id$
Dave Chapmanc7282472007-07-03 09:25:36 +00009 *
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
24CODEC_HEADER
25
Michael Giacomellif8613202007-11-22 05:32:26 +000026int packet_count=0;
27
Dave Chapman88e32c22007-07-09 17:24:00 +000028/* The output buffer containing the decoded samples (channels 0 and 1)
29 BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2.
30 */
Dave Chapmanc7282472007-07-03 09:25:36 +000031
Marcoen Hirschbergc43629f2007-08-06 23:34:28 +000032static uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS];
Dave Chapmanc7282472007-07-03 09:25:36 +000033
Dave Chapmane6a3f7d2007-07-18 09:43:12 +000034/* NOTE: WMADecodeContext is 120152 bytes (on x86) */
Dave Chapmanc7282472007-07-03 09:25:36 +000035static WMADecodeContext wmadec;
36
Dave Chapmanc7282472007-07-03 09:25:36 +000037enum 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 */
48};
49
50/* Read an unaligned 32-bit little endian long from buffer. */
51static unsigned long get_long_le(void* buf)
52{
53 unsigned char* p = (unsigned char*) buf;
54
55 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
56}
57
58/* Read an unaligned 16-bit little endian short from buffer. */
59static unsigned short get_short_le(void* buf)
60{
61 unsigned char* p = (unsigned char*) buf;
62
63 return p[0] | (p[1] << 8);
64}
65
66#define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits)
67
68#define GETVALUE2b(bits, data) \
69 (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \
70 0 : *(data) : get_short_le(data) : get_long_le(data))
71
Dave Chapman47e96922007-07-10 13:37:16 +000072static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx)
Dave Chapmanc7282472007-07-03 09:25:36 +000073{
74 uint8_t tmp8, packet_flags, packet_property;
Dave Chapman47e96922007-07-10 13:37:16 +000075 int stream_id;
Dave Chapmanc7282472007-07-03 09:25:36 +000076 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;
Dave Chapman47e96922007-07-10 13:37:16 +000093 uint8_t* buf;
94 size_t bufsize;
95 int i;
Michael Giacomellif8613202007-11-22 05:32:26 +000096 DEBUGF("Reading new packet at %d bytes ", (int)ci->curpos);
Dave Chapmanc7282472007-07-03 09:25:36 +000097
98 if (ci->read_filebuf(&tmp8, 1) == 0) {
99 return ASF_ERROR_EOF;
100 }
101 bytesread++;
102
Dave Chapman47e96922007-07-10 13:37:16 +0000103 //DEBUGF("tmp8=0x%02x\n",tmp8);
Dave Chapmanc7282472007-07-03 09:25:36 +0000104 /* TODO: We need a better way to detect endofstream */
Michael Giacomellif8613202007-11-22 05:32:26 +0000105 if (tmp8 != 0x82) {
106 DEBUGF("Read failed: packet did not sync\n");
107 return -1;
108 }
Dave Chapmanc7282472007-07-03 09:25:36 +0000109
Dave Chapmanc7282472007-07-03 09:25:36 +0000110
111 if (tmp8 & 0x80) {
112 ec_length = tmp8 & 0x0f;
113 opaque_data = (tmp8 >> 4) & 0x01;
114 ec_length_type = (tmp8 >> 5) & 0x03;
115
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;
119 }
120
121 /* Skip ec_data */
122 ci->advance_buffer(ec_length);
123 bytesread += ec_length;
124 } else {
125 ec_length = 0;
126 }
127
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;
131
132 datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
133 GETLEN2b((packet_flags >> 3) & 0x03) +
134 GETLEN2b((packet_flags >> 5) & 0x03) + 6;
135
136#if 0
137 if (datalen > sizeof(data)) {
138 DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
139 return ASF_ERROR_OUTOFMEM;
140 }
141#endif
142
143 if (ci->read_filebuf(data, datalen) == 0) {
144 return ASF_ERROR_EOF;
145 }
146
147 bytesread += datalen;
148
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;
Michael Giacomellif8613202007-11-22 05:32:26 +0000161 DEBUGF("and duration %d ms\n", duration);
Dave Chapmanc7282472007-07-03 09:25:36 +0000162
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;
168 }
169
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
172 */
173 if (length < wfx->packet_size) {
174 padding_length += wfx->packet_size - length;
175 length = wfx->packet_size;
176 }
177
178 if (length > wfx->packet_size) {
179 DEBUGF("packet with too big length value\n");
180 return ASF_ERROR_INVALID_LENGTH;
181 }
182
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;
187 }
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 */
194 }
195
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;
200 }
201
Dave Chapman47e96922007-07-10 13:37:16 +0000202
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.
206 */
207
208 *audiobuf = NULL;
209 *audiobufsize = 0;
210 *packetlength = length - bytesread;
211
212 buf = ci->request_buffer(&bufsize, length);
213 datap = buf;
214
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.
220 */
Dave Chapmane6a3f7d2007-07-18 09:43:12 +0000221 DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n",
222 (int)length,(int)bufsize,(int)ci->curpos);
Dave Chapman47e96922007-07-10 13:37:16 +0000223 return -1;
Dave Chapmanc7282472007-07-03 09:25:36 +0000224 }
Dave Chapmanc7282472007-07-03 09:25:36 +0000225
Dave Chapman47e96922007-07-10 13:37:16 +0000226 for (i=0; i<payload_count; i++) {
227 stream_id = datap[0]&0x7f;
228 datap++;
229 bytesread++;
Dave Chapmanc7282472007-07-03 09:25:36 +0000230
231 payload_hdrlen = GETLEN2b(packet_property & 0x03) +
232 GETLEN2b((packet_property >> 2) & 0x03) +
233 GETLEN2b((packet_property >> 4) & 0x03);
234
235 //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen);
236#if 0
237 /* TODO */
238 if (payload_hdrlen > size) {
239 return ASF_ERROR_INVALID_LENGTH;
240 }
241#endif
242 if (payload_hdrlen > sizeof(data)) {
243 DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
244 return ASF_ERROR_OUTOFMEM;
245 }
246
Dave Chapmanc7282472007-07-03 09:25:36 +0000247 bytesread += payload_hdrlen;
Dave Chapmanc7282472007-07-03 09:25:36 +0000248 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);
254
255 /* TODO: Validate replicated_length */
256 /* TODO: Is the content of this important for us? */
Dave Chapman47e96922007-07-10 13:37:16 +0000257 datap += replicated_length;
Dave Chapmanc7282472007-07-03 09:25:36 +0000258 bytesread += replicated_length;
259
Dave Chapmanc7282472007-07-03 09:25:36 +0000260 multiple = packet_flags & 0x01;
261
Michael Giacomellif8613202007-11-22 05:32:26 +0000262
Dave Chapmanc7282472007-07-03 09:25:36 +0000263 if (multiple) {
264 int x;
265
266 x = GETLEN2b(payload_length_type);
267
268 if (x != 2) {
269 /* in multiple payloads datalen should be a word */
270 return ASF_ERROR_INVALID_VALUE;
271 }
272
273#if 0
274 if (skip + tmp > datalen) {
275 /* not enough data */
276 return ASF_ERROR_INVALID_LENGTH;
277 }
278#endif
Dave Chapman47e96922007-07-10 13:37:16 +0000279 payload_datalen = GETVALUE2b(payload_length_type, datap);
280 datap += x;
Dave Chapmanc7282472007-07-03 09:25:36 +0000281 bytesread += x;
Dave Chapmanc7282472007-07-03 09:25:36 +0000282 } else {
Dave Chapman47e96922007-07-10 13:37:16 +0000283 payload_datalen = length - bytesread - padding_length;
Dave Chapmanc7282472007-07-03 09:25:36 +0000284 }
285
Dave Chapman47e96922007-07-10 13:37:16 +0000286 if (stream_id == wfx->audiostream)
287 {
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.
296 */
297 memmove(*audiobuf + *audiobufsize, datap, payload_datalen);
298 *audiobufsize += payload_datalen;
299 }
300 }
301 datap += payload_datalen;
302 bytesread += payload_datalen;
Dave Chapmanc7282472007-07-03 09:25:36 +0000303 }
Dave Chapman47e96922007-07-10 13:37:16 +0000304
305 if (*audiobuf != NULL)
306 return 1;
307 else
308 return 0;
Dave Chapmanc7282472007-07-03 09:25:36 +0000309}
310
Michael Giacomellif8613202007-11-22 05:32:26 +0000311
Dave Chapman05b158f2007-12-01 02:46:31 +0000312static int get_timestamp(int *duration)
313{
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;
Michael Giacomellif8613202007-11-22 05:32:26 +0000323
Dave Chapman05b158f2007-12-01 02:46:31 +0000324 //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");
Michael Giacomellif8613202007-11-22 05:32:26 +0000329 return ASF_ERROR_EOF;
Dave Chapman05b158f2007-12-01 02:46:31 +0000330 }
331 bytesread++;
Michael Giacomellif8613202007-11-22 05:32:26 +0000332
Dave Chapman05b158f2007-12-01 02:46:31 +0000333 /* TODO: We need a better way to detect endofstream */
334 if (tmp8 != 0x82) {
335 DEBUGF("Get timestamp: Detected end of stream\n");
336 return ASF_ERROR_EOF;
337 }
Michael Giacomellif8613202007-11-22 05:32:26 +0000338
339
Dave Chapman05b158f2007-12-01 02:46:31 +0000340 if (tmp8 & 0x80) {
Michael Giacomellif8613202007-11-22 05:32:26 +0000341 ec_length = tmp8 & 0x0f;
342 opaque_data = (tmp8 >> 4) & 0x01;
343 ec_length_type = (tmp8 >> 5) & 0x03;
344
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;
348 }
349
350 /* Skip ec_data */
351 ci->advance_buffer(ec_length);
352 bytesread += ec_length;
Dave Chapman05b158f2007-12-01 02:46:31 +0000353 } else {
354 ec_length = 0;
355 }
Michael Giacomellif8613202007-11-22 05:32:26 +0000356
Michael Giacomellidadd80a2008-01-06 02:50:01 +0000357 if (ci->read_filebuf(&packet_flags, 1) == 0) {
Dave Chapman05b158f2007-12-01 02:46:31 +0000358 DEBUGF("Detected end of stream 2\n");
359 return ASF_ERROR_EOF;
360 }
Michael Giacomellif8613202007-11-22 05:32:26 +0000361
Dave Chapman05b158f2007-12-01 02:46:31 +0000362 if (ci->read_filebuf(&packet_property, 1) == 0) {
363 DEBUGF("Detected end of stream3\n");
364 return ASF_ERROR_EOF;
365 }
366 bytesread += 2;
Michael Giacomellif8613202007-11-22 05:32:26 +0000367
Dave Chapman05b158f2007-12-01 02:46:31 +0000368 datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
369 GETLEN2b((packet_flags >> 3) & 0x03) +
370 GETLEN2b((packet_flags >> 5) & 0x03) + 6;
Michael Giacomellif8613202007-11-22 05:32:26 +0000371
Dave Chapman05b158f2007-12-01 02:46:31 +0000372 if (ci->read_filebuf(data, datalen) == 0) {
373 DEBUGF("Detected end of stream4\n");
374 return ASF_ERROR_EOF;
375 }
Michael Giacomellif8613202007-11-22 05:32:26 +0000376
Dave Chapman05b158f2007-12-01 02:46:31 +0000377 bytesread += datalen;
Michael Giacomellif8613202007-11-22 05:32:26 +0000378
Dave Chapman05b158f2007-12-01 02:46:31 +0000379 datap = data;
380 length = GETVALUE2b((packet_flags >> 5) & 0x03, datap);
381 datap += GETLEN2b((packet_flags >> 5) & 0x03);
382
383 /* sequence value is not used */
384 GETVALUE2b((packet_flags >> 1) & 0x03, datap);
385 datap += GETLEN2b((packet_flags >> 1) & 0x03);
386 padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
387 datap += GETLEN2b((packet_flags >> 3) & 0x03);
388 send_time = get_long_le(datap);
389 datap += 4;
390 *duration = get_short_le(datap);
391
392 return send_time;
393}
Michael Giacomellif8613202007-11-22 05:32:26 +0000394
395/*entry point for seeks*/
Dave Chapman05b158f2007-12-01 02:46:31 +0000396static int seek(int ms, asf_waveformatex_t* wfx)
397{
Michael Giacomelli1a5d5942007-11-30 00:46:32 +0000398 int time, duration, delta, temp, count=0;
Michael Giacomellif8613202007-11-22 05:32:26 +0000399
400 /*estimate packet number from bitrate*/
401 int initial_packet = ci->curpos/wfx->packet_size;
402 int packet_num = (ms*(wfx->bitrate>>3))/wfx->packet_size/1000;
403 int last_packet = ci->id3->filesize / wfx->packet_size;
404
Dave Chapman05b158f2007-12-01 02:46:31 +0000405 if (packet_num > last_packet) {
Michael Giacomellif8613202007-11-22 05:32:26 +0000406 packet_num = last_packet;
407 }
Dave Chapman05b158f2007-12-01 02:46:31 +0000408
Michael Giacomellif8613202007-11-22 05:32:26 +0000409 /*calculate byte address of the start of that packet*/
410 int packet_offset = packet_num*wfx->packet_size;
411
412 /*seek to estimated packet*/
413 ci->seek_buffer(ci->id3->first_frame_offset+packet_offset);
414 temp = ms;
Dave Chapman05b158f2007-12-01 02:46:31 +0000415 while (1)
416 {
Michael Giacomelli1a5d5942007-11-30 00:46:32 +0000417 /*for very large files it can be difficult and unimportant to find the exact packet*/
418 count++;
Michael Giacomellif8613202007-11-22 05:32:26 +0000419
420 /*check the time stamp of our packet*/
421 time = get_timestamp(&duration);
422 DEBUGF("seeked to %d ms with duration %d\n", time, duration);
423
Dave Chapman05b158f2007-12-01 02:46:31 +0000424 if (time < 0) {
Michael Giacomellif8613202007-11-22 05:32:26 +0000425 /*unknown error, try to recover*/
426 DEBUGF("UKNOWN SEEK ERROR\n");
427 ci->seek_buffer(ci->id3->first_frame_offset+initial_packet*wfx->packet_size);
Michael Giacomellidadd80a2008-01-06 02:50:01 +0000428 /*seek failed so return time stamp of the initial packet*/
429 return get_timestamp(&duration);
Michael Giacomellif8613202007-11-22 05:32:26 +0000430 }
431
Dave Chapman05b158f2007-12-01 02:46:31 +0000432 if ((time+duration>=ms && time<=ms) || count > 10) {
Michael Giacomellif8613202007-11-22 05:32:26 +0000433 /*the get_timestamp function advances us 12 bytes past the packet start*/
434 ci->seek_buffer(ci->curpos-12);
435 DEBUGF("Found our packet! Now at %d packet\n", packet_num);
436 return time;
Dave Chapman05b158f2007-12-01 02:46:31 +0000437 } else {
Michael Giacomellif8613202007-11-22 05:32:26 +0000438 /*seek again*/
439 delta = ms-time;
Michael Giacomellif8613202007-11-22 05:32:26 +0000440 /*estimate new packet number from bitrate and our current position*/
441 temp += delta;
Michael Giacomelli1a5d5942007-11-30 00:46:32 +0000442 packet_num = ((temp/1000)*(wfx->bitrate>>3) - (wfx->packet_size>>1))/wfx->packet_size; //round down!
Michael Giacomellif8613202007-11-22 05:32:26 +0000443 packet_offset = packet_num*wfx->packet_size;
Michael Giacomellif8613202007-11-22 05:32:26 +0000444 ci->seek_buffer(ci->id3->first_frame_offset+packet_offset);
445 }
Dave Chapman05b158f2007-12-01 02:46:31 +0000446 }
447}
Michael Giacomellif8613202007-11-22 05:32:26 +0000448
449
450
Dave Chapmanc7282472007-07-03 09:25:36 +0000451/* this is the codec entry point */
452enum codec_status codec_main(void)
453{
Dave Chapmanc7282472007-07-03 09:25:36 +0000454 uint32_t elapsedtime;
455 int retval;
456 asf_waveformatex_t wfx;
457 uint32_t currentframe;
Dave Chapmanc7282472007-07-03 09:25:36 +0000458 size_t resume_offset;
Dave Chapman88e32c22007-07-09 17:24:00 +0000459 int i;
Dave Chapman47e96922007-07-10 13:37:16 +0000460 int wmares, res;
461 uint8_t* audiobuf;
462 int audiobufsize;
463 int packetlength;
Michael Giacomellif8613202007-11-22 05:32:26 +0000464 int errcount = 0;
Dave Chapmanc7282472007-07-03 09:25:36 +0000465
466 /* Generic codec initialisation */
467 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
Dave Chapmanc7282472007-07-03 09:25:36 +0000468
Marcoen Hirschbergc43629f2007-08-06 23:34:28 +0000469 ci->configure(DSP_SET_SAMPLE_DEPTH, 30);
Dave Chapmanc7282472007-07-03 09:25:36 +0000470
Thom Johansena478e462007-10-17 12:51:37 +0000471next_track:
Dave Chapmanc7282472007-07-03 09:25:36 +0000472
473 /* Wait for the metadata to be read */
474 while (!*ci->taginfo_ready && !ci->stop_codec)
475 ci->sleep(1);
476
477 retval = CODEC_OK;
478
479 /* Remember the resume position - when the codec is opened, the
480 playback engine will reset it. */
481 resume_offset = ci->id3->offset;
Michael Giacomelli28b0c582007-07-11 16:58:11 +0000482
Dave Chapmanc7282472007-07-03 09:25:36 +0000483 if (codec_init()) {
484 LOGF("WMA: Error initialising codec\n");
485 retval = CODEC_ERROR;
486 goto exit;
487 }
488
Michael Giacomelli28b0c582007-07-11 16:58:11 +0000489 /* Copy the format metadata we've stored in the id3 TOC field. This
Dave Chapmanc7282472007-07-03 09:25:36 +0000490 saves us from parsing it again here. */
491 memcpy(&wfx, ci->id3->toc, sizeof(wfx));
492
Dave Chapmana9df7132007-07-11 23:07:41 +0000493 if (wma_decode_init(&wmadec,&wfx) < 0) {
494 LOGF("WMA: Unsupported or corrupt file\n");
495 retval = CODEC_ERROR;
496 goto exit;
Michael Giacomelli28b0c582007-07-11 16:58:11 +0000497 }
Dave Chapmanc7282472007-07-03 09:25:36 +0000498
499 /* Now advance the file position to the first frame */
500 ci->seek_buffer(ci->id3->first_frame_offset);
501
502 ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
503 ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
504 STEREO_MONO : STEREO_INTERLEAVED);
505 codec_set_replaygain(ci->id3);
506
507 /* The main decoding loop */
508
509 currentframe = 0;
Michael Giacomellif8613202007-11-22 05:32:26 +0000510 elapsedtime = 0;
Dave Chapmanc7282472007-07-03 09:25:36 +0000511
512 DEBUGF("**************** IN WMA.C ******************\n");
513 wma_decode_init(&wmadec,&wfx);
514
515 res = 1;
516 while (res >= 0)
517 {
518 ci->yield();
519 if (ci->stop_codec || ci->new_track) {
520 goto done;
521 }
522
Thom Johansena478e462007-10-17 12:51:37 +0000523 /* Deal with any pending seek requests */
Michael Giacomellif8613202007-11-22 05:32:26 +0000524 if (ci->seek_time){
525
Thom Johansen7113d112007-10-17 18:19:35 +0000526 if (ci->seek_time == 1) {
527 ci->seek_complete();
Thom Johansena478e462007-10-17 12:51:37 +0000528 goto next_track; /* Pretend you never saw this... */
Thom Johansen7113d112007-10-17 18:19:35 +0000529 }
Dave Chapmanc7282472007-07-03 09:25:36 +0000530
Michael Giacomellif8613202007-11-22 05:32:26 +0000531 elapsedtime = seek(ci->seek_time, &wfx);
Dave Chapman05b158f2007-12-01 02:46:31 +0000532 if (elapsedtime < 1){
Michael Giacomellif8613202007-11-22 05:32:26 +0000533 ci->seek_complete();
534 goto next_track;
535 }
Dave Chapman95c117c2007-11-30 19:58:59 +0000536 DEBUGF("Seek returned %d\n", (int)elapsedtime);
Michael Giacomellif8613202007-11-22 05:32:26 +0000537 ci->set_elapsed(elapsedtime);
538
539 /*flush the wma decoder state*/
Michael Giacomelli1a5d5942007-11-30 00:46:32 +0000540 wmadec.last_superframe_len = 0;
541 wmadec.last_bitoffset = 0;
542 ci->seek_complete();
Michael Giacomellif8613202007-11-22 05:32:26 +0000543 }
544 errcount = 0;
545new_packet:
Dave Chapman47e96922007-07-10 13:37:16 +0000546 res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
Michael Giacomellif8613202007-11-22 05:32:26 +0000547
Dave Chapman05b158f2007-12-01 02:46:31 +0000548 if (res < 0) {
549 /* We'll try to recover from a parse error a certain number of
550 * times. If we succeed, the error counter will be reset.
551 */
Michael Giacomellif8613202007-11-22 05:32:26 +0000552
Dave Chapman05b158f2007-12-01 02:46:31 +0000553 errcount++;
Michael Giacomellidadd80a2008-01-06 02:50:01 +0000554 DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
Dave Chapman05b158f2007-12-01 02:46:31 +0000555 if (errcount > 5) {
556 goto done;
557 } else {
558 ci->advance_buffer(packetlength);
559 goto new_packet;
560 }
561 } else if (res > 0) {
562 wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize);
Dave Chapmanc7282472007-07-03 09:25:36 +0000563
Dave Chapman88e32c22007-07-09 17:24:00 +0000564 for (i=0; i < wmadec.nb_frames; i++)
565 {
566 wmares = wma_decode_superframe_frame(&wmadec,
567 decoded,
Dave Chapman47e96922007-07-10 13:37:16 +0000568 audiobuf, audiobufsize);
Michael Giacomelli1a5d5942007-11-30 00:46:32 +0000569
Dave Chapman88e32c22007-07-09 17:24:00 +0000570 ci->yield ();
571
Dave Chapman05b158f2007-12-01 02:46:31 +0000572 if (wmares < 0) {
573 /* Do the above, but for errors in decode. */
Michael Giacomellif8613202007-11-22 05:32:26 +0000574 errcount++;
575 DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
576 if (errcount > 5) {
577 goto done;
578 } else {
579 ci->advance_buffer(packetlength);
580 goto new_packet;
581 }
Dave Chapman88e32c22007-07-09 17:24:00 +0000582 } else if (wmares > 0) {
583 ci->pcmbuf_insert(decoded, NULL, wmares);
Michael Giacomellif8613202007-11-22 05:32:26 +0000584 elapsedtime += (wmares*10)/(wfx.rate/100);
Dave Chapman88e32c22007-07-09 17:24:00 +0000585 ci->set_elapsed(elapsedtime);
586 }
Michael Giacomellif8613202007-11-22 05:32:26 +0000587 ci->yield();
Dave Chapmanc7282472007-07-03 09:25:36 +0000588 }
Dave Chapmanc7282472007-07-03 09:25:36 +0000589 }
Dave Chapman47e96922007-07-10 13:37:16 +0000590
591 ci->advance_buffer(packetlength);
Dave Chapmanc7282472007-07-03 09:25:36 +0000592 }
593 retval = CODEC_OK;
594
595done:
Michael Giacomellif8613202007-11-22 05:32:26 +0000596 LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);
Dave Chapmanc7282472007-07-03 09:25:36 +0000597
598 if (ci->request_next_track())
599 goto next_track;
Dave Chapmanc7282472007-07-03 09:25:36 +0000600exit:
601 return retval;
602}