From 5b241d9fc5eeef42eea4f237f6ec7cb2b254c005 Mon Sep 17 00:00:00 2001 From: Stephane Peter Date: Tue, 26 Oct 1999 02:24:17 +0000 Subject: [PATCH] *** empty log message *** --- CHANGES | 3 + mixer.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++------- mixer.h | 40 +++++++++++- music.c | 108 +++++++++++++++++------------- 4 files changed, 276 insertions(+), 74 deletions(-) diff --git a/CHANGES b/CHANGES index 3c7a776a..07b419b7 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,9 @@ SOL - Added autoconf support SP - Added MP3 support using SMPEG SP - Added fading in/out of music and samples +SP - Added dynamic allocation of channels +SP - Added channel grouping functions +SP - Added expiration delay for samples Initial Key: SOL - Sam Lantinga (hercules@lokigames.com) diff --git a/mixer.c b/mixer.c index e19e1e2c..542d5299 100644 --- a/mixer.c +++ b/mixer.c @@ -22,6 +22,8 @@ slouken@devolution.com */ +/* $Id$ */ + #include #include #include @@ -42,11 +44,14 @@ static struct _Mix_Channel { Uint8 *samples; int volume; int looping; + int tag; + int expire; Mix_Fading fading; int fade_volume; int fade_length; Uint32 ticks_fade; -} channel[MIX_CHANNELS]; +} *channel = NULL; +static int num_channels; #define MUSIC_VOL 64 /* Music volume 0-64 */ @@ -54,7 +59,7 @@ static struct _Mix_Channel { extern int music_active; extern void music_mixer(void *udata, Uint8 *stream, int len); static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer; -static int num_channels; /* Holds the number of channels actually mixed, for debugging */ +static int mixed_channels = 0; /* Holds the number of channels actually mixed, for debugging */ static int reserved_channels = 0; void *music_data = NULL; @@ -62,16 +67,24 @@ void *music_data = NULL; static void mix_channels(void *udata, Uint8 *stream, int len) { int i, mixable, volume; + Uint32 sdl_ticks; /* Grab the channels we need to mix */ SDL_mutexP(mixer_lock); - num_channels = 0; - for ( i=0; i 0 && channel[i].expire < sdl_ticks ) { + /* Expiration delay for that channel is reached */ + channel[i].playing = 0; + channel[i].fading = MIX_NO_FADING; + channel[i].expire = -1; + } else if ( channel[i].fading != MIX_NO_FADING ) { + Uint32 ticks = sdl_ticks - channel[i].ticks_fade; if( ticks > channel[i].fade_length ) { if( channel[i].fading == MIX_FADING_OUT ) { channel[i].playing = 0; + channel[i].expire = -1; Mix_Volume(i, channel[i].fading); /* Restore the volume */ } channel[i].fading = MIX_NO_FADING; @@ -85,7 +98,7 @@ static void mix_channels(void *udata, Uint8 *stream, int len) } } if ( channel[i].playing && !channel[i].paused ) { - ++ num_channels; + ++ mixed_channels; mixable = channel[i].playing; if ( mixable > len ) { mixable = len; @@ -160,13 +173,18 @@ int Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize) SDL_DestroyMutex(mixer_lock); return(-1); } - + + num_channels = MIX_CHANNELS; + channel = (struct _Mix_Channel *) malloc(num_channels * sizeof(struct _Mix_Channel)); + /* Clear out the audio channels */ - for ( i=0; i num_channels ) { + /* Initialize the new channels */ + int i; + for(i=num_channels; i < numchans; i++) { + channel[i].chunk = NULL; + channel[i].playing = 0; + channel[i].looping = 0; + channel[i].volume = SDL_MIX_MAXVOLUME; + channel[i].tag = -1; + channel[i].expire = -1; + } + } + num_channels = numchans; + SDL_mutexV(mixer_lock); + return(num_channels); +} + /* Return the actual mixer parameters */ int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels) { @@ -304,7 +357,7 @@ void Mix_FreeChunk(Mix_Chunk *chunk) if ( chunk ) { /* Guarantee that this chunk isn't playing */ SDL_mutexP(mixer_lock); - for ( i=0; i MIX_CHANNELS) - num = MIX_CHANNELS; + if (num > num_channels) + num = num_channels; reserved_channels = num; return num; } /* Play an audio chunk on a specific channel. If the specified channel is -1, play on the first free channel. + 'ticks' is the number of milliseconds at most to play the sample, or -1 + if there is no limit. Returns which channel was used to play the sound. */ -int Mix_PlayChannel(int which, Mix_Chunk *chunk, int loops) +int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks) { int i; @@ -371,11 +426,11 @@ int Mix_PlayChannel(int which, Mix_Chunk *chunk, int loops) { /* If which is -1, play on the first free channel */ if ( which == -1 ) { - for ( i=reserved_channels; i0) ? (SDL_GetTicks() + ticks) : -1; } } SDL_mutexV(mixer_lock); @@ -398,8 +454,27 @@ int Mix_PlayChannel(int which, Mix_Chunk *chunk, int loops) return(which); } +/* Change the expiration delay for a channel */ +int Mix_ExpireChannel(int which, int ticks) +{ + int status = 0; + + if ( which == -1 ) { + int i; + for ( i=0; i < num_channels; ++ i ) { + status += Mix_ExpireChannel(i, ticks); + } + } else if ( which < num_channels ) { + SDL_mutexP(mixer_lock); + channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : -1; + SDL_mutexV(mixer_lock); + ++ status; + } + return(status); +} + /* Fade in a sound on a channel, over ms milliseconds */ -int Mix_FadeInChannel(int which, Mix_Chunk *chunk, int loops, int ms) +int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks) { int i; @@ -413,11 +488,11 @@ int Mix_FadeInChannel(int which, Mix_Chunk *chunk, int loops, int ms) { /* If which is -1, play on the first free channel */ if ( which == -1 ) { - for ( i=reserved_channels; i 0) ? (SDL_GetTicks()+ticks) : -1; } } SDL_mutexV(mixer_lock); @@ -452,10 +528,10 @@ int Mix_Volume(int which, int volume) if ( which == -1 ) { prev_volume = 0; - for ( i=0; i 0 ) { ++status; } @@ -569,6 +672,7 @@ void Mix_CloseAudio(void) Mix_HaltChannel(-1); SDL_CloseAudio(); SDL_DestroyMutex(mixer_lock); + channel = NULL; } --audio_opened; } @@ -577,10 +681,11 @@ void Mix_CloseAudio(void) /* Pause a particular channel (or all) */ void Mix_Pause(int which) { + /* TODO: Handle properly expiring samples */ if ( which == -1 ) { int i; - for ( i=0; i 0 ) { channel[i].paused = 1; } @@ -598,7 +703,7 @@ void Mix_Resume(int which) if ( which == -1 ) { int i; - for ( i=0; i 0 ) { channel[i].paused = 0; } @@ -609,3 +714,47 @@ void Mix_Resume(int which) } } } + +/* Change the group of a channel */ +int Mix_GroupChannel(int which, int tag) +{ + if ( which < 0 || which > num_channels ) + return(0); + + SDL_mutexP(mixer_lock); + channel[which].tag = tag; + SDL_mutexV(mixer_lock); + return(1); +} + +/* Assign several consecutive channels to a group */ +int Mix_GroupChannels(int from, int to, int tag) +{ + int status = 0; + for( ; from <= to; ++ from ) { + status += Mix_GroupChannel(from, tag); + } + return(status); +} + +/* Finds the first available channel in a group of channels */ +int Mix_GroupAvailable(int tag) +{ + int i; + for( i=0; i < num_channels; i ++ ) { + if ( (channel[i].tag==tag || tag==-1) && channel[i].playing<=0 ) + return i; + } + return(-1); +} + +int Mix_GroupCount(int tag) +{ + int count = 0; + int i; + for( i=0; i < num_channels; i ++ ) { + if ( channel[i].tag==tag || tag==-1 ) + ++ count; + } + return(count); +} diff --git a/mixer.h b/mixer.h index 420baebd..763c4248 100644 --- a/mixer.h +++ b/mixer.h @@ -64,6 +64,13 @@ typedef struct _Mix_Music Mix_Music; extern int Mix_OpenAudio(int frequency, Uint16 format, int channels, int chunksize); +/* Dynamically change the number of channels managed by the mixer. + If decreasing the number of channels, the upper channels are + stopped. + This function returns the new number of allocated channels. + */ +extern int Mix_AllocateChannels(int numchans); + /* Find out what the actual audio device parameters are. This function returns 1 if the audio has been opened, 0 otherwise. */ @@ -96,18 +103,39 @@ extern void *Mix_GetMusicHookData(void); */ extern int Mix_ReserveChannels(int num); +/* Channel grouping functions */ + +/* Attach a tag to a channel. A tag can be assigned to several mixer + channels, to form groups of channels. + If 'tag' is -1, the tag is removed (actually -1 is the tag used to + represent the group of all the channels). + Returns true if everything was OK. + */ +extern int Mix_GroupChannel(int which, int tag); +/* Assign several consecutive channels to a group */ +extern int Mix_GroupChannels(int from, int to, int tag); +/* Finds the first available channel in a group of channels */ +extern int Mix_GroupAvailable(int tag); +/* Returns the number of channels in a group. This is also a subtle + way to get the total number of channels when 'tag' is -1 + */ +extern int Mix_GroupCount(int tag); + /* Play an audio chunk on a specific channel. If the specified channel is -1, play on the first free channel. If 'loops' is greater than zero, loop the sound that many times. If 'loops' is -1, loop inifinitely (~65000 times). Returns which channel was used to play the sound. */ -extern int Mix_PlayChannel(int channel, Mix_Chunk *chunk, int loops); +#define Mix_PlayChannel(channel,chunk,loops) Mix_PlayChannelTimed(channel,chunk,loops,-1) +/* The same as above, but the sound is played at most 'ticks' milliseconds */ +extern int Mix_PlayChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ticks); extern int Mix_PlayMusic(Mix_Music *music, int loops); /* Fade in music or a channel over "ms" milliseconds, same semantics as the "Play" functions */ extern int Mix_FadeInMusic(Mix_Music *music, int loops, int ms); -extern int Mix_FadeInChannel(int channel, Mix_Chunk *chunk, int loops, int ms); +#define Mix_FadeInChannel(channel,chunk,loops,ms) Mix_FadeInChannelTimed(channel,chunk,loops,ms,-1) +extern int Mix_FadeInChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ms, int ticks); /* Set the volume in the range of 0-128 of a specific channel or chunk. If the specified channel is -1, set volume for all channels. @@ -120,13 +148,21 @@ extern int Mix_VolumeMusic(int volume); /* Halt playing of a particular channel */ extern int Mix_HaltChannel(int channel); +extern int Mix_HaltGroup(int tag); extern int Mix_HaltMusic(void); +/* Change the expiration delay for a particular channel. + The sample will stop playing after the 'ticks' milliseconds have elapsed, + or remove the expiration if 'ticks' is -1 +*/ +extern int Mix_ExpireChannel(int channel, int ticks); + /* Halt a channel, fading it out progressively till it's silent The ms parameter indicates the number of milliseconds the fading will take. */ extern int Mix_FadeOutChannel(int which, int ms); +extern int Mix_FadeOutGroup(int tag, int ms); extern int Mix_FadeOutMusic(int ms); /* Query the fading status of a channel */ diff --git a/music.c b/music.c index a39feb94..976d158a 100644 --- a/music.c +++ b/music.c @@ -53,11 +53,11 @@ static SDL_AudioSpec used_mixer; #endif int music_active = 1; +static int music_stopped = 0; static int music_loops = 0; static char *music_cmd = NULL; static int samplesize; static Mix_Music *music_playing = 0; -static SDL_mutex *music_lock; static int music_volume; static int music_swap8; static int music_swap16; @@ -95,19 +95,27 @@ struct _Mix_Music { }; static int timidity_ok; +static void lowlevel_halt(void); + /* Mixing function */ void music_mixer(void *udata, Uint8 *stream, int len) { int i; if ( music_playing ) { - if( music_playing->fading != MIX_NO_FADING ) { + if ( music_stopped ) { + /* To avoid concurrency problems and the use of mutexes, + the music is always stopped from the sound thread */ + lowlevel_halt(); /* This function sets music_playing to NULL */ + return; + } + if ( music_playing->fading != MIX_NO_FADING ) { Uint32 ticks = SDL_GetTicks() - music_playing->ticks_fade; if( ticks > music_playing->fade_length ) { if ( music_playing->fading == MIX_FADING_OUT ) { music_volume = music_playing->fade_volume; music_playing->fading = MIX_NO_FADING; - Mix_HaltMusic(); + lowlevel_halt(); return; } music_playing->fading = MIX_NO_FADING; @@ -253,9 +261,8 @@ int open_music(SDL_AudioSpec *mixer) used_mixer = *mixer; #endif music_playing = 0; + music_stopped = 0; - /* Initialize the music lock */ - music_lock = SDL_CreateMutex(); if ( music_error ) { return(-1); } @@ -368,7 +375,7 @@ void Mix_FreeMusic(Mix_Music *music) { if ( music ) { /* Caution: If music is playing, mixer will crash */ - if ( music == music_playing ) { + if ( music == music_playing && !music_stopped ) { if ( music->fading == MIX_FADING_OUT ) { /* Wait for the fade out to finish */ while(music->fading == MIX_FADING_OUT) @@ -420,7 +427,7 @@ int Mix_PlayMusic(Mix_Music *music, int loops) return(-1); } /* If the current music is fading out, wait for the fade to complete */ - while ( music_playing && music_playing->fading==MIX_FADING_OUT ) { + while ( music_playing && !music_stopped && music_playing->fading==MIX_FADING_OUT ) { SDL_Delay(100); } switch (music->type) { @@ -463,6 +470,7 @@ int Mix_PlayMusic(Mix_Music *music, int loops) return(-1); } music_active = 1; + music_stopped = 0; music_playing = music; music_playing->fading = MIX_NO_FADING; return(0); @@ -496,7 +504,7 @@ int Mix_VolumeMusic(int volume) volume = SDL_MIX_MAXVOLUME; } music_volume = volume; - if ( music_playing ) { + if ( music_playing && !music_stopped ) { switch (music_playing->type) { #ifdef CMD_MUSIC case MUS_CMD: @@ -531,58 +539,63 @@ int Mix_VolumeMusic(int volume) return(prev_volume); } -/* Halt playing of music */ -int Mix_HaltMusic(void) +static void lowlevel_halt(void) { - /* This function can be called both from the main program thread - and from the SDL audio thread (when fading), so we need to ensure - that only one thread is running it at once */ - SDL_mutexP(music_lock); - if ( music_playing ) { - switch (music_playing->type) { + switch (music_playing->type) { #ifdef CMD_MUSIC - case MUS_CMD: - MusicCMD_Stop(music_playing->data.cmd); - break; + case MUS_CMD: + MusicCMD_Stop(music_playing->data.cmd); + break; #endif #ifdef WAV_MUSIC - case MUS_WAV: - WAVStream_Stop(); - break; + case MUS_WAV: + WAVStream_Stop(); + break; #endif #ifdef MOD_MUSIC - case MUS_MOD: - Player_Stop(); - break; + case MUS_MOD: + Player_Stop(); + break; #endif #ifdef MID_MUSIC - case MUS_MID: - Timidity_Stop(); - break; + case MUS_MID: + Timidity_Stop(); + break; #endif #ifdef MP3_MUSIC - case MUS_MP3: - SMPEG_stop(music_playing->data.mp3); - break; + case MUS_MP3: + SMPEG_stop(music_playing->data.mp3); + break; #endif - default: - /* Unknown music type?? */ - SDL_mutexV(music_lock); - return(-1); - } - if(music_playing->fading != MIX_NO_FADING) /* Restore volume */ - music_volume = music_playing->fade_volume; - music_playing->fading = MIX_NO_FADING; - music_playing = 0; + default: + /* Unknown music type?? */ + return; + } + if(music_playing->fading != MIX_NO_FADING) /* Restore volume */ + music_volume = music_playing->fade_volume; + music_playing->fading = MIX_NO_FADING; + music_playing = 0; + music_active = 0; + music_stopped = 0; +} + +/* Halt playing of music */ +int Mix_HaltMusic(void) +{ + if ( music_playing && !music_stopped ) { + /* Mark the music to be stopped from the sound thread */ + music_stopped = 1; + /* Wait for it to be actually stopped */ + while ( music_playing ) + SDL_Delay(10); } - SDL_mutexV(music_lock); return(0); } /* Progressively stop the music */ int Mix_FadeOutMusic(int ms) { - if ( music_playing && music_playing->fading==MIX_NO_FADING ) { + if ( music_playing && !music_stopped && music_playing->fading==MIX_NO_FADING ) { if ( music_volume>0 ) { music_playing->fading = MIX_FADING_OUT; music_playing->fade_volume = music_volume; @@ -596,7 +609,7 @@ int Mix_FadeOutMusic(int ms) Mix_Fading Mix_FadingMusic(void) { - if( music_playing ) + if( music_playing && !music_stopped ) return music_playing->fading; return MIX_NO_FADING; } @@ -604,7 +617,7 @@ Mix_Fading Mix_FadingMusic(void) /* Pause/Resume the music stream */ void Mix_PauseMusic(void) { - if ( music_playing ) { + if ( music_playing && !music_stopped ) { switch ( music_playing->type ) { #ifdef CMD_MUSIC case MUS_CMD: @@ -623,7 +636,7 @@ void Mix_PauseMusic(void) void Mix_ResumeMusic(void) { - if ( music_playing ) { + if ( music_playing && !music_stopped ) { switch ( music_playing->type ) { #ifdef CMD_MUSIC case MUS_CMD: @@ -642,13 +655,14 @@ void Mix_ResumeMusic(void) void Mix_RewindMusic(void) { - if ( music_playing ) { + if ( music_playing && !music_stopped ) { switch ( music_playing->type ) { #ifdef MP3_MUSIC case MUS_MP3: SMPEG_rewind(music_playing->data.mp3); break; #endif + /* TODO: Implement this for other music backends */ } } } @@ -656,7 +670,7 @@ void Mix_RewindMusic(void) /* Check the status of the music */ int Mix_PlayingMusic(void) { - if ( music_playing ) { + if ( music_playing && !music_stopped ) { switch (music_playing->type) { #ifdef CMD_MUSIC case MUS_CMD: