Commit FS#10423 by Yoshihisa Uchida. Adds support for floating point PCM to libpcm.
[kugel-rb.git] / apps / codecs / wav.c
blob25a82cbcfe3d645896a41e7fa90ebbe91149c4d1
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_IEEE_FLOAT = 0x0003, /* IEEE Float */
49 WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */
50 WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */
51 WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */
52 IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */
53 IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */
54 WAVE_FORMAT_EXTENSIBLE = 0xFFFE
57 const struct pcm_entry wave_codecs[] = {
58 { WAVE_FORMAT_UNKNOWN, 0 },
59 { WAVE_FORMAT_PCM, get_linear_pcm_codec },
60 { WAVE_FORMAT_IEEE_FLOAT, get_ieee_float_codec },
61 { WAVE_FORMAT_ALAW, get_itut_g711_alaw_codec },
62 { WAVE_FORMAT_MULAW, get_itut_g711_mulaw_codec },
63 { WAVE_FORMAT_DVI_ADPCM, get_dvi_adpcm_codec },
64 { IBM_FORMAT_MULAW, get_itut_g711_mulaw_codec },
65 { IBM_FORMAT_ALAW, get_itut_g711_alaw_codec },
68 #define NUM_FORMATS 8
70 static const struct pcm_codec *get_wave_codec(uint32_t formattag)
72 int i;
74 for (i = 0; i < NUM_FORMATS; i++)
76 if (wave_codecs[i].format_tag == formattag)
78 if (wave_codecs[i].get_codec)
79 return wave_codecs[i].get_codec();
80 return 0;
83 return 0;
87 /* this is the codec entry point */
88 enum codec_status codec_main(void)
90 int status = CODEC_OK;
91 struct pcm_format format;
92 uint32_t bytesdone, decodedbytes;
93 uint32_t i;
94 size_t n;
95 int bufcount;
96 int endofstream;
97 unsigned char *buf;
98 uint8_t *wavbuf;
99 off_t firstblockposn; /* position of the first block in file */
100 const struct pcm_codec *codec;
102 /* Generic codec initialisation */
103 ci->configure(DSP_SET_SAMPLE_DEPTH, 28);
105 next_track:
106 if (codec_init()) {
107 DEBUGF("codec_init() error\n");
108 status = CODEC_ERROR;
109 goto exit;
112 while (!*ci->taginfo_ready && !ci->stop_codec)
113 ci->sleep(1);
115 codec_set_replaygain(ci->id3);
117 /* Need to save offset for later use (cleared indirectly by advance_buffer) */
118 bytesdone = ci->id3->offset;
120 /* get RIFF chunk header */
121 buf = ci->request_buffer(&n, 12);
122 if (n < 12) {
123 DEBUGF("request_buffer error\n");
124 status = CODEC_ERROR;
125 goto done;
127 if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) {
128 status = CODEC_ERROR;
129 goto done;
132 /* advance to first WAVE chunk */
133 ci->advance_buffer(12);
135 firstblockposn = 12;
136 ci->memset(&format, 0, sizeof(struct pcm_format));
137 format.is_signed = true;
138 format.is_little_endian = true;
140 decodedbytes = 0;
141 codec = 0;
143 /* iterate over WAVE chunks until the 'data' chunk, which should be after the 'fmt ' chunk */
144 while (true) {
145 /* get WAVE chunk header */
146 buf = ci->request_buffer(&n, 1024);
147 if (n < 8) {
148 DEBUGF("data chunk request_buffer error\n");
149 /* no more chunks, 'data' chunk must not have been found */
150 status = CODEC_ERROR;
151 goto done;
154 /* chunkSize */
155 i = (buf[4]|(buf[5]<<8)|(buf[6]<<16)|(buf[7]<<24));
156 if (memcmp(buf, "fmt ", 4) == 0) {
157 if (i < 16) {
158 DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n",
159 (unsigned long)i);
160 status = CODEC_ERROR;
161 goto done;
163 /* wFormatTag */
164 format.formattag=buf[8]|(buf[9]<<8);
165 /* wChannels */
166 format.channels=buf[10]|(buf[11]<<8);
167 /* skipping dwSamplesPerSec */
168 /* dwAvgBytesPerSec */
169 format.avgbytespersec = buf[16]|(buf[17]<<8)|(buf[18]<<16)|(buf[19]<<24);
170 /* wBlockAlign */
171 format.blockalign=buf[20]|(buf[21]<<8);
172 /* wBitsPerSample */
173 format.bitspersample=buf[22]|(buf[23]<<8);
174 if (format.formattag != WAVE_FORMAT_PCM) {
175 if (i < 18) {
176 /* this is not a fatal error with some formats,
177 * we'll see later if we can't decode it */
178 DEBUGF("CODEC_WARNING: non-PCM WAVE (formattag=0x%x) "
179 "doesn't have ext. fmt descr (chunksize=%d<18).\n",
180 (unsigned int)format.formattag, i);
182 else
184 format.size = buf[24]|(buf[25]<<8);
185 if (format.formattag != WAVE_FORMAT_EXTENSIBLE)
186 format.samplesperblock = buf[26]|(buf[27]<<8);
187 else {
188 if (format.size < 22) {
189 DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is "
190 "missing extension\n");
191 status = CODEC_ERROR;
192 goto done;
194 /* wValidBitsPerSample */
195 format.bitspersample = buf[26]|(buf[27]<<8);
196 /* skipping dwChannelMask (4bytes) */
197 /* SubFormat (only get the first two bytes) */
198 format.formattag = buf[32]|(buf[33]<<8);
203 /* get codec */
204 codec = get_wave_codec(format.formattag);
205 if (!codec)
207 DEBUGF("CODEC_ERROR: unsupported wave format %x\n",
208 (unsigned int) format.formattag);
209 status = CODEC_ERROR;
210 goto done;
213 /* riff 8bit linear pcm is unsigned */
214 if (format.formattag == WAVE_FORMAT_PCM && format.bitspersample == 8)
215 format.is_signed = false;
217 /* set format, parse codec specific tag, check format, and calculate chunk size */
218 if (!codec->set_format(&format, buf))
220 status = CODEC_ERROR;
221 goto done;
223 } else if (memcmp(buf, "data", 4) == 0) {
224 format.numbytes = i;
225 /* advance to start of data */
226 ci->advance_buffer(8);
227 firstblockposn += 8;
228 break;
229 } else if (memcmp(buf, "fact", 4) == 0) {
230 /* dwSampleLength */
231 if (i >= 4)
232 format.totalsamples =
233 (buf[8]|(buf[9]<<8)|(buf[10]<<16)|(buf[11]<<24));
234 } else {
235 DEBUGF("unknown WAVE chunk: '%c%c%c%c', size=%lu\n",
236 buf[0], buf[1], buf[2], buf[3], (unsigned long)i);
239 /* go to next chunk (even chunk sizes must be padded) */
240 if (i & 0x01)
241 i++;
242 ci->advance_buffer(i+8);
243 firstblockposn += i + 8;
246 if (!codec)
248 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n");
249 status = CODEC_ERROR;
250 goto done;
253 /* common format check */
254 if (format.channels == 0) {
255 DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n");
256 status = CODEC_ERROR;
257 goto done;
259 if (format.numbytes == 0) {
260 DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n");
261 status = CODEC_ERROR;
262 goto done;
265 ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency);
266 if (format.channels == 2) {
267 ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
268 } else if (format.channels == 1) {
269 ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO);
270 } else {
271 DEBUGF("CODEC_ERROR: more than 2 channels\n");
272 status = CODEC_ERROR;
273 goto done;
276 /* make sure we're at the correct offset */
277 if (bytesdone > (uint32_t) firstblockposn) {
278 /* Round down to previous block */
279 uint32_t offset = bytesdone - bytesdone % format.blockalign;
281 ci->advance_buffer(offset-firstblockposn);
282 bytesdone = offset - firstblockposn;
283 } else {
284 /* already where we need to be */
285 bytesdone = 0;
288 /* The main decoder loop */
289 endofstream = 0;
291 while (!endofstream) {
292 ci->yield();
293 if (ci->stop_codec || ci->new_track) {
294 break;
297 if (ci->seek_time) {
298 uint32_t newpos = codec->get_seek_pos(ci->seek_time);
300 if (newpos > format.numbytes)
301 break;
302 if (ci->seek_buffer(firstblockposn + newpos))
304 bytesdone = newpos;
306 ci->seek_complete();
309 wavbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize);
310 if (n == 0)
311 break; /* End of stream */
312 if (bytesdone + n > format.numbytes) {
313 n = format.numbytes - bytesdone;
314 endofstream = 1;
317 status = codec->decode(wavbuf, n, samples, &bufcount);
318 if (status == CODEC_ERROR)
320 DEBUGF("codec error\n");
321 goto done;
324 ci->pcmbuf_insert(samples, NULL, bufcount);
325 ci->advance_buffer(n);
326 bytesdone += n;
327 decodedbytes += bufcount;
329 if (bytesdone >= format.numbytes)
330 endofstream = 1;
331 ci->set_elapsed(decodedbytes*1000LL/ci->id3->frequency);
333 status = CODEC_OK;
335 done:
336 if (ci->request_next_track())
337 goto next_track;
339 exit:
340 return status;