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