mixer: support looping bgms with a separate intro
This commit is contained in:
parent
8965ad4cb2
commit
05954b2764
4 changed files with 98 additions and 25 deletions
|
@ -19,7 +19,7 @@
|
|||
|
||||
static bool mixer_loaded = false;
|
||||
|
||||
const char *mixer_audio_exts[] = { ".wav", ".ogg", ".mp3", ".mod", ".xm", ".s3m",
|
||||
const char *mixer_audio_exts[] = { ".bgm", ".wav", ".ogg", ".mp3", ".mod", ".xm", ".s3m",
|
||||
".669", ".it", ".med", ".mid", ".flac", ".aiff", ".voc",
|
||||
NULL };
|
||||
|
||||
|
@ -109,8 +109,8 @@ void audio_backend_set_bgm_volume(float gain) {
|
|||
Mix_VolumeMusic(gain * MIX_MAX_VOLUME);
|
||||
}
|
||||
|
||||
char* audio_mixer_sound_path(const char *prefix, const char *name) {
|
||||
for(const char **ext = mixer_audio_exts; *ext; ++ext) {
|
||||
char* audio_mixer_sound_path(const char *prefix, const char *name, bool isbgm) {
|
||||
for(const char **ext = mixer_audio_exts + !isbgm; *ext; ++ext) {
|
||||
char *p = strjoin(prefix, name, *ext, NULL);
|
||||
|
||||
if(vfs_query(p).exists) {
|
||||
|
@ -124,16 +124,31 @@ char* audio_mixer_sound_path(const char *prefix, const char *name) {
|
|||
}
|
||||
|
||||
bool audio_mixer_check_sound_path(const char *path, bool isbgm) {
|
||||
if(strstartswith(resource_util_filename(path), "bgm_") == isbgm) {
|
||||
return strendswith_any(path, mixer_audio_exts);
|
||||
}
|
||||
return strendswith_any(path, mixer_audio_exts + !isbgm);
|
||||
}
|
||||
|
||||
return false;
|
||||
static Mix_Music *next_loop;
|
||||
|
||||
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("Intro stopped playing");
|
||||
|
||||
if(next_loop) {
|
||||
if(Mix_PlayMusic(next_loop, -1) == -1) {
|
||||
log_warn("Mix_PlayMusic() failed: %s", Mix_GetError());
|
||||
} else {
|
||||
next_loop = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_backend_music_stop(void) {
|
||||
if(mixer_loaded)
|
||||
if(mixer_loaded) {
|
||||
Mix_HookMusicFinished(NULL);
|
||||
Mix_HaltMusic();
|
||||
}
|
||||
}
|
||||
|
||||
bool audio_backend_music_is_paused(void) {
|
||||
|
@ -145,20 +160,40 @@ bool audio_backend_music_is_playing(void) {
|
|||
}
|
||||
|
||||
void audio_backend_music_resume(void) {
|
||||
if(mixer_loaded)
|
||||
if(mixer_loaded) {
|
||||
Mix_HookMusicFinished(mixer_music_finished);
|
||||
Mix_ResumeMusic();
|
||||
}
|
||||
}
|
||||
|
||||
void audio_backend_music_pause(void) {
|
||||
if(mixer_loaded)
|
||||
if(mixer_loaded) {
|
||||
Mix_HookMusicFinished(NULL);
|
||||
Mix_PauseMusic();
|
||||
}
|
||||
}
|
||||
|
||||
bool audio_backend_music_play(void *impl) {
|
||||
if(!mixer_loaded)
|
||||
return false;
|
||||
|
||||
bool result = (Mix_PlayMusic((Mix_Music*)impl, -1) != -1);
|
||||
MixerInternalMusic *imus = impl;
|
||||
Mix_Music *mmus;
|
||||
int loops;
|
||||
|
||||
if(imus->intro) {
|
||||
next_loop = imus->loop;
|
||||
mmus = imus->intro;
|
||||
loops = 0;
|
||||
assert(next_loop != NULL);
|
||||
Mix_HookMusicFinished(mixer_music_finished);
|
||||
} else {
|
||||
mmus = imus->loop;
|
||||
loops = -1;
|
||||
Mix_HookMusicFinished(NULL);
|
||||
}
|
||||
|
||||
bool result = (Mix_PlayMusic(mmus, loops) != -1);
|
||||
|
||||
if(!result) {
|
||||
log_warn("Mix_PlayMusic() failed: %s", Mix_GetError());
|
||||
|
@ -193,7 +228,6 @@ bool audio_backend_sound_loop(void *impl) {
|
|||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool audio_backend_sound_stop_loop(void *impl) {
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <SDL_mixer.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// I needed to add this for supporting loop sounds since Mixer doesn’t remember
|
||||
// what channel a sound is playing on.
|
||||
|
||||
|
@ -16,3 +19,10 @@ typedef struct {
|
|||
int loopchan; // channel the sound may be looping on. -1 if not looping
|
||||
} MixerInternalSound;
|
||||
|
||||
typedef struct MixerInternalMusic {
|
||||
Mix_Music *intro;
|
||||
Mix_Music *loop;
|
||||
} MixerInternalMusic;
|
||||
|
||||
char* audio_mixer_sound_path(const char *prefix, const char *name, bool isbgm);
|
||||
bool audio_mixer_check_sound_path(const char *path, bool isbgm);
|
||||
|
|
|
@ -10,20 +10,22 @@
|
|||
#include <SDL_mixer.h>
|
||||
|
||||
#include "resource.h"
|
||||
#include "sfx.h"
|
||||
|
||||
char* audio_mixer_sound_path(const char *prefix, const char *name);
|
||||
bool audio_mixer_check_sound_path(const char *path, bool isbgm);
|
||||
#include "bgm.h"
|
||||
#include "audio_mixer.h"
|
||||
|
||||
char* music_path(const char *name) {
|
||||
return audio_mixer_sound_path(BGM_PATH_PREFIX, name);
|
||||
return audio_mixer_sound_path(BGM_PATH_PREFIX, name, true);
|
||||
}
|
||||
|
||||
bool check_music_path(const char *path) {
|
||||
return strstartswith(path, BGM_PATH_PREFIX) && audio_mixer_check_sound_path(path, true);
|
||||
}
|
||||
|
||||
void* load_music_begin(const char *path, unsigned int flags) {
|
||||
static Mix_Music* load_mix_music(const char *path) {
|
||||
if(!path) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SDL_RWops *rwops = vfs_open(path, VFS_MODE_READ | VFS_MODE_SEEKABLE);
|
||||
|
||||
if(!rwops) {
|
||||
|
@ -38,8 +40,35 @@ void* load_music_begin(const char *path, unsigned int flags) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return music;
|
||||
}
|
||||
|
||||
void* load_music_begin(const char *path, unsigned int flags) {
|
||||
Music *mus = calloc(1, sizeof(Music));
|
||||
mus->impl = music;
|
||||
MixerInternalMusic *imus = calloc(1, sizeof(MixerInternalMusic));
|
||||
mus->impl = imus;
|
||||
|
||||
if(strendswith(path, ".bgm")) {
|
||||
Hashtable *bgm = parse_keyvalue_file(path, 8);
|
||||
|
||||
if(!bgm) {
|
||||
log_warn("Failed to parse bgm config '%s'", path);
|
||||
} else {
|
||||
imus->intro = load_mix_music(hashtable_get_string(bgm, "intro"));
|
||||
imus->loop = load_mix_music(hashtable_get_string(bgm, "loop"));
|
||||
hashtable_foreach(bgm, hashtable_iter_free_data, NULL);
|
||||
hashtable_free(bgm);
|
||||
}
|
||||
} else {
|
||||
imus->loop = load_mix_music(path);
|
||||
}
|
||||
|
||||
if(!imus->loop) {
|
||||
assert(imus->intro == NULL);
|
||||
free(imus);
|
||||
mus = NULL;
|
||||
log_warn("Failed to load bgm '%s'", path);
|
||||
}
|
||||
|
||||
return mus;
|
||||
}
|
||||
|
@ -50,6 +79,9 @@ void* load_music_end(void *opaque, const char *path, unsigned int flags) {
|
|||
|
||||
void unload_music(void *vmus) {
|
||||
Music *mus = vmus;
|
||||
Mix_FreeMusic((Mix_Music*)mus->impl);
|
||||
MixerInternalMusic *imus = mus->impl;
|
||||
Mix_FreeMusic(imus->intro);
|
||||
Mix_FreeMusic(imus->loop);
|
||||
free(mus->impl);
|
||||
free(mus);
|
||||
}
|
||||
|
|
|
@ -13,11 +13,8 @@
|
|||
#include "sfx.h"
|
||||
#include "audio_mixer.h"
|
||||
|
||||
char* audio_mixer_sound_path(const char *prefix, const char *name);
|
||||
bool audio_mixer_check_sound_path(const char *path, bool isbgm);
|
||||
|
||||
char* sound_path(const char *name) {
|
||||
return audio_mixer_sound_path(SFX_PATH_PREFIX, name);
|
||||
return audio_mixer_sound_path(SFX_PATH_PREFIX, name, true);
|
||||
}
|
||||
|
||||
bool check_sound_path(const char *path) {
|
||||
|
@ -35,7 +32,7 @@ void* load_sound_begin(const char *path, unsigned int flags) {
|
|||
Mix_Chunk *sound = Mix_LoadWAV_RW(rwops, true);
|
||||
|
||||
if(!sound) {
|
||||
log_warn("Mix_LoadWAV() failed: %s", Mix_GetError());
|
||||
log_warn("Mix_LoadWAV_RW() failed: %s", Mix_GetError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue