Rev 9097 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8645 | turbocat | 1 | /* |
2 | SDL_mixer: An audio mixer library based on the SDL library |
||
3 | Copyright (C) 1997-2012 Sam Lantinga |
||
4 | |||
5 | This software is provided 'as-is', without any express or implied |
||
6 | warranty. In no event will the authors be held liable for any damages |
||
7 | arising from the use of this software. |
||
8 | |||
9 | Permission is granted to anyone to use this software for any purpose, |
||
10 | including commercial applications, and to alter it and redistribute it |
||
11 | freely, subject to the following restrictions: |
||
12 | |||
13 | 1. The origin of this software must not be misrepresented; you must not |
||
14 | claim that you wrote the original software. If you use this software |
||
15 | in a product, an acknowledgment in the product documentation would be |
||
16 | appreciated but is not required. |
||
17 | 2. Altered source versions must be plainly marked as such, and must not be |
||
18 | misrepresented as being the original software. |
||
19 | 3. This notice may not be removed or altered from any source distribution. |
||
20 | */ |
||
21 | |||
22 | /* $Id$ */ |
||
23 | |||
24 | #include |
||
25 | #include |
||
26 | #include |
||
27 | |||
28 | #include "SDL_mutex.h" |
||
29 | #include "SDL_endian.h" |
||
30 | #include "SDL_timer.h" |
||
31 | |||
32 | #include "SDL_mixer.h" |
||
33 | #include "load_aiff.h" |
||
34 | #include "load_voc.h" |
||
35 | #include "load_ogg.h" |
||
36 | #include "load_flac.h" |
||
37 | #include "dynamic_flac.h" |
||
38 | #include "dynamic_mod.h" |
||
39 | #include "dynamic_mp3.h" |
||
40 | #include "dynamic_ogg.h" |
||
41 | |||
42 | #define __MIX_INTERNAL_EFFECT__ |
||
43 | #include "effects_internal.h" |
||
44 | |||
9213 | turbocat | 45 | #define uSDL_Delay SDL_Delay |
46 | #define uSDL_GetTicks SDL_GetTicks |
||
9097 | turbocat | 47 | |
8645 | turbocat | 48 | /* Magic numbers for various audio file formats */ |
49 | #define RIFF 0x46464952 /* "RIFF" */ |
||
50 | #define WAVE 0x45564157 /* "WAVE" */ |
||
51 | #define FORM 0x4d524f46 /* "FORM" */ |
||
52 | #define OGGS 0x5367674f /* "OggS" */ |
||
53 | #define CREA 0x61657243 /* "Crea" */ |
||
54 | #define FLAC 0x43614C66 /* "fLaC" */ |
||
55 | |||
56 | static int audio_opened = 0; |
||
57 | static SDL_AudioSpec mixer; |
||
58 | |||
59 | typedef struct _Mix_effectinfo |
||
60 | { |
||
61 | Mix_EffectFunc_t callback; |
||
62 | Mix_EffectDone_t done_callback; |
||
63 | void *udata; |
||
64 | struct _Mix_effectinfo *next; |
||
65 | } effect_info; |
||
66 | |||
67 | static struct _Mix_Channel { |
||
68 | Mix_Chunk *chunk; |
||
69 | int playing; |
||
70 | int paused; |
||
71 | Uint8 *samples; |
||
72 | int volume; |
||
73 | int looping; |
||
74 | int tag; |
||
75 | Uint32 expire; |
||
76 | Uint32 start_time; |
||
77 | Mix_Fading fading; |
||
78 | int fade_volume; |
||
79 | int fade_volume_reset; |
||
80 | Uint32 fade_length; |
||
81 | Uint32 ticks_fade; |
||
82 | effect_info *effects; |
||
83 | } *mix_channel = NULL; |
||
84 | |||
85 | static effect_info *posteffects = NULL; |
||
86 | |||
87 | static int num_channels; |
||
88 | static int reserved_channels = 0; |
||
89 | |||
90 | |||
91 | /* Support for hooking into the mixer callback system */ |
||
92 | static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL; |
||
93 | static void *mix_postmix_data = NULL; |
||
94 | |||
95 | /* rcg07062001 callback to alert when channels are done playing. */ |
||
96 | static void (*channel_done_callback)(int channel) = NULL; |
||
97 | |||
98 | /* Music function declarations */ |
||
99 | extern int open_music(SDL_AudioSpec *mixer); |
||
100 | extern void close_music(void); |
||
101 | |||
102 | /* Support for user defined music functions, plus the default one */ |
||
103 | extern int volatile music_active; |
||
104 | extern void music_mixer(void *udata, Uint8 *stream, int len); |
||
105 | static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer; |
||
106 | static void *music_data = NULL; |
||
107 | |||
108 | /* rcg06042009 report available decoders at runtime. */ |
||
109 | static const char **chunk_decoders = NULL; |
||
110 | static int num_decoders = 0; |
||
111 | |||
112 | /* Semicolon-separated SoundFont paths */ |
||
113 | #ifdef MID_MUSIC |
||
114 | extern char* soundfont_paths; |
||
115 | #endif |
||
116 | |||
117 | int Mix_GetNumChunkDecoders(void) |
||
118 | { |
||
119 | return(num_decoders); |
||
120 | } |
||
121 | |||
122 | const char *Mix_GetChunkDecoder(int index) |
||
123 | { |
||
124 | if ((index < 0) || (index >= num_decoders)) { |
||
125 | return NULL; |
||
126 | } |
||
127 | return(chunk_decoders[index]); |
||
128 | } |
||
129 | |||
130 | static void add_chunk_decoder(const char *decoder) |
||
131 | { |
||
132 | void *ptr = SDL_realloc(chunk_decoders, (num_decoders + 1) * sizeof (const char **)); |
||
133 | if (ptr == NULL) { |
||
134 | return; /* oh well, go on without it. */ |
||
135 | } |
||
136 | chunk_decoders = (const char **) ptr; |
||
137 | chunk_decoders[num_decoders++] = decoder; |
||
138 | } |
||
139 | |||
140 | /* rcg06192001 get linked library's version. */ |
||
141 | const SDL_version *Mix_Linked_Version(void) |
||
142 | { |
||
143 | static SDL_version linked_version; |
||
144 | SDL_MIXER_VERSION(&linked_version); |
||
145 | return(&linked_version); |
||
146 | } |
||
147 | |||
148 | static int initialized = 0; |
||
149 | |||
150 | int Mix_Init(int flags) |
||
151 | { |
||
152 | int result = 0; |
||
153 | |||
154 | if (flags & MIX_INIT_FLUIDSYNTH) { |
||
155 | #ifdef USE_FLUIDSYNTH_MIDI |
||
156 | if ((initialized & MIX_INIT_FLUIDSYNTH) || Mix_InitFluidSynth() == 0) { |
||
157 | result |= MIX_INIT_FLUIDSYNTH; |
||
158 | } |
||
159 | #else |
||
160 | Mix_SetError("Mixer not built with FluidSynth support"); |
||
161 | #endif |
||
162 | } |
||
163 | if (flags & MIX_INIT_FLAC) { |
||
164 | #ifdef FLAC_MUSIC |
||
165 | if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) { |
||
166 | result |= MIX_INIT_FLAC; |
||
167 | } |
||
168 | #else |
||
169 | Mix_SetError("Mixer not built with FLAC support"); |
||
170 | #endif |
||
171 | } |
||
172 | if (flags & MIX_INIT_MOD) { |
||
173 | #ifdef MOD_MUSIC |
||
174 | if ((initialized & MIX_INIT_MOD) || Mix_InitMOD() == 0) { |
||
175 | result |= MIX_INIT_MOD; |
||
176 | } |
||
177 | #else |
||
178 | Mix_SetError("Mixer not built with MOD support"); |
||
179 | #endif |
||
180 | } |
||
181 | if (flags & MIX_INIT_MP3) { |
||
182 | #ifdef MP3_MUSIC |
||
183 | if ((initialized & MIX_INIT_MP3) || Mix_InitMP3() == 0) { |
||
184 | result |= MIX_INIT_MP3; |
||
185 | } |
||
186 | #else |
||
187 | Mix_SetError("Mixer not built with MP3 support"); |
||
188 | #endif |
||
189 | } |
||
190 | if (flags & MIX_INIT_OGG) { |
||
191 | #ifdef OGG_MUSIC |
||
192 | if ((initialized & MIX_INIT_OGG) || Mix_InitOgg() == 0) { |
||
193 | result |= MIX_INIT_OGG; |
||
194 | } |
||
195 | #else |
||
196 | Mix_SetError("Mixer not built with Ogg Vorbis support"); |
||
197 | #endif |
||
198 | } |
||
199 | initialized |= result; |
||
200 | |||
201 | return (result); |
||
202 | } |
||
203 | |||
204 | void Mix_Quit() |
||
205 | { |
||
206 | #ifdef USE_FLUIDSYNTH_MIDI |
||
207 | if (initialized & MIX_INIT_FLUIDSYNTH) { |
||
208 | Mix_QuitFluidSynth(); |
||
209 | } |
||
210 | #endif |
||
211 | #ifdef FLAC_MUSIC |
||
212 | if (initialized & MIX_INIT_FLAC) { |
||
213 | Mix_QuitFLAC(); |
||
214 | } |
||
215 | #endif |
||
216 | #ifdef MOD_MUSIC |
||
217 | if (initialized & MIX_INIT_MOD) { |
||
218 | Mix_QuitMOD(); |
||
219 | } |
||
220 | #endif |
||
221 | #ifdef MP3_MUSIC |
||
222 | if (initialized & MIX_INIT_MP3) { |
||
223 | Mix_QuitMP3(); |
||
224 | } |
||
225 | #endif |
||
226 | #ifdef OGG_MUSIC |
||
227 | if (initialized & MIX_INIT_OGG) { |
||
228 | Mix_QuitOgg(); |
||
229 | } |
||
230 | #endif |
||
231 | #ifdef MID_MUSIC |
||
232 | if (soundfont_paths) { |
||
233 | SDL_free(soundfont_paths); |
||
234 | } |
||
235 | #endif |
||
236 | initialized = 0; |
||
237 | } |
||
238 | |||
239 | static int _Mix_remove_all_effects(int channel, effect_info **e); |
||
240 | |||
241 | /* |
||
242 | * rcg06122001 Cleanup effect callbacks. |
||
243 | * MAKE SURE SDL_LockAudio() is called before this (or you're in the |
||
244 | * audio callback). |
||
245 | */ |
||
246 | static void _Mix_channel_done_playing(int channel) |
||
247 | { |
||
248 | if (channel_done_callback) { |
||
249 | channel_done_callback(channel); |
||
250 | } |
||
251 | |||
252 | /* |
||
253 | * Call internal function directly, to avoid locking audio from |
||
254 | * inside audio callback. |
||
255 | */ |
||
256 | _Mix_remove_all_effects(channel, &mix_channel[channel].effects); |
||
257 | } |
||
258 | |||
259 | |||
260 | static void *Mix_DoEffects(int chan, void *snd, int len) |
||
261 | { |
||
262 | int posteffect = (chan == MIX_CHANNEL_POST); |
||
263 | effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects); |
||
264 | void *buf = snd; |
||
265 | |||
266 | if (e != NULL) { /* are there any registered effects? */ |
||
267 | /* if this is the postmix, we can just overwrite the original. */ |
||
268 | if (!posteffect) { |
||
269 | buf = SDL_malloc(len); |
||
270 | if (buf == NULL) { |
||
271 | return(snd); |
||
272 | } |
||
273 | memcpy(buf, snd, len); |
||
274 | } |
||
275 | |||
276 | for (; e != NULL; e = e->next) { |
||
277 | if (e->callback != NULL) { |
||
278 | e->callback(chan, buf, len, e->udata); |
||
279 | } |
||
280 | } |
||
281 | } |
||
282 | |||
283 | /* be sure to SDL_free() the return value if != snd ... */ |
||
284 | return(buf); |
||
285 | } |
||
286 | |||
287 | |||
288 | /* Mixing function */ |
||
289 | static void mix_channels(void *udata, Uint8 *stream, int len) |
||
290 | { |
||
291 | Uint8 *mix_input; |
||
292 | int i, mixable, volume = SDL_MIX_MAXVOLUME; |
||
293 | Uint32 sdl_ticks; |
||
294 | |||
295 | #if SDL_VERSION_ATLEAST(1, 3, 0) |
||
296 | /* Need to initialize the stream in SDL 1.3+ */ |
||
297 | memset(stream, mixer.silence, len); |
||
298 | #endif |
||
299 | |||
300 | /* Mix the music (must be done before the channels are added) */ |
||
301 | if ( music_active || (mix_music != music_mixer) ) { |
||
302 | mix_music(music_data, stream, len); |
||
303 | } |
||
304 | |||
305 | /* Mix any playing channels... */ |
||
9097 | turbocat | 306 | sdl_ticks = uSDL_GetTicks(); |
8645 | turbocat | 307 | for ( i=0; i |
308 | if( ! mix_channel[i].paused ) { |
||
309 | if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) { |
||
310 | /* Expiration delay for that channel is reached */ |
||
311 | mix_channel[i].playing = 0; |
||
312 | mix_channel[i].looping = 0; |
||
313 | mix_channel[i].fading = MIX_NO_FADING; |
||
314 | mix_channel[i].expire = 0; |
||
315 | _Mix_channel_done_playing(i); |
||
316 | } else if ( mix_channel[i].fading != MIX_NO_FADING ) { |
||
317 | Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade; |
||
318 | if( ticks > mix_channel[i].fade_length ) { |
||
319 | Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */ |
||
320 | if( mix_channel[i].fading == MIX_FADING_OUT ) { |
||
321 | mix_channel[i].playing = 0; |
||
322 | mix_channel[i].looping = 0; |
||
323 | mix_channel[i].expire = 0; |
||
324 | _Mix_channel_done_playing(i); |
||
325 | } |
||
326 | mix_channel[i].fading = MIX_NO_FADING; |
||
327 | } else { |
||
328 | if( mix_channel[i].fading == MIX_FADING_OUT ) { |
||
329 | Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks)) |
||
330 | / mix_channel[i].fade_length ); |
||
331 | } else { |
||
332 | Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length ); |
||
333 | } |
||
334 | } |
||
335 | } |
||
336 | if ( mix_channel[i].playing > 0 ) { |
||
337 | int index = 0; |
||
338 | int remaining = len; |
||
339 | while (mix_channel[i].playing > 0 && index < len) { |
||
340 | remaining = len - index; |
||
341 | volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME; |
||
342 | mixable = mix_channel[i].playing; |
||
343 | if ( mixable > remaining ) { |
||
344 | mixable = remaining; |
||
345 | } |
||
346 | |||
347 | mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable); |
||
348 | SDL_MixAudio(stream+index,mix_input,mixable,volume); |
||
349 | if (mix_input != mix_channel[i].samples) |
||
350 | SDL_free(mix_input); |
||
351 | |||
352 | mix_channel[i].samples += mixable; |
||
353 | mix_channel[i].playing -= mixable; |
||
354 | index += mixable; |
||
355 | |||
356 | /* rcg06072001 Alert app if channel is done playing. */ |
||
357 | if (!mix_channel[i].playing && !mix_channel[i].looping) { |
||
358 | _Mix_channel_done_playing(i); |
||
359 | } |
||
360 | } |
||
361 | |||
362 | /* If looping the sample and we are at its end, make sure |
||
363 | we will still return a full buffer */ |
||
364 | while ( mix_channel[i].looping && index < len ) { |
||
365 | int alen = mix_channel[i].chunk->alen; |
||
366 | remaining = len - index; |
||
367 | if (remaining > alen) { |
||
368 | remaining = alen; |
||
369 | } |
||
370 | |||
371 | mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining); |
||
372 | SDL_MixAudio(stream+index, mix_input, remaining, volume); |
||
373 | if (mix_input != mix_channel[i].chunk->abuf) |
||
374 | SDL_free(mix_input); |
||
375 | |||
376 | --mix_channel[i].looping; |
||
377 | mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining; |
||
378 | mix_channel[i].playing = mix_channel[i].chunk->alen - remaining; |
||
379 | index += remaining; |
||
380 | } |
||
381 | if ( ! mix_channel[i].playing && mix_channel[i].looping ) { |
||
382 | --mix_channel[i].looping; |
||
383 | mix_channel[i].samples = mix_channel[i].chunk->abuf; |
||
384 | mix_channel[i].playing = mix_channel[i].chunk->alen; |
||
385 | } |
||
386 | } |
||
387 | } |
||
388 | } |
||
389 | |||
390 | /* rcg06122001 run posteffects... */ |
||
391 | Mix_DoEffects(MIX_CHANNEL_POST, stream, len); |
||
392 | |||
393 | if ( mix_postmix ) { |
||
394 | mix_postmix(mix_postmix_data, stream, len); |
||
395 | } |
||
396 | } |
||
397 | |||
398 | #if 0 |
||
399 | static void PrintFormat(char *title, SDL_AudioSpec *fmt) |
||
400 | { |
||
401 | printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF), |
||
402 | (fmt->format&0x8000) ? "signed" : "unsigned", |
||
403 | (fmt->channels > 2) ? "surround" : |
||
404 | (fmt->channels > 1) ? "stereo" : "mono", fmt->freq); |
||
405 | } |
||
406 | #endif |
||
407 | |||
408 | |||
409 | /* Open the mixer with a certain desired audio format */ |
||
410 | int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize) |
||
411 | { |
||
412 | int i; |
||
413 | SDL_AudioSpec desired; |
||
414 | |||
415 | /* If the mixer is already opened, increment open count */ |
||
416 | if ( audio_opened ) { |
||
417 | if ( format == mixer.format && nchannels == mixer.channels ) { |
||
418 | ++audio_opened; |
||
419 | return(0); |
||
420 | } |
||
421 | while ( audio_opened ) { |
||
422 | Mix_CloseAudio(); |
||
423 | } |
||
424 | } |
||
425 | |||
426 | /* Set the desired format and frequency */ |
||
427 | desired.freq = frequency; |
||
428 | desired.format = format; |
||
429 | desired.channels = nchannels; |
||
430 | desired.samples = chunksize; |
||
431 | desired.callback = mix_channels; |
||
432 | desired.userdata = NULL; |
||
433 | |||
434 | /* Accept nearly any audio format */ |
||
435 | if ( SDL_OpenAudio(&desired, &mixer) < 0 ) { |
||
436 | return(-1); |
||
437 | } |
||
438 | #if 0 |
||
439 | PrintFormat("Audio device", &mixer); |
||
440 | #endif |
||
441 | |||
442 | /* Initialize the music players */ |
||
443 | if ( open_music(&mixer) < 0 ) { |
||
444 | SDL_CloseAudio(); |
||
445 | return(-1); |
||
446 | } |
||
447 | |||
448 | num_channels = MIX_CHANNELS; |
||
449 | mix_channel = (struct _Mix_Channel *) SDL_malloc(num_channels * sizeof(struct _Mix_Channel)); |
||
450 | |||
451 | /* Clear out the audio channels */ |
||
452 | for ( i=0; i |
||
453 | mix_channel[i].chunk = NULL; |
||
454 | mix_channel[i].playing = 0; |
||
455 | mix_channel[i].looping = 0; |
||
456 | mix_channel[i].volume = SDL_MIX_MAXVOLUME; |
||
457 | mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME; |
||
458 | mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME; |
||
459 | mix_channel[i].fading = MIX_NO_FADING; |
||
460 | mix_channel[i].tag = -1; |
||
461 | mix_channel[i].expire = 0; |
||
462 | mix_channel[i].effects = NULL; |
||
463 | mix_channel[i].paused = 0; |
||
464 | } |
||
465 | Mix_VolumeMusic(SDL_MIX_MAXVOLUME); |
||
466 | |||
467 | _Mix_InitEffects(); |
||
468 | |||
469 | /* This list is (currently) decided at build time. */ |
||
470 | add_chunk_decoder("WAVE"); |
||
471 | add_chunk_decoder("AIFF"); |
||
472 | add_chunk_decoder("VOC"); |
||
473 | #ifdef OGG_MUSIC |
||
474 | add_chunk_decoder("OGG"); |
||
475 | #endif |
||
476 | #ifdef FLAC_MUSIC |
||
477 | add_chunk_decoder("FLAC"); |
||
478 | #endif |
||
479 | |||
480 | audio_opened = 1; |
||
481 | SDL_PauseAudio(0); |
||
482 | return(0); |
||
483 | } |
||
484 | |||
485 | /* Dynamically change the number of channels managed by the mixer. |
||
486 | If decreasing the number of channels, the upper channels are |
||
487 | stopped. |
||
488 | */ |
||
489 | int Mix_AllocateChannels(int numchans) |
||
490 | { |
||
491 | if ( numchans<0 || numchans==num_channels ) |
||
492 | return(num_channels); |
||
493 | |||
494 | if ( numchans < num_channels ) { |
||
495 | /* Stop the affected channels */ |
||
496 | int i; |
||
497 | for(i=numchans; i < num_channels; i++) { |
||
498 | Mix_UnregisterAllEffects(i); |
||
499 | Mix_HaltChannel(i); |
||
500 | } |
||
501 | } |
||
502 | SDL_LockAudio(); |
||
503 | mix_channel = (struct _Mix_Channel *) SDL_realloc(mix_channel, numchans * sizeof(struct _Mix_Channel)); |
||
504 | if ( numchans > num_channels ) { |
||
505 | /* Initialize the new channels */ |
||
506 | int i; |
||
507 | for(i=num_channels; i < numchans; i++) { |
||
508 | mix_channel[i].chunk = NULL; |
||
509 | mix_channel[i].playing = 0; |
||
510 | mix_channel[i].looping = 0; |
||
511 | mix_channel[i].volume = SDL_MIX_MAXVOLUME; |
||
512 | mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME; |
||
513 | mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME; |
||
514 | mix_channel[i].fading = MIX_NO_FADING; |
||
515 | mix_channel[i].tag = -1; |
||
516 | mix_channel[i].expire = 0; |
||
517 | mix_channel[i].effects = NULL; |
||
518 | mix_channel[i].paused = 0; |
||
519 | } |
||
520 | } |
||
521 | num_channels = numchans; |
||
522 | SDL_UnlockAudio(); |
||
523 | return(num_channels); |
||
524 | } |
||
525 | |||
526 | /* Return the actual mixer parameters */ |
||
527 | int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels) |
||
528 | { |
||
529 | if ( audio_opened ) { |
||
530 | if ( frequency ) { |
||
531 | *frequency = mixer.freq; |
||
532 | } |
||
533 | if ( format ) { |
||
534 | *format = mixer.format; |
||
535 | } |
||
536 | if ( channels ) { |
||
537 | *channels = mixer.channels; |
||
538 | } |
||
539 | } |
||
540 | return(audio_opened); |
||
541 | } |
||
542 | |||
543 | |||
544 | /* |
||
545 | * !!! FIXME: Ideally, we want a Mix_LoadSample_RW(), which will handle the |
||
546 | * generic setup, then call the correct file format loader. |
||
547 | */ |
||
548 | |||
549 | /* Load a wave file */ |
||
550 | Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc) |
||
551 | { |
||
552 | Uint32 magic; |
||
553 | Mix_Chunk *chunk; |
||
554 | SDL_AudioSpec wavespec, *loaded; |
||
555 | SDL_AudioCVT wavecvt; |
||
556 | int samplesize; |
||
557 | |||
558 | /* rcg06012001 Make sure src is valid */ |
||
559 | if ( ! src ) { |
||
560 | SDL_SetError("Mix_LoadWAV_RW with NULL src"); |
||
561 | return(NULL); |
||
562 | } |
||
563 | |||
564 | /* Make sure audio has been opened */ |
||
565 | if ( ! audio_opened ) { |
||
566 | SDL_SetError("Audio device hasn't been opened"); |
||
567 | if ( freesrc && src ) { |
||
568 | SDL_RWclose(src); |
||
569 | } |
||
570 | return(NULL); |
||
571 | } |
||
572 | |||
573 | /* Allocate the chunk memory */ |
||
574 | chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk)); |
||
575 | if ( chunk == NULL ) { |
||
576 | SDL_SetError("Out of memory"); |
||
577 | if ( freesrc ) { |
||
578 | SDL_RWclose(src); |
||
579 | } |
||
580 | return(NULL); |
||
581 | } |
||
582 | |||
583 | /* Find out what kind of audio file this is */ |
||
584 | magic = SDL_ReadLE32(src); |
||
585 | /* Seek backwards for compatibility with older loaders */ |
||
586 | SDL_RWseek(src, -(int)sizeof(Uint32), SEEK_CUR); |
||
587 | |||
588 | switch (magic) { |
||
589 | case WAVE: |
||
590 | case RIFF: |
||
591 | loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, |
||
592 | (Uint8 **)&chunk->abuf, &chunk->alen); |
||
593 | break; |
||
594 | case FORM: |
||
595 | loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, |
||
596 | (Uint8 **)&chunk->abuf, &chunk->alen); |
||
597 | break; |
||
598 | #ifdef OGG_MUSIC |
||
599 | case OGGS: |
||
600 | loaded = Mix_LoadOGG_RW(src, freesrc, &wavespec, |
||
601 | (Uint8 **)&chunk->abuf, &chunk->alen); |
||
602 | break; |
||
603 | #endif |
||
604 | #ifdef FLAC_MUSIC |
||
605 | case FLAC: |
||
606 | loaded = Mix_LoadFLAC_RW(src, freesrc, &wavespec, |
||
607 | (Uint8 **)&chunk->abuf, &chunk->alen); |
||
608 | break; |
||
609 | #endif |
||
610 | case CREA: |
||
611 | loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, |
||
612 | (Uint8 **)&chunk->abuf, &chunk->alen); |
||
613 | break; |
||
614 | default: |
||
615 | SDL_SetError("Unrecognized sound file type"); |
||
616 | return(0); |
||
617 | } |
||
618 | if ( !loaded ) { |
||
619 | SDL_free(chunk); |
||
620 | if ( freesrc ) { |
||
621 | SDL_RWclose(src); |
||
622 | } |
||
623 | return(NULL); |
||
624 | } |
||
625 | |||
626 | #if 0 |
||
627 | PrintFormat("Audio device", &mixer); |
||
628 | PrintFormat("-- Wave file", &wavespec); |
||
629 | #endif |
||
630 | |||
631 | /* Build the audio converter and create conversion buffers */ |
||
632 | if ( wavespec.format != mixer.format || |
||
633 | wavespec.channels != mixer.channels || |
||
634 | wavespec.freq != mixer.freq ) { |
||
635 | if ( SDL_BuildAudioCVT(&wavecvt, |
||
636 | wavespec.format, wavespec.channels, wavespec.freq, |
||
637 | mixer.format, mixer.channels, mixer.freq) < 0 ) { |
||
638 | SDL_free(chunk->abuf); |
||
639 | SDL_free(chunk); |
||
640 | return(NULL); |
||
641 | } |
||
642 | samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels; |
||
643 | wavecvt.len = chunk->alen & ~(samplesize-1); |
||
644 | wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult); |
||
645 | if ( wavecvt.buf == NULL ) { |
||
646 | SDL_SetError("Out of memory"); |
||
647 | SDL_free(chunk->abuf); |
||
648 | SDL_free(chunk); |
||
649 | return(NULL); |
||
650 | } |
||
651 | memcpy(wavecvt.buf, chunk->abuf, chunk->alen); |
||
652 | SDL_free(chunk->abuf); |
||
653 | |||
654 | /* Run the audio converter */ |
||
655 | if ( SDL_ConvertAudio(&wavecvt) < 0 ) { |
||
656 | SDL_free(wavecvt.buf); |
||
657 | SDL_free(chunk); |
||
658 | return(NULL); |
||
659 | } |
||
660 | |||
661 | chunk->abuf = wavecvt.buf; |
||
662 | chunk->alen = wavecvt.len_cvt; |
||
663 | } |
||
664 | |||
665 | chunk->allocated = 1; |
||
666 | chunk->volume = MIX_MAX_VOLUME; |
||
667 | |||
668 | return(chunk); |
||
669 | } |
||
670 | |||
671 | /* Load a wave file of the mixer format from a memory buffer */ |
||
672 | Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem) |
||
673 | { |
||
674 | Mix_Chunk *chunk; |
||
675 | Uint8 magic[4]; |
||
676 | |||
677 | /* Make sure audio has been opened */ |
||
678 | if ( ! audio_opened ) { |
||
679 | SDL_SetError("Audio device hasn't been opened"); |
||
680 | return(NULL); |
||
681 | } |
||
682 | |||
683 | /* Allocate the chunk memory */ |
||
684 | chunk = (Mix_Chunk *)SDL_calloc(1,sizeof(Mix_Chunk)); |
||
685 | if ( chunk == NULL ) { |
||
686 | SDL_SetError("Out of memory"); |
||
687 | return(NULL); |
||
688 | } |
||
689 | |||
690 | /* Essentially just skip to the audio data (no error checking - fast) */ |
||
691 | chunk->allocated = 0; |
||
692 | mem += 12; /* WAV header */ |
||
693 | do { |
||
694 | memcpy(magic, mem, 4); |
||
695 | mem += 4; |
||
696 | chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0])); |
||
697 | mem += 4; |
||
698 | chunk->abuf = mem; |
||
699 | mem += chunk->alen; |
||
700 | } while ( memcmp(magic, "data", 4) != 0 ); |
||
701 | chunk->volume = MIX_MAX_VOLUME; |
||
702 | |||
703 | return(chunk); |
||
704 | } |
||
705 | |||
706 | /* Load raw audio data of the mixer format from a memory buffer */ |
||
707 | Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len) |
||
708 | { |
||
709 | Mix_Chunk *chunk; |
||
710 | |||
711 | /* Make sure audio has been opened */ |
||
712 | if ( ! audio_opened ) { |
||
713 | SDL_SetError("Audio device hasn't been opened"); |
||
714 | return(NULL); |
||
715 | } |
||
716 | |||
717 | /* Allocate the chunk memory */ |
||
718 | chunk = (Mix_Chunk *)SDL_malloc(sizeof(Mix_Chunk)); |
||
719 | if ( chunk == NULL ) { |
||
720 | SDL_SetError("Out of memory"); |
||
721 | return(NULL); |
||
722 | } |
||
723 | |||
724 | /* Essentially just point at the audio data (no error checking - fast) */ |
||
725 | chunk->allocated = 0; |
||
726 | chunk->alen = len; |
||
727 | chunk->abuf = mem; |
||
728 | chunk->volume = MIX_MAX_VOLUME; |
||
729 | |||
730 | return(chunk); |
||
731 | } |
||
732 | |||
733 | /* Free an audio chunk previously loaded */ |
||
734 | void Mix_FreeChunk(Mix_Chunk *chunk) |
||
735 | { |
||
736 | int i; |
||
737 | |||
738 | /* Caution -- if the chunk is playing, the mixer will crash */ |
||
739 | if ( chunk ) { |
||
740 | /* Guarantee that this chunk isn't playing */ |
||
741 | SDL_LockAudio(); |
||
742 | if ( mix_channel ) { |
||
743 | for ( i=0; i |
||
744 | if ( chunk == mix_channel[i].chunk ) { |
||
745 | mix_channel[i].playing = 0; |
||
746 | mix_channel[i].looping = 0; |
||
747 | } |
||
748 | } |
||
749 | } |
||
750 | SDL_UnlockAudio(); |
||
751 | /* Actually free the chunk */ |
||
752 | if ( chunk->allocated ) { |
||
753 | SDL_free(chunk->abuf); |
||
754 | } |
||
755 | SDL_free(chunk); |
||
756 | } |
||
757 | } |
||
758 | |||
759 | /* Set a function that is called after all mixing is performed. |
||
760 | This can be used to provide real-time visual display of the audio stream |
||
761 | or add a custom mixer filter for the stream data. |
||
762 | */ |
||
763 | void Mix_SetPostMix(void (*mix_func) |
||
764 | (void *udata, Uint8 *stream, int len), void *arg) |
||
765 | { |
||
766 | SDL_LockAudio(); |
||
767 | mix_postmix_data = arg; |
||
768 | mix_postmix = mix_func; |
||
769 | SDL_UnlockAudio(); |
||
770 | } |
||
771 | |||
772 | /* Add your own music player or mixer function. |
||
773 | If 'mix_func' is NULL, the default music player is re-enabled. |
||
774 | */ |
||
775 | void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len), |
||
776 | void *arg) |
||
777 | { |
||
778 | SDL_LockAudio(); |
||
779 | if ( mix_func != NULL ) { |
||
780 | music_data = arg; |
||
781 | mix_music = mix_func; |
||
782 | } else { |
||
783 | music_data = NULL; |
||
784 | mix_music = music_mixer; |
||
785 | } |
||
786 | SDL_UnlockAudio(); |
||
787 | } |
||
788 | |||
789 | void *Mix_GetMusicHookData(void) |
||
790 | { |
||
791 | return(music_data); |
||
792 | } |
||
793 | |||
794 | void Mix_ChannelFinished(void (*channel_finished)(int channel)) |
||
795 | { |
||
796 | SDL_LockAudio(); |
||
797 | channel_done_callback = channel_finished; |
||
798 | SDL_UnlockAudio(); |
||
799 | } |
||
800 | |||
801 | |||
802 | /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate |
||
803 | them dynamically to the next sample if requested with a -1 value below. |
||
804 | Returns the number of reserved channels. |
||
805 | */ |
||
806 | int Mix_ReserveChannels(int num) |
||
807 | { |
||
808 | if (num > num_channels) |
||
809 | num = num_channels; |
||
810 | reserved_channels = num; |
||
811 | return num; |
||
812 | } |
||
813 | |||
814 | static int checkchunkintegral(Mix_Chunk *chunk) |
||
815 | { |
||
816 | int frame_width = 1; |
||
817 | |||
818 | if ((mixer.format & 0xFF) == 16) frame_width = 2; |
||
819 | frame_width *= mixer.channels; |
||
820 | while (chunk->alen % frame_width) chunk->alen--; |
||
821 | return chunk->alen; |
||
822 | } |
||
823 | |||
824 | /* Play an audio chunk on a specific channel. |
||
825 | If the specified channel is -1, play on the first free channel. |
||
826 | 'ticks' is the number of milliseconds at most to play the sample, or -1 |
||
827 | if there is no limit. |
||
828 | Returns which channel was used to play the sound. |
||
829 | */ |
||
830 | int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks) |
||
831 | { |
||
832 | int i; |
||
833 | |||
834 | /* Don't play null pointers :-) */ |
||
835 | if ( chunk == NULL ) { |
||
836 | Mix_SetError("Tried to play a NULL chunk"); |
||
837 | return(-1); |
||
838 | } |
||
839 | if ( !checkchunkintegral(chunk)) { |
||
840 | Mix_SetError("Tried to play a chunk with a bad frame"); |
||
841 | return(-1); |
||
842 | } |
||
843 | |||
844 | /* Lock the mixer while modifying the playing channels */ |
||
845 | SDL_LockAudio(); |
||
846 | { |
||
847 | /* If which is -1, play on the first free channel */ |
||
848 | if ( which == -1 ) { |
||
849 | for ( i=reserved_channels; i |
||
850 | if ( mix_channel[i].playing <= 0 ) |
||
851 | break; |
||
852 | } |
||
853 | if ( i == num_channels ) { |
||
854 | Mix_SetError("No free channels available"); |
||
855 | which = -1; |
||
856 | } else { |
||
857 | which = i; |
||
858 | } |
||
859 | } |
||
860 | |||
861 | /* Queue up the audio data for this channel */ |
||
862 | if ( which >= 0 && which < num_channels ) { |
||
9097 | turbocat | 863 | Uint32 sdl_ticks = uSDL_GetTicks(); |
8645 | turbocat | 864 | if (Mix_Playing(which)) |
865 | _Mix_channel_done_playing(which); |
||
866 | mix_channel[which].samples = chunk->abuf; |
||
867 | mix_channel[which].playing = chunk->alen; |
||
868 | mix_channel[which].looping = loops; |
||
869 | mix_channel[which].chunk = chunk; |
||
870 | mix_channel[which].paused = 0; |
||
871 | mix_channel[which].fading = MIX_NO_FADING; |
||
872 | mix_channel[which].start_time = sdl_ticks; |
||
873 | mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0; |
||
874 | } |
||
875 | } |
||
876 | SDL_UnlockAudio(); |
||
877 | |||
878 | /* Return the channel on which the sound is being played */ |
||
879 | return(which); |
||
880 | } |
||
881 | |||
882 | /* Change the expiration delay for a channel */ |
||
883 | int Mix_ExpireChannel(int which, int ticks) |
||
884 | { |
||
885 | int status = 0; |
||
886 | |||
887 | if ( which == -1 ) { |
||
888 | int i; |
||
889 | for ( i=0; i < num_channels; ++ i ) { |
||
890 | status += Mix_ExpireChannel(i, ticks); |
||
891 | } |
||
892 | } else if ( which < num_channels ) { |
||
893 | SDL_LockAudio(); |
||
9097 | turbocat | 894 | mix_channel[which].expire = (ticks>0) ? ( uSDL_GetTicks() + ticks) : 0; |
8645 | turbocat | 895 | SDL_UnlockAudio(); |
896 | ++ status; |
||
897 | } |
||
898 | return(status); |
||
899 | } |
||
900 | |||
901 | /* Fade in a sound on a channel, over ms milliseconds */ |
||
902 | int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks) |
||
903 | { |
||
904 | int i; |
||
905 | |||
906 | /* Don't play null pointers :-) */ |
||
907 | if ( chunk == NULL ) { |
||
908 | return(-1); |
||
909 | } |
||
910 | if ( !checkchunkintegral(chunk)) { |
||
911 | Mix_SetError("Tried to play a chunk with a bad frame"); |
||
912 | return(-1); |
||
913 | } |
||
914 | |||
915 | /* Lock the mixer while modifying the playing channels */ |
||
916 | SDL_LockAudio(); |
||
917 | { |
||
918 | /* If which is -1, play on the first free channel */ |
||
919 | if ( which == -1 ) { |
||
920 | for ( i=reserved_channels; i |
||
921 | if ( mix_channel[i].playing <= 0 ) |
||
922 | break; |
||
923 | } |
||
924 | if ( i == num_channels ) { |
||
925 | which = -1; |
||
926 | } else { |
||
927 | which = i; |
||
928 | } |
||
929 | } |
||
930 | |||
931 | /* Queue up the audio data for this channel */ |
||
932 | if ( which >= 0 && which < num_channels ) { |
||
9097 | turbocat | 933 | Uint32 sdl_ticks = uSDL_GetTicks(); |
8645 | turbocat | 934 | if (Mix_Playing(which)) |
935 | _Mix_channel_done_playing(which); |
||
936 | mix_channel[which].samples = chunk->abuf; |
||
937 | mix_channel[which].playing = chunk->alen; |
||
938 | mix_channel[which].looping = loops; |
||
939 | mix_channel[which].chunk = chunk; |
||
940 | mix_channel[which].paused = 0; |
||
941 | mix_channel[which].fading = MIX_FADING_IN; |
||
942 | mix_channel[which].fade_volume = mix_channel[which].volume; |
||
943 | mix_channel[which].fade_volume_reset = mix_channel[which].volume; |
||
944 | mix_channel[which].volume = 0; |
||
945 | mix_channel[which].fade_length = (Uint32)ms; |
||
946 | mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks; |
||
947 | mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0; |
||
948 | } |
||
949 | } |
||
950 | SDL_UnlockAudio(); |
||
951 | |||
952 | /* Return the channel on which the sound is being played */ |
||
953 | return(which); |
||
954 | } |
||
955 | |||
956 | /* Set volume of a particular channel */ |
||
957 | int Mix_Volume(int which, int volume) |
||
958 | { |
||
959 | int i; |
||
960 | int prev_volume = 0; |
||
961 | |||
962 | if ( which == -1 ) { |
||
963 | for ( i=0; i |
||
964 | prev_volume += Mix_Volume(i, volume); |
||
965 | } |
||
966 | prev_volume /= num_channels; |
||
967 | } else if ( which < num_channels ) { |
||
968 | prev_volume = mix_channel[which].volume; |
||
969 | if ( volume >= 0 ) { |
||
970 | if ( volume > SDL_MIX_MAXVOLUME ) { |
||
971 | volume = SDL_MIX_MAXVOLUME; |
||
972 | } |
||
973 | mix_channel[which].volume = volume; |
||
974 | } |
||
975 | } |
||
976 | return(prev_volume); |
||
977 | } |
||
978 | /* Set volume of a particular chunk */ |
||
979 | int Mix_VolumeChunk(Mix_Chunk *chunk, int volume) |
||
980 | { |
||
981 | int prev_volume; |
||
982 | |||
983 | prev_volume = chunk->volume; |
||
984 | if ( volume >= 0 ) { |
||
985 | if ( volume > MIX_MAX_VOLUME ) { |
||
986 | volume = MIX_MAX_VOLUME; |
||
987 | } |
||
988 | chunk->volume = volume; |
||
989 | } |
||
990 | return(prev_volume); |
||
991 | } |
||
992 | |||
993 | /* Halt playing of a particular channel */ |
||
994 | int Mix_HaltChannel(int which) |
||
995 | { |
||
996 | int i; |
||
997 | |||
998 | if ( which == -1 ) { |
||
999 | for ( i=0; i |
||
1000 | Mix_HaltChannel(i); |
||
1001 | } |
||
1002 | } else if ( which < num_channels ) { |
||
1003 | SDL_LockAudio(); |
||
1004 | if (mix_channel[which].playing) { |
||
1005 | _Mix_channel_done_playing(which); |
||
1006 | mix_channel[which].playing = 0; |
||
1007 | mix_channel[which].looping = 0; |
||
1008 | } |
||
1009 | mix_channel[which].expire = 0; |
||
1010 | if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */ |
||
1011 | mix_channel[which].volume = mix_channel[which].fade_volume_reset; |
||
1012 | mix_channel[which].fading = MIX_NO_FADING; |
||
1013 | SDL_UnlockAudio(); |
||
1014 | } |
||
1015 | return(0); |
||
1016 | } |
||
1017 | |||
1018 | /* Halt playing of a particular group of channels */ |
||
1019 | int Mix_HaltGroup(int tag) |
||
1020 | { |
||
1021 | int i; |
||
1022 | |||
1023 | for ( i=0; i |
||
1024 | if( mix_channel[i].tag == tag ) { |
||
1025 | Mix_HaltChannel(i); |
||
1026 | } |
||
1027 | } |
||
1028 | return(0); |
||
1029 | } |
||
1030 | |||
1031 | /* Fade out a channel and then stop it automatically */ |
||
1032 | int Mix_FadeOutChannel(int which, int ms) |
||
1033 | { |
||
1034 | int status; |
||
1035 | |||
1036 | status = 0; |
||
1037 | if ( audio_opened ) { |
||
1038 | if ( which == -1 ) { |
||
1039 | int i; |
||
1040 | |||
1041 | for ( i=0; i |
||
1042 | status += Mix_FadeOutChannel(i, ms); |
||
1043 | } |
||
1044 | } else if ( which < num_channels ) { |
||
1045 | SDL_LockAudio(); |
||
1046 | if ( mix_channel[which].playing && |
||
1047 | (mix_channel[which].volume > 0) && |
||
1048 | (mix_channel[which].fading != MIX_FADING_OUT) ) { |
||
1049 | mix_channel[which].fade_volume = mix_channel[which].volume; |
||
1050 | mix_channel[which].fading = MIX_FADING_OUT; |
||
1051 | mix_channel[which].fade_length = ms; |
||
9097 | turbocat | 1052 | mix_channel[which].ticks_fade = uSDL_GetTicks(); |
8645 | turbocat | 1053 | |
1054 | /* only change fade_volume_reset if we're not fading. */ |
||
1055 | if (mix_channel[which].fading == MIX_NO_FADING) { |
||
1056 | mix_channel[which].fade_volume_reset = mix_channel[which].volume; |
||
1057 | } |
||
1058 | ++status; |
||
1059 | } |
||
1060 | SDL_UnlockAudio(); |
||
1061 | } |
||
1062 | } |
||
1063 | return(status); |
||
1064 | } |
||
1065 | |||
1066 | /* Halt playing of a particular group of channels */ |
||
1067 | int Mix_FadeOutGroup(int tag, int ms) |
||
1068 | { |
||
1069 | int i; |
||
1070 | int status = 0; |
||
1071 | for ( i=0; i |
||
1072 | if( mix_channel[i].tag == tag ) { |
||
1073 | status += Mix_FadeOutChannel(i,ms); |
||
1074 | } |
||
1075 | } |
||
1076 | return(status); |
||
1077 | } |
||
1078 | |||
1079 | Mix_Fading Mix_FadingChannel(int which) |
||
1080 | { |
||
1081 | if ( which < 0 || which >= num_channels ) { |
||
1082 | return MIX_NO_FADING; |
||
1083 | } |
||
1084 | return mix_channel[which].fading; |
||
1085 | } |
||
1086 | |||
1087 | /* Check the status of a specific channel. |
||
1088 | If the specified mix_channel is -1, check all mix channels. |
||
1089 | */ |
||
1090 | int Mix_Playing(int which) |
||
1091 | { |
||
1092 | int status; |
||
1093 | |||
1094 | status = 0; |
||
1095 | if ( which == -1 ) { |
||
1096 | int i; |
||
1097 | |||
1098 | for ( i=0; i |
||
1099 | if ((mix_channel[i].playing > 0) || |
||
1100 | (mix_channel[i].looping > 0)) |
||
1101 | { |
||
1102 | ++status; |
||
1103 | } |
||
1104 | } |
||
1105 | } else if ( which < num_channels ) { |
||
1106 | if ( (mix_channel[which].playing > 0) || |
||
1107 | (mix_channel[which].looping > 0) ) |
||
1108 | { |
||
1109 | ++status; |
||
1110 | } |
||
1111 | } |
||
1112 | return(status); |
||
1113 | } |
||
1114 | |||
1115 | /* rcg06072001 Get the chunk associated with a channel. */ |
||
1116 | Mix_Chunk *Mix_GetChunk(int channel) |
||
1117 | { |
||
1118 | Mix_Chunk *retval = NULL; |
||
1119 | |||
1120 | if ((channel >= 0) && (channel < num_channels)) { |
||
1121 | retval = mix_channel[channel].chunk; |
||
1122 | } |
||
1123 | |||
1124 | return(retval); |
||
1125 | } |
||
1126 | |||
1127 | /* Close the mixer, halting all playing audio */ |
||
1128 | void Mix_CloseAudio(void) |
||
1129 | { |
||
1130 | int i; |
||
1131 | |||
1132 | if ( audio_opened ) { |
||
1133 | if ( audio_opened == 1 ) { |
||
1134 | for (i = 0; i < num_channels; i++) { |
||
1135 | Mix_UnregisterAllEffects(i); |
||
1136 | } |
||
1137 | Mix_UnregisterAllEffects(MIX_CHANNEL_POST); |
||
1138 | close_music(); |
||
1139 | Mix_HaltChannel(-1); |
||
1140 | _Mix_DeinitEffects(); |
||
1141 | SDL_CloseAudio(); |
||
1142 | SDL_free(mix_channel); |
||
1143 | mix_channel = NULL; |
||
1144 | |||
1145 | /* rcg06042009 report available decoders at runtime. */ |
||
1146 | SDL_free(chunk_decoders); |
||
1147 | chunk_decoders = NULL; |
||
1148 | num_decoders = 0; |
||
1149 | } |
||
1150 | --audio_opened; |
||
1151 | } |
||
1152 | } |
||
1153 | |||
1154 | /* Pause a particular channel (or all) */ |
||
1155 | void Mix_Pause(int which) |
||
1156 | { |
||
9097 | turbocat | 1157 | Uint32 sdl_ticks = uSDL_GetTicks(); |
8645 | turbocat | 1158 | if ( which == -1 ) { |
1159 | int i; |
||
1160 | |||
1161 | for ( i=0; i |
||
1162 | if ( mix_channel[i].playing > 0 ) { |
||
1163 | mix_channel[i].paused = sdl_ticks; |
||
1164 | } |
||
1165 | } |
||
1166 | } else if ( which < num_channels ) { |
||
1167 | if ( mix_channel[which].playing > 0 ) { |
||
1168 | mix_channel[which].paused = sdl_ticks; |
||
1169 | } |
||
1170 | } |
||
1171 | } |
||
1172 | |||
1173 | /* Resume a paused channel */ |
||
1174 | void Mix_Resume(int which) |
||
1175 | { |
||
9097 | turbocat | 1176 | Uint32 sdl_ticks = uSDL_GetTicks(); |
8645 | turbocat | 1177 | |
1178 | SDL_LockAudio(); |
||
1179 | if ( which == -1 ) { |
||
1180 | int i; |
||
1181 | |||
1182 | for ( i=0; i |
||
1183 | if ( mix_channel[i].playing > 0 ) { |
||
1184 | if(mix_channel[i].expire > 0) |
||
1185 | mix_channel[i].expire += sdl_ticks - mix_channel[i].paused; |
||
1186 | mix_channel[i].paused = 0; |
||
1187 | } |
||
1188 | } |
||
1189 | } else if ( which < num_channels ) { |
||
1190 | if ( mix_channel[which].playing > 0 ) { |
||
1191 | if(mix_channel[which].expire > 0) |
||
1192 | mix_channel[which].expire += sdl_ticks - mix_channel[which].paused; |
||
1193 | mix_channel[which].paused = 0; |
||
1194 | } |
||
1195 | } |
||
1196 | SDL_UnlockAudio(); |
||
1197 | } |
||
1198 | |||
1199 | int Mix_Paused(int which) |
||
1200 | { |
||
1201 | if ( which < 0 ) { |
||
1202 | int status = 0; |
||
1203 | int i; |
||
1204 | for( i=0; i < num_channels; ++i ) { |
||
1205 | if ( mix_channel[i].paused ) { |
||
1206 | ++ status; |
||
1207 | } |
||
1208 | } |
||
1209 | return(status); |
||
1210 | } else if ( which < num_channels ) { |
||
1211 | return(mix_channel[which].paused != 0); |
||
1212 | } else { |
||
1213 | return(0); |
||
1214 | } |
||
1215 | } |
||
1216 | |||
1217 | /* Change the group of a channel */ |
||
1218 | int Mix_GroupChannel(int which, int tag) |
||
1219 | { |
||
1220 | if ( which < 0 || which > num_channels ) |
||
1221 | return(0); |
||
1222 | |||
1223 | SDL_LockAudio(); |
||
1224 | mix_channel[which].tag = tag; |
||
1225 | SDL_UnlockAudio(); |
||
1226 | return(1); |
||
1227 | } |
||
1228 | |||
1229 | /* Assign several consecutive channels to a group */ |
||
1230 | int Mix_GroupChannels(int from, int to, int tag) |
||
1231 | { |
||
1232 | int status = 0; |
||
1233 | for( ; from <= to; ++ from ) { |
||
1234 | status += Mix_GroupChannel(from, tag); |
||
1235 | } |
||
1236 | return(status); |
||
1237 | } |
||
1238 | |||
1239 | /* Finds the first available channel in a group of channels */ |
||
1240 | int Mix_GroupAvailable(int tag) |
||
1241 | { |
||
1242 | int i; |
||
1243 | for( i=0; i < num_channels; i ++ ) { |
||
1244 | if ( ((tag == -1) || (tag == mix_channel[i].tag)) && |
||
1245 | (mix_channel[i].playing <= 0) ) |
||
1246 | return i; |
||
1247 | } |
||
1248 | return(-1); |
||
1249 | } |
||
1250 | |||
1251 | int Mix_GroupCount(int tag) |
||
1252 | { |
||
1253 | int count = 0; |
||
1254 | int i; |
||
1255 | for( i=0; i < num_channels; i ++ ) { |
||
1256 | if ( mix_channel[i].tag==tag || tag==-1 ) |
||
1257 | ++ count; |
||
1258 | } |
||
1259 | return(count); |
||
1260 | } |
||
1261 | |||
1262 | /* Finds the "oldest" sample playing in a group of channels */ |
||
1263 | int Mix_GroupOldest(int tag) |
||
1264 | { |
||
1265 | int chan = -1; |
||
9097 | turbocat | 1266 | Uint32 mintime = uSDL_GetTicks(); |
8645 | turbocat | 1267 | int i; |
1268 | for( i=0; i < num_channels; i ++ ) { |
||
1269 | if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0 |
||
1270 | && mix_channel[i].start_time <= mintime ) { |
||
1271 | mintime = mix_channel[i].start_time; |
||
1272 | chan = i; |
||
1273 | } |
||
1274 | } |
||
1275 | return(chan); |
||
1276 | } |
||
1277 | |||
1278 | /* Finds the "most recent" (i.e. last) sample playing in a group of channels */ |
||
1279 | int Mix_GroupNewer(int tag) |
||
1280 | { |
||
1281 | int chan = -1; |
||
1282 | Uint32 maxtime = 0; |
||
1283 | int i; |
||
1284 | for( i=0; i < num_channels; i ++ ) { |
||
1285 | if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0 |
||
1286 | && mix_channel[i].start_time >= maxtime ) { |
||
1287 | maxtime = mix_channel[i].start_time; |
||
1288 | chan = i; |
||
1289 | } |
||
1290 | } |
||
1291 | return(chan); |
||
1292 | } |
||
1293 | |||
1294 | |||
1295 | |||
1296 | /* |
||
1297 | * rcg06122001 The special effects exportable API. |
||
1298 | * Please see effect_*.c for internally-implemented effects, such |
||
1299 | * as Mix_SetPanning(). |
||
1300 | */ |
||
1301 | |||
1302 | /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ |
||
1303 | static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f, |
||
1304 | Mix_EffectDone_t d, void *arg) |
||
1305 | { |
||
1306 | effect_info *new_e; |
||
1307 | |||
1308 | if (!e) { |
||
1309 | Mix_SetError("Internal error"); |
||
1310 | return(0); |
||
1311 | } |
||
1312 | |||
1313 | if (f == NULL) { |
||
1314 | Mix_SetError("NULL effect callback"); |
||
1315 | return(0); |
||
1316 | } |
||
1317 | |||
1318 | new_e = SDL_malloc(sizeof (effect_info)); |
||
1319 | if (new_e == NULL) { |
||
1320 | Mix_SetError("Out of memory"); |
||
1321 | return(0); |
||
1322 | } |
||
1323 | |||
1324 | new_e->callback = f; |
||
1325 | new_e->done_callback = d; |
||
1326 | new_e->udata = arg; |
||
1327 | new_e->next = NULL; |
||
1328 | |||
1329 | /* add new effect to end of linked list... */ |
||
1330 | if (*e == NULL) { |
||
1331 | *e = new_e; |
||
1332 | } else { |
||
1333 | effect_info *cur = *e; |
||
1334 | while (1) { |
||
1335 | if (cur->next == NULL) { |
||
1336 | cur->next = new_e; |
||
1337 | break; |
||
1338 | } |
||
1339 | cur = cur->next; |
||
1340 | } |
||
1341 | } |
||
1342 | |||
1343 | return(1); |
||
1344 | } |
||
1345 | |||
1346 | |||
1347 | /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ |
||
1348 | static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f) |
||
1349 | { |
||
1350 | effect_info *cur; |
||
1351 | effect_info *prev = NULL; |
||
1352 | effect_info *next = NULL; |
||
1353 | |||
1354 | if (!e) { |
||
1355 | Mix_SetError("Internal error"); |
||
1356 | return(0); |
||
1357 | } |
||
1358 | |||
1359 | for (cur = *e; cur != NULL; cur = cur->next) { |
||
1360 | if (cur->callback == f) { |
||
1361 | next = cur->next; |
||
1362 | if (cur->done_callback != NULL) { |
||
1363 | cur->done_callback(channel, cur->udata); |
||
1364 | } |
||
1365 | SDL_free(cur); |
||
1366 | |||
1367 | if (prev == NULL) { /* removing first item of list? */ |
||
1368 | *e = next; |
||
1369 | } else { |
||
1370 | prev->next = next; |
||
1371 | } |
||
1372 | return(1); |
||
1373 | } |
||
1374 | prev = cur; |
||
1375 | } |
||
1376 | |||
1377 | Mix_SetError("No such effect registered"); |
||
1378 | return(0); |
||
1379 | } |
||
1380 | |||
1381 | |||
1382 | /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ |
||
1383 | static int _Mix_remove_all_effects(int channel, effect_info **e) |
||
1384 | { |
||
1385 | effect_info *cur; |
||
1386 | effect_info *next; |
||
1387 | |||
1388 | if (!e) { |
||
1389 | Mix_SetError("Internal error"); |
||
1390 | return(0); |
||
1391 | } |
||
1392 | |||
1393 | for (cur = *e; cur != NULL; cur = next) { |
||
1394 | next = cur->next; |
||
1395 | if (cur->done_callback != NULL) { |
||
1396 | cur->done_callback(channel, cur->udata); |
||
1397 | } |
||
1398 | SDL_free(cur); |
||
1399 | } |
||
1400 | *e = NULL; |
||
1401 | |||
1402 | return(1); |
||
1403 | } |
||
1404 | |||
1405 | |||
1406 | /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ |
||
1407 | int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, |
||
1408 | Mix_EffectDone_t d, void *arg) |
||
1409 | { |
||
1410 | effect_info **e = NULL; |
||
1411 | |||
1412 | if (channel == MIX_CHANNEL_POST) { |
||
1413 | e = &posteffects; |
||
1414 | } else { |
||
1415 | if ((channel < 0) || (channel >= num_channels)) { |
||
1416 | Mix_SetError("Invalid channel number"); |
||
1417 | return(0); |
||
1418 | } |
||
1419 | e = &mix_channel[channel].effects; |
||
1420 | } |
||
1421 | |||
1422 | return _Mix_register_effect(e, f, d, arg); |
||
1423 | } |
||
1424 | |||
1425 | int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f, |
||
1426 | Mix_EffectDone_t d, void *arg) |
||
1427 | { |
||
1428 | int retval; |
||
1429 | SDL_LockAudio(); |
||
1430 | retval = _Mix_RegisterEffect_locked(channel, f, d, arg); |
||
1431 | SDL_UnlockAudio(); |
||
1432 | return retval; |
||
1433 | } |
||
1434 | |||
1435 | |||
1436 | /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ |
||
1437 | int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f) |
||
1438 | { |
||
1439 | effect_info **e = NULL; |
||
1440 | |||
1441 | if (channel == MIX_CHANNEL_POST) { |
||
1442 | e = &posteffects; |
||
1443 | } else { |
||
1444 | if ((channel < 0) || (channel >= num_channels)) { |
||
1445 | Mix_SetError("Invalid channel number"); |
||
1446 | return(0); |
||
1447 | } |
||
1448 | e = &mix_channel[channel].effects; |
||
1449 | } |
||
1450 | |||
1451 | return _Mix_remove_effect(channel, e, f); |
||
1452 | } |
||
1453 | |||
1454 | int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f) |
||
1455 | { |
||
1456 | int retval; |
||
1457 | SDL_LockAudio(); |
||
1458 | retval = _Mix_UnregisterEffect_locked(channel, f); |
||
1459 | SDL_UnlockAudio(); |
||
1460 | return(retval); |
||
1461 | } |
||
1462 | |||
1463 | /* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ |
||
1464 | int _Mix_UnregisterAllEffects_locked(int channel) |
||
1465 | { |
||
1466 | effect_info **e = NULL; |
||
1467 | |||
1468 | if (channel == MIX_CHANNEL_POST) { |
||
1469 | e = &posteffects; |
||
1470 | } else { |
||
1471 | if ((channel < 0) || (channel >= num_channels)) { |
||
1472 | Mix_SetError("Invalid channel number"); |
||
1473 | return(0); |
||
1474 | } |
||
1475 | e = &mix_channel[channel].effects; |
||
1476 | } |
||
1477 | |||
1478 | return _Mix_remove_all_effects(channel, e); |
||
1479 | } |
||
1480 | |||
1481 | int Mix_UnregisterAllEffects(int channel) |
||
1482 | { |
||
1483 | int retval; |
||
1484 | SDL_LockAudio(); |
||
1485 | retval = _Mix_UnregisterAllEffects_locked(channel); |
||
1486 | SDL_UnlockAudio(); |
||
1487 | return(retval); |
||
1488 | } |
||
1489 | |||
1490 | /* end of mixer.c ... */>>>>=>>>=>>=>>>>>>>>>>>>>>>=>>>>=>8)|(mem[0])); |
||
1491 |