refactor audio (module system like that of renderer)

This commit is contained in:
Andrei Alexeyev 2019-03-05 21:43:01 +02:00
parent 68111e6ccb
commit abe4ddf260
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
34 changed files with 820 additions and 599 deletions

View file

@ -146,12 +146,6 @@ if package_data and not enable_zip
error('ZIP support must be enabled for data packaging to work')
endif
if dep_sdl2_mixer.found() and get_option('enable_audio') != 'false'
taisei_deps += dep_sdl2_mixer
elif get_option('enable_audio') == 'true'
error('Audio support enabled but SDL2_mixer not found')
endif
config.set('TAISEI_BUILDCONF_USE_ZIP', taisei_deps.contains(dep_zip))
have_posix = cc.has_header_symbol('unistd.h', '_POSIX_VERSION')
@ -278,38 +272,6 @@ if meson.is_cross_build()
systype = '@0@ (cross-compiling)'.format(systype)
endif
summary = '''
Summary:
Taisei version: @9@
System type: @0@
Audio enabled: @1@
Package data: @2@
Relative install paths: @3@
Prefix: @4@
Executables: @5@
Data: @6@
Documentation: @7@
Build type: @8@
'''.format(
systype,
taisei_deps.contains(dep_sdl2_mixer),
taisei_deps.contains(dep_zip),
config.get('TAISEI_BUILDCONF_RELATIVE_DATA_PATH'),
get_option('prefix'),
# the $ is intentional
join_paths('$prefix', bindir),
join_paths('$prefix', data_path),
join_paths('$prefix', doc_path),
get_option('buildtype'),
taisei_version_string
)
version_deps = []
subdir('misc')
@ -320,6 +282,45 @@ subdir('xdg')
subdir('atlas')
subdir('src')
summary = '''
Summary:
Taisei version: @0@
System type: @1@
Build type: @2@
Audio backends: @3@ (default: @4@)
Renderers: @5@ (default: @6@)
Shader translation: @7@
ZIP support: @8@
Package data: @9@
Relative install paths: @10@
Prefix: @11@
Executables: @12@
Data: @13@
Documentation: @14@
'''.format(
taisei_version_string,
systype,
get_option('buildtype'),
', '.join(enabled_audio_backends),
get_option('a_default'),
', '.join(enabled_renderers),
get_option('r_default'),
get_option('shader_transpiler'),
enable_zip,
package_data,
config.get('TAISEI_BUILDCONF_RELATIVE_DATA_PATH'),
get_option('prefix'),
# the $ is intentional
join_paths('$prefix', bindir),
join_paths('$prefix', data_path),
join_paths('$prefix', doc_path)
)
message(summary)
run_command(postconf_command)

View file

