14
|
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
|
|
20 /*
|
|
21 * VOC decoder for SDL_sound.
|
|
22 *
|
|
23 * This driver handles Creative Labs VOC audio data...this is a legacy format,
|
|
24 * but there's some game ports that could make use of such a decoder. Plus,
|
|
25 * VOC is fairly straightforward to decode, so this is a more complex, but
|
|
26 * still palatable example of an SDL_sound decoder. Y'know, in case the
|
|
27 * RAW decoder didn't do it for you. :)
|
|
28 *
|
|
29 * This code was ripped from a decoder I had written for SDL_mixer, which was
|
|
30 * largely ripped from sox v12.17.1's voc.c.
|
|
31 *
|
|
32 * SDL_mixer: http://www.libsdl.org/projects/SDL_mixer/
|
|
33 * sox: http://www.freshmeat.net/projects/sox/
|
|
34 *
|
|
35 * Please see the file LICENSE in the source's root directory.
|
|
36 *
|
|
37 * This file written by Ryan C. Gordon. (icculus@clutteredmind.org)
|
|
38 */
|
|
39
|
|
40 #include <stdio.h>
|
|
41 #include <stdlib.h>
|
|
42 #include <string.h>
|
|
43 #include <assert.h>
|
|
44
|
|
45 #include "SDL.h"
|
|
46 #include "SDL_endian.h"
|
|
47 #include "SDL_sound.h"
|
|
48
|
|
49 #define __SDL_SOUND_INTERNAL__
|
|
50 #include "SDL_sound_internal.h"
|
|
51
|
|
52 #if (!defined SOUND_SUPPORTS_VOC)
|
|
53 #error SOUND_SUPPORTS_VOC must be defined.
|
|
54 #endif
|
|
55
|
|
56
|
|
57 static int VOC_open(Sound_Sample *sample, const char *ext);
|
|
58 static void VOC_close(Sound_Sample *sample);
|
|
59 static Uint32 VOC_read(Sound_Sample *sample);
|
|
60
|
|
61 const Sound_DecoderFunctions __Sound_DecoderFunctions_VOC =
|
|
62 {
|
|
63 {
|
|
64 "VOC",
|
|
65 "Creative Labs Voice format",
|
|
66 "Ryan C. Gordon <icculus@clutteredmind.org>",
|
|
67 "http://www.icculus.org/SDL_sound/"
|
|
68 },
|
|
69
|
|
70 VOC_open, /* open() method */
|
|
71 VOC_close, /* close() method */
|
|
72 VOC_read /* read() method */
|
|
73 };
|
|
74
|
|
75
|
|
76 /* Private data for VOC file */
|
|
77 typedef struct vocstuff {
|
|
78 Uint32 rest; /* bytes remaining in current block */
|
|
79 Uint32 rate; /* rate code (byte) of this chunk */
|
|
80 int silent; /* sound or silence? */
|
|
81 Uint32 srate; /* rate code (byte) of silence */
|
|
82 Uint32 blockseek; /* start of current output block */
|
|
83 Uint32 samples; /* number of samples output */
|
|
84 Uint32 size; /* word length of data */
|
|
85 int channels; /* number of sound channels */
|
|
86 int extended; /* Has an extended block been read? */
|
|
87 Uint32 bufpos; /* byte position in internal->buffer. */
|
|
88 } vs_t;
|
|
89
|
|
90 /* Size field */
|
|
91 /* SJB: note that the 1st 3 are sometimes used as sizeof(type) */
|
|
92 #define ST_SIZE_BYTE 1
|
|
93 #define ST_SIZE_8BIT 1
|
|
94 #define ST_SIZE_WORD 2
|
|
95 #define ST_SIZE_16BIT 2
|
|
96 #define ST_SIZE_DWORD 4
|
|
97 #define ST_SIZE_32BIT 4
|
|
98 #define ST_SIZE_FLOAT 5
|
|
99 #define ST_SIZE_DOUBLE 6
|
|
100 #define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */
|
|
101
|
|
102 /* Style field */
|
|
103 #define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */
|
|
104 #define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */
|
|
105 #define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */
|
|
106 #define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */
|
|
107 #define ST_ENCODING_ADPCM 5 /* Compressed PCM */
|
|
108 #define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */
|
|
109 #define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */
|
|
110
|
|
111 #define VOC_TERM 0
|
|
112 #define VOC_DATA 1
|
|
113 #define VOC_CONT 2
|
|
114 #define VOC_SILENCE 3
|
|
115 #define VOC_MARKER 4
|
|
116 #define VOC_TEXT 5
|
|
117 #define VOC_LOOP 6
|
|
118 #define VOC_LOOPEND 7
|
|
119 #define VOC_EXTENDED 8
|
|
120 #define VOC_DATA_16 9
|
|
121
|
|
122
|
|
123 static __inline__ int voc_check_header(SDL_RWops *src)
|
|
124 {
|
|
125 /* VOC magic header */
|
|
126 Uint8 signature[20]; /* "Creative Voice File\032" */
|
|
127 Uint16 datablockofs;
|
|
128
|
|
129 if (SDL_RWread(src, signature, sizeof (signature), 1) != 1)
|
|
130 return(0);
|
|
131
|
|
132 if (memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0) {
|
|
133 Sound_SetError("Unrecognized file type (not VOC)");
|
|
134 return(0);
|
|
135 }
|
|
136
|
|
137 /* get the offset where the first datablock is located */
|
|
138 if (SDL_RWread(src, &datablockofs, sizeof (Uint16), 1) != 1)
|
|
139 return(0);
|
|
140
|
|
141 datablockofs = SDL_SwapLE16(datablockofs);
|
|
142
|
|
143 if (SDL_RWseek(src, datablockofs, SEEK_SET) != datablockofs)
|
|
144 return(0);
|
|
145
|
|
146 return(1); /* success! */
|
|
147 } /* voc_check_header */
|
|
148
|
|
149 /* !!! FIXME : Add a flag to vs_t that distinguishes EOF and Error conditions. */
|
|
150
|
|
151 /* Read next block header, save info, leave position at start of data */
|
|
152 static int voc_get_block(Sound_Sample *sample)
|
|
153 {
|
|
154 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
|
|
155 SDL_RWops *src = internal->rw;
|
|
156 vs_t *v = (vs_t *) internal->decoder_private;
|
|
157 Uint8 bits24[3];
|
|
158 Uint8 uc, block;
|
|
159 Uint32 sblen;
|
|
160 Uint16 new_rate_short;
|
|
161 Uint32 new_rate_long;
|
|
162 Uint8 trash[6];
|
|
163 Uint16 period;
|
|
164 int i;
|
|
165
|
|
166 v->silent = 0;
|
|
167 while (v->rest == 0)
|
|
168 {
|
|
169 if (SDL_RWread(src, &block, sizeof (block), 1) != 1)
|
|
170 return 1; /* assume that's the end of the file. */
|
|
171
|
|
172 if (block == VOC_TERM)
|
|
173 return 1;
|
|
174
|
|
175 if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1)
|
|
176 return 1; /* assume that's the end of the file. */
|
|
177
|
|
178 /* Size is an 24-bit value. Ugh. */
|
|
179 sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) );
|
|
180
|
|
181 switch(block)
|
|
182 {
|
|
183 case VOC_DATA:
|
|
184 if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
|
|
185 return 0;
|
|
186
|
|
187 /* When DATA block preceeded by an EXTENDED */
|
|
188 /* block, the DATA blocks rate value is invalid */
|
|
189 if (!v->extended)
|
|
190 {
|
|
191 if (uc == 0)
|
|
192 {
|
|
193 Sound_SetError("VOC Sample rate is zero?");
|
|
194 return 0;
|
|
195 }
|
|
196
|
|
197 if ((v->rate != -1) && (uc != v->rate))
|
|
198 {
|
|
199 Sound_SetError("VOC sample rate codes differ");
|
|
200 return 0;
|
|
201 }
|
|
202
|
|
203 v->rate = uc;
|
|
204 sample->actual.rate = 1000000.0/(256 - v->rate);
|
|
205 v->channels = 1;
|
|
206 }
|
|
207
|
|
208 if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
|
|
209 return 0;
|
|
210
|
|
211 if (uc != 0)
|
|
212 {
|
|
213 Sound_SetError("VOC decoder only interprets 8-bit data");
|
|
214 return 0;
|
|
215 }
|
|
216
|
|
217 v->extended = 0;
|
|
218 v->rest = sblen - 2;
|
|
219 v->size = ST_SIZE_BYTE;
|
|
220 return 1;
|
|
221
|
|
222 case VOC_DATA_16:
|
|
223 if (SDL_RWread(src, &new_rate_long, sizeof (new_rate_long), 1) != 1)
|
|
224 return 0;
|
|
225 new_rate_long = SDL_SwapLE32(new_rate_long);
|
|
226 if (new_rate_long == 0)
|
|
227 {
|
|
228 Sound_SetError("VOC Sample rate is zero?");
|
|
229 return 0;
|
|
230 }
|
|
231 if ((v->rate != -1) && (new_rate_long != v->rate))
|
|
232 {
|
|
233 Sound_SetError("VOC sample rate codes differ");
|
|
234 return 0;
|
|
235 }
|
|
236 v->rate = new_rate_long;
|
|
237 sample->actual.rate = new_rate_long;
|
|
238
|
|
239 if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
|
|
240 return 0;
|
|
241
|
|
242 switch (uc)
|
|
243 {
|
|
244 case 8: v->size = ST_SIZE_BYTE; break;
|
|
245 case 16: v->size = ST_SIZE_WORD; break;
|
|
246 default:
|
|
247 Sound_SetError("VOC with unknown data size");
|
|
248 return 0;
|
|
249 }
|
|
250
|
|
251 if (SDL_RWread(src, &v->channels, sizeof (Uint8), 1) != 1)
|
|
252 return 0;
|
|
253
|
|
254 if (SDL_RWread(src, trash, sizeof (Uint8), 6) != 6)
|
|
255 return 0;
|
|
256
|
|
257 v->rest = sblen - 12;
|
|
258 return 1;
|
|
259
|
|
260 case VOC_CONT:
|
|
261 v->rest = sblen;
|
|
262 return 1;
|
|
263
|
|
264 case VOC_SILENCE:
|
|
265 if (SDL_RWread(src, &period, sizeof (period), 1) != 1)
|
|
266 return 0;
|
|
267 period = SDL_SwapLE16(period);
|
|
268
|
|
269 if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
|
|
270 return 0;
|
|
271 if (uc == 0)
|
|
272 {
|
|
273 Sound_SetError("VOC silence sample rate is zero");
|
|
274 return 0;
|
|
275 }
|
|
276
|
|
277 /*
|
|
278 * Some silence-packed files have gratuitously
|
|
279 * different sample rate codes in silence.
|
|
280 * Adjust period.
|
|
281 */
|
|
282 if ((v->rate != -1) && (uc != v->rate))
|
|
283 period = (period * (256 - uc))/(256 - v->rate);
|
|
284 else
|
|
285 v->rate = uc;
|
|
286 v->rest = period;
|
|
287 v->silent = 1;
|
|
288 return 1;
|
|
289
|
|
290 case VOC_LOOP:
|
|
291 case VOC_LOOPEND:
|
|
292 for(i = 0; i < sblen; i++) /* skip repeat loops. */
|
|
293 {
|
|
294 if (SDL_RWread(src, trash, sizeof (Uint8), 1) != 1)
|
|
295 return 0;
|
|
296 }
|
|
297 break;
|
|
298
|
|
299 case VOC_EXTENDED:
|
|
300 /* An Extended block is followed by a data block */
|
|
301 /* Set this byte so we know to use the rate */
|
|
302 /* value from the extended block and not the */
|
|
303 /* data block. */
|
|
304 v->extended = 1;
|
|
305 if (SDL_RWread(src, &new_rate_short, sizeof (new_rate_short), 1) != 1)
|
|
306 return 0;
|
|
307 new_rate_short = SDL_SwapLE16(new_rate_short);
|
|
308 if (new_rate_short == 0)
|
|
309 {
|
|
310 Sound_SetError("VOC sample rate is zero");
|
|
311 return 0;
|
|
312 }
|
|
313 if ((v->rate != -1) && (new_rate_short != v->rate))
|
|
314 {
|
|
315 Sound_SetError("VOC sample rate codes differ");
|
|
316 return 0;
|
|
317 }
|
|
318 v->rate = new_rate_short;
|
|
319
|
|
320 if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
|
|
321 return 0;
|
|
322
|
|
323 if (uc != 0)
|
|
324 {
|
|
325 Sound_SetError("VOC decoder only interprets 8-bit data");
|
|
326 return 0;
|
|
327 }
|
|
328
|
|
329 if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1)
|
|
330 return 0;
|
|
331
|
|
332 if (uc)
|
|
333 sample->actual.channels = 2; /* Stereo */
|
|
334 /* Needed number of channels before finishing
|
|
335 compute for rate */
|
|
336 sample->actual.rate =
|
|
337 (256000000L/(65536L - v->rate)) / sample->actual.channels;
|
|
338 /* An extended block must be followed by a data */
|
|
339 /* block to be valid so loop back to top so it */
|
|
340 /* can be grabed. */
|
|
341 continue;
|
|
342
|
|
343 case VOC_MARKER:
|
|
344 if (SDL_RWread(src, trash, sizeof (Uint8), 2) != 2)
|
|
345 return 0;
|
|
346
|
|
347 /* Falling! Falling! */
|
|
348
|
|
349 default: /* text block or other krapola. */
|
|
350 for(i = 0; i < sblen; i++)
|
|
351 {
|
|
352 if (SDL_RWread(src, &trash, sizeof (Uint8), 1) != 1)
|
|
353 return 0;
|
|
354 }
|
|
355
|
|
356 if (block == VOC_TEXT)
|
|
357 continue; /* get next block */
|
|
358 }
|
|
359 }
|
|
360
|
|
361 return 1;
|
|
362 }
|
|
363
|
|
364
|
|
365 static int voc_read(Sound_Sample *sample)
|
|
366 {
|
|
367 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
|
|
368 SDL_RWops *src = internal->rw;
|
|
369 vs_t *v = (vs_t *) internal->decoder_private;
|
|
370 int done = 0;
|
|
371 Uint8 silence = 0x80;
|
|
372 Uint8 *buf = internal->buffer;
|
|
373
|
|
374 if (v->rest == 0)
|
|
375 {
|
|
376 if (!voc_get_block(sample))
|
|
377 return 0;
|
|
378 }
|
|
379
|
|
380 if (v->rest == 0)
|
|
381 return 0;
|
|
382
|
|
383 if (v->silent)
|
|
384 {
|
|
385 if (v->size == ST_SIZE_WORD)
|
|
386 silence = 0x00;
|
|
387
|
|
388 /* Fill in silence */
|
|
389 memset(buf, silence, v->rest);
|
|
390 done = v->rest;
|
|
391 v->rest = 0;
|
|
392 }
|
|
393
|
|
394 else
|
|
395 {
|
|
396 Uint32 max = (v->rest < internal->buffer_size) ?
|
|
397 v->rest : internal->buffer_size;
|
|
398 done = SDL_RWread(src, buf + v->bufpos, 1, max);
|
|
399 v->rest -= done;
|
|
400 v->bufpos += done;
|
|
401 if (v->size == ST_SIZE_WORD)
|
|
402 {
|
|
403 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
404 for (; v->rest > 0; v->rest -= 2)
|
|
405 {
|
|
406 *((Uint16 *) buf) = SDL_SwapLE16(*((Uint16 *) buf));
|
|
407 ((Uint16 *) buf)++;
|
|
408 }
|
|
409 #endif
|
|
410 }
|
|
411 }
|
|
412
|
|
413 return done;
|
|
414 } /* voc_read */
|
|
415
|
|
416
|
|
417 static int VOC_open(Sound_Sample *sample, const char *ext)
|
|
418 {
|
|
419 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
|
|
420 vs_t *v = NULL;
|
|
421
|
|
422 if (!voc_check_header(internal->rw))
|
|
423 return(0);
|
|
424
|
|
425 v = (vs_t *) malloc(sizeof (vs_t));
|
|
426 BAIL_IF_MACRO(v == NULL, ERR_OUT_OF_MEMORY, 0);
|
|
427 memset(v, '\0', sizeof (vs_t));
|
|
428 internal->decoder_private = v;
|
|
429
|
|
430 v->rate = -1;
|
|
431 if (!voc_get_block(sample))
|
|
432 {
|
|
433 free(v);
|
|
434 return(0);
|
|
435 } /* if */
|
|
436
|
|
437 if (v->rate == -1)
|
|
438 {
|
|
439 Sound_SetError("VOC data had no sound!");
|
|
440 free(v);
|
|
441 return(0);
|
|
442 } /* if */
|
|
443
|
|
444 _D(("VOC: Accepting data stream.\n"));
|
|
445 sample->actual.format = (v->size == ST_SIZE_WORD) ? AUDIO_S16LSB:AUDIO_U8;
|
|
446 sample->actual.channels = v->channels;
|
|
447 sample->flags = SOUND_SAMPLEFLAG_NONE;
|
|
448 return(1);
|
|
449 } /* VOC_open */
|
|
450
|
|
451
|
|
452 static void VOC_close(Sound_Sample *sample)
|
|
453 {
|
|
454 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
|
|
455 free(internal->decoder_private);
|
|
456 } /* VOC_close */
|
|
457
|
|
458
|
|
459 static Uint32 VOC_read(Sound_Sample *sample)
|
|
460 {
|
|
461 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
|
|
462 vs_t *v = (vs_t *) internal->decoder_private;
|
|
463
|
|
464 v->bufpos = 0;
|
|
465 while (v->bufpos < internal->buffer_size)
|
|
466 {
|
|
467 Uint32 rc = voc_read(sample);
|
|
468 if (rc == 0) /* !!! FIXME: Could be an error... */
|
|
469 {
|
|
470 sample->flags |= SOUND_SAMPLEFLAG_EOF;
|
|
471 break;
|
|
472 } /* if */
|
|
473
|
|
474 if (!voc_get_block(sample))
|
|
475 {
|
|
476 sample->flags |= SOUND_SAMPLEFLAG_ERROR;
|
|
477 break;
|
|
478 } /* if */
|
|
479 } /* while */
|
|
480
|
|
481 return(v->bufpos);
|
|
482 } /* VOC_read */
|
|
483
|
|
484
|
|
485 /* end of voc.c ... */
|
|
486
|