Mercurial > almixer_isolated
annotate Isolated/LGPL/wav.c @ 79:358b0bd5df43 tip
Added support for Apportable's alcSuspend()/alcResume() in BeginInterruption()/EndInterruption. You must define ALMIXER_USE_APPORTABLE_OPENAL_EXTENSIONS to compile in this support.
author | Eric Wing <ewing@coronalabs.com> |
---|---|
date | Tue, 30 Oct 2012 16:01:30 -0700 |
parents | 12e4e093c6e0 |
children |
rev | line source |
---|---|
38 | 1 /* |
2 * SDL_sound -- An abstract sound format decoding API. | |
3 * Copyright (C) 2001 Ryan C. Gordon. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2.1 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
20 /* |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
21 Attention: This is a stripped down file of SDL_endian for our purposes. |
38 | 22 This code is licensed under the LGPL. |
23 This means we must not compile this code into anything that we are not willing to | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
24 publicly release source code. |
38 | 25 You should compile this into a separate dynamic library that is isolated from proprietary code. |
26 */ | |
27 | |
28 /* | |
29 * WAV decoder for SDL_sound. | |
30 * | |
31 * This driver handles Microsoft .WAVs, in as many of the thousands of | |
32 * variations as we can. | |
33 * | |
34 * Please see the file LICENSE.txt in the source's root directory. | |
35 * | |
36 * This file written by Ryan C. Gordon. (icculus@icculus.org) | |
37 */ | |
38 | |
39 #if HAVE_CONFIG_H | |
40 # include <config.h> | |
41 #endif | |
42 | |
43 #ifdef SOUND_SUPPORTS_WAV | |
44 | |
45 #include <stdio.h> | |
46 #include <stdlib.h> | |
47 #include <string.h> | |
48 | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
49 /* |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
50 #include "SDL_sound.h" |
38 | 51 |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
52 #define __SDL_SOUND_INTERNAL__ |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
53 #include "SDL_sound_internal.h" |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
54 */ |
38 | 55 |
56 #include "SoundDecoder.h" | |
57 | |
58 #include "SoundDecoder_Internal.h" | |
59 #include "SDL_endian_minimal.h" | |
60 #include "ALmixer_RWops.h" | |
61 | |
62 #define ERR_IO_ERROR "I/O error" | |
63 #define assert(x) | |
64 | |
65 static int WAV_init(void); | |
66 static void WAV_quit(void); | |
67 static int WAV_open(Sound_Sample *sample, const char *ext); | |
68 static void WAV_close(Sound_Sample *sample); | |
69 static uint32_t WAV_read(Sound_Sample *sample); | |
70 static int WAV_rewind(Sound_Sample *sample); | |
71 static int WAV_seek(Sound_Sample *sample, uint32_t ms); | |
72 | |
73 static const char *extensions_wav[] = { "WAV", NULL }; | |
74 const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV = | |
75 { | |
76 { | |
77 extensions_wav, | |
78 "Microsoft WAVE audio format", | |
79 "Ryan C. Gordon <icculus@icculus.org>", | |
80 "http://www.icculus.org/SDL_sound/" | |
81 }, | |
82 | |
83 WAV_init, /* init() method */ | |
84 WAV_quit, /* quit() method */ | |
85 WAV_open, /* open() method */ | |
86 WAV_close, /* close() method */ | |
87 WAV_read, /* read() method */ | |
88 WAV_rewind, /* rewind() method */ | |
89 WAV_seek /* seek() method */ | |
90 }; | |
91 | |
92 | |
93 /* Better than SDL_ReadLE16, since you can detect i/o errors... */ | |
94 static __inline__ int read_le16(ALmixer_RWops *rw, uint16_t *ui16) | |
95 { | |
96 int rc = ALmixer_RWread(rw, ui16, sizeof (uint16_t), 1); | |
97 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); | |
98 *ui16 = SDL_SwapLE16(*ui16); | |
99 return(1); | |
100 } /* read_le16 */ | |
101 | |
102 | |
103 /* Better than SDL_ReadLE32, since you can detect i/o errors... */ | |
104 static __inline__ int read_le32(ALmixer_RWops *rw, uint32_t *ui32) | |
105 { | |
106 int rc = ALmixer_RWread(rw, ui32, sizeof (uint32_t), 1); | |
107 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); | |
108 *ui32 = SDL_SwapLE32(*ui32); | |
109 return(1); | |
110 } /* read_le32 */ | |
111 | |
112 | |
113 /* This is just cleaner on the caller's end... */ | |
114 static __inline__ int read_uint8_t(ALmixer_RWops *rw, uint8_t *ui8) | |
115 { | |
116 int rc = ALmixer_RWread(rw, ui8, sizeof (uint8_t), 1); | |
117 BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); | |
118 return(1); | |
119 } /* read_uint8_t */ | |
120 | |
121 | |
122 static __inline__ uint16_t SDL_ReadLE16( ALmixer_RWops *rw ) | |
123 { | |
124 uint16_t result = 0; | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
125 |
38 | 126 int rc = read_le16( rw, &result ); |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
127 |
38 | 128 return result; |
129 } | |
130 static __inline__ uint32_t SDL_ReadLE32( ALmixer_RWops *rw ) | |
131 { | |
132 uint32_t result = 0; | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
133 |
38 | 134 int rc = read_le32( rw, &result ); |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
135 |
38 | 136 return result; |
137 } | |
138 | |
139 /* Chunk management code... */ | |
140 | |
141 #define riffID 0x46464952 /* "RIFF", in ascii. */ | |
142 #define waveID 0x45564157 /* "WAVE", in ascii. */ | |
143 #define factID 0x74636166 /* "fact", in ascii. */ | |
144 | |
145 | |
146 /***************************************************************************** | |
147 * The FORMAT chunk... * | |
148 *****************************************************************************/ | |
149 | |
150 #define fmtID 0x20746D66 /* "fmt ", in ascii. */ | |
151 | |
152 #define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */ | |
153 #define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */ | |
154 | |
155 typedef struct | |
156 { | |
157 int16_t iCoef1; | |
158 int16_t iCoef2; | |
159 } ADPCMCOEFSET; | |
160 | |
161 typedef struct | |
162 { | |
163 uint8_t bPredictor; | |
164 uint16_t iDelta; | |
165 int16_t iSamp1; | |
166 int16_t iSamp2; | |
167 } ADPCMBLOCKHEADER; | |
168 | |
169 typedef struct S_WAV_FMT_T | |
170 { | |
171 uint32_t chunkID; | |
172 int32_t chunkSize; | |
173 int16_t wFormatTag; | |
174 uint16_t wChannels; | |
175 uint32_t dwSamplesPerSec; | |
176 uint32_t dwAvgBytesPerSec; | |
177 uint16_t wBlockAlign; | |
178 uint16_t wBitsPerSample; | |
179 | |
180 uint32_t next_chunk_offset; | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
181 |
38 | 182 uint32_t sample_frame_size; |
183 uint32_t data_starting_offset; | |
184 uint32_t total_bytes; | |
185 | |
186 void (*free)(struct S_WAV_FMT_T *fmt); | |
187 uint32_t (*read_sample)(Sound_Sample *sample); | |
188 int (*rewind_sample)(Sound_Sample *sample); | |
189 int (*seek_sample)(Sound_Sample *sample, uint32_t ms); | |
190 | |
191 union | |
192 { | |
193 struct | |
194 { | |
195 uint16_t cbSize; | |
196 uint16_t wSamplesPerBlock; | |
197 uint16_t wNumCoef; | |
198 ADPCMCOEFSET *aCoef; | |
199 ADPCMBLOCKHEADER *blockheaders; | |
200 uint32_t samples_left_in_block; | |
201 int nibble_state; | |
202 int8_t nibble; | |
203 } adpcm; | |
204 | |
205 /* put other format-specific data here... */ | |
206 } fmt; | |
207 } fmt_t; | |
208 | |
209 | |
210 /* | |
211 * Read in a fmt_t from disk. This makes this process safe regardless of | |
212 * the processor's byte order or how the fmt_t structure is packed. | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
213 * Note that the union "fmt" is not read in here; that is handled as |
38 | 214 * needed in the read_fmt_* functions. |
215 */ | |
216 static int read_fmt_chunk(ALmixer_RWops *rw, fmt_t *fmt) | |
217 { | |
218 /* skip reading the chunk ID, since it was already read at this point... */ | |
219 fmt->chunkID = fmtID; | |
220 | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
221 BAIL_IF_MACRO(!read_le32(rw, (uint32_t*)&fmt->chunkSize), NULL, 0); |
38 | 222 BAIL_IF_MACRO(fmt->chunkSize < 16, "WAV: Invalid chunk size", 0); |
223 fmt->next_chunk_offset = ALmixer_RWtell(rw) + fmt->chunkSize; | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
224 |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
225 BAIL_IF_MACRO(!read_le16(rw, (uint16_t*)&fmt->wFormatTag), NULL, 0); |
38 | 226 BAIL_IF_MACRO(!read_le16(rw, &fmt->wChannels), NULL, 0); |
227 BAIL_IF_MACRO(!read_le32(rw, &fmt->dwSamplesPerSec), NULL, 0); | |
228 BAIL_IF_MACRO(!read_le32(rw, &fmt->dwAvgBytesPerSec), NULL, 0); | |
229 BAIL_IF_MACRO(!read_le16(rw, &fmt->wBlockAlign), NULL, 0); | |
230 BAIL_IF_MACRO(!read_le16(rw, &fmt->wBitsPerSample), NULL, 0); | |
231 | |
232 return(1); | |
233 } /* read_fmt_chunk */ | |
234 | |
235 | |
236 | |
237 /***************************************************************************** | |
238 * The DATA chunk... * | |
239 *****************************************************************************/ | |
240 | |
241 #define dataID 0x61746164 /* "data", in ascii. */ | |
242 | |
243 typedef struct | |
244 { | |
245 uint32_t chunkID; | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
246 |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
247 /* Johnson Lin wanted to clean up compiler warnings on Windows/CodeBlocks. |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
248 * This was originally a signed int32_t. The code usage and intent seems to imply that it should be a uint32_t. |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
249 */ |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
250 /* int32_t chunkSize; */ |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
251 uint32_t chunkSize; |
38 | 252 /* Then, (chunkSize) bytes of waveform data... */ |
253 } data_t; | |
254 | |
255 | |
256 /* | |
257 * Read in a data_t from disk. This makes this process safe regardless of | |
258 * the processor's byte order or how the fmt_t structure is packed. | |
259 */ | |
260 static int read_data_chunk(ALmixer_RWops *rw, data_t *data) | |
261 { | |
262 /* skip reading the chunk ID, since it was already read at this point... */ | |
263 data->chunkID = dataID; | |
264 BAIL_IF_MACRO(!read_le32(rw, &data->chunkSize), NULL, 0); | |
265 return(1); | |
266 } /* read_data_chunk */ | |
267 | |
268 | |
269 | |
270 | |
271 /***************************************************************************** | |
272 * this is what we store in our internal->decoder_private field... * | |
273 *****************************************************************************/ | |
274 | |
275 typedef struct | |
276 { | |
277 fmt_t *fmt; | |
278 int32_t bytesLeft; | |
279 } wav_t; | |
280 | |
281 | |
282 | |
283 | |
284 /***************************************************************************** | |
285 * Normal, uncompressed waveform handler... * | |
286 *****************************************************************************/ | |
287 | |
288 /* | |
289 * Sound_Decode() lands here for uncompressed WAVs... | |
290 */ | |
291 static uint32_t read_sample_fmt_normal(Sound_Sample *sample) | |
292 { | |
293 uint32_t retval; | |
294 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
295 wav_t *w = (wav_t *) internal->decoder_private; | |
296 uint32_t max = (internal->buffer_size < (uint32_t) w->bytesLeft) ? | |
297 internal->buffer_size : (uint32_t) w->bytesLeft; | |
298 | |
299 assert(max > 0); | |
300 | |
301 /* | |
302 * We don't actually do any decoding, so we read the wav data | |
303 * directly into the internal buffer... | |
304 */ | |
305 retval = ALmixer_RWread(internal->rw, internal->buffer, 1, max); | |
306 | |
307 w->bytesLeft -= retval; | |
308 | |
309 /* Make sure the read went smoothly... */ | |
310 if ((retval == 0) || (w->bytesLeft == 0)) | |
311 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
312 | |
313 else if (retval == -1) | |
314 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
315 | |
316 /* (next call this EAGAIN may turn into an EOF or error.) */ | |
317 else if (retval < internal->buffer_size) | |
318 sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; | |
319 | |
320 return(retval); | |
321 } /* read_sample_fmt_normal */ | |
322 | |
323 | |
324 static int seek_sample_fmt_normal(Sound_Sample *sample, uint32_t ms) | |
325 { | |
326 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
327 wav_t *w = (wav_t *) internal->decoder_private; | |
328 fmt_t *fmt = w->fmt; | |
329 int offset = __Sound_convertMsToBytePos(&sample->actual, ms); | |
330 int pos = (int) (fmt->data_starting_offset + offset); | |
331 int rc = ALmixer_RWseek(internal->rw, pos, SEEK_SET); | |
332 BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); | |
333 w->bytesLeft = fmt->total_bytes - offset; | |
334 return(1); /* success. */ | |
335 } /* seek_sample_fmt_normal */ | |
336 | |
337 | |
338 static int rewind_sample_fmt_normal(Sound_Sample *sample) | |
339 { | |
340 /* no-op. */ | |
341 return(1); | |
342 } /* rewind_sample_fmt_normal */ | |
343 | |
344 | |
345 static int read_fmt_normal(ALmixer_RWops *rw, fmt_t *fmt) | |
346 { | |
347 /* (don't need to read more from the RWops...) */ | |
348 fmt->free = NULL; | |
349 fmt->read_sample = read_sample_fmt_normal; | |
350 fmt->rewind_sample = rewind_sample_fmt_normal; | |
351 fmt->seek_sample = seek_sample_fmt_normal; | |
352 return(1); | |
353 } /* read_fmt_normal */ | |
354 | |
355 | |
356 | |
357 /***************************************************************************** | |
358 * ADPCM compression handler... * | |
359 *****************************************************************************/ | |
360 | |
361 #define FIXED_POINT_COEF_BASE 256 | |
362 #define FIXED_POINT_ADAPTION_BASE 256 | |
363 #define SMALLEST_ADPCM_DELTA 16 | |
364 | |
365 | |
366 static __inline__ int read_adpcm_block_headers(Sound_Sample *sample) | |
367 { | |
368 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
369 ALmixer_RWops *rw = internal->rw; | |
370 wav_t *w = (wav_t *) internal->decoder_private; | |
371 fmt_t *fmt = w->fmt; | |
372 ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; | |
373 int i; | |
374 int max = fmt->wChannels; | |
375 | |
376 if (w->bytesLeft < fmt->wBlockAlign) | |
377 { | |
378 sample->flags |= SOUND_SAMPLEFLAG_EOF; | |
379 return(0); | |
380 } /* if */ | |
381 | |
382 w->bytesLeft -= fmt->wBlockAlign; | |
383 | |
384 for (i = 0; i < max; i++) | |
385 BAIL_IF_MACRO(!read_uint8_t(rw, &headers[i].bPredictor), NULL, 0); | |
386 | |
387 for (i = 0; i < max; i++) | |
388 BAIL_IF_MACRO(!read_le16(rw, &headers[i].iDelta), NULL, 0); | |
389 | |
390 for (i = 0; i < max; i++) | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
391 BAIL_IF_MACRO(!read_le16(rw, (uint16_t*)&headers[i].iSamp1), NULL, 0); |
38 | 392 |
393 for (i = 0; i < max; i++) | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
394 BAIL_IF_MACRO(!read_le16(rw, (uint16_t*)&headers[i].iSamp2), NULL, 0); |
38 | 395 |
396 fmt->fmt.adpcm.samples_left_in_block = fmt->fmt.adpcm.wSamplesPerBlock; | |
397 fmt->fmt.adpcm.nibble_state = 0; | |
398 return(1); | |
399 } /* read_adpcm_block_headers */ | |
400 | |
401 | |
402 static __inline__ void do_adpcm_nibble(uint8_t nib, | |
403 ADPCMBLOCKHEADER *header, | |
404 int32_t lPredSamp) | |
405 { | |
406 static const int32_t max_audioval = ((1<<(16-1))-1); | |
407 static const int32_t min_audioval = -(1<<(16-1)); | |
408 static const int32_t AdaptionTable[] = | |
409 { | |
410 230, 230, 230, 230, 307, 409, 512, 614, | |
411 768, 614, 512, 409, 307, 230, 230, 230 | |
412 }; | |
413 | |
414 int32_t lNewSamp; | |
415 int32_t delta; | |
416 | |
417 if (nib & 0x08) | |
418 lNewSamp = lPredSamp + (header->iDelta * (nib - 0x10)); | |
419 else | |
420 lNewSamp = lPredSamp + (header->iDelta * nib); | |
421 | |
422 /* clamp value... */ | |
423 if (lNewSamp < min_audioval) | |
424 lNewSamp = min_audioval; | |
425 else if (lNewSamp > max_audioval) | |
426 lNewSamp = max_audioval; | |
427 | |
428 delta = ((int32_t) header->iDelta * AdaptionTable[nib]) / | |
429 FIXED_POINT_ADAPTION_BASE; | |
430 | |
431 if (delta < SMALLEST_ADPCM_DELTA) | |
432 delta = SMALLEST_ADPCM_DELTA; | |
433 | |
434 header->iDelta = delta; | |
435 header->iSamp2 = header->iSamp1; | |
436 header->iSamp1 = lNewSamp; | |
437 } /* do_adpcm_nibble */ | |
438 | |
439 | |
440 static __inline__ int decode_adpcm_sample_frame(Sound_Sample *sample) | |
441 { | |
442 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
443 wav_t *w = (wav_t *) internal->decoder_private; | |
444 fmt_t *fmt = w->fmt; | |
445 ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; | |
446 ALmixer_RWops *rw = internal->rw; | |
447 int i; | |
448 int max = fmt->wChannels; | |
449 int32_t delta; | |
450 uint8_t nib = fmt->fmt.adpcm.nibble; | |
451 | |
452 for (i = 0; i < max; i++) | |
453 { | |
454 uint8_t byte; | |
455 int16_t iCoef1 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef1; | |
456 int16_t iCoef2 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef2; | |
457 int32_t lPredSamp = ((headers[i].iSamp1 * iCoef1) + | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
458 (headers[i].iSamp2 * iCoef2)) / |
38 | 459 FIXED_POINT_COEF_BASE; |
460 | |
461 if (fmt->fmt.adpcm.nibble_state == 0) | |
462 { | |
463 BAIL_IF_MACRO(!read_uint8_t(rw, &nib), NULL, 0); | |
464 fmt->fmt.adpcm.nibble_state = 1; | |
465 do_adpcm_nibble(nib >> 4, &headers[i], lPredSamp); | |
466 } /* if */ | |
467 else | |
468 { | |
469 fmt->fmt.adpcm.nibble_state = 0; | |
470 do_adpcm_nibble(nib & 0x0F, &headers[i], lPredSamp); | |
471 } /* else */ | |
472 } /* for */ | |
473 | |
474 fmt->fmt.adpcm.nibble = nib; | |
475 return(1); | |
476 } /* decode_adpcm_sample_frame */ | |
477 | |
478 | |
479 static __inline__ void put_adpcm_sample_frame1(void *_buf, fmt_t *fmt) | |
480 { | |
481 uint16_t *buf = (uint16_t *) _buf; | |
482 ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; | |
483 int i; | |
484 for (i = 0; i < fmt->wChannels; i++) | |
485 *(buf++) = headers[i].iSamp1; | |
486 } /* put_adpcm_sample_frame1 */ | |
487 | |
488 | |
489 static __inline__ void put_adpcm_sample_frame2(void *_buf, fmt_t *fmt) | |
490 { | |
491 uint16_t *buf = (uint16_t *) _buf; | |
492 ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; | |
493 int i; | |
494 for (i = 0; i < fmt->wChannels; i++) | |
495 *(buf++) = headers[i].iSamp2; | |
496 } /* put_adpcm_sample_frame2 */ | |
497 | |
498 | |
499 /* | |
500 * Sound_Decode() lands here for ADPCM-encoded WAVs... | |
501 */ | |
502 static uint32_t read_sample_fmt_adpcm(Sound_Sample *sample) | |
503 { | |
504 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
505 wav_t *w = (wav_t *) internal->decoder_private; | |
506 fmt_t *fmt = w->fmt; | |
507 uint32_t bw = 0; | |
508 | |
509 while (bw < internal->buffer_size) | |
510 { | |
511 /* write ongoing sample frame before reading more data... */ | |
512 switch (fmt->fmt.adpcm.samples_left_in_block) | |
513 { | |
514 case 0: /* need to read a new block... */ | |
515 if (!read_adpcm_block_headers(sample)) | |
516 { | |
517 if ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0) | |
518 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
519 return(bw); | |
520 } /* if */ | |
521 | |
522 /* only write first sample frame for now. */ | |
523 put_adpcm_sample_frame2((uint8_t *) internal->buffer + bw, fmt); | |
524 fmt->fmt.adpcm.samples_left_in_block--; | |
525 bw += fmt->sample_frame_size; | |
526 break; | |
527 | |
528 case 1: /* output last sample frame of block... */ | |
529 put_adpcm_sample_frame1((uint8_t *) internal->buffer + bw, fmt); | |
530 fmt->fmt.adpcm.samples_left_in_block--; | |
531 bw += fmt->sample_frame_size; | |
532 break; | |
533 | |
534 default: /* output latest sample frame and read a new one... */ | |
535 put_adpcm_sample_frame1((uint8_t *) internal->buffer + bw, fmt); | |
536 fmt->fmt.adpcm.samples_left_in_block--; | |
537 bw += fmt->sample_frame_size; | |
538 | |
539 if (!decode_adpcm_sample_frame(sample)) | |
540 { | |
541 sample->flags |= SOUND_SAMPLEFLAG_ERROR; | |
542 return(bw); | |
543 } /* if */ | |
544 } /* switch */ | |
545 } /* while */ | |
546 | |
547 return(bw); | |
548 } /* read_sample_fmt_adpcm */ | |
549 | |
550 | |
551 /* | |
552 * Sound_FreeSample() lands here for ADPCM-encoded WAVs... | |
553 */ | |
554 static void free_fmt_adpcm(fmt_t *fmt) | |
555 { | |
556 if (fmt->fmt.adpcm.aCoef != NULL) | |
557 free(fmt->fmt.adpcm.aCoef); | |
558 | |
559 if (fmt->fmt.adpcm.blockheaders != NULL) | |
560 free(fmt->fmt.adpcm.blockheaders); | |
561 } /* free_fmt_adpcm */ | |
562 | |
563 | |
564 static int rewind_sample_fmt_adpcm(Sound_Sample *sample) | |
565 { | |
566 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
567 wav_t *w = (wav_t *) internal->decoder_private; | |
568 w->fmt->fmt.adpcm.samples_left_in_block = 0; | |
569 return(1); | |
570 } /* rewind_sample_fmt_adpcm */ | |
571 | |
572 | |
573 static int seek_sample_fmt_adpcm(Sound_Sample *sample, uint32_t ms) | |
574 { | |
575 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
576 wav_t *w = (wav_t *) internal->decoder_private; | |
577 fmt_t *fmt = w->fmt; | |
578 uint32_t origsampsleft = fmt->fmt.adpcm.samples_left_in_block; | |
579 int origpos = ALmixer_RWtell(internal->rw); | |
580 int offset = __Sound_convertMsToBytePos(&sample->actual, ms); | |
581 int bpb = (fmt->fmt.adpcm.wSamplesPerBlock * fmt->sample_frame_size); | |
582 int skipsize = (offset / bpb) * fmt->wBlockAlign; | |
583 int pos = skipsize + fmt->data_starting_offset; | |
584 int rc = ALmixer_RWseek(internal->rw, pos, SEEK_SET); | |
585 BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); | |
586 | |
587 /* The offset we need is in this block, so we need to decode to there. */ | |
588 skipsize += (offset % bpb); | |
589 rc = (offset % bpb); /* bytes into this block we need to decode */ | |
590 if (!read_adpcm_block_headers(sample)) | |
591 { | |
592 ALmixer_RWseek(internal->rw, origpos, SEEK_SET); /* try to make sane. */ | |
593 return(0); | |
594 } /* if */ | |
595 | |
596 /* first sample frame of block is a freebie. :) */ | |
597 fmt->fmt.adpcm.samples_left_in_block--; | |
598 rc -= fmt->sample_frame_size; | |
599 while (rc > 0) | |
600 { | |
601 if (!decode_adpcm_sample_frame(sample)) | |
602 { | |
603 ALmixer_RWseek(internal->rw, origpos, SEEK_SET); | |
604 fmt->fmt.adpcm.samples_left_in_block = origsampsleft; | |
605 return(0); | |
606 } /* if */ | |
607 | |
608 fmt->fmt.adpcm.samples_left_in_block--; | |
609 rc -= fmt->sample_frame_size; | |
610 } /* while */ | |
611 | |
612 w->bytesLeft = fmt->total_bytes - skipsize; | |
613 return(1); /* success. */ | |
614 } /* seek_sample_fmt_adpcm */ | |
615 | |
616 | |
617 /* | |
618 * Read in the adpcm-specific info from disk. This makes this process | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
619 * safe regardless of the processor's byte order or how the fmt_t |
38 | 620 * structure is packed. |
621 */ | |
622 static int read_fmt_adpcm(ALmixer_RWops *rw, fmt_t *fmt) | |
623 { | |
624 size_t i; | |
625 | |
626 memset(&fmt->fmt.adpcm, '\0', sizeof (fmt->fmt.adpcm)); | |
627 fmt->free = free_fmt_adpcm; | |
628 fmt->read_sample = read_sample_fmt_adpcm; | |
629 fmt->rewind_sample = rewind_sample_fmt_adpcm; | |
630 fmt->seek_sample = seek_sample_fmt_adpcm; | |
631 | |
632 BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.cbSize), NULL, 0); | |
633 BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wSamplesPerBlock), NULL, 0); | |
634 BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wNumCoef), NULL, 0); | |
635 | |
636 /* fmt->free() is always called, so these malloc()s will be cleaned up. */ | |
637 | |
638 i = sizeof (ADPCMCOEFSET) * fmt->fmt.adpcm.wNumCoef; | |
639 fmt->fmt.adpcm.aCoef = (ADPCMCOEFSET *) malloc(i); | |
640 BAIL_IF_MACRO(fmt->fmt.adpcm.aCoef == NULL, ERR_OUT_OF_MEMORY, 0); | |
641 | |
642 for (i = 0; i < fmt->fmt.adpcm.wNumCoef; i++) | |
643 { | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
644 BAIL_IF_MACRO(!read_le16(rw, (uint16_t*)&fmt->fmt.adpcm.aCoef[i].iCoef1), NULL, 0); |
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
645 BAIL_IF_MACRO(!read_le16(rw, (uint16_t*)&fmt->fmt.adpcm.aCoef[i].iCoef2), NULL, 0); |
38 | 646 } /* for */ |
647 | |
648 i = sizeof (ADPCMBLOCKHEADER) * fmt->wChannels; | |
649 fmt->fmt.adpcm.blockheaders = (ADPCMBLOCKHEADER *) malloc(i); | |
650 BAIL_IF_MACRO(fmt->fmt.adpcm.blockheaders == NULL, ERR_OUT_OF_MEMORY, 0); | |
651 | |
652 return(1); | |
653 } /* read_fmt_adpcm */ | |
654 | |
655 | |
656 | |
657 /***************************************************************************** | |
658 * Everything else... * | |
659 *****************************************************************************/ | |
660 | |
661 static int WAV_init(void) | |
662 { | |
663 return(1); /* always succeeds. */ | |
664 } /* WAV_init */ | |
665 | |
666 | |
667 static void WAV_quit(void) | |
668 { | |
669 /* it's a no-op. */ | |
670 } /* WAV_quit */ | |
671 | |
672 | |
673 static int read_fmt(ALmixer_RWops *rw, fmt_t *fmt) | |
674 { | |
675 /* if it's in this switch statement, we support the format. */ | |
676 switch (fmt->wFormatTag) | |
677 { | |
678 case FMT_NORMAL: | |
679 SNDDBG(("WAV: Appears to be uncompressed audio.\n")); | |
680 return(read_fmt_normal(rw, fmt)); | |
681 | |
682 case FMT_ADPCM: | |
683 SNDDBG(("WAV: Appears to be ADPCM compressed audio.\n")); | |
684 return(read_fmt_adpcm(rw, fmt)); | |
685 | |
686 /* add other types here. */ | |
687 | |
688 default: | |
689 #ifdef ANDROID_NDK | |
690 SNDDBG(("WAV: Format is unknown.\n")); | |
691 #else | |
692 SNDDBG(("WAV: Format 0x%X is unknown.\n", | |
693 (unsigned int) fmt->wFormatTag)); | |
694 #endif | |
695 BAIL_MACRO("WAV: Unsupported format", 0); | |
696 } /* switch */ | |
697 | |
698 assert(0); /* shouldn't hit this point. */ | |
699 return(0); | |
700 } /* read_fmt */ | |
701 | |
702 | |
703 /* | |
704 * Locate a specific chunk in the WAVE file by ID... | |
705 */ | |
706 static int find_chunk(ALmixer_RWops *rw, uint32_t id) | |
707 { | |
76
12e4e093c6e0
Compiler warning cleanups for wav.c on Windows/CodeBlocks.
Eric Wing <ewing . public |-at-| gmail . com>
parents:
38
diff
changeset
|
708 uint32_t siz = 0; |
38 | 709 uint32_t _id = 0; |
710 uint32_t pos = ALmixer_RWtell(rw); | |
711 | |
712 while (1) | |
713 { | |
714 BAIL_IF_MACRO(!read_le32(rw, &_id), NULL, 0); | |
715 if (_id == id) | |
716 return(1); | |
717 | |
718 /* skip ahead and see what next chunk is... */ | |
719 BAIL_IF_MACRO(!read_le32(rw, &siz), NULL, 0); | |
720 assert(siz >= 0); | |
721 pos += (sizeof (uint32_t) * 2) + siz; | |
722 if (siz > 0) | |
723 BAIL_IF_MACRO(ALmixer_RWseek(rw, pos, SEEK_SET) != pos, NULL, 0); | |
724 } /* while */ | |
725 | |
726 return(0); /* shouldn't hit this, but just in case... */ | |
727 } /* find_chunk */ | |
728 | |
729 | |
730 static int WAV_open_internal(Sound_Sample *sample, const char *ext, fmt_t *fmt) | |
731 { | |
732 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
733 ALmixer_RWops *rw = internal->rw; | |
734 data_t d; | |
735 wav_t *w; | |
736 uint32_t pos; | |
737 | |
738 BAIL_IF_MACRO(SDL_ReadLE32(rw) != riffID, "WAV: Not a RIFF file.", 0); | |
739 SDL_ReadLE32(rw); /* throw the length away; we get this info later. */ | |
740 BAIL_IF_MACRO(SDL_ReadLE32(rw) != waveID, "WAV: Not a WAVE file.", 0); | |
741 BAIL_IF_MACRO(!find_chunk(rw, fmtID), "WAV: No format chunk.", 0); | |
742 BAIL_IF_MACRO(!read_fmt_chunk(rw, fmt), "WAV: Can't read format chunk.", 0); | |
743 | |
744 sample->actual.channels = (uint8_t) fmt->wChannels; | |
745 sample->actual.rate = fmt->dwSamplesPerSec; | |
746 if ((fmt->wBitsPerSample == 4) /*|| (fmt->wBitsPerSample == 0) */ ) | |
747 sample->actual.format = AUDIO_S16SYS; | |
748 else if (fmt->wBitsPerSample == 8) | |
749 sample->actual.format = AUDIO_U8; | |
750 else if (fmt->wBitsPerSample == 16) | |
751 sample->actual.format = AUDIO_S16LSB; | |
752 else | |
753 { | |
754 #ifdef ANDROID_NDK | |
755 SNDDBG(("WAV: unsupported sample size.\n")); | |
756 #else | |
757 SNDDBG(("WAV: %d bits per sample!?\n", (int) fmt->wBitsPerSample)); | |
758 #endif | |
759 BAIL_MACRO("WAV: Unsupported sample size.", 0); | |
760 } /* else */ | |
761 | |
762 BAIL_IF_MACRO(!read_fmt(rw, fmt), NULL, 0); | |
763 ALmixer_RWseek(rw, fmt->next_chunk_offset, SEEK_SET); | |
764 BAIL_IF_MACRO(!find_chunk(rw, dataID), "WAV: No data chunk.", 0); | |
765 BAIL_IF_MACRO(!read_data_chunk(rw, &d), "WAV: Can't read data chunk.", 0); | |
766 | |
767 w = (wav_t *) malloc(sizeof(wav_t)); | |
768 BAIL_IF_MACRO(w == NULL, ERR_OUT_OF_MEMORY, 0); | |
769 w->fmt = fmt; | |
770 fmt->total_bytes = w->bytesLeft = d.chunkSize; | |
771 fmt->data_starting_offset = ALmixer_RWtell(rw); | |
772 fmt->sample_frame_size = ( ((sample->actual.format & 0xFF) / 8) * | |
773 sample->actual.channels ); | |
774 internal->decoder_private = (void *) w; | |
775 | |
776 internal->total_time = (fmt->total_bytes / fmt->dwAvgBytesPerSec) * 1000; | |
777 internal->total_time += (fmt->total_bytes % fmt->dwAvgBytesPerSec) | |
778 * 1000 / fmt->dwAvgBytesPerSec; | |
779 | |
780 sample->flags = SOUND_SAMPLEFLAG_NONE; | |
781 if (fmt->seek_sample != NULL) | |
782 sample->flags |= SOUND_SAMPLEFLAG_CANSEEK; | |
783 | |
784 SNDDBG(("WAV: Accepting data stream.\n")); | |
785 return(1); /* we'll handle this data. */ | |
786 } /* WAV_open_internal */ | |
787 | |
788 | |
789 static int WAV_open(Sound_Sample *sample, const char *ext) | |
790 { | |
791 int rc; | |
792 | |
793 fmt_t *fmt = (fmt_t *) malloc(sizeof (fmt_t)); | |
794 BAIL_IF_MACRO(fmt == NULL, ERR_OUT_OF_MEMORY, 0); | |
795 memset(fmt, '\0', sizeof (fmt_t)); | |
796 | |
797 rc = WAV_open_internal(sample, ext, fmt); | |
798 if (!rc) | |
799 { | |
800 if (fmt->free != NULL) | |
801 fmt->free(fmt); | |
802 free(fmt); | |
803 } /* if */ | |
804 | |
805 return(rc); | |
806 } /* WAV_open */ | |
807 | |
808 | |
809 static void WAV_close(Sound_Sample *sample) | |
810 { | |
811 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
812 wav_t *w = (wav_t *) internal->decoder_private; | |
813 | |
814 if (w->fmt->free != NULL) | |
815 w->fmt->free(w->fmt); | |
816 | |
817 free(w->fmt); | |
818 free(w); | |
819 } /* WAV_close */ | |
820 | |
821 | |
822 static uint32_t WAV_read(Sound_Sample *sample) | |
823 { | |
824 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
825 wav_t *w = (wav_t *) internal->decoder_private; | |
826 return(w->fmt->read_sample(sample)); | |
827 } /* WAV_read */ | |
828 | |
829 | |
830 static int WAV_rewind(Sound_Sample *sample) | |
831 { | |
832 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
833 wav_t *w = (wav_t *) internal->decoder_private; | |
834 fmt_t *fmt = w->fmt; | |
835 int rc = ALmixer_RWseek(internal->rw, fmt->data_starting_offset, SEEK_SET); | |
836 BAIL_IF_MACRO(rc != fmt->data_starting_offset, ERR_IO_ERROR, 0); | |
837 w->bytesLeft = fmt->total_bytes; | |
838 return(fmt->rewind_sample(sample)); | |
839 } /* WAV_rewind */ | |
840 | |
841 | |
842 static int WAV_seek(Sound_Sample *sample, uint32_t ms) | |
843 { | |
844 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; | |
845 wav_t *w = (wav_t *) internal->decoder_private; | |
846 return(w->fmt->seek_sample(sample, ms)); | |
847 } /* WAV_seek */ | |
848 | |
849 #endif /* SOUND_SUPPORTS_WAV */ | |
850 | |
851 /* end of wav.c ... */ | |
852 |