@ -11,13 +11,6 @@ option(
description : 'Make a "developer" build with cheats and extra debugging features'
)
option(
'enable_audio',
type : 'combo',
choices : ['auto', 'true', 'false'],
description : 'Enable audio support (requires SDL2_mixer)'
)
option(
'enable_zip',
type : 'boolean',
@ -156,6 +149,27 @@ option(
description : 'Build the no-op renderer (nothing is displayed)'
)
option(
'a_default',
type : 'combo',
choices : ['sdl2mixer', 'null'],
description : 'Which audio backend to use by default'
)
option(
'a_sdl2mixer',
type : 'boolean',
value : true,
description : 'Build the SDL2_Mixer audio backend'
)
option(
'a_null',
type : 'boolean',
value : true,
description : 'Build the no-op audio backend (silence); you want this on!'
)
option(
'objpools',
type : 'boolean',

View file

@ -12,9 +12,12 @@
#include <stdio.h>
#include "audio.h"
#include "backend.h"
#include "resource/resource.h"
#include "global.h"
#define B (_a_backend.funcs)
CurrentBGM current_bgm = { .name = NULL };
static char *saved_bgm;
@ -29,6 +32,10 @@ static struct enqueued_sound {
} *sound_queue;
static void play_sound_internal(const char *name, bool is_ui, int cooldown, bool replace, int delay) {
if(!audio_output_works()) {
return;
}
if(delay > 0) {
struct enqueued_sound *s = malloc(sizeof(struct enqueued_sound));
s->time = global.frames + delay;
@ -39,7 +46,7 @@ static void play_sound_internal(const char *name, bool is_ui, int cooldown, bool
return;
}
if(!audio_backend_initialized() || global.frameskip) {
if(global.frameskip) {
return;
}
@ -51,7 +58,7 @@ static void play_sound_internal(const char *name, bool is_ui, int cooldown, bool
snd->lastplayframe = global.frames;
(replace ? audio_backend_sound_play_or_restart : audio_backend_sound_play)
(replace ? B.sound_play_or_restart : B.sound_play)
(snd->impl, is_ui ? SNDGROUP_UI : SNDGROUP_MAIN);
}
@ -86,7 +93,7 @@ void play_ui_sound(const char *name) {
}
void play_loop(const char *name) {
if(!audio_backend_initialized() || global.frameskip) {
if(!audio_output_works() || global.frameskip) {
return;
}
@ -97,9 +104,10 @@ void play_loop(const char *name) {
}
if(!snd->islooping) {
audio_backend_sound_loop(snd->impl, SNDGROUP_MAIN);
B.sound_loop(snd->impl, SNDGROUP_MAIN);
snd->islooping = true;
}
if(snd->islooping == LS_LOOPING) {
snd->lastplayframe = global.frames;
}
@ -115,7 +123,7 @@ static void* reset_sounds_callback(const char *name, Resource *res, void *arg) {
}
if(snd->islooping && (global.frames > snd->lastplayframe + LOOPTIMEOUTFRAMES || reset)) {
audio_backend_sound_stop_loop(snd->impl);
B.sound_stop_loop(snd->impl);
snd->islooping = LS_FADEOUT;
}
@ -145,15 +153,15 @@ void update_sounds(void) {
}
void pause_sounds(void) {
audio_backend_sound_pause_all(SNDGROUP_MAIN);
B.sound_pause_all(SNDGROUP_MAIN);
}
void resume_sounds(void) {
audio_backend_sound_resume_all(SNDGROUP_MAIN);
B.sound_resume_all(SNDGROUP_MAIN);
}
void stop_sounds(void) {
audio_backend_sound_stop_all(SNDGROUP_MAIN);
B.sound_stop_all(SNDGROUP_MAIN);
}
Sound* get_sound(const char *name) {
@ -211,15 +219,15 @@ static void stop_bgm_internal(bool pause, double fadetime) {
stralloc(&current_bgm.name, NULL);
}
if(audio_backend_music_is_playing() && !audio_backend_music_is_paused()) {
if(B.music_is_playing() && !B.music_is_paused()) {
if(pause) {
audio_backend_music_pause();
B.music_pause();
log_debug("BGM paused");
} else if(fadetime > 0) {
audio_backend_music_fade(fadetime);
B.music_fade(fadetime);
log_debug("BGM fading out");
} else {
audio_backend_music_stop();
B.music_stop();
log_debug("BGM stopped");
}
} else {
@ -255,7 +263,7 @@ void start_bgm(const char *name) {
// if BGM has changed, change it and start from beginning
if(!current_bgm.name || strcmp(name, current_bgm.name)) {
audio_backend_music_stop();
B.music_stop();
stralloc(&current_bgm.name, name);
@ -268,15 +276,15 @@ void start_bgm(const char *name) {
}
}
if(audio_backend_music_is_paused()) {
audio_backend_music_resume();
if(B.music_is_paused()) {
B.music_resume();
}
if(audio_backend_music_is_playing()) {
if(B.music_is_playing()) {
return;
}
if(!audio_backend_music_play(current_bgm.music->impl)) {
if(!B.music_play(current_bgm.music->impl)) {
return;
}
@ -293,12 +301,12 @@ void start_bgm(const char *name) {
}
static bool audio_config_updated(SDL_Event *evt, void *arg) {
if (config_get_int(CONFIG_MUTE_AUDIO) == 1) {
audio_backend_set_sfx_volume(0.0);
audio_backend_set_bgm_volume(0.0);
if(config_get_int(CONFIG_MUTE_AUDIO)) {
B.sound_set_global_volume(0.0);
B.music_set_global_volume(0.0);
} else {
audio_backend_set_sfx_volume(config_get_float(CONFIG_SFX_VOLUME));
audio_backend_set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
B.sound_set_global_volume(config_get_float(CONFIG_SFX_VOLUME));
B.music_set_global_volume(config_get_float(CONFIG_BGM_VOLUME));
}
return false;
@ -315,6 +323,14 @@ void audio_init(void) {
void audio_shutdown(void) {
events_unregister_handler(audio_config_updated);
audio_backend_shutdown();
B.shutdown();
ht_destroy(&sfx_volumes);
}
bool audio_output_works(void) {
return B.output_works();
}
void audio_music_set_position(double pos) {
B.music_set_position(pos);
}

View file

@ -6,8 +6,8 @@
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#ifndef IGUARD_audio_h
#define IGUARD_audio_h
#ifndef IGUARD_audio_audio_h
#define IGUARD_audio_audio_h
#include "taisei.h"
@ -19,6 +19,26 @@
#define LOOPFADEOUT 50
typedef struct MusicImpl MusicImpl;
typedef struct SoundImpl SoundImpl;
typedef enum {
LS_OFF,
LS_LOOPING,
LS_FADEOUT,
} LoopState;
typedef struct Sound {
int lastplayframe;
LoopState islooping;
void *impl;
} Sound;
typedef struct Music {
char *title;
MusicImpl *impl;
} Music;
typedef struct CurrentBGM {
char *name;
char *title;
@ -29,35 +49,10 @@ 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);
void audio_backend_set_sfx_volume(float gain);
void audio_backend_set_bgm_volume(float gain);
bool audio_backend_music_is_paused(void);
bool audio_backend_music_is_playing(void);
void audio_backend_music_resume(void);
void audio_backend_music_stop(void);
void audio_backend_music_fade(double fadetime);
void audio_backend_music_pause(void);
bool audio_backend_music_play(void *impl);
bool audio_backend_music_set_position(double pos);
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group);
bool audio_backend_sound_play_or_restart(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);
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);
@ -82,4 +77,4 @@ void resume_bgm(void);
void save_bgm(void); // XXX: this is broken
void restore_bgm(void); // XXX: this is broken
#endif // IGUARD_audio_h
#endif // IGUARD_audio_audio_h

66
src/audio/backend.c Normal file
View file

@ -0,0 +1,66 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "backend.h"
#include "util.h"
#undef A
#define A(x) extern AudioBackend _a_backend_##x;
TAISEI_BUILDCONF_AUDIO_BACKENDS
#undef A
AudioBackend *_a_backends[] = {
#define A(x) &_a_backend_##x,
TAISEI_BUILDCONF_AUDIO_BACKENDS
#undef A
NULL,
};
AudioBackend _a_backend;
static AudioBackend *_audio_find_backend(const char *name) {
for(AudioBackend **b = _a_backends; *b; ++b) {
if(!strcmp((*b)->name, name)) {
return *b;
}
}
log_error("Unknown audio backend '%s'", name);
return NULL;
}
static bool _audio_backend_try_init(AudioBackend *backend) {
_a_backend = *backend;
log_debug("Trying %s", _a_backend.name);
return _a_backend.funcs.init();
}
bool audio_backend_init(void) {
const char *name = env_get("TAISEI_AUDIO_BACKEND", "");
bool initialized = false;
if(!*name) {
name = TAISEI_BUILDCONF_AUDIO_DEFAULT;
}
AudioBackend *backend = _audio_find_backend(name);
if(backend) {
initialized = _audio_backend_try_init(backend);
}
for(AudioBackend **b = _a_backends; *b && !initialized; ++b) {
if(*b != backend) {
initialized = _audio_backend_try_init(*b);
}
}
return initialized;
}

64
src/audio/backend.h Normal file
View file

@ -0,0 +1,64 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#ifndef IGUARD_audio_backend_h
#define IGUARD_audio_backend_h
#include "taisei.h"
#include "audio.h"
typedef enum {
SNDGROUP_ALL,
SNDGROUP_MAIN,
SNDGROUP_UI,
} AudioBackendSoundGroup;
typedef struct AudioFuncs {
bool (*init)(void);
bool (*music_fade)(double fadetime);
bool (*music_is_paused)(void);
bool (*music_is_playing)(void);
bool (*music_pause)(void);
bool (*music_play)(MusicImpl *mus);
bool (*music_resume)(void);
bool (*music_set_global_volume)(double gain);
bool (*music_set_loop_point)(MusicImpl *mus, double pos);
bool (*music_set_position)(double pos);
bool (*music_stop)(void);
bool (*output_works)(void);
bool (*set_sfx_volume)(float gain);
bool (*shutdown)(void);
bool (*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);
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);
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);
SoundImpl* (*sound_load)(const char *vfspath);
void (*music_unload)(MusicImpl *mus);
void (*sound_unload)(SoundImpl *snd);
} AudioFuncs;
typedef struct AudioBackend {
const char *name;
AudioFuncs funcs;
void *custom;
} AudioBackend;
extern AudioBackend _a_backend;
bool audio_backend_init(void);
#endif // IGUARD_audio_backend_h

50
src/audio/meson.build Normal file
View file

@ -0,0 +1,50 @@
default_backend = get_option('a_default')
if not get_option('a_@0@'.format(default_backend))
error('Default audio backend \'@0@\' is not enabled. Enable it with -Da_@0@=true, or set a_default to something else.'.format(default_backend))
endif
audio_src = files(
'audio.c',
'backend.c',
)
audio_deps = []
enabled_audio_backends = []
# NOTE: Order matters here.
subdir('sdl2mixer')
subdir('null')
modules = [
'sdl2mixer',
'null',
]
included_deps = []
needed_deps = []
a_macro = []
foreach m : modules
if get_option('a_@0@'.format(m))
audio_src += get_variable('a_@0@_src'.format(m))
a_macro += ['A(@0@)'.format(m)]
enabled_audio_backends += [m]
needed_deps += get_variable('a_@0@_deps'.format(m))
included_deps += [m]
audio_deps += get_variable('a_@0@_libdeps'.format(m))
endif
endforeach
foreach dep : needed_deps
if not included_deps.contains(dep)
included_deps += [dep]
audio_src += get_variable('a_@0@_src'.format(dep))
audio_deps += get_variable('a_@0@_libdeps'.format(dep))
endif
endforeach
a_macro = ' '.join(a_macro)
config.set('TAISEI_BUILDCONF_AUDIO_BACKENDS', a_macro)
config.set_quoted('TAISEI_BUILDCONF_AUDIO_DEFAULT', default_backend)

View file

@ -0,0 +1,82 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "../backend.h"
#include "util.h"
static bool audio_null_init(void) {
if(strcmp(env_get("TAISEI_AUDIO_BACKEND", ""), "null")) { // don't warn if we asked for this
log_warn("Audio subsystem initialized with the 'null' backend. There will be no sound");
}
return true;
}
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; }
static bool audio_null_music_pause(void) { return true; }
static bool audio_null_music_play(MusicImpl *impl) { return true; }
static bool audio_null_music_resume(void) { return true; }
static bool audio_null_music_set_global_volume(double gain) { return true; }
static bool audio_null_music_set_loop_point(MusicImpl *impl, double pos) { return true; }
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 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 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 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; }
static SoundImpl* audio_null_sound_load(const char *vfspath) { return NULL; }
static void audio_null_music_unload(MusicImpl *mus) { }
static void audio_null_sound_unload(SoundImpl *snd) { }
AudioBackend _a_backend_null = {
.name = "null",
.funcs = {
.get_supported_music_exts = audio_null_get_supported_exts,
.get_supported_sound_exts = audio_null_get_supported_exts,
.init = audio_null_init,
.music_fade = audio_null_music_fade,
.music_is_paused = audio_null_music_is_paused,
.music_is_playing = audio_null_music_is_playing,
.music_load = audio_null_music_load,
.music_pause = audio_null_music_pause,
.music_play = audio_null_music_play,
.music_resume = audio_null_music_resume,
.music_set_global_volume = audio_null_music_set_global_volume,
.music_set_loop_point = audio_null_music_set_loop_point,
.music_set_position = audio_null_music_set_position,
.music_stop = audio_null_music_stop,
.music_unload = audio_null_music_unload,
.output_works = audio_null_output_works,
.shutdown = audio_null_shutdown,
.sound_load = audio_null_sound_load,
.sound_loop = audio_null_sound_loop,
.sound_pause_all = audio_null_sound_pause_all,
.sound_play = audio_null_sound_play,
.sound_play_or_restart = audio_null_sound_play_or_restart,
.sound_resume_all = audio_null_sound_resume_all,
.sound_set_global_volume = audio_null_sound_set_global_volume,
.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_unload = audio_null_sound_unload,
}
};

View file

@ -0,0 +1,7 @@
a_null_src = files(
'audio_null.c',
)
a_null_deps = []
a_null_libdeps = []

View file

@ -10,11 +10,12 @@
#include <SDL_mixer.h>
#include "audio.h"
#include "audio_mixer.h"
#include "../backend.h"
#include "global.h"
#include "list.h"
#define B (_a_backend.funcs)
#define AUDIO_FREQ 44100
#define AUDIO_FORMAT MIX_DEFAULT_FORMAT
#define AUDIO_CHANNELS 100
@ -22,35 +23,44 @@
#define UI_CHANNEL_GROUP 0
#define MAIN_CHANNEL_GROUP 1
static bool mixer_loaded = false;
// I needed to add this for supporting loop sounds since Mixer doesnt remember
// what channel a sound is playing on. - laochailan
struct SoundImpl {
Mix_Chunk *ch;
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
};
struct MusicImpl {
Mix_Music *loop;
double loop_point;
};
static struct {
uchar first;
uchar num;
} groups[2];
static const char *mixer_audio_exts[] = { ".ogg", ".wav", NULL };
void audio_backend_init(void) {
if(mixer_loaded) {
return;
}
static Mix_Music *next_loop;
static double next_loop_point;
static bool audio_sdl2mixer_init(void) {
if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
log_sdl_error(LOG_ERROR, "SDL_InitSubSystem");
return;
return false;
}
if(Mix_OpenAudio(AUDIO_FREQ, AUDIO_FORMAT, 2, config_get_int(CONFIG_MIXER_CHUNKSIZE)) == -1) {
log_error("Mix_OpenAudio() failed: %s", Mix_GetError());
Mix_Quit();
return;
return false;
}
if(!(Mix_Init(MIX_INIT_OGG) & MIX_INIT_OGG)) {
log_error("Mix_Init() failed: %s", Mix_GetError());
Mix_Quit(); // Try to shutdown mixer if it was partly initialized
return;
Mix_Quit();
return false;
}
int channels = Mix_AllocateChannels(AUDIO_CHANNELS);
@ -59,7 +69,7 @@ void audio_backend_init(void) {
log_error("Unable to allocate any channels");
Mix_CloseAudio();
Mix_Quit();
return;
return false;
}
if(channels < AUDIO_CHANNELS) {
@ -96,10 +106,8 @@ void audio_backend_init(void) {
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));
audio_backend_set_bgm_volume(config_get_float(CONFIG_BGM_VOLUME));
B.sound_set_global_volume(config_get_float(CONFIG_SFX_VOLUME));
B.music_set_global_volume(config_get_float(CONFIG_BGM_VOLUME));
int frequency = 0;
uint16_t format = 0;
@ -125,59 +133,36 @@ void audio_backend_init(void) {
lv = Mix_Linked_Version();
log_info("Using SDL_mixer %u.%u.%u", lv->major, lv->minor, lv->patch);
return true;
}
void audio_backend_shutdown(void) {
mixer_loaded = false;
static bool audio_sdl2mixer_shutdown(void) {
Mix_CloseAudio();
Mix_Quit();
SDL_QuitSubSystem(SDL_INIT_AUDIO);
log_info("Audio subsystem uninitialized (SDL2_Mixer)");
log_info("Audio subsystem deinitialized (SDL2_Mixer)");
return true;
}
bool audio_backend_initialized(void) {
return mixer_loaded;
static bool audio_sdl2mixer_output_works(void) {
return true;
}
void audio_backend_set_sfx_volume(float gain) {
if(mixer_loaded)
Mix_Volume(-1, gain * MIX_MAX_VOLUME);
static inline int gain_to_mixvol(double gain) {
return iclamp(gain * SDL_MIX_MAXVOLUME, 0, SDL_MIX_MAXVOLUME);
}
void audio_backend_set_bgm_volume(float gain) {
if(mixer_loaded)
Mix_VolumeMusic(gain * MIX_MAX_VOLUME);
static bool audio_sdl2mixer_sound_set_global_volume(double gain) {
Mix_Volume(-1, gain_to_mixvol(gain));
return true;
}
char* audio_mixer_sound_path(const char *prefix, const char *name, bool isbgm) {
char *p = NULL;
if(isbgm && (p = try_path(prefix, name, ".bgm"))) {
return p;
}
for(const char **ext = mixer_audio_exts; *ext; ++ext) {
if((p = try_path(prefix, name, *ext))) {
return p;
}
}
return NULL;
static bool audio_sdl2mixer_music_set_global_volume(double gain) {
Mix_VolumeMusic(gain * gain_to_mixvol(gain));
return true;
}
bool audio_mixer_check_sound_path(const char *path, bool isbgm) {
if(isbgm && strendswith(path, ".bgm")) {
return true;
}
return strendswith_any(path, mixer_audio_exts);
}
static Mix_Music *next_loop;
static double next_loop_point;
static void mixer_music_finished(void) {
// XXX: there may be a race condition in here
// probably should protect next_loop with a mutex
@ -195,71 +180,55 @@ static void mixer_music_finished(void) {
}
}
void audio_backend_music_stop(void) {
if(mixer_loaded) {
Mix_HookMusicFinished(NULL);
Mix_HaltMusic();
}
static bool audio_sdl2mixer_music_stop(void) {
Mix_HookMusicFinished(NULL);
Mix_HaltMusic();
return true;
}
void audio_backend_music_fade(double fadetime) {
if(mixer_loaded) {
Mix_HookMusicFinished(NULL);
Mix_FadeOutMusic(floor(1000 * fadetime));
}
static bool audio_sdl2mixer_music_fade(double fadetime) {
Mix_HookMusicFinished(NULL);
Mix_FadeOutMusic(floor(1000 * fadetime));
return true;
}
bool audio_backend_music_is_paused(void) {
return mixer_loaded && Mix_PausedMusic();
static bool audio_sdl2mixer_music_is_paused(void) {
return Mix_PausedMusic();
}
bool audio_backend_music_is_playing(void) {
return mixer_loaded && Mix_PlayingMusic() && Mix_FadingMusic() != MIX_FADING_OUT;
static bool audio_sdl2mixer_music_is_playing(void) {
return Mix_PlayingMusic() && Mix_FadingMusic() != MIX_FADING_OUT;
}
void audio_backend_music_resume(void) {
if(mixer_loaded) {
Mix_HookMusicFinished(mixer_music_finished);
Mix_ResumeMusic();
}
static bool audio_sdl2mixer_music_resume(void) {
Mix_HookMusicFinished(mixer_music_finished);
Mix_ResumeMusic();
return true;
}
void audio_backend_music_pause(void) {
if(mixer_loaded) {
Mix_HookMusicFinished(NULL);
Mix_PauseMusic();
}
static bool audio_sdl2mixer_music_pause(void) {
Mix_HookMusicFinished(NULL);
Mix_PauseMusic();
return true;
}
bool audio_backend_music_play(void *impl) {
if(!mixer_loaded)
return false;
MixerInternalMusic *imus = impl;
static bool audio_sdl2mixer_music_play(MusicImpl *imus) {
Mix_Music *mmus;
int loops;
Mix_HookMusicFinished(NULL);
Mix_HaltMusic();
if(imus->intro) {
next_loop = imus->loop;
next_loop_point = next_loop ? imus->loop_point : 0;
mmus = imus->intro;
mmus = imus->loop;
next_loop_point = imus->loop_point;
if(next_loop_point >= 0) {
loops = 0;
next_loop = imus->loop;
Mix_HookMusicFinished(mixer_music_finished);
} else {
mmus = imus->loop;
next_loop_point = imus->loop_point;
if(next_loop_point >= 0) {
loops = 0;
next_loop = imus->loop;
Mix_HookMusicFinished(mixer_music_finished);
} else {
loops = -1;
Mix_HookMusicFinished(NULL);
}
loops = -1;
Mix_HookMusicFinished(NULL);
}
bool result = (Mix_PlayMusic(mmus, loops) != -1);
@ -271,13 +240,7 @@ bool audio_backend_music_play(void *impl) {
return result;
}
bool audio_backend_music_set_position(double pos) {
if(!mixer_loaded) {
return false;
}
// FIXME: BGMs that have intros are not handled correctly!
static bool audio_sdl2mixer_music_set_position(double pos) {
Mix_RewindMusic();
if(Mix_SetMusicPosition(pos)) {
@ -288,6 +251,11 @@ bool audio_backend_music_set_position(double pos) {
return true;
}
static bool audio_sdl2mixer_music_set_loop_point(MusicImpl *imus, double pos) {
imus->loop_point = pos;
return true;
}
static int translate_group(AudioBackendSoundGroup group, int defmixgroup) {
switch(group) {
case SNDGROUP_MAIN: return MAIN_CHANNEL_GROUP;
@ -310,7 +278,7 @@ static int pick_channel(AudioBackendSoundGroup group, int defmixgroup) {
return channel;
}
static int audio_backend_sound_play_on_channel(int chan, MixerInternalSound *isnd) {
static int audio_sdl2mixer_sound_play_on_channel(int chan, SoundImpl *isnd) {
chan = Mix_PlayChannel(chan, isnd->ch, 0);
Mix_UnregisterAllEffects(chan);
@ -323,38 +291,25 @@ static int audio_backend_sound_play_on_channel(int chan, MixerInternalSound *isn
return true;
}
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group) {
if(!mixer_loaded)
return false;
MixerInternalSound *isnd = impl;
return audio_backend_sound_play_on_channel(pick_channel(group, MAIN_CHANNEL_GROUP), isnd);
static bool audio_sdl2mixer_sound_play(SoundImpl *isnd, AudioBackendSoundGroup group) {
return audio_sdl2mixer_sound_play_on_channel(pick_channel(group, MAIN_CHANNEL_GROUP), isnd);
}
bool audio_backend_sound_play_or_restart(void *impl, AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
MixerInternalSound *isnd = impl;
static bool 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_backend_sound_play_on_channel(chan, isnd);
return audio_sdl2mixer_sound_play_on_channel(chan, isnd);
}
bool audio_backend_sound_loop(void *impl, AudioBackendSoundGroup group) {
if(!mixer_loaded)
return false;
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);
MixerInternalSound *snd = (MixerInternalSound *)impl;
snd->loopchan = Mix_PlayChannel(pick_channel(group, MAIN_CHANNEL_GROUP), snd->ch, -1);
Mix_UnregisterAllEffects(snd->loopchan);
if(snd->loopchan == -1) {
if(isnd->loopchan == -1) {
log_error("Mix_PlayChannel() failed: %s", Mix_GetError());
return false;
}
@ -385,33 +340,22 @@ static void custom_fadeout_proc(int chan, void *stream, int len, void *udata) {
}
}
bool audio_backend_sound_stop_loop(void *impl) {
if(!mixer_loaded)
return false;
MixerInternalSound *snd = (MixerInternalSound *)impl;
if(snd->loopchan == -1) {
static bool audio_sdl2mixer_sound_stop_loop(SoundImpl *isnd) {
if(isnd->loopchan == -1) {
return false;
}
CustomFadeout *effect = calloc(1,sizeof(CustomFadeout));
CustomFadeout *effect = calloc(1, sizeof(CustomFadeout));
effect->counter = 0;
effect->duration = LOOPFADEOUT*AUDIO_FREQ/1000;
Mix_ExpireChannel(snd->loopchan, LOOPFADEOUT);
Mix_RegisterEffect(snd->loopchan, custom_fadeout_proc, custom_fadeout_free, effect);
Mix_ExpireChannel(isnd->loopchan, LOOPFADEOUT);
Mix_RegisterEffect(isnd->loopchan, custom_fadeout_proc, custom_fadeout_free, effect);
return true;
}
bool audio_backend_sound_pause_all(AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
static bool audio_sdl2mixer_sound_pause_all(AudioBackendSoundGroup group) {
int mixgroup = translate_group(group, -1);
if(mixgroup == -1) {
@ -428,11 +372,7 @@ bool audio_backend_sound_pause_all(AudioBackendSoundGroup group) {
}
bool audio_backend_sound_resume_all(AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
static bool audio_sdl2mixer_sound_resume_all(AudioBackendSoundGroup group) {
int mixgroup = translate_group(group, -1);
if(mixgroup == -1) {
@ -448,11 +388,7 @@ bool audio_backend_sound_resume_all(AudioBackendSoundGroup group) {
return true;
}
bool audio_backend_sound_stop_all(AudioBackendSoundGroup group) {
if(!mixer_loaded) {
return false;
}
static bool audio_sdl2mixer_sound_stop_all(AudioBackendSoundGroup group) {
int mixgroup = translate_group(group, -1);
if(mixgroup == -1) {
@ -463,3 +399,102 @@ bool audio_backend_sound_stop_all(AudioBackendSoundGroup group) {
return true;
}
static const char *const *audio_sdl2mixer_get_supported_exts(uint *numexts) {
static const char *const exts[] = { ".ogg", ".wav" };
*numexts = ARRAY_SIZE(exts);
return exts;
}
static MusicImpl *audio_sdl2mixer_music_load(const char *vfspath) {
SDL_RWops *rw = vfs_open(vfspath, VFS_MODE_READ | VFS_MODE_SEEKABLE);
if(!rw) {
log_error("VFS error: %s", vfs_get_error());
return NULL;
}
Mix_Music *mus = Mix_LoadMUS_RW(rw, true);
if(!mus) {
log_error("Mix_LoadMUS_RW() failed: %s", Mix_GetError());
return NULL;
}
MusicImpl *imus = calloc(1, sizeof(*imus));
imus->loop = mus;
return imus;
}
static void audio_sdl2mixer_music_unload(MusicImpl *imus) {
Mix_FreeMusic(imus->loop);
free(imus);
}
static SoundImpl *audio_sdl2mixer_sound_load(const char *vfspath) {
SDL_RWops *rw = vfs_open(vfspath, VFS_MODE_READ | VFS_MODE_SEEKABLE);
if(!rw) {
log_error("VFS error: %s", vfs_get_error());
return NULL;
}
Mix_Chunk *snd = Mix_LoadWAV_RW(rw, true);
if(!snd) {
log_error("Mix_LoadWAV_RW() failed: %s", Mix_GetError());
return NULL;
}
SoundImpl *isnd = calloc(1, sizeof(*isnd));
isnd->ch = snd;
isnd->loopchan = -1;
isnd->playchan = -1;
return isnd;
}
static void audio_sdl2mixer_sound_unload(SoundImpl *isnd) {
Mix_FreeChunk(isnd->ch);
free(isnd);
}
static bool audio_sdl2mixer_sound_set_volume(SoundImpl *isnd, double gain) {
Mix_VolumeChunk(isnd->ch, gain_to_mixvol(gain));
return true;
}
AudioBackend _a_backend_sdl2mixer = {
.name = "sdl2mixer",
.funcs = {
.get_supported_music_exts = audio_sdl2mixer_get_supported_exts,
.get_supported_sound_exts = audio_sdl2mixer_get_supported_exts,
.init = audio_sdl2mixer_init,
.music_fade = audio_sdl2mixer_music_fade,
.music_is_paused = audio_sdl2mixer_music_is_paused,
.music_is_playing = audio_sdl2mixer_music_is_playing,
.music_load = audio_sdl2mixer_music_load,
.music_pause = audio_sdl2mixer_music_pause,
.music_play = audio_sdl2mixer_music_play,
.music_resume = audio_sdl2mixer_music_resume,
.music_set_global_volume = audio_sdl2mixer_music_set_global_volume,
.music_set_loop_point = audio_sdl2mixer_music_set_loop_point,
.music_set_position = audio_sdl2mixer_music_set_position,
.music_stop = audio_sdl2mixer_music_stop,
.music_unload = audio_sdl2mixer_music_unload,
.output_works = audio_sdl2mixer_output_works,
.shutdown = audio_sdl2mixer_shutdown,
.sound_load = audio_sdl2mixer_sound_load,
.sound_loop = audio_sdl2mixer_sound_loop,
.sound_pause_all = audio_sdl2mixer_sound_pause_all,
.sound_play = audio_sdl2mixer_sound_play,
.sound_play_or_restart = audio_sdl2mixer_sound_play_or_restart,
.sound_resume_all = audio_sdl2mixer_sound_resume_all,
.sound_set_global_volume = audio_sdl2mixer_sound_set_global_volume,
.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_unload = audio_sdl2mixer_sound_unload,
}
};

View file

@ -0,0 +1,11 @@
a_sdl2mixer_src = files(
'audio_sdl2mixer.c',
)
a_sdl2mixer_deps = []
a_sdl2mixer_libdeps = [dep_sdl2_mixer]
if get_option('a_sdl2mixer') and not dep_sdl2_mixer.found()
error('sdl2mixer backend enabled, but SDL2_mixer was not found. Install SDL2_mixer or disable a_sdl2mixer')
endif

View file

@ -1,34 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#ifndef IGUARD_audio_mixer_h
#define IGUARD_audio_mixer_h
#include "taisei.h"
#include <SDL_mixer.h>
// I needed to add this for supporting loop sounds since Mixer doesnt remember
// what channel a sound is playing on.
typedef struct {
Mix_Chunk *ch;
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
} MixerInternalSound;
typedef struct MixerInternalMusic {
Mix_Music *intro;
Mix_Music *loop;
double loop_point;
} 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);
#endif // IGUARD_audio_mixer_h

View file

@ -1,32 +0,0 @@
/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include "audio.h"
void audio_backend_init(void) {}
void audio_backend_shutdown(void) {}
bool audio_backend_initialized(void) { return false; }
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) {}
void audio_backend_music_stop(void) {}
void audio_backend_music_fade(double fadetime) {}
void audio_backend_music_pause(void) {}
bool audio_backend_music_play(void *impl) { return false; }
bool audio_backend_sound_play_or_restart(void *impl, AudioBackendSoundGroup group) { return false; }
bool audio_backend_music_set_position(double pos) { return false; }
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group) { return false; }
bool audio_backend_sound_loop(void *impl, AudioBackendSoundGroup group) { 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

@ -41,7 +41,7 @@
#include "events.h"
#include "difficulty.h"
#include "color.h"
#include "audio.h"
#include "audio/audio.h"
#include "rwops/all.h"
#include "cli.h"
#include "hirestime.h"

View file

@ -13,7 +13,7 @@
#include "global.h"
#include "video.h"
#include "audio.h"
#include "audio/audio.h"
#include "stage.h"
#include "menu/mainmenu.h"
#include "menu/savereplay.h"
@ -216,6 +216,7 @@ int main(int argc, char **argv) {
if(headless) {
env_set("SDL_AUDIODRIVER", "dummy", true);
env_set("SDL_VIDEODRIVER", "dummy", true);
env_set("TAISEI_AUDIO_BACKEND", "null", true);
env_set("TAISEI_RENDERER", "null", true);
env_set("TAISEI_NOPRELOAD", true, false);
env_set("TAISEI_PRELOAD_REQUIRED", false, false);
@ -244,10 +245,7 @@ int main(int argc, char **argv) {
r_post_init();
draw_loading_screen();
if(!headless) {
audio_init();
}
audio_init();
load_resources();
gamepad_init();
progress_load();

View file

@ -297,10 +297,6 @@ static int bind_common_intplus1_set(OptionBinding *b, int v) {
// --- Binding callbacks for individual options --- //
static bool bind_audio_dependence(void) {
return audio_backend_initialized();
}
static bool bind_resizable_dependence(void) {
return !config_get_int(CONFIG_FULLSCREEN);
}
@ -729,11 +725,11 @@ void create_options_menu(MenuData *m) {
add_menu_entry(m, "SFX Volume", do_nothing,
b = bind_scale(CONFIG_SFX_VOLUME, 0, 1, 0.1)
); bind_setdependence(b, bind_audio_dependence);
); bind_setdependence(b, audio_output_works);
add_menu_entry(m, "BGM Volume", do_nothing,
b = bind_scale(CONFIG_BGM_VOLUME, 0, 1, 0.1)
); bind_setdependence(b, bind_audio_dependence);
); bind_setdependence(b, audio_output_works);
add_menu_entry(m, "Mute audio", do_nothing,
b = bind_option(CONFIG_MUTE_AUDIO, bind_common_onoff_get, bind_common_onoff_set)

View file

@ -46,7 +46,6 @@ int main(int argc, char **argv) {
taisei_src = files(
'aniplayer.c',
'audio_common.c',
'boss.c',
'cli.c',
'color.c',
@ -101,6 +100,7 @@ endif
sse42_src = []
subdir('audio')
subdir('dialog')
subdir('menu')
subdir('plrmodes')