Submit initial patch from FS#12176. Adds support for several new game music formats...
[kugel-rb.git] / apps / codecs / wma.c
blobc327fafb5a12f35ee59dab40169ee4b15dd28626
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Dave Chapman
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "codeclib.h"
23 #include "libasf/asf.h"
24 #include "libwma/wmadec.h"
26 CODEC_HEADER
28 /* NOTE: WMADecodeContext is 120152 bytes (on x86) */
29 static WMADecodeContext wmadec;
31 /* this is the codec entry point */
32 enum codec_status codec_main(enum codec_entry_call_reason reason)
34 if (reason == CODEC_LOAD) {
35 /* Generic codec initialisation */
36 ci->configure(DSP_SET_SAMPLE_DEPTH, 29);
39 return CODEC_OK;
42 /* this is called for each file to process */
43 enum codec_status codec_run(void)
45 uint32_t elapsedtime;
46 asf_waveformatex_t wfx;
47 size_t resume_offset;
48 int i;
49 int wmares;
50 int res = 0;
51 uint8_t* audiobuf;
52 int audiobufsize;
53 int packetlength = 0;
54 int errcount = 0;
55 intptr_t param;
57 /* Proper reset of the decoder context. */
58 memset(&wmadec, 0, sizeof(wmadec));
60 /* Remember the resume position - when the codec is opened, the
61 playback engine will reset it. */
62 resume_offset = ci->id3->offset;
64 restart_track:
65 if (codec_init()) {
66 LOGF("WMA: Error initialising codec\n");
67 return CODEC_ERROR;
70 /* Copy the format metadata we've stored in the id3 TOC field. This
71 saves us from parsing it again here. */
72 memcpy(&wfx, ci->id3->toc, sizeof(wfx));
74 ci->seek_buffer(ci->id3->first_frame_offset);
75 if (wma_decode_init(&wmadec,&wfx) < 0) {
76 LOGF("WMA: Unsupported or corrupt file\n");
77 return CODEC_ERROR;
80 if (resume_offset > ci->id3->first_frame_offset)
82 /* Get start of current packet */
83 int packet_offset = (resume_offset - ci->id3->first_frame_offset)
84 % wfx.packet_size;
85 ci->seek_buffer(resume_offset - packet_offset);
86 elapsedtime = asf_get_timestamp(&i);
87 ci->set_elapsed(elapsedtime);
89 else
91 /* Now advance the file position to the first frame */
92 ci->seek_buffer(ci->id3->first_frame_offset);
93 elapsedtime = 0;
96 resume_offset = 0;
97 ci->configure(DSP_SWITCH_FREQUENCY, wfx.rate);
98 ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ?
99 STEREO_MONO : STEREO_NONINTERLEAVED);
100 codec_set_replaygain(ci->id3);
102 /* The main decoding loop */
103 while (res >= 0)
105 enum codec_command_action action = ci->get_command(&param);
107 if (action == CODEC_ACTION_HALT)
108 break;
110 /* Deal with any pending seek requests */
111 if (action == CODEC_ACTION_SEEK_TIME) {
113 if (param == 0) {
114 ci->set_elapsed(0);
115 ci->seek_complete();
116 goto restart_track; /* Pretend you never saw this... */
119 elapsedtime = asf_seek(param, &wfx);
120 if (elapsedtime < 1){
121 ci->set_elapsed(0);
122 ci->seek_complete();
123 break;
125 /*DEBUGF("Seek returned %d\n", (int)elapsedtime);*/
127 /*flush the wma decoder state*/
128 wmadec.last_superframe_len = 0;
129 wmadec.last_bitoffset = 0;
131 ci->set_elapsed(elapsedtime);
132 ci->seek_complete();
134 errcount = 0;
135 new_packet:
136 res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx);
138 if (res < 0) {
139 /* We'll try to recover from a parse error a certain number of
140 * times. If we succeed, the error counter will be reset.
143 if (res == ASF_ERROR_EOF) {
144 /* File ended - not an error */
145 break;
148 errcount++;
149 DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount);
150 if (errcount > 5) {
151 return CODEC_ERROR;
152 } else {
153 ci->advance_buffer(packetlength);
154 goto new_packet;
156 } else if (res > 0) {
157 wma_decode_superframe_init(&wmadec, audiobuf, audiobufsize);
159 for (i=0; i < wmadec.nb_frames; i++)
161 wmares = wma_decode_superframe_frame(&wmadec,
162 audiobuf, audiobufsize);
164 ci->yield ();
166 if (wmares < 0) {
167 /* Do the above, but for errors in decode. */
168 errcount++;
169 DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount);
170 if (errcount > 5) {
171 return CODEC_ERROR;
172 } else {
173 ci->advance_buffer(packetlength);
174 goto new_packet;
176 } else if (wmares > 0) {
177 ci->pcmbuf_insert((*wmadec.frame_out)[0], (*wmadec.frame_out)[1], wmares);
178 elapsedtime += (wmares*10)/(wfx.rate/100);
179 ci->set_elapsed(elapsedtime);
184 ci->advance_buffer(packetlength);
187 /*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/
188 return CODEC_OK;