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