mixer.c
author Ryan C. Gordon <icculus@icculus.org>
Sun, 11 Oct 2009 02:59:12 +0000
changeset 442 884a700fb3ff
parent 412 4c1f81481f87
child 443 d842a759e68a
permissions -rw-r--r--
Corrected race condition in positional effects.

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