diff options
Diffstat (limited to 'apps/codecs/wav.c')
-rw-r--r-- | apps/codecs/wav.c | 122 |
1 files changed, 110 insertions, 12 deletions
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c index a642f99a0f..293089a737 100644 --- a/apps/codecs/wav.c +++ b/apps/codecs/wav.c | |||
@@ -45,27 +45,37 @@ enum | |||
45 | { | 45 | { |
46 | WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Unknown Wave Format */ | 46 | WAVE_FORMAT_UNKNOWN = 0x0000, /* Microsoft Unknown Wave Format */ |
47 | WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ | 47 | WAVE_FORMAT_PCM = 0x0001, /* Microsoft PCM Format */ |
48 | WAVE_FORMAT_ADPCM = 0x0002, /* Microsoft ADPCM Format */ | ||
48 | WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ | 49 | WAVE_FORMAT_IEEE_FLOAT = 0x0003, /* IEEE Float */ |
49 | WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ | 50 | WAVE_FORMAT_ALAW = 0x0006, /* Microsoft ALAW */ |
50 | WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ | 51 | WAVE_FORMAT_MULAW = 0x0007, /* Microsoft MULAW */ |
51 | WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ | 52 | WAVE_FORMAT_DVI_ADPCM = 0x0011, /* Intel's DVI ADPCM */ |
53 | WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017, /* Dialogic OKI ADPCM */ | ||
54 | WAVE_FORMAT_YAMAHA_ADPCM = 0x0020, /* Yamaha ADPCM */ | ||
55 | WAVE_FORMAT_XBOX_ADPCM = 0x0069, /* XBOX ADPCM */ | ||
52 | IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ | 56 | IBM_FORMAT_MULAW = 0x0101, /* same as WAVE_FORMAT_MULAW */ |
53 | IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ | 57 | IBM_FORMAT_ALAW = 0x0102, /* same as WAVE_FORMAT_ALAW */ |
58 | WAVE_FORMAT_SWF_ADPCM = 0x5346, /* Adobe SWF ADPCM */ | ||
54 | WAVE_FORMAT_EXTENSIBLE = 0xFFFE | 59 | WAVE_FORMAT_EXTENSIBLE = 0xFFFE |
55 | }; | 60 | }; |
56 | 61 | ||
57 | const struct pcm_entry wave_codecs[] = { | 62 | const struct pcm_entry wave_codecs[] = { |
58 | { WAVE_FORMAT_UNKNOWN, 0 }, | 63 | { WAVE_FORMAT_UNKNOWN, 0 }, |
59 | { WAVE_FORMAT_PCM, get_linear_pcm_codec }, | 64 | { WAVE_FORMAT_PCM, get_linear_pcm_codec }, |
65 | { WAVE_FORMAT_ADPCM, get_ms_adpcm_codec }, | ||
60 | { WAVE_FORMAT_IEEE_FLOAT, get_ieee_float_codec }, | 66 | { WAVE_FORMAT_IEEE_FLOAT, get_ieee_float_codec }, |
61 | { WAVE_FORMAT_ALAW, get_itut_g711_alaw_codec }, | 67 | { WAVE_FORMAT_ALAW, get_itut_g711_alaw_codec }, |
62 | { WAVE_FORMAT_MULAW, get_itut_g711_mulaw_codec }, | 68 | { WAVE_FORMAT_MULAW, get_itut_g711_mulaw_codec }, |
63 | { WAVE_FORMAT_DVI_ADPCM, get_dvi_adpcm_codec }, | 69 | { WAVE_FORMAT_DVI_ADPCM, get_dvi_adpcm_codec }, |
70 | { WAVE_FORMAT_DIALOGIC_OKI_ADPCM, get_dialogic_oki_adpcm_codec }, | ||
71 | { WAVE_FORMAT_YAMAHA_ADPCM, get_yamaha_adpcm_codec }, | ||
72 | { WAVE_FORMAT_XBOX_ADPCM, get_dvi_adpcm_codec }, | ||
64 | { IBM_FORMAT_MULAW, get_itut_g711_mulaw_codec }, | 73 | { IBM_FORMAT_MULAW, get_itut_g711_mulaw_codec }, |
65 | { IBM_FORMAT_ALAW, get_itut_g711_alaw_codec }, | 74 | { IBM_FORMAT_ALAW, get_itut_g711_alaw_codec }, |
75 | { WAVE_FORMAT_SWF_ADPCM, get_swf_adpcm_codec }, | ||
66 | }; | 76 | }; |
67 | 77 | ||
68 | #define NUM_FORMATS 8 | 78 | #define NUM_FORMATS 13 |
69 | 79 | ||
70 | static const struct pcm_codec *get_wave_codec(uint32_t formattag) | 80 | static const struct pcm_codec *get_wave_codec(uint32_t formattag) |
71 | { | 81 | { |
@@ -83,13 +93,67 @@ static const struct pcm_codec *get_wave_codec(uint32_t formattag) | |||
83 | return 0; | 93 | return 0; |
84 | } | 94 | } |
85 | 95 | ||
96 | static struct pcm_format format; | ||
97 | static uint32_t bytesdone; | ||
98 | |||
99 | static bool set_msadpcm_coeffs(const uint8_t *buf) | ||
100 | { | ||
101 | int i; | ||
102 | int num; | ||
103 | int size; | ||
104 | |||
105 | buf += 4; /* skip 'fmt ' */ | ||
106 | size = buf[0] | (buf[1] << 8) | (buf[1] << 16) | (buf[1] << 24); | ||
107 | if (size < 50) | ||
108 | { | ||
109 | DEBUGF("CODEC_ERROR: microsoft adpcm 'fmt ' chunk size=%lu < 50\n", | ||
110 | (unsigned long)size); | ||
111 | return false; | ||
112 | } | ||
113 | |||
114 | /* get nNumCoef */ | ||
115 | buf += 24; | ||
116 | num = buf[0] | (buf[1] << 8); | ||
117 | |||
118 | /* | ||
119 | * In many case, nNumCoef is 7. | ||
120 | * Depending upon the encoder, as for this value there is a possibility of | ||
121 | * increasing more. | ||
122 | * If you found the file where this value exceeds 7, please report. | ||
123 | */ | ||
124 | if (num != MSADPCM_NUM_COEFF) | ||
125 | { | ||
126 | DEBUGF("CODEC_ERROR: microsoft adpcm nNumCoef=%d != 7\n", num); | ||
127 | return false; | ||
128 | } | ||
129 | |||
130 | /* get aCoeffs */ | ||
131 | buf += 2; | ||
132 | for (i = 0; i < MSADPCM_NUM_COEFF; i++) | ||
133 | { | ||
134 | format.coeffs[i][0] = buf[0] | (SE(buf[1]) << 8); | ||
135 | format.coeffs[i][1] = buf[2] | (SE(buf[3]) << 8); | ||
136 | buf += 4; | ||
137 | } | ||
138 | |||
139 | return true; | ||
140 | } | ||
141 | |||
142 | static uint8_t *read_buffer(size_t *realsize) | ||
143 | { | ||
144 | uint8_t *buffer = (uint8_t *)ci->request_buffer(realsize, format.chunksize); | ||
145 | if (bytesdone + (*realsize) > format.numbytes) | ||
146 | *realsize = format.numbytes - bytesdone; | ||
147 | bytesdone += *realsize; | ||
148 | ci->advance_buffer(*realsize); | ||
149 | return buffer; | ||
150 | } | ||
86 | 151 | ||
87 | /* this is the codec entry point */ | 152 | /* this is the codec entry point */ |
88 | enum codec_status codec_main(void) | 153 | enum codec_status codec_main(void) |
89 | { | 154 | { |
90 | int status = CODEC_OK; | 155 | int status = CODEC_OK; |
91 | struct pcm_format format; | 156 | uint32_t decodedsamples; |
92 | uint32_t bytesdone, decodedbytes; | ||
93 | uint32_t i; | 157 | uint32_t i; |
94 | size_t n; | 158 | size_t n; |
95 | int bufcount; | 159 | int bufcount; |
@@ -125,6 +189,7 @@ next_track: | |||
125 | goto done; | 189 | goto done; |
126 | } | 190 | } |
127 | if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) { | 191 | if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) { |
192 | DEBUGF("CODEC_ERROR: missing riff header\n"); | ||
128 | status = CODEC_ERROR; | 193 | status = CODEC_ERROR; |
129 | goto done; | 194 | goto done; |
130 | } | 195 | } |
@@ -137,7 +202,7 @@ next_track: | |||
137 | format.is_signed = true; | 202 | format.is_signed = true; |
138 | format.is_little_endian = true; | 203 | format.is_little_endian = true; |
139 | 204 | ||
140 | decodedbytes = 0; | 205 | decodedsamples = 0; |
141 | codec = 0; | 206 | codec = 0; |
142 | 207 | ||
143 | /* iterate over WAVE chunks until the 'data' chunk, which should be after the 'fmt ' chunk */ | 208 | /* iterate over WAVE chunks until the 'data' chunk, which should be after the 'fmt ' chunk */ |
@@ -200,11 +265,21 @@ next_track: | |||
200 | } | 265 | } |
201 | } | 266 | } |
202 | 267 | ||
268 | /* msadpcm specific */ | ||
269 | if (format.formattag == WAVE_FORMAT_ADPCM) | ||
270 | { | ||
271 | if (!set_msadpcm_coeffs(buf)) | ||
272 | { | ||
273 | status = CODEC_ERROR; | ||
274 | goto done; | ||
275 | } | ||
276 | } | ||
277 | |||
203 | /* get codec */ | 278 | /* get codec */ |
204 | codec = get_wave_codec(format.formattag); | 279 | codec = get_wave_codec(format.formattag); |
205 | if (!codec) | 280 | if (!codec) |
206 | { | 281 | { |
207 | DEBUGF("CODEC_ERROR: unsupported wave format %x\n", | 282 | DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n", |
208 | (unsigned int) format.formattag); | 283 | (unsigned int) format.formattag); |
209 | status = CODEC_ERROR; | 284 | status = CODEC_ERROR; |
210 | goto done; | 285 | goto done; |
@@ -215,7 +290,7 @@ next_track: | |||
215 | format.is_signed = false; | 290 | format.is_signed = false; |
216 | 291 | ||
217 | /* set format, parse codec specific tag, check format, and calculate chunk size */ | 292 | /* set format, parse codec specific tag, check format, and calculate chunk size */ |
218 | if (!codec->set_format(&format, buf)) | 293 | if (!codec->set_format(&format)) |
219 | { | 294 | { |
220 | status = CODEC_ERROR; | 295 | status = CODEC_ERROR; |
221 | goto done; | 296 | goto done; |
@@ -256,12 +331,34 @@ next_track: | |||
256 | status = CODEC_ERROR; | 331 | status = CODEC_ERROR; |
257 | goto done; | 332 | goto done; |
258 | } | 333 | } |
334 | if (format.samplesperblock == 0) { | ||
335 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); | ||
336 | status = CODEC_ERROR; | ||
337 | goto done; | ||
338 | } | ||
339 | if (format.blockalign == 0) | ||
340 | { | ||
341 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); | ||
342 | i = CODEC_ERROR; | ||
343 | goto done; | ||
344 | } | ||
259 | if (format.numbytes == 0) { | 345 | if (format.numbytes == 0) { |
260 | DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); | 346 | DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); |
261 | status = CODEC_ERROR; | 347 | status = CODEC_ERROR; |
262 | goto done; | 348 | goto done; |
263 | } | 349 | } |
264 | 350 | ||
351 | /* check chunksize */ | ||
352 | if ((format.chunksize / format.blockalign) * format.samplesperblock * format.channels | ||
353 | > PCM_CHUNK_SIZE) | ||
354 | format.chunksize = (PCM_CHUNK_SIZE / format.blockalign) * format.blockalign; | ||
355 | if (format.chunksize == 0) | ||
356 | { | ||
357 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | ||
358 | i = CODEC_ERROR; | ||
359 | goto done; | ||
360 | } | ||
361 | |||
265 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 362 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
266 | if (format.channels == 2) { | 363 | if (format.channels == 2) { |
267 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); | 364 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); |
@@ -295,13 +392,14 @@ next_track: | |||
295 | } | 392 | } |
296 | 393 | ||
297 | if (ci->seek_time) { | 394 | if (ci->seek_time) { |
298 | uint32_t newpos = codec->get_seek_pos(ci->seek_time); | 395 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, &read_buffer); |
299 | 396 | ||
300 | if (newpos > format.numbytes) | 397 | decodedsamples = newpos->samples; |
398 | if (newpos->pos > format.numbytes) | ||
301 | break; | 399 | break; |
302 | if (ci->seek_buffer(firstblockposn + newpos)) | 400 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
303 | { | 401 | { |
304 | bytesdone = newpos; | 402 | bytesdone = newpos->pos; |
305 | } | 403 | } |
306 | ci->seek_complete(); | 404 | ci->seek_complete(); |
307 | } | 405 | } |
@@ -324,11 +422,11 @@ next_track: | |||
324 | ci->pcmbuf_insert(samples, NULL, bufcount); | 422 | ci->pcmbuf_insert(samples, NULL, bufcount); |
325 | ci->advance_buffer(n); | 423 | ci->advance_buffer(n); |
326 | bytesdone += n; | 424 | bytesdone += n; |
327 | decodedbytes += bufcount; | 425 | decodedsamples += bufcount; |
328 | 426 | ||
329 | if (bytesdone >= format.numbytes) | 427 | if (bytesdone >= format.numbytes) |
330 | endofstream = 1; | 428 | endofstream = 1; |
331 | ci->set_elapsed(decodedbytes*1000LL/ci->id3->frequency); | 429 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); |
332 | } | 430 | } |
333 | status = CODEC_OK; | 431 | status = CODEC_OK; |
334 | 432 | ||