improve sfx management

pause/restore all game sounds when entering/leaving an ingame menu
stop all game sounds when the stage loop is over
This commit is contained in:
Andrei Alexeyev 2017-10-08 08:15:43 +03:00
parent 4d22777056
commit 4972f590aa
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
5 changed files with 167 additions and 34 deletions

View file

@ -24,6 +24,12 @@ typedef struct CurrentBGM {
extern CurrentBGM current_bgm;
typedef enum {
SNDGROUP_ALL,
SNDGROUP_MAIN,
SNDGROUP_UI,
} AudioBackendSoundGroup;
void audio_backend_init(void);
void audio_backend_shutdown(void);
bool audio_backend_initialized(void);
@ -35,9 +41,12 @@ void audio_backend_music_resume(void);
void audio_backend_music_stop(void);
void audio_backend_music_pause(void);
bool audio_backend_music_play(void *impl);
bool audio_backend_sound_play(void *impl);
bool audio_backend_sound_loop(void *impl);
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group);
bool audio_backend_sound_loop(void *impl, AudioBackendSoundGroup group);
bool audio_backend_sound_stop_loop(void *impl);
bool audio_backend_sound_pause_all(AudioBackendSoundGroup group);
bool audio_backend_sound_resume_all(AudioBackendSoundGroup group);
bool audio_backend_sound_stop_all(AudioBackendSoundGroup group);
void audio_init(void);
void audio_shutdown(void);
@ -47,9 +56,10 @@ void play_sound_cooldown(const char *name, int cooldown);
void play_loop(const char *name);
void play_ui_sound(const char *name);
void reset_sounds(void);
void pause_sounds(void);
void resume_sounds(void);
void stop_sounds(void);
void update_sounds(void); // checks if loops need to be stopped
void stop_looping_sounds(void); // force-stops all loops
int get_default_sfx_volume(const char *sfx);

View file

