comparison SDL_sound.c @ 4:341cea3e13c6

Initial add.
author Ryan C. Gordon <icculus@icculus.org>
date Mon, 17 Sep 2001 18:12:03 +0000
parents
children cc2c32349380
comparison
equal deleted inserted replaced
3:fd6cd0e04e6f 4:341cea3e13c6
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 * This file implements the core API, which is relatively simple.
22 * The real meat of SDL_sound is in the decoders directory.
23 *
24 * Documentation is in SDL_sound.h ... It's verbose, honest. :)
25 *
26 * Please see the file LICENSE in the source's root directory.
27 *
28 * This file written by Ryan C. Gordon. (icculus@clutteredmind.org)
29 */
30
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36 #include <ctype.h>
37
38 #include "SDL.h"
39 #include "SDL_sound.h"
40
41 #define __SDL_SOUND_INTERNAL__
42 #include "SDL_sound_internal.h"
43
44
45 /* The various decoder drivers... */
46
47 #if (defined SOUND_SUPPORTS_VOC)
48 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VOC;
49 #endif
50
51 #if (defined SOUND_SUPPORTS_RAW)
52 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_RAW;
53 #endif
54
55 static const Sound_DecoderFunctions *decoderFuncs[] =
56 {
57 #if (defined SOUND_SUPPORTS_VOC)
58 &__Sound_DecoderFunctions_VOC,
59 #endif
60
61 #if (defined SOUND_SUPPORTS_RAW)
62 &__Sound_DecoderFunctions_RAW,
63 #endif
64
65 NULL
66 };
67
68
69
70 /* General SDL_sound state ... */
71
72 static int initialized = 0;
73 static Sound_Sample *samplesList = NULL; /* this is a linked list. */
74 static const Sound_DecoderInfo **available_decoders = NULL;
75
76
77 /* functions ... */
78
79 void Sound_GetLinkedVersion(Sound_Version *ver)
80 {
81 if (ver != NULL)
82 {
83 ver->major = SOUND_VER_MAJOR;
84 ver->minor = SOUND_VER_MINOR;
85 ver->patch = SOUND_VER_PATCH;
86 } /* if */
87 } /* Sound_GetLinkedVersion */
88
89
90 int Sound_Init(void)
91 {
92 size_t i;
93 BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0);
94 samplesList = NULL;
95
96 SDL_Init(SDL_INIT_AUDIO);
97
98 for (i = 0; decoderFuncs[i] != NULL; i++)
99 ; /* do nothing. */
100
101 i++;
102 available_decoders = (const Sound_DecoderInfo **)
103 malloc(i * sizeof (Sound_DecoderInfo *));
104 BAIL_IF_MACRO(available_decoders == NULL, ERR_OUT_OF_MEMORY, 0);
105
106 for (i = 0; decoderFuncs[i] != NULL; i++)
107 available_decoders[i] = &decoderFuncs[i]->info;
108
109 initialized = 1;
110 return(1);
111 } /* Sound_Init */
112
113
114 int Sound_Quit(void)
115 {
116 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
117
118 while (((volatile Sound_Sample *) samplesList) != NULL)
119 Sound_FreeSample(samplesList);
120
121 if (available_decoders != NULL)
122 free(available_decoders);
123 available_decoders = NULL;
124
125 initialized = 0;
126
127 return(1);
128 } /* Sound_Quit */
129
130
131 const Sound_DecoderInfo **Sound_AvailableDecoders(void)
132 {
133 return(available_decoders); /* READ. ONLY. */
134 } /* Sound_AvailableDecoders */
135
136
137 const char *Sound_GetError(void)
138 {
139 return(SDL_GetError());
140 } /* Sound_GetError */
141
142
143 void Sound_ClearError(void)
144 {
145 SDL_ClearError();
146 } /* Sound_ClearError */
147
148
149 /*
150 * This is declared in the internal header.
151 */
152 void Sound_SetError(const char *err)
153 {
154 if (err != NULL)
155 SDL_SetError(err);
156 } /* Sound_SetError */
157
158
159 /*
160 * -ansi and -pedantic flags prevent use of strcasecmp() on Linux, and
161 * I honestly don't want to mess around with figuring out if a given
162 * platform has "strcasecmp", "stricmp", or
163 * "compare_two_damned_strings_case_insensitive", which I hear is in the
164 * next release of Carbon. :) This is exported so decoders may use it if
165 * they like.
166 */
167 int __Sound_strcasecmp(const char *x, const char *y)
168 {
169 int ux, uy;
170
171 do
172 {
173 ux = toupper((int) *x);
174 uy = toupper((int) *y);
175 if (ux > uy)
176 return(1);
177 else if (ux < uy)
178 return(-1);
179 x++;
180 y++;
181 } while ((ux) && (uy));
182
183 return(0);
184 } /* __Sound_strcasecmp */
185
186
187 /*
188 * Allocate a Sound_Sample, and fill in most of its fields. Those that need
189 * to be filled in later, by a decoder, will be initialized to zero.
190 */
191 static Sound_Sample *alloc_sample(SDL_RWops *rw, Sound_AudioInfo *desired,
192 Uint32 bufferSize)
193 {
194 Sound_Sample *retval = malloc(sizeof (Sound_Sample));
195 Sound_SampleInternal *internal = malloc(sizeof (Sound_SampleInternal));
196 if ((retval == NULL) || (internal == NULL))
197 {
198 Sound_SetError(ERR_OUT_OF_MEMORY);
199 if (retval)
200 free(retval);
201 if (internal)
202 free(internal);
203
204 return(NULL);
205 } /* if */
206
207 memset(retval, '\0', sizeof (Sound_Sample));
208 memset(internal, '\0', sizeof (Sound_SampleInternal));
209
210 assert(bufferSize > 0);
211 retval->buffer = malloc(bufferSize); /* pure ugly. */
212 if (!retval->buffer)
213 {
214 Sound_SetError(ERR_OUT_OF_MEMORY);
215 free(internal);
216 free(retval);
217 return(NULL);
218 } /* if */
219 memset(retval->buffer, '\0', bufferSize);
220 retval->buffer_size = bufferSize;
221
222 if (desired != NULL)
223 memcpy(&retval->desired, desired, sizeof (Sound_AudioInfo));
224
225 internal->rw = rw;
226 retval->opaque = internal;
227 return(retval);
228 } /* alloc_sample */
229
230
231 /*
232 * The bulk of the Sound_NewSample() work is done here...
233 * Ask the specified decoder to handle the data in (rw), and if
234 * so, construct the Sound_Sample. Otherwise, try to wind (rw)'s stream
235 * back to where it was, and return false.
236 *
237 * !!! FIXME: This is big, ugly, nasty, and smelly.
238 */
239 static int init_sample(const Sound_DecoderFunctions *funcs,
240 Sound_Sample *sample, const char *ext,
241 Sound_AudioInfo *_desired)
242 {
243 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
244 int pos = SDL_RWtell(internal->rw); /* !!! FIXME: Int? Really? */
245 Sound_AudioInfo desired;
246
247 /* fill in the funcs for this decoder... */
248 sample->decoder = &funcs->info;
249 internal->funcs = funcs;
250 if (!funcs->open(sample, ext))
251 {
252 SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */
253 return(0);
254 } /* if */
255
256 /* success; we've got a decoder! */
257
258 /* Now we need to set up the conversion buffer... */
259
260 memcpy(&desired, (_desired != NULL) ? _desired : &sample->actual,
261 sizeof (Sound_AudioInfo));
262
263 if (SDL_BuildAudioCVT(&internal->sdlcvt,
264 sample->actual.format,
265 sample->actual.channels,
266 (int) sample->actual.rate, /* !!! FIXME: Int? Really? */
267 desired.format,
268 desired.channels,
269 (int) desired.rate) == -1) /* !!! FIXME: Int? Really? */
270 {
271 Sound_SetError(SDL_GetError());
272 funcs->close(sample);
273 SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */
274 return(0);
275 } /* if */
276
277 if (internal->sdlcvt.len_mult > 1)
278 {
279 void *rc = realloc(sample->buffer,
280 sample->buffer_size * internal->sdlcvt.len_mult);
281 if (rc == NULL)
282 {
283 funcs->close(sample);
284 SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */
285 return(0);
286 } /* if */
287
288 sample->buffer = rc;
289 } /* if */
290
291 /* these pointers are all one and the same. */
292 memcpy(&sample->desired, &desired, sizeof (Sound_AudioInfo));
293 internal->sdlcvt.buf = internal->buffer = sample->buffer;
294 internal->buffer_size = sample->buffer_size / internal->sdlcvt.len_mult;
295 internal->sdlcvt.len = internal->buffer_size;
296
297 /* Prepend our new Sound_Sample to the samplesList... */
298 if (samplesList != NULL)
299 {
300 internal->next = samplesList;
301 if (samplesList != NULL)
302 internal->prev = sample;
303 } /* if */
304 samplesList = sample;
305
306 return(1);
307 } /* init_sample */
308
309
310 Sound_Sample *Sound_NewSample(SDL_RWops *rw, const char *ext,
311 Sound_AudioInfo *desired, Uint32 bSize)
312 {
313 size_t i;
314 Sound_Sample *retval;
315
316 /* sanity checks. */
317 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
318 BAIL_IF_MACRO(rw == NULL, ERR_INVALID_ARGUMENT, NULL);
319
320 retval = alloc_sample(rw, desired, bSize);
321 if (!retval)
322 return(NULL); /* alloc_sample() sets error message... */
323
324 if (ext != NULL)
325 {
326 for (i = 0; decoderFuncs[i] != NULL; i++)
327 {
328 const char *decoderExt = decoderFuncs[i]->info.extension;
329 if (__Sound_strcasecmp(decoderExt, ext) == 0)
330 {
331 if (init_sample(decoderFuncs[i], retval, ext, desired))
332 return(retval);
333 } /* if */
334 } /* for */
335 } /* if */
336
337 /* no direct extension match? Try everything we've got... */
338 for (i = 0; decoderFuncs[i] != NULL; i++)
339 {
340 if (init_sample(decoderFuncs[i], retval, ext, desired))
341 return(retval);
342 } /* for */
343
344 /* nothing could handle the sound data... */
345 free(retval->opaque);
346 if (retval->buffer != NULL)
347 free(retval->buffer);
348 free(retval);
349 SDL_RWclose(rw);
350 Sound_SetError(ERR_UNSUPPORTED_FORMAT);
351 return(NULL);
352 } /* Sound_NewSample */
353
354
355 Sound_Sample *Sound_NewSampleFromFile(const char *filename,
356 Sound_AudioInfo *desired,
357 Uint32 bufferSize)
358 {
359 const char *ext;
360 SDL_RWops *rw;
361
362 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL);
363 BAIL_IF_MACRO(filename == NULL, ERR_INVALID_ARGUMENT, NULL);
364
365 ext = strrchr(filename, '.');
366 rw = SDL_RWFromFile(filename, "rb");
367 BAIL_IF_MACRO(rw == NULL, SDL_GetError(), NULL);
368
369 if (ext != NULL)
370 ext++;
371
372 return(Sound_NewSample(rw, ext, desired, bufferSize));
373 } /* Sound_NewSampleFromFile */
374
375
376 void Sound_FreeSample(Sound_Sample *sample)
377 {
378 Sound_SampleInternal *internal;
379
380 if (!initialized)
381 {
382 Sound_SetError(ERR_NOT_INITIALIZED);
383 return;
384 } /* if */
385
386 if (sample == NULL)
387 {
388 Sound_SetError(ERR_INVALID_ARGUMENT);
389 return;
390 } /* if */
391
392 internal = (Sound_SampleInternal *) sample->opaque;
393
394 internal->funcs->close(sample);
395
396 /* update the samplesList... */
397 if (internal->prev != NULL)
398 {
399 Sound_SampleInternal *prevInternal;
400 prevInternal = (Sound_SampleInternal *) internal->prev->opaque;
401 prevInternal->next = internal->next;
402 } /* if */
403 else
404 {
405 assert(samplesList == sample);
406 samplesList = internal->next;
407 } /* else */
408
409 if (internal->next != NULL)
410 {
411 Sound_SampleInternal *nextInternal;
412 nextInternal = (Sound_SampleInternal *) internal->next->opaque;
413 nextInternal->prev = internal->prev;
414 } /* if */
415
416 /* nuke it... */
417 if (internal->rw != NULL) /* this condition is a "just in case" thing. */
418 SDL_RWclose(internal->rw);
419 assert(internal->buffer != NULL);
420 if (internal->buffer != sample->buffer)
421 free(internal->buffer);
422 free(internal);
423 if (sample->buffer != NULL)
424 free(sample->buffer);
425 free(sample);
426 } /* Sound_FreeSample */
427
428
429 int Sound_SetBufferSize(Sound_Sample *sample, Uint32 newSize)
430 {
431 void *newBuf = NULL;
432 Sound_SampleInternal *internal = NULL;
433
434 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
435 BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0);
436 internal = ((Sound_SampleInternal *) sample->opaque);
437 newBuf = realloc(sample->buffer, newSize * internal->sdlcvt.len_mult);
438 BAIL_IF_MACRO(newBuf == NULL, ERR_OUT_OF_MEMORY, 0);
439
440 internal->sdlcvt.buf = internal->buffer = sample->buffer = newBuf;
441 sample->buffer_size = newSize;
442 internal->buffer_size = newSize / internal->sdlcvt.len_mult;
443 internal->sdlcvt.len = internal->buffer_size;
444
445 return(1);
446 } /* Sound_SetBufferSize */
447
448
449 Uint32 Sound_Decode(Sound_Sample *sample)
450 {
451 Sound_SampleInternal *internal = NULL;
452 Uint32 retval = 0;
453
454 /* a boatload of sanity checks... */
455 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
456 BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0);
457 BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0);
458 BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0);
459
460 internal = (Sound_SampleInternal *) sample->opaque;
461
462 assert(sample->buffer != NULL);
463 assert(sample->buffer_size > 0);
464 assert(internal->buffer != NULL);
465 assert(internal->buffer_size > 0);
466
467 /* reset EAGAIN. Decoder can flip it back on if it needs to. */
468 sample->flags &= !SOUND_SAMPLEFLAG_EAGAIN;
469
470 retval = internal->funcs->read(sample);
471 if (internal->sdlcvt.needed)
472 {
473 internal->sdlcvt.len = retval;
474 SDL_ConvertAudio(&internal->sdlcvt);
475 retval *= internal->sdlcvt.len_mult;
476 } /* if */
477
478 return(retval);
479 } /* Sound_Decode */
480
481
482 Uint32 Sound_DecodeAll(Sound_Sample *sample)
483 {
484 Sound_SampleInternal *internal = NULL;
485 void *buf = NULL;
486 Uint32 newBufSize = 0;
487
488 BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
489
490 internal = (Sound_SampleInternal *) sample->opaque;
491
492 while ( ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0) &&
493 ((sample->flags & SOUND_SAMPLEFLAG_ERROR) == 0) )
494 {
495 void *ptr = realloc(buf, newBufSize + sample->buffer_size);
496 if (ptr == NULL)
497 {
498 sample->flags |= SOUND_SAMPLEFLAG_ERROR;
499 Sound_SetError(ERR_OUT_OF_MEMORY);
500 } /* if */
501 else
502 {
503 Uint32 br = Sound_Decode(sample);
504 memcpy( ((char *) buf) + newBufSize, sample->buffer, br );
505 newBufSize += br;
506 } /* else */
507 } /* while */
508
509 free(sample->buffer);
510 sample->buffer = internal->buffer = internal->sdlcvt.buf = buf;
511 sample->buffer_size = newBufSize;
512 internal->buffer_size = newBufSize / internal->sdlcvt.len_mult;
513 internal->sdlcvt.len_mult = internal->buffer_size;
514
515 return(newBufSize);
516 } /* Sound_DecodeAll */
517
518
519 /* end of SDL_sound.c ... */
520