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