@ -18,19 +18,19 @@ static Hashtable *bgm_descriptions;
static Hashtable *sfx_volumes;
CurrentBGM current_bgm = { .name = NULL };
static void play_sound_internal(const char *name, bool unconditional, int cooldown) {
static void play_sound_internal(const char *name, bool is_ui, int cooldown) {
if(!audio_backend_initialized() || global.frameskip) {
return;
}
Sound *snd = get_sound(name);
if(!snd || (!unconditional && snd->lastplayframe + 3 + cooldown >= global.frames) || snd->islooping) {
if(!snd || (!is_ui && snd->lastplayframe + 3 + cooldown >= global.frames) || snd->islooping) {
return;
}
snd->lastplayframe = global.frames;
audio_backend_sound_play(snd->impl);
audio_backend_sound_play(snd->impl, is_ui ? SNDGROUP_UI : SNDGROUP_MAIN);
}
void play_sound(const char *name) {
@ -55,22 +55,22 @@ void play_loop(const char *name) {
if(!snd) {
return;
}
snd->lastplayframe = global.frames;
if(!snd->islooping) {
audio_backend_sound_loop(snd->impl);
audio_backend_sound_loop(snd->impl, SNDGROUP_MAIN);
snd->islooping = true;
}
}
void reset_sounds(void) {
Resource *snd;
for(HashtableIterator *i = hashtable_iter(resources.handlers[RES_SFX].mapping);
hashtable_iter_next(i, 0, (void**)&snd);) {
snd->sound->lastplayframe = 0;
if(snd->sound->islooping) {
audio_backend_sound_stop_loop(snd->sound->impl);
snd->sound->islooping = false;
audio_backend_sound_stop_loop(snd->sound->impl);
}
}
}
@ -86,15 +86,16 @@ void update_sounds(void) {
}
}
void stop_looping_sounds(void) {
Resource *snd;
for(HashtableIterator *i = hashtable_iter(resources.handlers[RES_SFX].mapping);
hashtable_iter_next(i, 0, (void**)&snd);) {
if(snd->sound->islooping) {
snd->sound->islooping = false;
audio_backend_sound_stop_loop(snd->sound->impl);
}
}
void pause_sounds(void) {
audio_backend_sound_pause_all(SNDGROUP_MAIN);
}
void resume_sounds(void) {
audio_backend_sound_resume_all(SNDGROUP_MAIN);
}
void stop_sounds(void) {
audio_backend_sound_stop_all(SNDGROUP_MAIN);
}
Sound* get_sound(const char *name) {

View file

@ -16,9 +16,17 @@
#define AUDIO_FREQ 44100
#define AUDIO_FORMAT MIX_DEFAULT_FORMAT
#define AUDIO_CHANNELS 100
#define UI_CHANNELS 4
#define UI_CHANNEL_GROUP 0
#define MAIN_CHANNEL_GROUP 1
static bool mixer_loaded = false;
static struct {
unsigned char first;
unsigned char num;
} groups[2];
const char *mixer_audio_exts[] = { ".bgm", ".wav", ".ogg", ".mp3", ".mod", ".xm", ".s3m",
".669", ".it", ".med", ".mid", ".flac", ".aiff", ".voc",
NULL };
@ -46,6 +54,7 @@ void audio_backend_init(void) {
}
int channels = Mix_AllocateChannels(AUDIO_CHANNELS);
if(!channels) {
log_warn("Unable to allocate any channels");
Mix_CloseAudio();
@ -54,9 +63,39 @@ void audio_backend_init(void) {
}
if(channels < AUDIO_CHANNELS) {
log_warn("Allocated only %d of %d channels", channels, AUDIO_CHANNELS);
log_warn("Allocated only %d out of %d channels", channels, AUDIO_CHANNELS);
}
int wanted_ui_channels = UI_CHANNELS;
if(wanted_ui_channels > channels / 2) {
wanted_ui_channels = channels / 2;
log_warn("Will not reserve more than %i channels for UI sounds (wanted %i channels)", wanted_ui_channels, UI_CHANNELS);
}
int ui_channels;
if((ui_channels = Mix_GroupChannels(0, wanted_ui_channels - 1, UI_CHANNEL_GROUP)) != wanted_ui_channels) {
log_warn("Assigned only %d out of %d channels to the UI group", ui_channels, wanted_ui_channels);
}
int wanted_main_channels = channels - ui_channels, main_channels;
if((main_channels = Mix_GroupChannels(ui_channels, ui_channels + wanted_main_channels - 1, MAIN_CHANNEL_GROUP)) != wanted_main_channels) {
log_warn("Assigned only %d out of %d channels to the main group", main_channels, wanted_main_channels);
}
int unused_channels = channels - ui_channels - main_channels;
if(unused_channels) {
log_warn("%i channels not used", unused_channels);
}
groups[UI_CHANNEL_GROUP].first = 0;
groups[UI_CHANNEL_GROUP].num = ui_channels;
groups[MAIN_CHANNEL_GROUP].first = ui_channels;
groups[MAIN_CHANNEL_GROUP].num = main_channels;
mixer_loaded = true;
audio_backend_set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
@ -87,10 +126,10 @@ void audio_backend_init(void) {
}
void audio_backend_shutdown(void) {
mixer_loaded = false;
Mix_CloseAudio();
Mix_Quit();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
mixer_loaded = false;
log_info("Audio subsystem uninitialized (SDL2_Mixer)");
}
@ -202,11 +241,33 @@ bool audio_backend_music_play(void *impl) {
return result;
}
bool audio_backend_sound_play(void *impl) {
static int translate_group(AudioBackendSoundGroup group, int defmixgroup) {
switch(group) {
case SNDGROUP_MAIN: return MAIN_CHANNEL_GROUP;
case SNDGROUP_UI: return UI_CHANNEL_GROUP;
default: return defmixgroup;
}
}
static int pick_channel(AudioBackendSoundGroup group, int defmixgroup) {
int mixgroup = translate_group(group, MAIN_CHANNEL_GROUP);
int channel = -1;
if((channel = Mix_GroupAvailable(mixgroup)) < 0) {
// all channels busy? try to override the oldest playing sound
if((channel = Mix_GroupOldest(mixgroup)) < 0) {
log_warn("No suitable channel available in group %i to play the sample on", mixgroup);
}
}
return channel;
}
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group) {
if(!mixer_loaded)
return false;
bool result = (Mix_PlayChannel(-1, ((MixerInternalSound*)impl)->ch, 0) != -1);
bool result = (Mix_PlayChannel(pick_channel(group, MAIN_CHANNEL_GROUP), ((MixerInternalSound*)impl)->ch, 0) != -1);
if(!result) {
log_warn("Mix_PlayChannel() failed: %s", Mix_GetError());
@ -215,12 +276,12 @@ bool audio_backend_sound_play(void *impl) {
return result;
}
bool audio_backend_sound_loop(void *impl) {
bool audio_backend_sound_loop(void *impl, AudioBackendSoundGroup group) {
if(!mixer_loaded)
return false;
MixerInternalSound *snd = (MixerInternalSound *)impl;
snd->loopchan = Mix_PlayChannel(-1, snd->ch, -1);
snd->loopchan = Mix_PlayChannel(pick_channel(group, MAIN_CHANNEL_GROUP), snd->ch, -1);
if(snd->loopchan == -1) {
log_warn("Mix_PlayChannel() failed: %s", Mix_GetError());
@ -242,3 +303,60 @@ bool audio_backend_sound_stop_loop(void *impl) {
return true;
}
bool audio_backend_sound_pause_all(AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
int mixgroup = translate_group(group, -1);
if(mixgroup == -1) {
Mix_Pause(-1);
} else {
// why is there no Mix_PauseGroup?
for(int i = groups[mixgroup].first; i < groups[mixgroup].first + groups[mixgroup].num; ++i) {
Mix_Pause(i);
}
}
return true;
}
bool audio_backend_sound_resume_all(AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
int mixgroup = translate_group(group, -1);
if(mixgroup == -1) {
Mix_Resume(-1);
} else {
// why is there no Mix_ResumeGroup?
for(int i = groups[mixgroup].first; i < groups[mixgroup].first + groups[mixgroup].num; ++i) {
Mix_Resume(i);
}
}
return true;
}
bool audio_backend_sound_stop_all(AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
int mixgroup = translate_group(group, -1);
if(mixgroup == -1) {
Mix_HaltChannel(-1);
} else {
Mix_HaltGroup(mixgroup);
}
return true;
}

View file

@ -15,11 +15,13 @@ void audio_backend_set_sfx_volume(float gain) {}
void audio_backend_set_bgm_volume(float gain) {}
bool audio_backend_music_is_paused(void) { return false; }
bool audio_backend_music_is_playing(void) { return false; }
void audio_backend_music_resume(void) { return; }
void audio_backend_music_resume(void) {}
void audio_backend_music_stop(void) {}
void audio_backend_music_pause(void) {}
bool audio_backend_music_play(void *impl) { return false; }
bool audio_backend_sound_play(void *impl) { return false; }
bool audio_backend_sound_loop(void *impl) {return false;}
bool audio_backend_sound_stop_loop(void *impl) {return false;}
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group) { return false; }
bool audio_backend_sound_loop(void *impl) { return false; }
bool audio_backend_sound_stop_loop(void *impl) { return false; }
bool audio_backend_sound_pause_all(AudioBackendSoundGroup group) { return false; }
bool audio_backend_sound_resume_all(AudioBackendSoundGroup group) { return false; }
bool audio_backend_sound_stop_all(AudioBackendSoundGroup group) { return false; }

View file

@ -203,16 +203,16 @@ static void stage_start(StageInfo *stage) {
void stage_pause(void) {
MenuData menu;
stop_bgm(false);
if(global.replaymode == REPLAY_PLAY) {
create_ingame_menu_replay(&menu);
} else {
create_ingame_menu(&menu);
}
stop_looping_sounds();
pause_sounds();
stop_bgm(false);
menu_loop(&menu);
resume_sounds();
resume_bgm();
}
@ -240,9 +240,10 @@ void stage_gameover(void) {
}
*/
pause_sounds();
stop_bgm(false);
stop_looping_sounds();
menu_loop(&menu);
resume_sounds();
/*
if(interrupt_bgm) {
@ -659,4 +660,5 @@ void stage_loop(StageInfo *stage) {
stage_free();
tsrand_switch(&global.rand_visual);
free_all_refs();
stop_sounds();
}