refactor audio (module system like that of renderer)
This commit is contained in:
parent
68111e6ccb
commit
abe4ddf260
34 changed files with 820 additions and 599 deletions
77
meson.build
77
meson.build
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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(¤t_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(¤t_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);
|
||||
}
|
|
@ -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
66
src/audio/backend.c
Normal 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
64
src/audio/backend.h
Normal 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
50
src/audio/meson.build
Normal 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)
|
82
src/audio/null/audio_null.c
Normal file
82
src/audio/null/audio_null.c
Normal 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,
|
||||
}
|
||||
};
|
7
src/audio/null/meson.build
Normal file
7
src/audio/null/meson.build
Normal file
|
@ -0,0 +1,7 @@
|
|||
|
||||
a_null_src = files(
|
||||
'audio_null.c',
|
||||
)
|
||||
|
||||
a_null_deps = []
|
||||
a_null_libdeps = []
|
|
@ -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 doesn’t 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,
|
||||
}
|
||||
};
|
11
src/audio/sdl2mixer/meson.build
Normal file
11
src/audio/sdl2mixer/meson.build
Normal 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
|
|
@ -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 doesn’t 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
|
|
@ -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; }
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
||||
|