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