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:
parent
4d22777056
commit
4972f590aa
5 changed files with 167 additions and 34 deletions
18
src/audio.h
18
src/audio.h
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
10
src/stage.c
10
src/stage.c
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue