Improved FPS test plugin: * Better precision for low frame rates (take extra ticks...
[Rockbox.git] / apps / codecs / wma.c
blobbf1184145180b280223d797a1262f292958c88a7
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 /* The output buffer containing the decoded samples (channels 0 and 1)
27 BLOCK_MAX_SIZE is 2048 (samples) and MAX_CHANNELS is 2.
30 static uint32_t decoded[BLOCK_MAX_SIZE * MAX_CHANNELS];
32 /* NOTE: WMADecodeContext is 120152 bytes (on x86) */
33 static WMADecodeContext wmadec;
35 enum asf_error_e {
36 ASF_ERROR_INTERNAL = -1, /* incorrect input to API calls */
37 ASF_ERROR_OUTOFMEM = -2, /* some malloc inside program failed */
38 ASF_ERROR_EOF = -3, /* unexpected end of file */
39 ASF_ERROR_IO = -4, /* error reading or writing to file */
40 ASF_ERROR_INVALID_LENGTH = -5, /* length value conflict in input data */
41 ASF_ERROR_INVALID_VALUE = -6, /* other value conflict in input data */
42 ASF_ERROR_INVALID_OBJECT = -7, /* ASF object missing or in wrong place */
43 ASF_ERROR_OBJECT_SIZE = -8, /* invalid ASF object size (too small) */
44 ASF_ERROR_SEEKABLE = -9, /* file not seekable */
45 ASF_ERROR_SEEK = -10 /* file is seekable but seeking failed */
48 /* Read an unaligned 32-bit little endian long from buffer. */
49 static unsigned long get_long_le(void* buf)
51 unsigned char* p = (unsigned char*) buf;
53 return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
56 /* Read an unaligned 16-bit little endian short from buffer. */
57 static unsigned short get_short_le(void* buf)
59 unsigned char* p = (unsigned char*) buf;
61 return p[0] | (p[1] << 8);
64 #define GETLEN2b(bits) (((bits) == 0x03) ? 4 : bits)
66 #define GETVALUE2b(bits, data) \
67 (((bits) != 0x03) ? ((bits) != 0x02) ? ((bits) != 0x01) ? \
68 0 : *(data) : get_short_le(data) : get_long_le(data))
70 static int asf_read_packet(uint8_t** audiobuf, int* audiobufsize, int* packetlength, asf_waveformatex_t* wfx)
72 uint8_t tmp8, packet_flags, packet_property;
73 int stream_id;
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 uint8_t* buf;
92 size_t bufsize;
93 int i;
95 if (ci->read_filebuf(&tmp8, 1) == 0) {
96 return ASF_ERROR_EOF;
98 bytesread++;
100 //DEBUGF("tmp8=0x%02x\n",tmp8);
101 /* TODO: We need a better way to detect endofstream */
102 if (tmp8 != 0x82) { return -1; }
105 if (tmp8 & 0x80) {
106 ec_length = tmp8 & 0x0f;
107 opaque_data = (tmp8 >> 4) & 0x01;
108 ec_length_type = (tmp8 >> 5) & 0x03;
110 if (ec_length_type != 0x00 || opaque_data != 0 || ec_length != 0x02) {
111 DEBUGF("incorrect error correction flags\n");
112 return ASF_ERROR_INVALID_VALUE;
115 /* Skip ec_data */
116 ci->advance_buffer(ec_length);
117 bytesread += ec_length;
118 } else {
119 ec_length = 0;
122 if (ci->read_filebuf(&packet_flags, 1) == 0) { return ASF_ERROR_EOF; }
123 if (ci->read_filebuf(&packet_property, 1) == 0) { return ASF_ERROR_EOF; }
124 bytesread += 2;
126 datalen = GETLEN2b((packet_flags >> 1) & 0x03) +
127 GETLEN2b((packet_flags >> 3) & 0x03) +
128 GETLEN2b((packet_flags >> 5) & 0x03) + 6;
130 #if 0
131 if (datalen > sizeof(data)) {
132 DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
133 return ASF_ERROR_OUTOFMEM;
135 #endif
137 if (ci->read_filebuf(data, datalen) == 0) {
138 return ASF_ERROR_EOF;
141 bytesread += datalen;
143 datap = data;
144 length = GETVALUE2b((packet_flags >> 5) & 0x03, datap);
145 datap += GETLEN2b((packet_flags >> 5) & 0x03);
146 /* sequence value is not used */
147 GETVALUE2b((packet_flags >> 1) & 0x03, datap);
148 datap += GETLEN2b((packet_flags >> 1) & 0x03);
149 padding_length = GETVALUE2b((packet_flags >> 3) & 0x03, datap);
150 datap += GETLEN2b((packet_flags >> 3) & 0x03);
151 send_time = get_long_le(datap);
152 datap += 4;
153 duration = get_short_le(datap);
154 datap += 2;
156 /* this is really idiotic, packet length can (and often will) be
157 * undefined and we just have to use the header packet size as the size
158 * value */
159 if (!((packet_flags >> 5) & 0x03)) {
160 length = wfx->packet_size;
163 /* this is also really idiotic, if packet length is smaller than packet
164 * size, we need to manually add the additional bytes into padding length
166 if (length < wfx->packet_size) {
167 padding_length += wfx->packet_size - length;
168 length = wfx->packet_size;
171 if (length > wfx->packet_size) {
172 DEBUGF("packet with too big length value\n");
173 return ASF_ERROR_INVALID_LENGTH;
176 /* check if we have multiple payloads */
177 if (packet_flags & 0x01) {
178 if (ci->read_filebuf(&tmp8, 1) == 0) {
179 return ASF_ERROR_EOF;
181 payload_count = tmp8 & 0x3f;
182 payload_length_type = (tmp8 >> 6) & 0x03;
183 bytesread++;
184 } else {
185 payload_count = 1;
186 payload_length_type = 0x02; /* not used */
189 if (length < bytesread) {
190 DEBUGF("header exceeded packet size, invalid file - length=%d, bytesread=%d\n",(int)length,(int)bytesread);
191 /* FIXME: should this be checked earlier? */
192 return ASF_ERROR_INVALID_LENGTH;
196 /* We now parse the individual payloads, and move all payloads
197 belonging to our audio stream to a contiguous block, starting at
198 the location of the first payload.
201 *audiobuf = NULL;
202 *audiobufsize = 0;
203 *packetlength = length - bytesread;
205 buf = ci->request_buffer(&bufsize, length);
206 datap = buf;
208 if (bufsize != length) {
209 /* This should only happen with packets larger than 32KB (the
210 guard buffer size). All the streams I've seen have
211 relatively small packets less than about 8KB), but I don't
212 know what is expected.
214 DEBUGF("Could not read packet (requested %d bytes, received %d), curpos=%d, aborting\n",
215 (int)length,(int)bufsize,(int)ci->curpos);
216 return -1;
219 for (i=0; i<payload_count; i++) {
220 stream_id = datap[0]&0x7f;
221 datap++;
222 bytesread++;
224 payload_hdrlen = GETLEN2b(packet_property & 0x03) +
225 GETLEN2b((packet_property >> 2) & 0x03) +
226 GETLEN2b((packet_property >> 4) & 0x03);
228 //DEBUGF("payload_hdrlen = %d\n",payload_hdrlen);
229 #if 0
230 /* TODO */
231 if (payload_hdrlen > size) {
232 return ASF_ERROR_INVALID_LENGTH;
234 #endif
235 if (payload_hdrlen > sizeof(data)) {
236 DEBUGF("Unexpectedly long datalen in data - %d\n",datalen);
237 return ASF_ERROR_OUTOFMEM;
240 bytesread += payload_hdrlen;
241 media_object_number = GETVALUE2b((packet_property >> 4) & 0x03, datap);
242 datap += GETLEN2b((packet_property >> 4) & 0x03);
243 media_object_offset = GETVALUE2b((packet_property >> 2) & 0x03, datap);
244 datap += GETLEN2b((packet_property >> 2) & 0x03);
245 replicated_length = GETVALUE2b(packet_property & 0x03, datap);
246 datap += GETLEN2b(packet_property & 0x03);
248 /* TODO: Validate replicated_length */
249 /* TODO: Is the content of this important for us? */
250 datap += replicated_length;
251 bytesread += replicated_length;
253 multiple = packet_flags & 0x01;
255 if (multiple) {
256 int x;
258 x = GETLEN2b(payload_length_type);
260 if (x != 2) {
261 /* in multiple payloads datalen should be a word */
262 return ASF_ERROR_INVALID_VALUE;
265 #if 0
266 if (skip + tmp > datalen) {
267 /* not enough data */
268 return ASF_ERROR_INVALID_LENGTH;
270 #endif
271 payload_datalen = GETVALUE2b(payload_length_type, datap);
272 datap += x;
273 bytesread += x;
274 } else {
275 payload_datalen = length - bytesread - padding_length;
278 if (stream_id == wfx->audiostream)
280 if (*audiobuf == NULL) {
281 /* The first payload can stay where it is */
282 *audiobuf = datap;
283 *audiobufsize = payload_datalen;
284 } else {
285 /* The second and subsequent payloads in this packet
286 that belong to the audio stream need to be moved to be
287 contiguous with the first payload.
289 memmove(*audiobuf + *audiobufsize, datap, payload_datalen);
290 *audiobufsize += payload_datalen;
293 datap += payload_datalen;
294 bytesread += payload_datalen;
297 if (*audiobuf != NULL)
298 return 1;
299 else
300 return 0;
303 /* this is the codec entry point */
304 enum codec_status codec_main(void)
306 uint32_t samplesdone;
307 uint32_t elapsedtime;
308 int retval;
309 asf_waveformatex_t wfx;
310 uint32_t currentframe;
311 size_t resume_offset;
312 int i;
313 int wmares, res;
314 uint8_t* audiobuf;
315 int audiobufsize;
316 int packetlength;
318 /* Generic codec initialisation */
319 ci->configure(CODEC_SET_FILEBUF_WATERMARK, 1024*512);
320 ci->configure(CODEC_SET_FILEBUF_CHUNKSIZE, 1024*128);
322 ci->configure(DSP_SET_SAMPLE_DEPTH, 30);
324 next_track:
326 /* Wait for the metadata to be read */
327 while (!*ci->taginfo_ready && !ci->stop_codec)
328 ci->sleep(1);
330 retval = CODEC_OK;
332 /* Remember the resume position - when the codec is opened, the
333 playback engine will reset it. */
334 resume_offset = ci->id3->offset;
336 if (codec_init()) {
337 LOGF("WMA: Error initialising codec\n");
338 retval = CODEC_ERROR;
339 goto exit;
342 /* Copy the format metadata we've stored in the id3 TOC field. This
343 saves us from parsing it again here. */
344 memcpy(&wfx, ci->id3->toc, sizeof(wfx));
346 if (wma_decode_init(&wmadec,&wfx) < 0) {
347 LOGF("WMA: Unsupported or corrupt file\n");
348 retval = CODEC_ERROR;
349 goto exit;
352 /* Now advance the file position to the first frame */
353 ci->seek_buffer(ci->id3->first_frame_offset);
355 ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
356 ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
357 STEREO_MONO : STEREO_INTERLEAVED);
358 codec_set_replaygain(ci->id3);
360 /* The main decoding loop */
362 currentframe = 0;
363 samplesdone = 0;
365 DEBUGF("**************** IN WMA.C ******************\n");
366 wma_decode_init(&wmadec,&wfx);
368 res = 1;
369 while (res >= 0)
371 ci->yield();
372 if (ci->stop_codec || ci->new_track) {
373 goto done;
376 /* Deal with any pending seek requests - ignore them */
377 if (ci->seek_time)
379 ci->seek_complete();
382 res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
383 if (res > 0) {
384 wma_decode_superframe_init(&wmadec,
385 audiobuf, audiobufsize);
387 for (i=0; i < wmadec.nb_frames; i++)
389 wmares = wma_decode_superframe_frame(&wmadec,
390 decoded,
391 audiobuf, audiobufsize);
393 ci->yield ();
395 if (wmares < 0)
397 LOGF("WMA decode error %d\n",wmares);
398 goto done;
399 } else if (wmares > 0) {
400 ci->pcmbuf_insert(decoded, NULL, wmares);
401 samplesdone += wmares;
402 elapsedtime = (samplesdone*10)/(wfx.rate/100);
403 ci->set_elapsed(elapsedtime);
405 ci->yield ();
409 ci->advance_buffer(packetlength);
411 retval = CODEC_OK;
413 done:
414 LOGF("WMA: Decoded %ld samples\n",samplesdone);
416 if (ci->request_next_track())
417 goto next_track;
419 exit:
420 return retval;