Mercurial > SDL_sound_CoreAudio
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 |