basic way to stop a specific sound instance
This commit is contained in:
parent
69095837a4
commit
d913612612
5 changed files with 152 additions and 68 deletions
|
@ -28,9 +28,9 @@ static struct enqueued_sound {
|
|||
bool replace;
|
||||
} *sound_queue;
|
||||
|
||||
static void play_sound_internal(const char *name, bool is_ui, int cooldown, bool replace, int delay) {
|
||||
static SoundID play_sound_internal(const char *name, bool is_ui, int cooldown, bool replace, int delay) {
|
||||
if(!audio_output_works() || global.frameskip) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(delay > 0) {
|
||||
|
@ -40,23 +40,31 @@ static void play_sound_internal(const char *name, bool is_ui, int cooldown, bool
|
|||
s->cooldown = cooldown;
|
||||
s->replace = replace;
|
||||
list_push(&sound_queue, s);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(taisei_is_skip_mode_enabled()) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Sound *snd = get_sound(name);
|
||||
|
||||
if(!snd || (!is_ui && snd->lastplayframe + 3 + cooldown >= global.frames)) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
snd->lastplayframe = global.frames;
|
||||
|
||||
(replace ? B.sound_play_or_restart : B.sound_play)
|
||||
(snd->impl, is_ui ? SNDGROUP_UI : SNDGROUP_MAIN);
|
||||
AudioBackendSoundGroup group = is_ui ? SNDGROUP_UI : SNDGROUP_MAIN;
|
||||
SoundID sid;
|
||||
|
||||
if(replace) {
|
||||
sid = B.sound_play_or_restart(snd->impl, group);
|
||||
} else {
|
||||
sid = B.sound_play(snd->impl, group);
|
||||
}
|
||||
|
||||
return sid;
|
||||
}
|
||||
|
||||
static void* discard_enqueued_sound(List **queue, List *vsnd, void *arg) {
|
||||
|
@ -76,12 +84,12 @@ static void* play_enqueued_sound(struct enqueued_sound **queue, struct enqueued_
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void play_sound(const char *name) {
|
||||
play_sound_internal(name, false, 0, false, 0);
|
||||
SoundID play_sound(const char *name) {
|
||||
return play_sound_internal(name, false, 0, false, 0);
|
||||
}
|
||||
|
||||
void play_sound_ex(const char *name, int cooldown, bool replace) {
|
||||
play_sound_internal(name, false, cooldown, replace, 0);
|
||||
SoundID play_sound_ex(const char *name, int cooldown, bool replace) {
|
||||
return play_sound_internal(name, false, cooldown, replace, 0);
|
||||
}
|
||||
|
||||
void play_sound_delayed(const char *name, int cooldown, bool replace, int delay) {
|
||||
|
@ -92,6 +100,17 @@ void play_ui_sound(const char *name) {
|
|||
play_sound_internal(name, true, 0, true, 0);
|
||||
}
|
||||
|
||||
void stop_sound(SoundID sid) {
|
||||
if(sid) {
|
||||
B.sound_stop_id(sid);
|
||||
}
|
||||
}
|
||||
|
||||
void replace_sound(SoundID sid, const char *name) {
|
||||
stop_sound(sid);
|
||||
play_sound(name);
|
||||
}
|
||||
|
||||
void play_loop(const char *name) {
|
||||
if(!audio_output_works() || global.frameskip) {
|
||||
return;
|
||||
|
|
|
@ -48,6 +48,8 @@ typedef struct CurrentBGM {
|
|||
Music *music;
|
||||
} CurrentBGM;
|
||||
|
||||
typedef uint64_t SoundID;
|
||||
|
||||
extern CurrentBGM current_bgm;
|
||||
|
||||
void audio_init(void);
|
||||
|
@ -55,11 +57,13 @@ void audio_shutdown(void);
|
|||
bool audio_output_works(void);
|
||||
void audio_music_set_position(double pos);
|
||||
|
||||
void play_sound(const char *name) attr_nonnull(1);
|
||||
void play_sound_ex(const char *name, int cooldown, bool replace) attr_nonnull(1);
|
||||
SoundID play_sound(const char *name) attr_nonnull(1);
|
||||
SoundID play_sound_ex(const char *name, int cooldown, bool replace) attr_nonnull(1);
|
||||
void play_sound_delayed(const char *name, int cooldown, bool replace, int delay) attr_nonnull(1);
|
||||
void play_loop(const char *name) attr_nonnull(1);
|
||||
void play_ui_sound(const char *name) attr_nonnull(1);
|
||||
void stop_sound(SoundID sid);
|
||||
void replace_sound(SoundID sid, const char *name) attr_nonnull(2);
|
||||
void reset_sounds(void);
|
||||
void pause_sounds(void);
|
||||
void resume_sounds(void);
|
||||
|
|
|
@ -34,15 +34,16 @@ typedef struct AudioFuncs {
|
|||
bool (*output_works)(void);
|
||||
bool (*set_sfx_volume)(float gain);
|
||||
bool (*shutdown)(void);
|
||||
bool (*sound_loop)(SoundImpl *snd, AudioBackendSoundGroup group);
|
||||
SoundID (*sound_loop)(SoundImpl *snd, AudioBackendSoundGroup group);
|
||||
bool (*sound_pause_all)(AudioBackendSoundGroup group);
|
||||
bool (*sound_play)(SoundImpl *snd, AudioBackendSoundGroup group);
|
||||
bool (*sound_play_or_restart)(SoundImpl *snd, AudioBackendSoundGroup group);
|
||||
SoundID (*sound_play)(SoundImpl *snd, AudioBackendSoundGroup group);
|
||||
SoundID (*sound_play_or_restart)(SoundImpl *snd, AudioBackendSoundGroup group);
|
||||
bool (*sound_resume_all)(AudioBackendSoundGroup group);
|
||||
bool (*sound_set_global_volume)(double gain);
|
||||
bool (*sound_set_volume)(SoundImpl *snd, double vol);
|
||||
bool (*sound_stop_all)(AudioBackendSoundGroup group);
|
||||
bool (*sound_stop_loop)(SoundImpl *snd);
|
||||
bool (*sound_stop_id)(SoundID);
|
||||
const char* const* (*get_supported_music_exts)(uint *out_numexts);
|
||||
const char* const* (*get_supported_sound_exts)(uint *out_numexts);
|
||||
MusicImpl* (*music_load)(const char *vfspath);
|
||||
|
|
|
@ -19,6 +19,8 @@ static bool audio_null_init(void) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#define FAKE_SOUND_ID ((SoundID)1)
|
||||
|
||||
static bool audio_null_music_fade(double fadetime) { return true; }
|
||||
static bool audio_null_music_is_paused(void) { return false; }
|
||||
static bool audio_null_music_is_playing(void) { return false; }
|
||||
|
@ -31,15 +33,16 @@ static bool audio_null_music_set_position(double pos) { return true; }
|
|||
static bool audio_null_music_stop(void) { return true; }
|
||||
static bool audio_null_output_works(void) { return false; }
|
||||
static bool audio_null_shutdown(void) { return true; }
|
||||
static bool audio_null_sound_loop(SoundImpl *impl, AudioBackendSoundGroup group) { return true; }
|
||||
static SoundID audio_null_sound_loop(SoundImpl *impl, AudioBackendSoundGroup group) { return FAKE_SOUND_ID; }
|
||||
static bool audio_null_sound_pause_all(AudioBackendSoundGroup group) { return true; }
|
||||
static bool audio_null_sound_play(SoundImpl *impl, AudioBackendSoundGroup group) { return true; }
|
||||
static bool audio_null_sound_play_or_restart(SoundImpl *impl, AudioBackendSoundGroup group) { return true; }
|
||||
static SoundID audio_null_sound_play(SoundImpl *impl, AudioBackendSoundGroup group) { return FAKE_SOUND_ID; }
|
||||
static SoundID audio_null_sound_play_or_restart(SoundImpl *impl, AudioBackendSoundGroup group) { return FAKE_SOUND_ID; }
|
||||
static bool audio_null_sound_resume_all(AudioBackendSoundGroup group) { return true; }
|
||||
static bool audio_null_sound_set_global_volume(double gain) { return true; }
|
||||
static bool audio_null_sound_set_volume(SoundImpl *snd, double gain) { return true; }
|
||||
static bool audio_null_sound_stop_all(AudioBackendSoundGroup group) { return true; }
|
||||
static bool audio_null_sound_stop_loop(SoundImpl *impl) { return true; }
|
||||
static bool audio_null_sound_stop_id(SoundID sid) { return true; }
|
||||
|
||||
static const char* const* audio_null_get_supported_exts(uint *out_numexts) { *out_numexts = 0; return NULL; }
|
||||
static MusicImpl* audio_null_music_load(const char *vfspath) { return NULL; }
|
||||
|
@ -77,6 +80,7 @@ AudioBackend _a_backend_null = {
|
|||
.sound_set_volume = audio_null_sound_set_volume,
|
||||
.sound_stop_all = audio_null_sound_stop_all,
|
||||
.sound_stop_loop = audio_null_sound_stop_loop,
|
||||
.sound_stop_id = audio_null_sound_stop_id,
|
||||
.sound_unload = audio_null_sound_unload,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -35,11 +35,12 @@ typedef float sample_t;
|
|||
#error Audio format not recognized
|
||||
#endif
|
||||
|
||||
// I needed to add this for supporting loop sounds since Mixer doesn’t remember
|
||||
// what channel a sound is playing on. - laochailan
|
||||
|
||||
struct SoundImpl {
|
||||
Mix_Chunk *ch;
|
||||
|
||||
// I needed to add this for supporting loop sounds since Mixer doesn’t remember
|
||||
// what channel a sound is playing on. - laochailan
|
||||
int loopchan; // channel the sound may be looping on. -1 if not looping
|
||||
int playchan; // channel the sound was last played on (looping does NOT set this). -1 if never played
|
||||
};
|
||||
|
@ -50,12 +51,18 @@ struct MusicImpl {
|
|||
};
|
||||
|
||||
static struct {
|
||||
uchar first;
|
||||
uchar num;
|
||||
} groups[2];
|
||||
Mix_Music *next_loop;
|
||||
double next_loop_point;
|
||||
uint32_t play_counter;
|
||||
uint32_t chan_play_ids[AUDIO_CHANNELS];
|
||||
|
||||
static Mix_Music *next_loop;
|
||||
static double next_loop_point;
|
||||
struct {
|
||||
uchar first;
|
||||
uchar num;
|
||||
} groups[2];
|
||||
} mixer;
|
||||
|
||||
#define IS_VALID_CHANNEL(chan) ((uint)(chan) < AUDIO_CHANNELS)
|
||||
|
||||
static bool audio_sdl2mixer_init(void) {
|
||||
if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
|
@ -129,10 +136,10 @@ static bool audio_sdl2mixer_init(void) {
|
|||
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.groups[UI_CHANNEL_GROUP].first = 0;
|
||||
mixer.groups[UI_CHANNEL_GROUP].num = ui_channels;
|
||||
mixer.groups[MAIN_CHANNEL_GROUP].first = ui_channels;
|
||||
mixer.groups[MAIN_CHANNEL_GROUP].num = main_channels;
|
||||
|
||||
B.sound_set_global_volume(config_get_float(CONFIG_SFX_VOLUME));
|
||||
B.music_set_global_volume(config_get_float(CONFIG_BGM_VOLUME));
|
||||
|
@ -195,15 +202,15 @@ static void mixer_music_finished(void) {
|
|||
// XXX: there may be a race condition in here
|
||||
// probably should protect next_loop with a mutex
|
||||
|
||||
log_debug("%s stopped playing", next_loop_point ? "Loop" : "Intro");
|
||||
log_debug("%s stopped playing", mixer.next_loop_point ? "Loop" : "Intro");
|
||||
|
||||
if(next_loop) {
|
||||
if(Mix_PlayMusic(next_loop, next_loop_point ? 0 : -1) == -1) {
|
||||
if(mixer.next_loop) {
|
||||
if(Mix_PlayMusic(mixer.next_loop, mixer.next_loop_point ? 0 : -1) == -1) {
|
||||
log_error("Mix_PlayMusic() failed: %s", Mix_GetError());
|
||||
} else if(next_loop_point >= 0) {
|
||||
Mix_SetMusicPosition(next_loop_point);
|
||||
} else if(mixer.next_loop_point >= 0) {
|
||||
Mix_SetMusicPosition(mixer.next_loop_point);
|
||||
} else {
|
||||
next_loop = NULL;
|
||||
mixer.next_loop = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,11 +255,11 @@ static bool audio_sdl2mixer_music_play(MusicImpl *imus) {
|
|||
Mix_HaltMusic();
|
||||
|
||||
mmus = imus->loop;
|
||||
next_loop_point = imus->loop_point;
|
||||
mixer.next_loop_point = imus->loop_point;
|
||||
|
||||
if(next_loop_point >= 0) {
|
||||
if(mixer.next_loop_point >= 0) {
|
||||
loops = 0;
|
||||
next_loop = imus->loop;
|
||||
mixer.next_loop = imus->loop;
|
||||
Mix_HookMusicFinished(mixer_music_finished);
|
||||
} else {
|
||||
loops = -1;
|
||||
|
@ -310,43 +317,71 @@ static int pick_channel(AudioBackendSoundGroup group, int defmixgroup) {
|
|||
return channel;
|
||||
}
|
||||
|
||||
static int audio_sdl2mixer_sound_play_on_channel(int chan, SoundImpl *isnd) {
|
||||
chan = Mix_PlayChannel(chan, isnd->ch, 0);
|
||||
INLINE SoundID make_sound_id(int chan, uint32_t play_id) {
|
||||
assert(IS_VALID_CHANNEL(chan));
|
||||
return ((uint32_t)chan + 1) | ((uint64_t)play_id << 32ull);
|
||||
}
|
||||
|
||||
Mix_UnregisterAllEffects(chan);
|
||||
if(chan < 0) {
|
||||
log_error("Mix_PlayChannel() failed: %s", Mix_GetError());
|
||||
return false;
|
||||
INLINE void unpack_sound_id(SoundID sid, int *chan, uint32_t *play_id) {
|
||||
int ch = (int)((uint32_t)(sid & 0xffffffff) - 1);
|
||||
assert(IS_VALID_CHANNEL(ch));
|
||||
*chan = ch;
|
||||
*play_id = sid >> 32ull;
|
||||
}
|
||||
|
||||
INLINE int get_soundid_chan(SoundID sid) {
|
||||
int chan;
|
||||
uint32_t play_id;
|
||||
unpack_sound_id(sid, &chan, &play_id);
|
||||
|
||||
if(mixer.chan_play_ids[chan] == play_id) {
|
||||
return chan;
|
||||
}
|
||||
|
||||
isnd->playchan = chan;
|
||||
return true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_play(SoundImpl *isnd, AudioBackendSoundGroup group) {
|
||||
return audio_sdl2mixer_sound_play_on_channel(pick_channel(group, MAIN_CHANNEL_GROUP), isnd);
|
||||
static SoundID play_sound_tracked(uint32_t chan, SoundImpl *isnd, int loops) {
|
||||
assert(IS_VALID_CHANNEL(chan));
|
||||
int actual_chan = Mix_PlayChannel(chan, isnd->ch, loops);
|
||||
|
||||
if(actual_chan < 0) {
|
||||
log_error("Mix_PlayChannel() failed: %s", Mix_GetError());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mix_UnregisterAllEffects(actual_chan);
|
||||
|
||||
if(loops) {
|
||||
isnd->loopchan = actual_chan;
|
||||
} else {
|
||||
isnd->playchan = chan;
|
||||
}
|
||||
|
||||
assert(IS_VALID_CHANNEL(actual_chan));
|
||||
chan = (uint32_t)actual_chan;
|
||||
uint32_t play_id = ++mixer.play_counter;
|
||||
mixer.chan_play_ids[chan] = play_id;
|
||||
|
||||
return make_sound_id(chan, play_id);
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_play_or_restart(SoundImpl *isnd, AudioBackendSoundGroup group) {
|
||||
static SoundID audio_sdl2mixer_sound_play(SoundImpl *isnd, AudioBackendSoundGroup group) {
|
||||
return play_sound_tracked(pick_channel(group, MAIN_CHANNEL_GROUP), isnd, 0);
|
||||
}
|
||||
|
||||
static SoundID audio_sdl2mixer_sound_play_or_restart(SoundImpl *isnd, AudioBackendSoundGroup group) {
|
||||
int chan = isnd->playchan;
|
||||
|
||||
if(chan < 0 || Mix_GetChunk(chan) != isnd->ch) {
|
||||
chan = pick_channel(group, MAIN_CHANNEL_GROUP);
|
||||
}
|
||||
|
||||
return audio_sdl2mixer_sound_play_on_channel(chan, isnd);
|
||||
return play_sound_tracked(chan, isnd, 0);
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_loop(SoundImpl *isnd, AudioBackendSoundGroup group) {
|
||||
isnd->loopchan = Mix_PlayChannel(pick_channel(group, MAIN_CHANNEL_GROUP), isnd->ch, -1);
|
||||
Mix_UnregisterAllEffects(isnd->loopchan);
|
||||
|
||||
if(isnd->loopchan == -1) {
|
||||
log_error("Mix_PlayChannel() failed: %s", Mix_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
static SoundID audio_sdl2mixer_sound_loop(SoundImpl *isnd, AudioBackendSoundGroup group) {
|
||||
return play_sound_tracked(pick_channel(group, MAIN_CHANNEL_GROUP), isnd, -1);
|
||||
}
|
||||
|
||||
// XXX: This custom fading effect circumvents https://bugzilla.libsdl.org/show_bug.cgi?id=2904
|
||||
|
@ -371,19 +406,39 @@ static void custom_fadeout_proc(int chan, void *stream, int len, void *udata) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_stop_loop(SoundImpl *isnd) {
|
||||
if(isnd->loopchan == -1) {
|
||||
static bool fade_channel(int chan, int fadeout) {
|
||||
assert(IS_VALID_CHANNEL(chan));
|
||||
|
||||
if(mixer.chan_play_ids[chan] == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomFadeout *effect = calloc(1, sizeof(CustomFadeout));
|
||||
effect->counter = 0;
|
||||
effect->duration = LOOPFADEOUT*AUDIO_FREQ/1000;
|
||||
Mix_ExpireChannel(isnd->loopchan, LOOPFADEOUT);
|
||||
Mix_RegisterEffect(isnd->loopchan, custom_fadeout_proc, custom_fadeout_free, effect);
|
||||
effect->duration = (fadeout * AUDIO_FREQ) / 1000;
|
||||
Mix_ExpireChannel(chan, fadeout);
|
||||
Mix_RegisterEffect(chan, custom_fadeout_proc, custom_fadeout_free, effect);
|
||||
mixer.chan_play_ids[chan] = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_stop_loop(SoundImpl *isnd) {
|
||||
if(isnd->loopchan < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return fade_channel(isnd->loopchan, LOOPFADEOUT);
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_stop_id(SoundID sid) {
|
||||
int chan = get_soundid_chan(sid);
|
||||
|
||||
if(chan >= 0) {
|
||||
return fade_channel(chan, LOOPFADEOUT * 3);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool audio_sdl2mixer_sound_pause_all(AudioBackendSoundGroup group) {
|
||||
|
@ -394,7 +449,7 @@ static bool audio_sdl2mixer_sound_pause_all(AudioBackendSoundGroup group) {
|
|||
} else {
|
||||
// why is there no Mix_PauseGroup?
|
||||
|
||||
for(int i = groups[mixgroup].first; i < groups[mixgroup].first + groups[mixgroup].num; ++i) {
|
||||
for(int i = mixer.groups[mixgroup].first; i < mixer.groups[mixgroup].first + mixer.groups[mixgroup].num; ++i) {
|
||||
Mix_Pause(i);
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +466,7 @@ static bool audio_sdl2mixer_sound_resume_all(AudioBackendSoundGroup group) {
|
|||
} else {
|
||||
// why is there no Mix_ResumeGroup?
|
||||
|
||||
for(int i = groups[mixgroup].first; i < groups[mixgroup].first + groups[mixgroup].num; ++i) {
|
||||
for(int i = mixer.groups[mixgroup].first; i < mixer.groups[mixgroup].first + mixer.groups[mixgroup].num; ++i) {
|
||||
Mix_Resume(i);
|
||||
}
|
||||
}
|
||||
|
@ -633,6 +688,7 @@ AudioBackend _a_backend_sdl2mixer = {
|
|||
.sound_set_volume = audio_sdl2mixer_sound_set_volume,
|
||||
.sound_stop_all = audio_sdl2mixer_sound_stop_all,
|
||||
.sound_stop_loop = audio_sdl2mixer_sound_stop_loop,
|
||||
.sound_stop_id = audio_sdl2mixer_sound_stop_id,
|
||||
.sound_unload = audio_sdl2mixer_sound_unload,
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue