2017-03-02 11:23:30 +01:00
|
|
|
/*
|
|
|
|
* This software is licensed under the terms of the MIT-License
|
|
|
|
* See COPYING for further information.
|
|
|
|
* ---
|
2018-01-04 18:14:31 +01:00
|
|
|
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
|
2017-03-02 11:23:30 +01:00
|
|
|
*/
|
|
|
|
|
2017-11-25 20:45:11 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
#include <SDL_mixer.h>
|
|
|
|
|
|
|
|
#include "audio.h"
|
2017-09-30 19:33:07 +02:00
|
|
|
#include "audio_mixer.h"
|
2017-03-02 11:23:30 +01:00
|
|
|
#include "global.h"
|
|
|
|
#include "list.h"
|
|
|
|
|
|
|
|
#define AUDIO_FREQ 44100
|
|
|
|
#define AUDIO_FORMAT MIX_DEFAULT_FORMAT
|
|
|
|
#define AUDIO_CHANNELS 100
|
2017-10-08 07:15:43 +02:00
|
|
|
#define UI_CHANNELS 4
|
|
|
|
#define UI_CHANNEL_GROUP 0
|
|
|
|
#define MAIN_CHANNEL_GROUP 1
|
2017-03-02 11:23:30 +01:00
|
|
|
|
|
|
|
static bool mixer_loaded = false;
|
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
static struct {
|
|
|
|
unsigned char first;
|
|
|
|
unsigned char num;
|
|
|
|
} groups[2];
|
|
|
|
|
2017-11-17 00:41:01 +01:00
|
|
|
const char *mixer_audio_exts[] = { ".ogg", ".wav", NULL };
|
2017-09-30 19:33:07 +02:00
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
void audio_backend_init(void) {
|
|
|
|
if(mixer_loaded) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_warn("SDL_InitSubSystem() failed: %s", SDL_GetError());
|
2017-03-02 11:23:30 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(Mix_OpenAudio(AUDIO_FREQ, AUDIO_FORMAT, 2, config_get_int(CONFIG_MIXER_CHUNKSIZE)) == -1) {
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_warn("Mix_OpenAudio() failed: %s", Mix_GetError());
|
2017-03-02 11:23:30 +01:00
|
|
|
Mix_Quit();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-20 02:46:27 +01:00
|
|
|
if(!(Mix_Init(MIX_INIT_OGG) & MIX_INIT_OGG)) {
|
|
|
|
log_warn("Mix_Init() failed: %s", Mix_GetError());
|
|
|
|
Mix_Quit(); // Try to shutdown mixer if it was partly initialized
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
int channels = Mix_AllocateChannels(AUDIO_CHANNELS);
|
2017-10-08 07:15:43 +02:00
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
if(!channels) {
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_warn("Unable to allocate any channels");
|
2017-03-02 11:23:30 +01:00
|
|
|
Mix_CloseAudio();
|
|
|
|
Mix_Quit();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(channels < AUDIO_CHANNELS) {
|
2017-10-08 07:15:43 +02:00
|
|
|
log_warn("Allocated only %d out of %d channels", channels, AUDIO_CHANNELS);
|
|
|
|
}
|
|
|
|
|
|
|
|
int wanted_ui_channels = UI_CHANNELS;
|
|
|
|
|
|
|
|
if(wanted_ui_channels > channels / 2) {
|
|
|
|
wanted_ui_channels = channels / 2;
|
|
|
|
log_warn("Will not reserve more than %i channels for UI sounds (wanted %i channels)", wanted_ui_channels, UI_CHANNELS);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ui_channels;
|
|
|
|
|
|
|
|
if((ui_channels = Mix_GroupChannels(0, wanted_ui_channels - 1, UI_CHANNEL_GROUP)) != wanted_ui_channels) {
|
|
|
|
log_warn("Assigned only %d out of %d channels to the UI group", ui_channels, wanted_ui_channels);
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
int wanted_main_channels = channels - ui_channels, main_channels;
|
|
|
|
|
|
|
|
if((main_channels = Mix_GroupChannels(ui_channels, ui_channels + wanted_main_channels - 1, MAIN_CHANNEL_GROUP)) != wanted_main_channels) {
|
|
|
|
log_warn("Assigned only %d out of %d channels to the main group", main_channels, wanted_main_channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
int unused_channels = channels - ui_channels - main_channels;
|
|
|
|
|
|
|
|
if(unused_channels) {
|
|
|
|
log_warn("%i channels not used", unused_channels);
|
|
|
|
}
|
|
|
|
|
|
|
|
groups[UI_CHANNEL_GROUP].first = 0;
|
|
|
|
groups[UI_CHANNEL_GROUP].num = ui_channels;
|
|
|
|
groups[MAIN_CHANNEL_GROUP].first = ui_channels;
|
|
|
|
groups[MAIN_CHANNEL_GROUP].num = main_channels;
|
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
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));
|
|
|
|
|
|
|
|
int frequency = 0;
|
|
|
|
uint16_t format = 0;
|
|
|
|
|
|
|
|
Mix_QuerySpec(&frequency, &format, &channels);
|
|
|
|
|
|
|
|
if(frequency != AUDIO_FREQ || format != AUDIO_FORMAT) {
|
2018-01-12 19:26:07 +01:00
|
|
|
log_warn(
|
|
|
|
"Mixer spec doesn't match our request, "
|
|
|
|
"requested (freq=%i, fmt=%u), got (freq=%i, fmt=%u). "
|
|
|
|
"Sound may be distorted.",
|
|
|
|
AUDIO_FREQ, AUDIO_FORMAT, frequency, format
|
|
|
|
);
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_info("Audio subsystem initialized (SDL2_Mixer)");
|
2017-03-20 07:08:09 +01:00
|
|
|
|
|
|
|
SDL_version v;
|
|
|
|
const SDL_version *lv;
|
|
|
|
|
|
|
|
SDL_MIXER_VERSION(&v);
|
|
|
|
log_info("Compiled against SDL_mixer %u.%u.%u", v.major, v.minor, v.patch);
|
|
|
|
|
|
|
|
lv = Mix_Linked_Version();
|
|
|
|
log_info("Using SDL_mixer %u.%u.%u", lv->major, lv->minor, lv->patch);
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void audio_backend_shutdown(void) {
|
2017-10-08 07:15:43 +02:00
|
|
|
mixer_loaded = false;
|
2017-11-20 02:46:27 +01:00
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
Mix_CloseAudio();
|
|
|
|
Mix_Quit();
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
|
|
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_info("Audio subsystem uninitialized (SDL2_Mixer)");
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_backend_initialized(void) {
|
|
|
|
return mixer_loaded;
|
|
|
|
}
|
|
|
|
|
|
|
|
void audio_backend_set_sfx_volume(float gain) {
|
|
|
|
if(mixer_loaded)
|
|
|
|
Mix_Volume(-1, gain * MIX_MAX_VOLUME);
|
|
|
|
}
|
|
|
|
|
|
|
|
void audio_backend_set_bgm_volume(float gain) {
|
|
|
|
if(mixer_loaded)
|
|
|
|
Mix_VolumeMusic(gain * MIX_MAX_VOLUME);
|
|
|
|
}
|
|
|
|
|
2017-11-17 00:41:01 +01:00
|
|
|
static char* try_path(const char *prefix, const char *name, const char *ext) {
|
|
|
|
char *p = strjoin(prefix, name, ext, NULL);
|
|
|
|
|
|
|
|
if(vfs_query(p).exists) {
|
|
|
|
log_debug("Returning %s", p);
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(p);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-02 02:37:29 +02:00
|
|
|
char* audio_mixer_sound_path(const char *prefix, const char *name, bool isbgm) {
|
2017-11-17 00:41:01 +01:00
|
|
|
char *p = NULL;
|
|
|
|
|
|
|
|
if(isbgm && (p = try_path(prefix, name, ".bgm"))) {
|
|
|
|
return p;
|
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
|
2017-11-17 00:41:01 +01:00
|
|
|
for(const char **ext = mixer_audio_exts; *ext; ++ext) {
|
|
|
|
if((p = try_path(prefix, name, *ext))) {
|
2017-03-02 11:23:30 +01:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_mixer_check_sound_path(const char *path, bool isbgm) {
|
2017-11-17 00:41:01 +01:00
|
|
|
if(isbgm && strendswith(path, ".bgm")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return strendswith_any(path, mixer_audio_exts);
|
2017-10-02 02:37:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static Mix_Music *next_loop;
|
2017-11-15 03:52:37 +01:00
|
|
|
static double next_loop_point;
|
2017-10-02 02:37:29 +02:00
|
|
|
|
|
|
|
static void mixer_music_finished(void) {
|
|
|
|
// XXX: there may be a race condition in here
|
|
|
|
// probably should protect next_loop with a mutex
|
|
|
|
|
2017-11-15 03:52:37 +01:00
|
|
|
log_debug("%s stopped playing", next_loop_point ? "Loop" : "Intro");
|
2017-03-02 11:23:30 +01:00
|
|
|
|
2017-10-02 02:37:29 +02:00
|
|
|
if(next_loop) {
|
2017-11-15 03:52:37 +01:00
|
|
|
if(Mix_PlayMusic(next_loop, next_loop_point ? 0 : -1) == -1) {
|
2017-10-02 02:37:29 +02:00
|
|
|
log_warn("Mix_PlayMusic() failed: %s", Mix_GetError());
|
2017-11-15 03:52:37 +01:00
|
|
|
} else if(next_loop_point >= 0) {
|
|
|
|
Mix_SetMusicPosition(next_loop_point);
|
2017-10-02 02:37:29 +02:00
|
|
|
} else {
|
|
|
|
next_loop = NULL;
|
|
|
|
}
|
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void audio_backend_music_stop(void) {
|
2017-10-02 02:37:29 +02:00
|
|
|
if(mixer_loaded) {
|
|
|
|
Mix_HookMusicFinished(NULL);
|
2017-03-02 11:23:30 +01:00
|
|
|
Mix_HaltMusic();
|
2017-10-02 02:37:29 +02:00
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
2017-10-08 08:01:52 +02:00
|
|
|
void audio_backend_music_fade(double fadetime) {
|
|
|
|
if(mixer_loaded) {
|
|
|
|
Mix_HookMusicFinished(NULL);
|
2017-12-17 01:00:06 +01:00
|
|
|
Mix_FadeOutMusic(floor(1000 * fadetime));
|
2017-10-08 08:01:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 11:23:30 +01:00
|
|
|
bool audio_backend_music_is_paused(void) {
|
|
|
|
return mixer_loaded && Mix_PausedMusic();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_backend_music_is_playing(void) {
|
2017-10-08 08:01:52 +02:00
|
|
|
return mixer_loaded && Mix_PlayingMusic() && Mix_FadingMusic() != MIX_FADING_OUT;
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void audio_backend_music_resume(void) {
|
2017-10-02 02:37:29 +02:00
|
|
|
if(mixer_loaded) {
|
|
|
|
Mix_HookMusicFinished(mixer_music_finished);
|
2017-03-02 11:23:30 +01:00
|
|
|
Mix_ResumeMusic();
|
2017-10-02 02:37:29 +02:00
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void audio_backend_music_pause(void) {
|
2017-10-02 02:37:29 +02:00
|
|
|
if(mixer_loaded) {
|
|
|
|
Mix_HookMusicFinished(NULL);
|
2017-03-02 11:23:30 +01:00
|
|
|
Mix_PauseMusic();
|
2017-10-02 02:37:29 +02:00
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_backend_music_play(void *impl) {
|
|
|
|
if(!mixer_loaded)
|
|
|
|
return false;
|
|
|
|
|
2017-10-02 02:37:29 +02:00
|
|
|
MixerInternalMusic *imus = impl;
|
|
|
|
Mix_Music *mmus;
|
|
|
|
int loops;
|
|
|
|
|
2017-10-08 08:01:52 +02:00
|
|
|
Mix_HookMusicFinished(NULL);
|
|
|
|
Mix_HaltMusic();
|
|
|
|
|
2017-10-02 02:37:29 +02:00
|
|
|
if(imus->intro) {
|
|
|
|
next_loop = imus->loop;
|
2018-01-09 00:32:52 +01:00
|
|
|
next_loop_point = next_loop ? imus->loop_point : 0;
|
2017-10-02 02:37:29 +02:00
|
|
|
mmus = imus->intro;
|
|
|
|
loops = 0;
|
|
|
|
Mix_HookMusicFinished(mixer_music_finished);
|
|
|
|
} else {
|
|
|
|
mmus = imus->loop;
|
2017-11-15 03:52:37 +01:00
|
|
|
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);
|
|
|
|
}
|
2017-10-02 02:37:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool result = (Mix_PlayMusic(mmus, loops) != -1);
|
2017-03-02 11:23:30 +01:00
|
|
|
|
|
|
|
if(!result) {
|
Implemented a simple and consistent logging subsystem
The goal of this change is mainly to clean up Taisei's codebase and
improve its console output. I've been frustrated by files littered with
inconsistent printf/fprintf/warnx/errx calls for a long time, and now I
actually did something about it.
All the above functions are now considered deprecated and result in a
compile-time warning when used. Instead, the following macros should be
used:
log_debug(format, ...)
log_info(format, ...)
log_warn(format, ...)
log_err(format, ...)
As you can see, all of them have the same printf-like interface. But
they have different functionality and purpose:
log_debug is intended for very verbose and specific information. It
does nothing in release builds, much like assert(), so don't use
expressions with side-effects in its arguments.
log_info is for various status updates that are expected during
normal operation of the program.
log_warn is for non-critical failures or other things that may be
worth investigating, but don't inherently render the program
non-functional.
log_err is for when the only choice is to give up. Like errx, it
also terminates the program. Unlike errx, it actually calls abort(),
which means the cleanup functions are not ran -- but on the other
hand, you get a debuggable backtrace. However, if you're trying to
catch programming errors, consider using assert() instead.
All of them produce output that contains a timestamp, the log level
identifier, the calling function's name, and the formatted message.
The newline at the end of the format string is not required -- no, it is
actually *prohibited*. The logging system will take care of the line
breaks by itself, don't litter the code with that shit.
Internally, the logging system is based on the SDL_RWops abstraction,
and may have multiple, configurable destinations. This makes it easily
extensible. Currently, log_debug and log_info are set to write to
stdout, log_warn and log_err to stderr, and all of them also to the file
log.txt in the Taisei config directory.
Consequently, the nasty freopen hacks we used to make Taisei write to
log files on Windows are no longer needed -- which is a very good thing,
considering they probably would break if the configdir path contains
UTF-8 characters. SDL_RWFromFile does not suffer this limitation.
As an added bonus, it's also thread-safe.
Note about printf and fprintf: in very few cases, the logging system is
not a good substitute for these functions. That is, when you care about
writing exactly to stdout/stderr and about exactly how the output looks.
However, I insist on keeping the deprecation warnings on them to not
tempt anyone to use them for logging/debugging out of habit and/or
laziness.
For this reason, I've added a tsfprintf function to util.c. It is
functionally identical to fprintf, except it returns void. Yes, the name
is deliberately ugly. Avoid using it if possible, but if you must, only
use it to write to stdout or stderr. Do not write to actual files with
it, use SDL_RWops.
2017-03-13 03:51:58 +01:00
|
|
|
log_warn("Mix_PlayMusic() failed: %s", Mix_GetError());
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-10-15 22:39:42 +02:00
|
|
|
bool audio_backend_music_set_position(double pos) {
|
|
|
|
if(!mixer_loaded) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: BGMs that have intros are not handled correctly!
|
|
|
|
|
|
|
|
Mix_RewindMusic();
|
|
|
|
|
|
|
|
if(Mix_SetMusicPosition(pos)) {
|
|
|
|
log_warn("Mix_SetMusicPosition() failed: %s", Mix_GetError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
static int translate_group(AudioBackendSoundGroup group, int defmixgroup) {
|
|
|
|
switch(group) {
|
2018-01-12 19:26:07 +01:00
|
|
|
case SNDGROUP_MAIN: return MAIN_CHANNEL_GROUP;
|
|
|
|
case SNDGROUP_UI: return UI_CHANNEL_GROUP;
|
|
|
|
default: return defmixgroup;
|
2017-10-08 07:15:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pick_channel(AudioBackendSoundGroup group, int defmixgroup) {
|
|
|
|
int mixgroup = translate_group(group, MAIN_CHANNEL_GROUP);
|
|
|
|
int channel = -1;
|
|
|
|
|
|
|
|
if((channel = Mix_GroupAvailable(mixgroup)) < 0) {
|
|
|
|
// all channels busy? try to override the oldest playing sound
|
|
|
|
if((channel = Mix_GroupOldest(mixgroup)) < 0) {
|
|
|
|
log_warn("No suitable channel available in group %i to play the sample on", mixgroup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return channel;
|
|
|
|
}
|
|
|
|
|
2017-10-29 02:30:05 +02:00
|
|
|
static int audio_backend_sound_play_on_channel(int chan, MixerInternalSound *isnd) {
|
|
|
|
chan = Mix_PlayChannel(chan, isnd->ch, 0);
|
|
|
|
|
|
|
|
if(chan < 0) {
|
|
|
|
log_warn("Mix_PlayChannel() failed: %s", Mix_GetError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
isnd->playchan = chan;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
bool audio_backend_sound_play(void *impl, AudioBackendSoundGroup group) {
|
2017-03-02 11:23:30 +01:00
|
|
|
if(!mixer_loaded)
|
|
|
|
return false;
|
|
|
|
|
2017-10-29 02:30:05 +02:00
|
|
|
MixerInternalSound *isnd = impl;
|
|
|
|
return audio_backend_sound_play_on_channel(pick_channel(group, MAIN_CHANNEL_GROUP), isnd);
|
|
|
|
}
|
2017-03-02 11:23:30 +01:00
|
|
|
|
2017-10-29 02:30:05 +02:00
|
|
|
bool audio_backend_sound_play_or_restart(void *impl, AudioBackendSoundGroup group) {
|
|
|
|
if(!mixer_loaded) {
|
|
|
|
return false;
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
|
|
|
|
2017-10-29 02:30:05 +02:00
|
|
|
MixerInternalSound *isnd = impl;
|
|
|
|
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);
|
2017-03-02 11:23:30 +01:00
|
|
|
}
|
2017-09-30 19:33:07 +02:00
|
|
|
|
2017-10-08 07:15:43 +02:00
|
|
|
bool audio_backend_sound_loop(void *impl, AudioBackendSoundGroup group) {
|
2017-09-30 19:33:07 +02:00
|
|
|
if(!mixer_loaded)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
MixerInternalSound *snd = (MixerInternalSound *)impl;
|
2017-10-08 07:15:43 +02:00
|
|
|
snd->loopchan = Mix_PlayChannel(pick_channel(group, MAIN_CHANNEL_GROUP), snd->ch, -1);
|
2017-09-30 19:33:07 +02:00
|
|
|
|
|
|
|
if(snd->loopchan == -1) {
|
|
|
|
log_warn("Mix_PlayChannel() failed: %s", Mix_GetError());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_backend_sound_stop_loop(void *impl) {
|
|
|
|
if(!mixer_loaded)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
MixerInternalSound *snd = (MixerInternalSound *)impl;
|
2018-01-08 17:56:46 +01:00
|
|
|
|
|
|
|
if(snd->loopchan == -1) {
|
2017-09-30 19:33:07 +02:00
|
|
|
return false;
|
2018-01-08 17:56:46 +01:00
|
|
|
}
|
|
|
|
|
2017-09-30 19:33:07 +02:00
|
|
|
Mix_HaltChannel(snd->loopchan);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
2017-10-08 07:15:43 +02:00
|
|
|
|
|
|
|
bool audio_backend_sound_pause_all(AudioBackendSoundGroup group) {
|
|
|
|
if(!mixer_loaded) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mixgroup = translate_group(group, -1);
|
|
|
|
|
|
|
|
if(mixgroup == -1) {
|
|
|
|
Mix_Pause(-1);
|
|
|
|
} else {
|
|
|
|
// why is there no Mix_PauseGroup?
|
|
|
|
|
|
|
|
for(int i = groups[mixgroup].first; i < groups[mixgroup].first + groups[mixgroup].num; ++i) {
|
|
|
|
Mix_Pause(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool audio_backend_sound_resume_all(AudioBackendSoundGroup group) {
|
|
|
|
if(!mixer_loaded) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mixgroup = translate_group(group, -1);
|
|
|
|
|
|
|
|
if(mixgroup == -1) {
|
|
|
|
Mix_Resume(-1);
|
|
|
|
} else {
|
|
|
|
// why is there no Mix_ResumeGroup?
|
|
|
|
|
|
|
|
for(int i = groups[mixgroup].first; i < groups[mixgroup].first + groups[mixgroup].num; ++i) {
|
|
|
|
Mix_Resume(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool audio_backend_sound_stop_all(AudioBackendSoundGroup group) {
|
|
|
|
if(!mixer_loaded) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mixgroup = translate_group(group, -1);
|
|
|
|
|
|
|
|
if(mixgroup == -1) {
|
|
|
|
Mix_HaltChannel(-1);
|
|
|
|
} else {
|
|
|
|
Mix_HaltGroup(mixgroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|