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