mixer.c
author Stephane Peter
Sat, 30 Oct 1999 22:57:49 +0000
changeset 11 3939a99bf422
parent 8 d2bf7cf9446c
child 13 1e489d8b97d8
permissions -rw-r--r--
Various bugfixes for music looping
     1 /*
     2     MIXERLIB:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997-1999  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     5635-34 Springhouse Dr.
    21     Pleasanton, CA 94588 (USA)
    22     slouken@devolution.com
    23 */
    24 
    25 /* $Id$ */
    26 
    27 #include <stdio.h>
    28 #include <stdlib.h>
    29 #include <string.h>
    30 
    31 #include <SDL/SDL_mutex.h>
    32 #include <SDL/SDL_endian.h>
    33 
    34 #include "mixer.h"
    35 
    36 static int audio_opened = 0;
    37 
    38 static SDL_AudioSpec mixer;
    39 static SDL_mutex *mixer_lock;
    40 static struct _Mix_Channel {
    41 	Mix_Chunk *chunk;
    42 	int playing;
    43 	int paused;
    44 	Uint8 *samples;
    45 	int volume;
    46 	int looping;
    47 	int tag;
    48 	int expire;
    49 	Uint32 start_time;
    50 	Mix_Fading fading;
    51 	int fade_volume;
    52 	int fade_length;
    53 	Uint32 ticks_fade;
    54 } *channel = NULL;
    55 static int num_channels;
    56 
    57 #define MUSIC_VOL	64		/* Music volume 0-64 */
    58 
    59 /* Support for user defined music functions, plus the default one */
    60 extern int music_active;
    61 extern void music_mixer(void *udata, Uint8 *stream, int len);
    62 static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;
    63 static int mixed_channels = 0; /* Holds the number of channels actually mixed, for debugging */
    64 static int reserved_channels = 0;
    65 void *music_data = NULL;
    66 
    67 /* Mixing function */
    68 static void mix_channels(void *udata, Uint8 *stream, int len)
    69 {
    70 	int i, mixable, volume;
    71 	Uint32 sdl_ticks;
    72 
    73 	/* Grab the channels we need to mix */
    74 	SDL_mutexP(mixer_lock);
    75 	mixed_channels = 0;
    76 	sdl_ticks = SDL_GetTicks();
    77 	for ( i=0; i<num_channels; ++i ) {
    78 		if( ! channel[i].paused ) {
    79 			if ( channel[i].expire > 0 && channel[i].expire < sdl_ticks ) {
    80 				/* Expiration delay for that channel is reached */
    81 				channel[i].playing = 0;
    82 				channel[i].fading = MIX_NO_FADING;
    83 				channel[i].expire = -1;
    84 			} else if ( channel[i].fading != MIX_NO_FADING ) {
    85 				Uint32 ticks = sdl_ticks - channel[i].ticks_fade;
    86 				if( ticks > channel[i].fade_length ) {
    87 					if( channel[i].fading == MIX_FADING_OUT ) {
    88 						channel[i].playing = 0;
    89 						channel[i].expire = -1;
    90 						Mix_Volume(i, channel[i].fading); /* Restore the volume */
    91 					}
    92 					channel[i].fading = MIX_NO_FADING;
    93 				} else {
    94 					if( channel[i].fading == MIX_FADING_OUT ) {
    95 						Mix_Volume(i, (channel[i].fade_volume * (channel[i].fade_length-ticks))
    96 								   / channel[i].fade_length );
    97 					} else {
    98 						Mix_Volume(i, (channel[i].fade_volume * ticks) / channel[i].fade_length );
    99 					}
   100 				}
   101 			}
   102 			if ( channel[i].playing > 0 ) {
   103 				++ mixed_channels;
   104 				mixable = channel[i].playing;
   105 				if ( mixable > len ) {
   106 					mixable = len;
   107 				}
   108 				volume = (channel[i].volume*channel[i].chunk->volume) /
   109 					MIX_MAX_VOLUME;
   110 				SDL_MixAudio(stream,channel[i].samples,mixable,volume);
   111 				channel[i].samples += mixable;
   112 				channel[i].playing -= mixable;
   113 				if ( ! channel[i].playing && channel[i].looping ) {
   114 					if ( --channel[i].looping ) {
   115 						channel[i].samples = channel[i].chunk->abuf;
   116 						channel[i].playing = channel[i].chunk->alen;
   117 					}
   118 				}
   119 			}
   120 		}
   121 	}
   122 	SDL_mutexV(mixer_lock);
   123 
   124 	/* Mix the music */
   125 	if ( music_active ) {
   126 		mix_music(music_data, stream, len);
   127 	}
   128 }
   129 
   130 static void PrintFormat(char *title, SDL_AudioSpec *fmt)
   131 {
   132 	printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF),
   133 			(fmt->format&0x8000) ? "signed" : "unsigned",
   134 			(fmt->channels > 1) ? "stereo" : "mono", fmt->freq);
   135 }
   136 
   137 /* Open the mixer with a certain desired audio format */
   138 int Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize)
   139 {
   140 	int i;
   141 	SDL_AudioSpec desired;
   142 
   143 	/* If the mixer is already opened, increment open count */
   144 	if ( audio_opened ) {
   145 	    ++audio_opened;
   146 	    return(0);
   147 	}
   148 
   149 	/* Set the desired format and frequency */
   150 	desired.freq = frequency;
   151 	desired.format = format;
   152 	desired.channels = channels;
   153 	desired.samples = chunksize;
   154 	desired.callback = mix_channels;
   155 	desired.userdata = NULL;
   156 
   157 	/* Accept nearly any audio format */
   158 	if ( SDL_OpenAudio(&desired, &mixer) < 0 ) {
   159 		return(-1);
   160 	}
   161 #if 0
   162 	PrintFormat("Audio device", &mixer);
   163 #endif
   164 
   165 	/* Create the channel lock mutex */
   166 	mixer_lock = SDL_CreateMutex();
   167 	if ( mixer_lock == NULL ) {
   168 		SDL_CloseAudio();
   169 		SDL_SetError("Unable to create mixer lock");
   170 		return(-1);
   171 	}
   172 
   173 	/* Initialize the music players */
   174 	if ( open_music(&mixer) < 0 ) {
   175 		SDL_CloseAudio();
   176 		SDL_DestroyMutex(mixer_lock);
   177 		return(-1);
   178 	}
   179 
   180 	num_channels = MIX_CHANNELS;
   181 	channel = (struct _Mix_Channel *) malloc(num_channels * sizeof(struct _Mix_Channel));
   182 
   183 	/* Clear out the audio channels */
   184 	for ( i=0; i<num_channels; ++i ) {
   185 		channel[i].chunk = NULL;
   186 		channel[i].playing = 0;
   187 		channel[i].looping = 0;
   188 		channel[i].volume = SDL_MIX_MAXVOLUME;
   189 		channel[i].tag = -1;
   190 		channel[i].expire = -1;
   191 	}
   192 	Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
   193 	audio_opened = 1;
   194 	SDL_PauseAudio(0);
   195 	return(0);
   196 }
   197 
   198 /* Dynamically change the number of channels managed by the mixer.
   199    If decreasing the number of channels, the upper channels are
   200    stopped.
   201  */
   202 int Mix_AllocateChannels(int numchans)
   203 {
   204 	if ( numchans<0 || numchans==num_channels )
   205 		return(num_channels);
   206 
   207 	if ( numchans < num_channels ) {
   208 		/* Stop the affected channels */
   209 		int i;
   210 		for(i=numchans; i < num_channels; i++) {
   211 			Mix_HaltChannel(i);
   212 		}
   213 	}
   214 	SDL_mutexP(mixer_lock);
   215 	channel = (struct _Mix_Channel *) realloc(channel, numchans * sizeof(struct _Mix_Channel));
   216 	if ( numchans > num_channels ) {
   217 		/* Initialize the new channels */
   218 		int i;
   219 		for(i=num_channels; i < numchans; i++) {
   220 			channel[i].chunk = NULL;
   221 			channel[i].playing = 0;
   222 			channel[i].looping = 0;
   223 			channel[i].volume = SDL_MIX_MAXVOLUME;
   224 			channel[i].tag = -1;
   225 			channel[i].expire = -1;
   226 		}
   227 	}
   228 	num_channels = numchans;
   229 	SDL_mutexV(mixer_lock);
   230 	return(num_channels);
   231 }
   232 
   233 /* Return the actual mixer parameters */
   234 int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels)
   235 {
   236 	if ( audio_opened ) {
   237 		if ( frequency ) {
   238 			*frequency = mixer.freq;
   239 		}
   240 		if ( format ) {
   241 			*format = mixer.format;
   242 		}
   243 		if ( channels ) {
   244 			*channels = mixer.channels;
   245 		}
   246 	}
   247 	return(audio_opened);
   248 }
   249 
   250 /* Load a wave file */
   251 Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
   252 {
   253 	Mix_Chunk *chunk;
   254 	SDL_AudioSpec wavespec;
   255 	SDL_AudioCVT wavecvt;
   256 
   257 	/* Make sure audio has been opened */
   258 	if ( ! audio_opened ) {
   259 		SDL_SetError("Audio device hasn't been opened");
   260 		if ( freesrc ) {
   261 			SDL_RWclose(src);
   262 		}
   263 		return(NULL);
   264 	}
   265 
   266 	/* Allocate the chunk memory */
   267 	chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));
   268 	if ( chunk == NULL ) {
   269 		SDL_SetError("Out of memory");
   270 		if ( freesrc ) {
   271 			SDL_RWclose(src);
   272 		}
   273 		return(NULL);
   274 	}
   275 
   276 	/* Load the WAV file into the chunk */
   277 	if ( SDL_LoadWAV_RW(src, freesrc,
   278 		&wavespec, (Uint8 **)&chunk->abuf, &chunk->alen) == NULL ) {
   279 		free(chunk);
   280 		return(NULL);
   281 	}
   282 #if 0
   283 	PrintFormat("Audio device", &mixer);
   284 	PrintFormat("-- Wave file", &wavespec);
   285 #endif
   286 
   287 	/* Build the audio converter and create conversion buffers */
   288 	if ( SDL_BuildAudioCVT(&wavecvt,
   289 			wavespec.format, wavespec.channels, wavespec.freq,
   290 			mixer.format, mixer.channels, mixer.freq) < 0 ) {
   291 		SDL_FreeWAV(chunk->abuf);
   292 		free(chunk);
   293 		return(NULL);
   294 	}
   295 	wavecvt.len = chunk->alen;
   296 	wavecvt.buf = (Uint8 *)malloc(wavecvt.len*wavecvt.len_mult);
   297 	if ( wavecvt.buf == NULL ) {
   298 		SDL_SetError("Out of memory");
   299 		SDL_FreeWAV(chunk->abuf);
   300 		free(chunk);
   301 		return(NULL);
   302 	}
   303 	memcpy(wavecvt.buf, chunk->abuf, chunk->alen);
   304 	SDL_FreeWAV(chunk->abuf);
   305 
   306 	/* Run the audio converter */
   307 	if ( SDL_ConvertAudio(&wavecvt) < 0 ) {
   308 		free(wavecvt.buf);
   309 		free(chunk);
   310 		return(NULL);
   311 	}
   312 	chunk->allocated = 1;
   313 	chunk->abuf = wavecvt.buf;
   314 	chunk->alen = wavecvt.len_cvt;
   315 	chunk->volume = MIX_MAX_VOLUME;
   316 	return(chunk);
   317 }
   318 
   319 /* Load a wave file of the mixer format from a memory buffer */
   320 Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem)
   321 {
   322 	Mix_Chunk *chunk;
   323 	Uint8 magic[4];
   324 
   325 	/* Make sure audio has been opened */
   326 	if ( ! audio_opened ) {
   327 		SDL_SetError("Audio device hasn't been opened");
   328 		return(NULL);
   329 	}
   330 
   331 	/* Allocate the chunk memory */
   332 	chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));
   333 	if ( chunk == NULL ) {
   334 		SDL_SetError("Out of memory");
   335 		return(NULL);
   336 	}
   337 
   338 	/* Essentially just skip to the audio data (no error checking - fast) */
   339 	chunk->allocated = 0;
   340 	mem += 12; /* WAV header */
   341 	do {
   342 		memcpy(magic, mem, 4);
   343 		mem += 4;
   344 		chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));
   345 		mem += 4;
   346 		chunk->abuf = mem;
   347 		mem += chunk->alen;
   348 	} while ( memcmp(magic, "data", 4) != 0 );
   349 	chunk->volume = MIX_MAX_VOLUME;
   350 
   351 	return(chunk);
   352 }
   353 
   354 /* Free an audio chunk previously loaded */
   355 void Mix_FreeChunk(Mix_Chunk *chunk)
   356 {
   357 	int i;
   358 
   359 	/* Caution -- if the chunk is playing, the mixer will crash */
   360 	if ( chunk ) {
   361 		/* Guarantee that this chunk isn't playing */
   362 		SDL_mutexP(mixer_lock);
   363 		for ( i=0; i<num_channels; ++i ) {
   364 			if ( chunk == channel[i].chunk ) {
   365 				channel[i].playing = 0;
   366 			}
   367 		}
   368 		SDL_mutexV(mixer_lock);
   369 
   370 		/* Actually free the chunk */
   371 		if ( chunk->allocated ) {
   372 			free(chunk->abuf);
   373 		}
   374 		free(chunk);
   375 	}
   376 }
   377 
   378 /* Add your own music player or mixer function.
   379    If 'mix_func' is NULL, the default music player is re-enabled.
   380  */
   381 void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len),
   382                                                                 void *arg)
   383 {
   384 	SDL_LockAudio();
   385 	if ( mix_func != NULL ) {
   386 		music_data = arg;
   387 		mix_music = mix_func;
   388 	} else {
   389 		music_data = NULL;
   390 		mix_music = music_mixer;
   391 	}
   392 	SDL_UnlockAudio();
   393 }
   394 
   395 void *Mix_GetMusicHookData(void)
   396 {
   397 	return(music_data);
   398 }
   399 
   400 /* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
   401    them dynamically to the next sample if requested with a -1 value below.
   402    Returns the number of reserved channels.
   403  */
   404 int Mix_ReserveChannels(int num)
   405 {
   406 	if (num > num_channels)
   407 		num = num_channels;
   408 	reserved_channels = num;
   409 	return num;
   410 }
   411 
   412 /* Play an audio chunk on a specific channel.
   413    If the specified channel is -1, play on the first free channel.
   414    'ticks' is the number of milliseconds at most to play the sample, or -1
   415    if there is no limit.
   416    Returns which channel was used to play the sound.
   417 */
   418 int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks)
   419 {
   420 	int i;
   421 
   422 	/* Don't play null pointers :-) */
   423 	if ( chunk == NULL ) {
   424 		return(-1);
   425 	}
   426 
   427 	/* Lock the mixer while modifying the playing channels */
   428 	SDL_mutexP(mixer_lock);
   429 	{
   430 		/* If which is -1, play on the first free channel */
   431 		if ( which == -1 ) {
   432 			for ( i=reserved_channels; i<num_channels; ++i ) {
   433 				if ( channel[i].playing <= 0 )
   434 					break;
   435 			}
   436 			if ( i == num_channels ) {
   437 				which = -1;
   438 			} else {
   439 				which = i;
   440 			}
   441 		}
   442 
   443 		/* Queue up the audio data for this channel */
   444 		if ( which >= 0 ) {
   445 			Uint32 sdl_ticks = SDL_GetTicks();
   446 			channel[which].samples = chunk->abuf;
   447 			channel[which].playing = chunk->alen;
   448 			channel[which].looping = loops;
   449 			channel[which].chunk = chunk;
   450 			channel[which].paused = 0;
   451 			channel[which].fading = MIX_NO_FADING;
   452 			channel[which].start_time = sdl_ticks;
   453 			channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : -1;
   454 		}
   455 	}
   456 	SDL_mutexV(mixer_lock);
   457 
   458 	/* Return the channel on which the sound is being played */
   459 	return(which);
   460 }
   461 
   462 /* Change the expiration delay for a channel */
   463 int Mix_ExpireChannel(int which, int ticks)
   464 {
   465 	int status = 0;
   466 
   467 	if ( which == -1 ) {
   468 		int i;
   469 		for ( i=0; i < num_channels; ++ i ) {
   470 			status += Mix_ExpireChannel(i, ticks);
   471 		}
   472 	} else if ( which < num_channels ) {
   473 		SDL_mutexP(mixer_lock);
   474 		channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : -1;		
   475 		SDL_mutexV(mixer_lock);
   476 		++ status;
   477 	}
   478 	return(status);
   479 }
   480 
   481 /* Fade in a sound on a channel, over ms milliseconds */
   482 int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks)
   483 {
   484 	int i;
   485 
   486 	/* Don't play null pointers :-) */
   487 	if ( chunk == NULL ) {
   488 		return(-1);
   489 	}
   490 
   491 	/* Lock the mixer while modifying the playing channels */
   492 	SDL_mutexP(mixer_lock);
   493 	{
   494 		/* If which is -1, play on the first free channel */
   495 		if ( which == -1 ) {
   496 			for ( i=reserved_channels; i<num_channels; ++i ) {
   497 				if ( channel[i].playing <= 0 )
   498 					break;
   499 			}
   500 			if ( i == num_channels ) {
   501 				which = -1;
   502 			} else {
   503 				which = i;
   504 			}
   505 		}
   506 
   507 		/* Queue up the audio data for this channel */
   508 		if ( which >= 0 ) {
   509 			Uint32 sdl_ticks = SDL_GetTicks();
   510 			channel[which].samples = chunk->abuf;
   511 			channel[which].playing = chunk->alen;
   512 			channel[which].looping = loops;
   513 			channel[which].chunk = chunk;
   514 			channel[which].paused = 0;
   515 			channel[which].fading = MIX_FADING_IN;
   516 			channel[which].fade_volume = channel[which].volume;
   517 			channel[which].volume = 0;
   518 			channel[which].fade_length = ms;
   519 			channel[which].start_time = channel[which].ticks_fade = sdl_ticks;
   520 			channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : -1;
   521 		}
   522 	}
   523 	SDL_mutexV(mixer_lock);
   524 
   525 	/* Return the channel on which the sound is being played */
   526 	return(which);
   527 }
   528 
   529 /* Set volume of a particular channel */
   530 int Mix_Volume(int which, int volume)
   531 {
   532 	int i;
   533 	int prev_volume;
   534 
   535 	if ( which == -1 ) {
   536 		prev_volume = 0;
   537 		for ( i=0; i<num_channels; ++i ) {
   538 			prev_volume += Mix_Volume(i, volume);
   539 		}
   540 		prev_volume /= num_channels;
   541 	} else {
   542 		prev_volume = channel[which].volume;
   543 		if ( volume < 0 ) {
   544 			volume = 0;
   545 		}
   546 		if ( volume > SDL_MIX_MAXVOLUME ) {
   547 			volume = SDL_MIX_MAXVOLUME;
   548 		}
   549 		channel[which].volume = volume;
   550 	}
   551 	return(prev_volume);
   552 }
   553 /* Set volume of a particular chunk */
   554 int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
   555 {
   556 	int prev_volume;
   557 
   558 	prev_volume = chunk->volume;
   559 	if ( volume >= 0 ) {
   560 		if ( volume > MIX_MAX_VOLUME ) {
   561 			volume = MIX_MAX_VOLUME;
   562 		}
   563 		chunk->volume = volume;
   564 	}
   565 	return(prev_volume);
   566 }
   567 
   568 /* Halt playing of a particular channel */
   569 int Mix_HaltChannel(int which)
   570 {
   571 	int i;
   572 
   573 	if ( which == -1 ) {
   574 		for ( i=0; i<num_channels; ++i ) {
   575 			Mix_HaltChannel(i);
   576 		}
   577 	} else {
   578 		SDL_mutexP(mixer_lock);
   579 		channel[which].playing = 0;
   580 		channel[which].expire = -1;
   581 		if(channel[which].fading != MIX_NO_FADING) /* Restore volume */
   582 			channel[which].volume = channel[which].fade_volume;
   583 		channel[which].fading = MIX_NO_FADING;
   584 		SDL_mutexV(mixer_lock);
   585 	}
   586 	return(0);
   587 }
   588 
   589 /* Halt playing of a particular group of channels */
   590 int Mix_HaltGroup(int tag)
   591 {
   592 	int i;
   593 
   594 	for ( i=0; i<num_channels; ++i ) {
   595 		if( channel[i].tag == tag ) {
   596 			Mix_HaltChannel(i);
   597 		}
   598 	}
   599 	return(0);
   600 }
   601 
   602 /* Fade out a channel and then stop it automatically */
   603 int Mix_FadeOutChannel(int which, int ms)
   604 {
   605 	int status;
   606 
   607 	status = 0;
   608 	if ( which == -1 ) {
   609 		int i;
   610 
   611 		for ( i=0; i<num_channels; ++i ) {
   612 			status += Mix_FadeOutChannel(i,ms);
   613 		}
   614 	} else {
   615 		SDL_mutexP(mixer_lock);
   616 		if ( channel[which].playing && channel[which].volume>0 && 
   617 			 channel[which].fading==MIX_NO_FADING ) {
   618 
   619 			channel[which].fading = MIX_FADING_OUT;
   620 			channel[which].fade_volume = channel[which].volume;
   621 			channel[which].fade_length = ms;
   622 			channel[which].ticks_fade = SDL_GetTicks();
   623 			++ status;
   624 		}
   625 		SDL_mutexV(mixer_lock);
   626 	}
   627 	return(status);
   628 }
   629 
   630 /* Halt playing of a particular group of channels */
   631 int Mix_FadeOutGroup(int tag, int ms)
   632 {
   633 	int i;
   634 	int status = 0;
   635 	for ( i=0; i<num_channels; ++i ) {
   636 		if( channel[i].tag == tag ) {
   637 			status += Mix_FadeOutChannel(i,ms);
   638 		}
   639 	}
   640 	return(status);
   641 }
   642 
   643 Mix_Fading Mix_FadingChannel(int which)
   644 {
   645 	return channel[which].fading;
   646 }
   647 
   648 /* Check the status of a specific channel.
   649    If the specified channel is -1, check all channels.
   650 */
   651 int Mix_Playing(int which)
   652 {
   653 	int status;
   654 
   655 	status = 0;
   656 	if ( which == -1 ) {
   657 		int i;
   658 
   659 		for ( i=0; i<num_channels; ++i ) {
   660 			if ( channel[i].playing > 0 ) {
   661 				++status;
   662 			}
   663 		}
   664 	} else {
   665 		if ( channel[which].playing > 0 ) {
   666 			++status;
   667 		}
   668 	}
   669 	return(status);
   670 }
   671 
   672 /* Close the mixer, halting all playing audio */
   673 void Mix_CloseAudio(void)
   674 {
   675 	if ( audio_opened ) {
   676 		if ( audio_opened == 1 ) {
   677 			close_music();
   678 			Mix_HaltChannel(-1);
   679 			SDL_CloseAudio();
   680 			SDL_DestroyMutex(mixer_lock);
   681 			free(channel);
   682 			channel = NULL;
   683 		}
   684 		--audio_opened;
   685 	}
   686 }
   687 
   688 /* Pause a particular channel (or all) */
   689 void Mix_Pause(int which)
   690 {
   691 	Uint32 sdl_ticks = SDL_GetTicks();
   692 	if ( which == -1 ) {
   693 		int i;
   694 
   695 		for ( i=0; i<num_channels; ++i ) {
   696 			if ( channel[i].playing > 0 ) {
   697 				channel[i].paused = sdl_ticks;
   698 			}
   699 		}
   700 	} else {
   701 		if ( channel[which].playing > 0 ) {
   702 			channel[which].paused = sdl_ticks;
   703 		}
   704 	}
   705 }
   706 
   707 /* Resume a paused channel */
   708 void Mix_Resume(int which)
   709 {
   710 	Uint32 sdl_ticks = SDL_GetTicks();
   711 	if ( which == -1 ) {
   712 		int i;
   713 
   714 		SDL_mutexP(mixer_lock);
   715 		for ( i=0; i<num_channels; ++i ) {
   716 			if ( channel[i].playing > 0 ) {
   717 				if(channel[i].expire > 0)
   718 					channel[i].expire += sdl_ticks - channel[i].paused;
   719 				channel[i].paused = 0;
   720 			}
   721 		}
   722 		SDL_mutexV(mixer_lock);
   723 	} else {
   724 		SDL_mutexP(mixer_lock);
   725 		if ( channel[which].playing > 0 ) {
   726 			if(channel[which].expire > 0)
   727 				channel[which].expire += sdl_ticks - channel[which].paused;
   728 			channel[which].paused = 0;
   729 		}
   730 		SDL_mutexV(mixer_lock);
   731 	}
   732 }
   733 
   734 /* Change the group of a channel */
   735 int Mix_GroupChannel(int which, int tag)
   736 {
   737 	if ( which < 0 || which > num_channels )
   738 		return(0);
   739 
   740 	SDL_mutexP(mixer_lock);
   741 	channel[which].tag = tag;
   742 	SDL_mutexV(mixer_lock);
   743 	return(1);
   744 }
   745 
   746 /* Assign several consecutive channels to a group */
   747 int Mix_GroupChannels(int from, int to, int tag)
   748 {
   749 	int status = 0;
   750 	for( ; from <= to; ++ from ) {
   751 		status += Mix_GroupChannel(from, tag);
   752 	}
   753 	return(status);
   754 }
   755 
   756 /* Finds the first available channel in a group of channels */
   757 int Mix_GroupAvailable(int tag)
   758 {
   759 	int i;
   760 	for( i=0; i < num_channels; i ++ ) {
   761 		if ( (channel[i].tag==tag || tag==-1) && channel[i].playing<=0 )
   762 			return i;
   763 	}
   764 	return(-1);
   765 }
   766 
   767 int Mix_GroupCount(int tag)
   768 {
   769 	int count = 0;
   770 	int i;
   771 	for( i=0; i < num_channels; i ++ ) {
   772 		if ( channel[i].tag==tag || tag==-1 )
   773 			++ count;
   774 	}
   775 	return(count);
   776 }
   777 
   778 /* Finds the "oldest" sample playing in a group of channels */
   779 int Mix_GroupOldest(int tag)
   780 {
   781 	int chan = -1;
   782 	Uint32 mintime = SDL_GetTicks();
   783 	int i;
   784 	for( i=0; i < num_channels; i ++ ) {
   785 		if ( (channel[i].tag==tag || tag==-1) && channel[i].playing > 0 
   786 			 && channel[i].start_time <= mintime ) {
   787 			mintime = channel[i].start_time;
   788 			chan = i;
   789 		}
   790 	}
   791 	return(chan);
   792 }
   793 
   794 /* Finds the "most recent" (i.e. last) sample playing in a group of channels */
   795 int Mix_GroupNewer(int tag)
   796 {
   797 	int chan = -1;
   798 	Uint32 maxtime = 0;
   799 	int i;
   800 	for( i=0; i < num_channels; i ++ ) {
   801 		if ( (channel[i].tag==tag || tag==-1) && channel[i].playing > 0 
   802 			 && channel[i].start_time >= maxtime ) {
   803 			maxtime = channel[i].start_time;
   804 			chan = i;
   805 		}
   806 	}
   807 	return(chan);
   808 }