mixer.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 11 Oct 2009 05:29:55 +0000
changeset 443 d842a759e68a
parent 442 884a700fb3ff
child 444 41b7df493dad
permissions -rw-r--r--
Correctly reset channel volumes after a fade out interrupts a fade in.

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