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