Mercurial > SDL_sound_CoreAudio
comparison decoders/wav.c @ 183:26996236d790
Updated comments to reflect change from LICENSE to COPYING file, and put a
good chunk of the ADPCM decoder in place. Does NOT work, is NOT complete,
but is more than halfway there.
author | Ryan C. Gordon <icculus@icculus.org> |
---|---|
date | Wed, 26 Dec 2001 10:39:16 +0000 |
parents | bdbe09014724 |
children | 6bb68b3bdcf1 |
comparison
equal
deleted
inserted
replaced
182:3849438b735e | 183:26996236d790 |
---|---|
21 * WAV decoder for SDL_sound. | 21 * WAV decoder for SDL_sound. |
22 * | 22 * |
23 * This driver handles Microsoft .WAVs, in as many of the thousands of | 23 * This driver handles Microsoft .WAVs, in as many of the thousands of |
24 * variations as we can. | 24 * variations as we can. |
25 * | 25 * |
26 * Please see the file LICENSE in the source's root directory. | 26 * Please see the file COPYING in the source's root directory. |
27 * | 27 * |
28 * This file written by Ryan C. Gordon. (icculus@clutteredmind.org) | 28 * This file written by Ryan C. Gordon. (icculus@clutteredmind.org) |
29 */ | 29 */ |
30 | 30 |
31 #if HAVE_CONFIG_H | 31 #if HAVE_CONFIG_H |
66 WAV_close, /* close() method */ | 66 WAV_close, /* close() method */ |
67 WAV_read /* read() method */ | 67 WAV_read /* read() method */ |
68 }; | 68 }; |
69 | 69 |
70 | 70 |
71 /* Better than SDL_ReadLE16, since you can detect i/o errors... */ | |
72 static inline int read_le16(SDL_RWops *rw, Uint16 *ui16) | |
73 { | |
74 int rc = SDL_RWread(rw, ui16, sizeof (Uint16), 1); | |
75 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); | |
76 *ui16 = SDL_SwapLE16(*ui16); | |
77 return(1); | |
78 } /* read_le16 */ | |
79 | |
80 | |
81 /* Better than SDL_ReadLE32, since you can detect i/o errors... */ | |
82 static inline int read_le32(SDL_RWops *rw, Uint32 *ui32) | |
83 { | |
84 int rc = SDL_RWread(rw, ui32, sizeof (Uint32), 1); | |
85 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); | |
86 *ui32 = SDL_SwapLE32(*ui32); | |
87 return(1); | |
88 } /* read_le32 */ | |
89 | |
90 | |
91 /* This is just cleaner on the caller's end... */ | |
92 static inline int read_uint8(SDL_RWops *rw, Uint8 *ui8) | |
93 { | |
94 int rc = SDL_RWread(rw, ui8, sizeof (Uint8), 1); | |
95 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); | |
96 return(1); | |
97 } /* read_uint8 */ | |
98 | |
99 | |
71 /* Chunk management code... */ | 100 /* Chunk management code... */ |
72 | 101 |
73 #define riffID 0x46464952 /* "RIFF", in ascii. */ | 102 #define riffID 0x46464952 /* "RIFF", in ascii. */ |
74 #define waveID 0x45564157 /* "WAVE", in ascii. */ | 103 #define waveID 0x45564157 /* "WAVE", in ascii. */ |
75 | 104 |
81 #define fmtID 0x20746D66 /* "fmt ", in ascii. */ | 110 #define fmtID 0x20746D66 /* "fmt ", in ascii. */ |
82 | 111 |
83 #define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */ | 112 #define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */ |
84 #define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */ | 113 #define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */ |
85 | 114 |
86 typedef struct { | 115 typedef struct |
116 { | |
87 Uint16 iCoef1; | 117 Uint16 iCoef1; |
88 Uint16 iCoef2; | 118 Uint16 iCoef2; |
89 } ADPCMCOEFSET; | 119 } ADPCMCOEFSET; |
120 | |
121 typedef struct | |
122 { | |
123 Uint8 bPredictor; | |
124 Uint16 iDelta; | |
125 Sint16 iSamp1; | |
126 Sint16 iSamp2; | |
127 } ADPCMBLOCKHEADER; | |
90 | 128 |
91 typedef struct S_WAV_FMT_T | 129 typedef struct S_WAV_FMT_T |
92 { | 130 { |
93 Uint32 chunkID; | 131 Uint32 chunkID; |
94 Sint32 chunkSize; | 132 Sint32 chunkSize; |
98 Uint32 dwAvgBytesPerSec; | 136 Uint32 dwAvgBytesPerSec; |
99 Uint16 wBlockAlign; | 137 Uint16 wBlockAlign; |
100 Uint16 wBitsPerSample; | 138 Uint16 wBitsPerSample; |
101 | 139 |
102 void (*free)(struct S_WAV_FMT_T *fmt); | 140 void (*free)(struct S_WAV_FMT_T *fmt); |
103 Uint32(*read_sample)(Sound_Sample *sample); | 141 Uint32 (*read_sample)(Sound_Sample *sample); |
104 | 142 |
105 union | 143 union |
106 { | 144 { |
107 struct | 145 struct |
108 { | 146 { |
109 Uint16 cbSize; | 147 Uint16 cbSize; |
110 Uint16 wSamplesPerBlock; | 148 Uint16 wSamplesPerBlock; |
111 Uint16 wNumCoef; | 149 Uint16 wNumCoef; |
112 ADPCMCOEFSET *aCoeff; | 150 ADPCMCOEFSET *aCoef; |
151 ADPCMBLOCKHEADER *blockheaders; | |
113 } adpcm; | 152 } adpcm; |
114 | 153 |
115 /* put other format-specific data here... */ | 154 /* put other format-specific data here... */ |
116 } fmt; | 155 } fmt; |
117 } fmt_t; | 156 } fmt_t; |
126 static int read_fmt_chunk(SDL_RWops *rw, fmt_t *fmt) | 165 static int read_fmt_chunk(SDL_RWops *rw, fmt_t *fmt) |
127 { | 166 { |
128 /* skip reading the chunk ID, since it was already read at this point... */ | 167 /* skip reading the chunk ID, since it was already read at this point... */ |
129 fmt->chunkID = fmtID; | 168 fmt->chunkID = fmtID; |
130 | 169 |
131 if (SDL_RWread(rw, &fmt->chunkSize, sizeof (fmt->chunkSize), 1) != 1) | 170 BAIL_IF_MACRO(!read_le32(rw, &fmt->chunkSize), NULL, 0); |
132 return(0); | 171 BAIL_IF_MACRO(!read_le16(rw, &fmt->wFormatTag), NULL, 0); |
133 fmt->chunkSize = SDL_SwapLE32(fmt->chunkSize); | 172 BAIL_IF_MACRO(!read_le16(rw, &fmt->wChannels), NULL, 0); |
134 | 173 BAIL_IF_MACRO(!read_le32(rw, &fmt->dwSamplesPerSec), NULL, 0); |
135 if (SDL_RWread(rw, &fmt->wFormatTag, sizeof (fmt->wFormatTag), 1) != 1) | 174 BAIL_IF_MACRO(!read_le32(rw, &fmt->dwAvgBytesPerSec), NULL, 0); |
136 return(0); | 175 BAIL_IF_MACRO(!read_le16(rw, &fmt->wBlockAlign), NULL, 0); |
137 fmt->wFormatTag = SDL_SwapLE16(fmt->wFormatTag); | 176 BAIL_IF_MACRO(!read_le16(rw, &fmt->wBitsPerSample), NULL, 0); |
138 | |
139 if (SDL_RWread(rw, &fmt->wChannels, sizeof (fmt->wChannels), 1) != 1) | |
140 return(0); | |
141 fmt->wChannels = SDL_SwapLE16(fmt->wChannels); | |
142 | |
143 if (SDL_RWread(rw, &fmt->dwSamplesPerSec, | |
144 sizeof (fmt->dwSamplesPerSec), 1) != 1) | |
145 return(0); | |
146 fmt->dwSamplesPerSec = SDL_SwapLE32(fmt->dwSamplesPerSec); | |
147 | |
148 if (SDL_RWread(rw, &fmt->dwAvgBytesPerSec, | |
149 sizeof (fmt->dwAvgBytesPerSec), 1) != 1) | |
150 return(0); | |
151 fmt->dwAvgBytesPerSec = SDL_SwapLE32(fmt->dwAvgBytesPerSec); | |
152 | |
153 if (SDL_RWread(rw, &fmt->wBlockAlign, sizeof (fmt->wBlockAlign), 1) != 1) | |
154 return(0); | |
155 fmt->wBlockAlign = SDL_SwapLE16(fmt->wBlockAlign); | |
156 | |
157 if (SDL_RWread(rw, &fmt->wBitsPerSample, | |
158 sizeof (fmt->wBitsPerSample), 1) != 1) | |
159 return(0); | |
160 fmt->wBitsPerSample = SDL_SwapLE16(fmt->wBitsPerSample); | |
161 | 177 |
162 return(1); | 178 return(1); |
163 } /* read_fmt_chunk */ | 179 } /* read_fmt_chunk */ |
164 | 180 |
165 | 181 |
184 */ | 200 */ |
185 static int read_data_chunk(SDL_RWops *rw, data_t *data) | 201 static int read_data_chunk(SDL_RWops *rw, data_t *data) |
186 { | 202 { |
187 /* skip reading the chunk ID, since it was already read at this point... */ | 203 /* skip reading the chunk ID, since it was already read at this point... */ |
188 data->chunkID = dataID; | 204 data->chunkID = dataID; |
189 | 205 BAIL_IF_MACRO(!read_le32(rw, &data->chunkSize), NULL, 0); |
190 if (SDL_RWread(rw, &data->chunkSize, sizeof (data->chunkSize), 1) != 1) | |
191 return(0); | |
192 data->chunkSize = SDL_SwapLE32(data->chunkSize); | |
193 | |
194 return(1); | 206 return(1); |
195 } /* read_data_chunk */ | 207 } /* read_data_chunk */ |
196 | 208 |
197 | 209 |
198 | 210 |
245 | 257 |
246 return(retval); | 258 return(retval); |
247 } /* read_sample_fmt_normal */ | 259 } /* read_sample_fmt_normal */ |
248 | 260 |
249 | 261 |
250 static void free_fmt_normal(fmt_t *fmt) | |
251 { | |
252 /* it's a no-op. */ | |
253 } /* free_fmt_normal */ | |
254 | |
255 | 262 |
256 static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt) | 263 static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt) |
257 { | 264 { |
258 /* (don't need to read more from the RWops...) */ | 265 /* (don't need to read more from the RWops...) */ |
259 fmt->free = free_fmt_normal; | 266 fmt->free = NULL; |
260 fmt->read_sample = read_sample_fmt_normal; | 267 fmt->read_sample = read_sample_fmt_normal; |
261 return(1); | 268 return(1); |
262 } /* read_fmt_normal */ | 269 } /* read_fmt_normal */ |
263 | 270 |
264 | 271 |
265 | 272 |
266 /***************************************************************************** | 273 /***************************************************************************** |
267 * ADPCM compression handler... * | 274 * ADPCM compression handler... * |
268 *****************************************************************************/ | 275 *****************************************************************************/ |
269 | 276 |
277 static int read_adpcm_block_headers(SDL_RWops *rw, fmt_t *fmt) | |
278 { | |
279 ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; | |
280 int i; | |
281 int max = fmt->wChannels; | |
282 | |
283 for (i = 0; i < max; i++) | |
284 BAIL_IF_MACRO(!read_uint8(rw, &headers[i].bPredictor), NULL, 0); | |
285 | |
286 for (i = 0; i < max; i++) | |
287 BAIL_IF_MACRO(!read_le16(rw, &headers[i].iDelta), NULL, 0); | |
288 | |
289 for (i = 0; i < max; i++) | |
290 BAIL_IF_MACRO(!read_le16(rw, &headers[i].iSamp1), NULL, 0); | |
291 | |
292 for (i = 0; i < max; i++) | |
293 BAIL_IF_MACRO(!read_le16(rw, &headers[i].iSamp2), NULL, 0); | |
294 | |
295 return(1); | |
296 } /* read_adpcm_block_headers */ | |
297 | |
298 | |
299 static int decode_adpcm_block(Sound_Sample *sample) | |
300 { | |
301 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
302 SDL_RWops *rw = internal->rw; | |
303 wav_t *w = (wav_t *) internal->decoder_private; | |
304 fmt_t *fmt = w->fmt; | |
305 ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; | |
306 int i; | |
307 int max = fmt->wChannels; | |
308 | |
309 if (!read_adpcm_block_headers(rw, fmt)) | |
310 { | |
311 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
312 return(0); | |
313 } /* if */ | |
314 | |
315 for (i = 0; i < max; i++) | |
316 { | |
317 Uint16 iCoef1 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef1; | |
318 Uint16 iCoef2 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef2; | |
319 /* | |
320 Sint32 lPredSamp = ((headers[i].iSamp1 * iCoef1) + | |
321 (headers[i].iSamp2 * iCoef2)) / | |
322 FIXED_POINT_COEF_BASE; | |
323 */ | |
324 } /* for */ | |
325 } /* decode_adpcm_block */ | |
326 | |
327 | |
270 static Uint32 read_sample_fmt_adpcm(Sound_Sample *sample) | 328 static Uint32 read_sample_fmt_adpcm(Sound_Sample *sample) |
271 { | 329 { |
272 /* !!! FIXME: Write this. */ | 330 /* !!! FIXME: Write this. */ |
273 sample->flags | SOUND_SAMPLEFLAG_ERROR; | 331 Sound_SetError("WAV: Not implemented. Soon."); |
332 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
274 return(0); | 333 return(0); |
275 } /* read_sample_fmt_adpcm */ | 334 } /* read_sample_fmt_adpcm */ |
276 | 335 |
277 | 336 |
278 static void free_fmt_adpcm(fmt_t *fmt) | 337 static void free_fmt_adpcm(fmt_t *fmt) |
279 { | 338 { |
280 if (fmt->fmt.adpcm.aCoeff != NULL) | 339 if (fmt->fmt.adpcm.aCoef != NULL) |
281 free(fmt->fmt.adpcm.aCoeff); | 340 free(fmt->fmt.adpcm.aCoef); |
341 | |
342 if (fmt->fmt.adpcm.blockheaders != NULL) | |
343 free(fmt->fmt.adpcm.blockheaders); | |
282 } /* free_fmt_adpcm */ | 344 } /* free_fmt_adpcm */ |
283 | 345 |
284 | 346 |
285 /* | 347 /* |
286 * Read in a the adpcm-specific info from disk. This makes this process | 348 * Read in a the adpcm-specific info from disk. This makes this process |
289 */ | 351 */ |
290 static int read_fmt_adpcm(SDL_RWops *rw, fmt_t *fmt) | 352 static int read_fmt_adpcm(SDL_RWops *rw, fmt_t *fmt) |
291 { | 353 { |
292 size_t i; | 354 size_t i; |
293 | 355 |
294 fmt->fmt.adpcm.aCoeff = NULL; | 356 memset(&fmt->fmt.adpcm, '\0', sizeof (fmt->fmt.adpcm)); |
295 fmt->free = free_fmt_adpcm; | 357 fmt->free = free_fmt_adpcm; |
296 fmt->read_sample = read_sample_fmt_adpcm; | 358 fmt->read_sample = read_sample_fmt_adpcm; |
297 | 359 |
298 if (SDL_RWread(rw, &fmt->fmt.adpcm.cbSize, | 360 BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.cbSize), NULL, 0); |
299 sizeof (fmt->fmt.adpcm.cbSize), 1) != 1) | 361 BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wSamplesPerBlock), NULL, 0); |
300 { | 362 BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wNumCoef), NULL, 0); |
301 return(0); | 363 |
364 /* | |
365 * !!! FIXME: SDL seems nervous about this, so I'll make a debug | |
366 * !!! FIXME: note for the time being... | |
367 */ | |
368 if (fmt->fmt.adpcm.wNumCoef != 7) | |
369 { | |
370 SNDDBG(("WAV: adpcm's wNumCoef is NOT seven (it's %d)!\n", | |
371 fmt->fmt.adpcm.wNumCoef)); | |
302 } /* if */ | 372 } /* if */ |
303 fmt->fmt.adpcm.cbSize = SDL_SwapLE16(fmt->fmt.adpcm.cbSize); | 373 |
304 | 374 /* fmt->free() is always called, so these malloc()s will be cleaned up. */ |
305 if (SDL_RWread(rw, &fmt->fmt.adpcm.wSamplesPerBlock, | |
306 sizeof (fmt->fmt.adpcm.wSamplesPerBlock), 1) != 1) | |
307 { | |
308 return(0); | |
309 } /* if */ | |
310 fmt->fmt.adpcm.wSamplesPerBlock = SDL_SwapLE16(fmt->fmt.adpcm.wSamplesPerBlock); | |
311 | |
312 if (SDL_RWread(rw, &fmt->fmt.adpcm.wNumCoef, | |
313 sizeof (fmt->fmt.adpcm.wNumCoef), 1) != 1) | |
314 { | |
315 return(0); | |
316 } /* if */ | |
317 fmt->fmt.adpcm.wNumCoef = SDL_SwapLE16(fmt->fmt.adpcm.wNumCoef); | |
318 | |
319 i = sizeof (ADPCMCOEFSET) * fmt->fmt.adpcm.wNumCoef; | 375 i = sizeof (ADPCMCOEFSET) * fmt->fmt.adpcm.wNumCoef; |
320 fmt->fmt.adpcm.aCoeff = (ADPCMCOEFSET *) malloc(i); | 376 fmt->fmt.adpcm.aCoef = (ADPCMCOEFSET *) malloc(i); |
321 BAIL_IF_MACRO(fmt->fmt.adpcm.aCoeff == NULL, ERR_OUT_OF_MEMORY, 0); | 377 BAIL_IF_MACRO(fmt->fmt.adpcm.aCoef == NULL, ERR_OUT_OF_MEMORY, 0); |
378 | |
379 i = sizeof (ADPCMBLOCKHEADER) * fmt->wChannels; | |
380 fmt->fmt.adpcm.blockheaders = (ADPCMBLOCKHEADER *) malloc(i); | |
381 BAIL_IF_MACRO(fmt->fmt.adpcm.blockheaders == NULL, ERR_OUT_OF_MEMORY, 0); | |
322 | 382 |
323 for (i = 0; i < fmt->fmt.adpcm.wNumCoef; i++) | 383 for (i = 0; i < fmt->fmt.adpcm.wNumCoef; i++) |
324 { | 384 { |
325 if (SDL_RWread(rw, &fmt->fmt.adpcm.aCoeff[i].iCoef1, | 385 int rc; |
326 sizeof (fmt->fmt.adpcm.aCoeff[i].iCoef1), 1) != 1) | 386 rc = SDL_RWread(rw, &fmt->fmt.adpcm.aCoef[i].iCoef1, |
327 { | 387 sizeof (fmt->fmt.adpcm.aCoef[i].iCoef1), 1); |
328 return(0); | 388 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); |
329 } /* if */ | 389 |
330 | 390 rc = SDL_RWread(rw, &fmt->fmt.adpcm.aCoef[i].iCoef2, |
331 if (SDL_RWread(rw, &fmt->fmt.adpcm.aCoeff[i].iCoef2, | 391 sizeof (fmt->fmt.adpcm.aCoef[i].iCoef2), 1); |
332 sizeof (fmt->fmt.adpcm.aCoeff[i].iCoef2), 1) != 1) | 392 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); |
333 { | |
334 return(0); | |
335 } /* if */ | |
336 } /* for */ | 393 } /* for */ |
337 | 394 |
338 return(1); | 395 return(1); |
339 } /* read_fmt_adpcm */ | 396 } /* read_fmt_adpcm */ |
340 | 397 |
391 Sint32 siz = 0; | 448 Sint32 siz = 0; |
392 Uint32 _id = 0; | 449 Uint32 _id = 0; |
393 | 450 |
394 while (1) | 451 while (1) |
395 { | 452 { |
396 BAIL_IF_MACRO(SDL_RWread(rw, &_id, sizeof (_id), 1) != 1, NULL, 0); | 453 BAIL_IF_MACRO(!read_le32(rw, &_id), NULL, 0); |
397 if (SDL_SwapLE32(_id) == id) | 454 if (_id == id) |
398 return(1); | 455 return(1); |
399 | 456 |
400 BAIL_IF_MACRO(SDL_RWread(rw, &siz, sizeof (siz), 1) != 1, NULL, 0); | 457 /* skip ahead and see what next chunk is... */ |
401 siz = SDL_SwapLE32(siz); | 458 BAIL_IF_MACRO(!read_le32(rw, &siz), NULL, 0); |
402 assert(siz > 0); | 459 assert(siz > 0); |
403 BAIL_IF_MACRO(SDL_RWseek(rw, siz, SEEK_SET) != siz, NULL, 0); | 460 BAIL_IF_MACRO(SDL_RWseek(rw, siz, SEEK_SET) != siz, NULL, 0); |
404 } /* while */ | 461 } /* while */ |
405 | 462 |
406 return(0); /* shouldn't hit this, but just in case... */ | 463 return(0); /* shouldn't hit this, but just in case... */ |
468 | 525 |
469 static void WAV_close(Sound_Sample *sample) | 526 static void WAV_close(Sound_Sample *sample) |
470 { | 527 { |
471 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | 528 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; |
472 wav_t *w = (wav_t *) internal->decoder_private; | 529 wav_t *w = (wav_t *) internal->decoder_private; |
473 w->fmt->free(w->fmt); | 530 |
531 if (w->fmt->free != NULL) | |
532 w->fmt->free(w->fmt); | |
533 | |
474 free(w->fmt); | 534 free(w->fmt); |
475 free(w); | 535 free(w); |
476 } /* WAV_close */ | 536 } /* WAV_close */ |
477 | 537 |
478 | 538 |