Fix warnings on 32 bit sim.
[kugel-rb.git] / apps / codecs / wav.c
blob774cfaf8efd865b8c73369f55910a7372d3365b4
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2005 Dave Chapman
11 * Copyright (C) 2009 Yoshihisa Uchida
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include "codeclib.h"
24 #include "inttypes.h"
25 #include "codecs/libpcm/support_formats.h"
27 CODEC_HEADER
29 /* WAVE (RIFF) codec:
31 * For a good documentation on WAVE files, see:
32 * http://www.tsp.ece.mcgill.ca/MMSP/Documents/AudioFormats/WAVE/WAVE.html
33 * and
34 * http://www.sonicspot.com/guide/wavefiles.html
36 * For sample WAV files, see:
37 * http://www.tsp.ece.mcgill.ca/MMSP/Documents/AudioFormats/WAVE/Samples.html
41 static int32_t samples[PCM_CHUNK_SIZE] IBSS_ATTR;
43 /* This codec support WAVE files with the following formats: */
44 enum
46 WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Unknown Wave Format */
47 WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */
48 WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */
49 WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */
50 WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */
51 IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */
52 IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */
53 WAVE_FORMAT_EXTENSIBLE = 0xFFFE
56 const struct pcm_entry wave_codecs[] = {
57 { WAVE_FORMAT_UNKNOWN, 0 },
58 { WAVE_FORMAT_PCM, get_linear_pcm_codec },
59 { WAVE_FORMAT_ALAW, get_itut_g711_alaw_codec },
60 { WAVE_FORMAT_MULAW, get_itut_g711_mulaw_codec },
61 { WAVE_FORMAT_DVI_ADPCM, get_dvi_adpcm_codec },
62 { IBM_FORMAT_MULAW, get_itut_g711_mulaw_codec },
63 { IBM_FORMAT_ALAW, get_itut_g711_alaw_codec },
66 #define NUM_FORMATS 7
68 static const struct pcm_codec *get_wave_codec(uint32_t formattag)
70 int i;
72 for (i = 0; i < NUM_FORMATS; i++)
74 if (wave_codecs[i].format_tag == formattag)
76 if (wave_codecs[i].get_codec)
77 return wave_codecs[i].get_codec();
78 return 0;
81 return 0;
85 /* this is the codec entry point */
86 enum codec_status codec_main(void)
88 int status = CODEC_OK;
89 struct pcm_format format;
90 uint32_t bytesdone, decodedbytes;
91 uint32_t i;
92 size_t n;
93 int bufcount;
94 int endofstream;
95 unsigned char *buf;
96 uint8_t *wavbuf;
97 off_t firstblockposn; /* position of the first block in file */
98 const struct pcm_codec *codec;
100 /* Generic codec initialisation */
101 ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
103 next_track:
104 if (codec_init()) {
105 DEBUGF("codec_init() error\n");
106 status = CODEC_ERROR;
107 goto exit;
110 while (!*ci->taginfo_ready && !ci->stop_codec)
111 ci->sleep(1);
113 codec_set_replaygain(ci->id3);
115 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
116 bytesdone = ci->id3->offset;
118 /* get RIFF chunk header */
119 buf = ci->request_buffer(&n, 12);
120 if (n < 12) {
121 DEBUGF("request_buffer error\n");
122 status = CODEC_ERROR;
123 goto done;
125 if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) {
126 status = CODEC_ERROR;
127 goto done;
130 /* advance to first WAVE chunk */
131 ci->advance_buffer(12);
133 firstblockposn = 12;
134 ci->memset(&format, 0, sizeof(struct pcm_format));
135 format.is_signed = true;
136 format.is_little_endian = true;
138 decodedbytes = 0;
139 codec = 0;
141 /* iterate over WAVE chunks until the 'data' chunk, which should be after the 'fmt ' chunk */
142 while (true) {
143 /* get WAVE chunk header */
144 buf = ci->request_buffer(&n, 1024);
145 if (n < 8) {
146 DEBUGF("data chunk request_buffer error\n");
147 /* no more chunks, 'data' chunk must not have been found */
148 status = CODEC_ERROR;
149 goto done;
152 /* chunkSize */
153 i = (buf[4]|(buf[5]<<8)|(buf[6]<<16)|(buf[7]<<24));
154 if (memcmp(buf, "fmt ", 4) == 0) {
155 if (i < 16) {
156 DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n",
157 (unsigned long)i);
158 status = CODEC_ERROR;
159 goto done;
161 /* wFormatTag */
162 format.formattag=buf[8]|(buf[9]<<8);
163 /* wChannels */
164 format.channels=buf[10]|(buf[11]<<8);
165 /* skipping dwSamplesPerSec */
166 /* dwAvgBytesPerSec */
167 format.avgbytespersec = buf[16]|(buf[17]<<8)|(buf[18]<<16)|(buf[19]<<24);
168 /* wBlockAlign */
169 format.blockalign=buf[20]|(buf[21]<<8);
170 /* wBitsPerSample */
171 format.bitspersample=buf[22]|(buf[23]<<8);
172 if (format.formattag != WAVE_FORMAT_PCM) {
173 if (i < 18) {
174 /* this is not a fatal error with some formats,
175 * we'll see later if we can't decode it */
176 DEBUGF("CODEC_WARNING: non-PCM WAVE (formattag=0x%x) "
177 "doesn't have ext. fmt descr (chunksize=%d<18).\n",
178 (unsigned int)format.formattag, i);
180 else
182 format.size = buf[24]|(buf[25]<<8);
183 if (format.formattag != WAVE_FORMAT_EXTENSIBLE)
184 format.samplesperblock = buf[26]|(buf[27]<<8);
185 else {
186 if (format.size < 22) {
187 DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
188 "missing extension\n");
189 status = CODEC_ERROR;
190 goto done;
192 /* wValidBitsPerSample */
193 format.bitspersample = buf[26]|(buf[27]<<8);
194 /* skipping dwChannelMask (4bytes) */
195 /* SubFormat (only get the first two bytes) */
196 format.formattag = buf[32]|(buf[33]<<8);
201 /* get codec */
202 codec = get_wave_codec(format.formattag);
203 if (!codec)
205 DEBUGF("CODEC_ERROR: unsupported wave format %x\n",
206 (unsigned int) format.formattag);
207 status = CODEC_ERROR;
208 goto done;
211 /* riff 8bit linear pcm is unsigned */
212 if (format.formattag == WAVE_FORMAT_PCM && format.bitspersample == 8)
213 format.is_signed = false;
215 /* set format, parse codec specific tag, check format, and calculate chunk size */
216 if (!codec->set_format(&format, buf))
218 status = CODEC_ERROR;
219 goto done;
221 } else if (memcmp(buf, "data", 4) == 0) {
222 format.numbytes = i;
223 /* advance to start of data */
224 ci->advance_buffer(8);
225 firstblockposn += 8;
226 break;
227 } else if (memcmp(buf, "fact", 4) == 0) {
228 /* dwSampleLength */
229 if (i >= 4)
230 format.totalsamples =
231 (buf[8]|(buf[9]<<8)|(buf[10]<<16)|(buf[11]<<24));
232 } else {
233 DEBUGF("unknown WAVE chunk: '%c%c%c%c', size=%lu\n",
234 buf[0], buf[1], buf[2], buf[3], (unsigned long)i);
237 /* go to next chunk (even chunk sizes must be padded) */
238 if (i & 0x01)
239 i++;
240 ci->advance_buffer(i+8);
241 firstblockposn += i + 8;
244 if (!codec)
246 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
247 status = CODEC_ERROR;
248 goto done;
251 /* common format check */
252 if (format.channels == 0) {
253 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
254 status = CODEC_ERROR;
255 goto done;
257 if (format.numbytes == 0) {
258 DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
259 status = CODEC_ERROR;
260 goto done;
263 ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
264 if (format.channels == 2) {
265 ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
266 } else if (format.channels == 1) {
267 ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
268 } else {
269 DEBUGF("CODEC_ERROR: more than 2 channels\n");
270 status = CODEC_ERROR;
271 goto done;
274 /* make sure we're at the correct offset */
275 if (bytesdone > (uint32_t) firstblockposn) {
276 /* Round down to previous block */
277 uint32_t offset = bytesdone - bytesdone % format.blockalign;
279 ci->advance_buffer(offset-firstblockposn);
280 bytesdone = offset - firstblockposn;
281 } else {
282 /* already where we need to be */
283 bytesdone = 0;
286 /* The main decoder loop */
287 endofstream = 0;
289 while (!endofstream) {
290 ci->yield();
291 if (ci->stop_codec || ci->new_track) {
292 break;
295 if (ci->seek_time) {
296 uint32_t newpos = codec->get_seek_pos(ci->seek_time);
298 if (newpos > format.numbytes)
299 break;
300 if (ci->seek_buffer(firstblockposn + newpos))
302 bytesdone = newpos;
304 ci->seek_complete();
307 wavbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
308 if (n == 0)
309 break; /* End of stream */
310 if (bytesdone + n > format.numbytes) {
311 n = format.numbytes - bytesdone;
312 endofstream = 1;
315 status = codec->decode(wavbuf, n, samples, &bufcount);
316 if (status == CODEC_ERROR)
318 DEBUGF("codec error\n");
319 goto done;
322 ci->pcmbuf_insert(samples, NULL, bufcount);
323 ci->advance_buffer(n);
324 bytesdone += n;
325 decodedbytes += bufcount;
327 if (bytesdone >= format.numbytes)
328 endofstream = 1;
329 ci->set_elapsed(decodedbytes*1000LL/ci->id3->frequency);
331 status = CODEC_OK;
333 done:
334 if (ci->request_next_track())
335 goto next_track;
337 exit:
338 return status;