Subversion Repositories Kolibri OS

Rev

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 ... */
1488