mixer.c
author Ryan C. Gordon <icculus@icculus.org>
Thu, 06 Mar 2008 15:02:39 +0000
changeset 383 0d05ac848d6a
parent 382 50501e45c57b
child 386 695494546b3c
permissions -rw-r--r--
My fix for this reported issue... --ryan.

Subject: Bug in SDL_mixer
Date: Thu, 6 Mar 2008 16:46:45 +1300
From: Karl Hendrikse
To: jcatki@jcatki.no-ip.org

I really don't know if I'm sending this to anywhere near the right
person, but I believe I have found a bug in SDL_mixer.

In mixer.c, in the mix_channels function, line 190 should be moved
outside the if statement. This will reset the volume of a channel at the
end of any fade, not just after a fade out.

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