Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch audio assets to Opus #167

Merged
merged 4 commits into from
May 11, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Switch SFX to Opus; use floating-point mixing
  • Loading branch information
Akaricchi committed May 7, 2019
commit 4c815c8596c0783c4bae04ac63b4741d3436a0fe
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Dependencies
- libpng >= 1.5.0
- libwebpdecoder >= 0.5 or libwebp >= 0.5
- libzip >= 1.2
- opusfile
- zlib

Optional:
Expand Down
3 changes: 2 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,14 @@ endif
static = get_option('static') or (host_machine.system() == 'emscripten')

dep_freetype = dependency('freetype2', required : true, static : static, fallback : ['freetype', 'freetype_dep'])
dep_opusfile = dependency('opusfile', required : false, static : static, fallback : ['opusfile', 'opusfile_dep'])
dep_png = dependency('libpng', version : '>=1.5', required : true, static : static, fallback : ['libpng', 'png_dep'])
dep_sdl2 = dependency('sdl2', version : '>=2.0.5', required : true, static : static, fallback : ['sdl2', 'sdl2_dep'])
dep_sdl2_mixer = dependency('SDL2_mixer', version : '>=2.0.4', required : false, static : static, fallback : ['sdl2_mixer', 'sdl2_mixer_dep'])
dep_webp = dependency('libwebp', version : '>=0.5', required : false, static : static, fallback : ['libwebp', 'webp_dep'])
dep_webpdecoder = dependency('libwebpdecoder', version : '>=0.5', required : false, static : static, fallback : ['libwebp', 'webpdecoder_dep'])
dep_zlib = dependency('zlib', required : true, static : static, fallback : ['zlib', 'zlib_dep'])
dep_zip = dependency('libzip', version : '>=1.2', required : false, static : static, fallback : ['libzip', 'libzip_dep'])
dep_zlib = dependency('zlib', required : true, static : static, fallback : ['zlib', 'zlib_dep'])
dep_crypto = dependency('libcrypto', required : false, static : static)

dep_m = cc.find_library('m', required : false)
Expand Down
Binary file removed resources/00-taisei.pkgdir/sfx/bomb_marisa_a.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/bomb_marisa_a.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/bomb_marisa_b.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/bomb_marisa_b.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/bomb_reimu_a.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/bomb_reimu_a.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/bomb_youmu_a.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/bomb_youmu_a.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/bomb_youmu_b.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/bomb_youmu_b.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/boom.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/boom.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/boon.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/boon.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/bossdeath.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/bossdeath.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/charge_extra.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/charge_extra.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/charge_generic.ogg
Binary file not shown.
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/death.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/death.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/enemydeath.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/enemydeath.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/extra_bomb.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/extra_bomb.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/extra_life.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/extra_life.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/generic_shot.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/generic_shot.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/graze.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/graze.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/hit.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/hit.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/hit0.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/hit0.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/hit1.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/hit1.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/item_generic.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/item_generic.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/laser1.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/laser1.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/noise1.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/noise1.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/powersurge_end.ogg
Binary file not shown.
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/powersurge_start.ogg
Binary file not shown.
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/powerup.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/powerup.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/redirect.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/redirect.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/shot1.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/shot1.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/shot1_loop.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/shot1_loop.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/shot2.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/shot2.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/shot3.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/shot3.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/shot_special1.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/shot_special1.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/spellclear.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/spellclear.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/spellend.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/spellend.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/timeout1.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/timeout1.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/timeout2.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/timeout2.opus
Binary file not shown.
Binary file removed resources/00-taisei.pkgdir/sfx/warp.ogg
Binary file not shown.
Binary file added resources/00-taisei.pkgdir/sfx/warp.opus
Binary file not shown.
145 changes: 126 additions & 19 deletions src/audio/sdl2mixer/audio_sdl2mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,32 @@
#include "taisei.h"

#include <SDL_mixer.h>
#include <opusfile.h>

#include "../backend.h"
#include "global.h"
#include "list.h"
#include "util/opuscruft.h"

#define B (_a_backend.funcs)

#define AUDIO_FREQ 44800
#define AUDIO_FORMAT MIX_DEFAULT_FORMAT
#define AUDIO_FREQ 48000
#define AUDIO_FORMAT AUDIO_F32SYS
#define AUDIO_CHANNELS 32
#define UI_CHANNELS 4
#define UI_CHANNEL_GROUP 0
#define MAIN_CHANNEL_GROUP 1

#if AUDIO_FORMAT == AUDIO_S16SYS
typedef int16_t sample_t;
#define OPUS_READ_SAMPLES_STEREO op_read_stereo
#elif AUDIO_FORMAT == AUDIO_F32SYS
typedef float sample_t;
#define OPUS_READ_SAMPLES_STEREO op_read_float_stereo
#else
#error Audio format not recognized
#endif

// I needed to add this for supporting loop sounds since Mixer doesn’t remember
// what channel a sound is playing on. - laochailan

Expand Down Expand Up @@ -71,7 +83,7 @@ static bool audio_sdl2mixer_init(void) {
return false;
}

if(!(Mix_Init(MIX_INIT_OGG) & MIX_INIT_OGG)) {
if(!(Mix_Init(MIX_INIT_OPUS) & MIX_INIT_OPUS)) {
log_error("Mix_Init() failed: %s", Mix_GetError());
Mix_Quit();
return false;
Expand Down Expand Up @@ -346,10 +358,9 @@ static void custom_fadeout_free(int chan, void *udata) {
static void custom_fadeout_proc(int chan, void *stream, int len, void *udata) {
CustomFadeout *e = udata;

assert(AUDIO_FORMAT == AUDIO_S16SYS); // if you wanna change the format, you get to implement it here. This is the hardcoded default format in SDL_Mixer by the way
sample_t *data = stream;
len /= sizeof(sample_t);

int16_t *data = stream;
len /= 2;
for(int i = 0; i < len; i++) {
e->counter++;
data[i]*=1.-min(1,(double)e->counter/(double)e->duration);
Expand Down Expand Up @@ -416,13 +427,7 @@ static bool audio_sdl2mixer_sound_stop_all(AudioBackendSoundGroup group) {
return true;
}

static const char *const *audio_sdl2mixer_get_supported_sfx_exts(uint *numexts) {
static const char *const exts[] = { ".ogg", ".wav" };
*numexts = ARRAY_SIZE(exts);
return exts;
}

static const char *const *audio_sdl2mixer_get_supported_bgm_exts(uint *numexts) {
static const char *const *audio_sdl2mixer_get_supported_exts(uint *numexts) {
static const char *const exts[] = { ".opus", ".ogg", ".wav" };
*numexts = ARRAY_SIZE(exts);
return exts;
Expand Down Expand Up @@ -455,6 +460,92 @@ static void audio_sdl2mixer_music_unload(MusicImpl *imus) {
free(imus);
}

static OggOpusFile *try_load_opus(SDL_RWops *rw) {
uint8_t buf[128];
SDL_RWread(rw, buf, 1, sizeof(buf));
int error = op_test(NULL, buf, sizeof(buf));

if(error != 0) {
log_debug("op_test() failed: %i", error);
goto fail;
}

OggOpusFile *opus = op_open_callbacks(rw, &opusutil_rwops_callbacks, buf, sizeof(buf), &error);

if(opus == NULL) {
log_debug("op_open_callbacks() failed: %i", error);
goto fail;
}

return opus;

fail:
SDL_RWseek(rw, 0, RW_SEEK_SET);
return NULL;
}

static Mix_Chunk *read_opus_chunk(OggOpusFile *opus, const char *vfspath) {
// WARNING: it's important to use the SDL_* allocation functions here for the pcm buffer,
// so that SDL_mixer can free it properly later.

const size_t read_size = 1920;
size_t buf_size = read_size * 4;
size_t samples = 0;
sample_t *pcm = SDL_malloc(buf_size * sizeof(*pcm)), *pcm_ptr = pcm;

for(;;) {
ptrdiff_t ofs = (ptrdiff_t)(pcm_ptr - pcm);

if(buf_size - ofs < read_size) {
buf_size *= 2;
pcm = SDL_realloc(pcm, buf_size * sizeof(*pcm));
pcm_ptr = pcm + ofs;
}

int r = OPUS_READ_SAMPLES_STEREO(opus, pcm_ptr, read_size);

if(r == OP_HOLE) {
continue;
}

if(r < 0) {
log_error("Opus decode error %i after %zu samples (%s)", r, samples, vfspath);
SDL_free(pcm);
op_free(opus);
return NULL;
}

// r is the number of samples read !!PER CHANNEL!!
// since we tell Opusfile to downmix to stereo, exactly 2 channels are guaranteed.
int read = r * 2;
samples += read;

if(read == 0) {
op_free(opus);

// try to avoid wasting memory
pcm = SDL_realloc(pcm, samples * sizeof(*pcm));

Mix_Chunk *snd = Mix_QuickLoad_RAW((void*)pcm, samples * sizeof(*pcm));

if(!snd) {
log_error("Mix_QuickLoad_RAW() failed: %s (%s)", Mix_GetError(), vfspath);
SDL_free(pcm);
return NULL;
}

// hack to make SDL_mixer free the buffer for us
snd->allocated = true;

return snd;
}

pcm_ptr += read;
}

UNREACHABLE;
}

static SoundImpl *audio_sdl2mixer_sound_load(const char *vfspath) {
SDL_RWops *rw = vfs_open(vfspath, VFS_MODE_READ | VFS_MODE_SEEKABLE);

Expand All @@ -463,11 +554,27 @@ static SoundImpl *audio_sdl2mixer_sound_load(const char *vfspath) {
return NULL;
}

Mix_Chunk *snd = Mix_LoadWAV_RW(rw, true);
// SDL2_Mixer as of 2.0.4 is too dumb to actually try to load Opus chunks,
// even if it was compiled with Opus support (which only works for music apparently /facepalm)
// So we'll try to load them via opusfile manually.

if(!snd) {
log_error("Mix_LoadWAV_RW() failed: %s (%s)", Mix_GetError(), vfspath);
return NULL;
Mix_Chunk *snd = NULL;
OggOpusFile *opus = try_load_opus(rw);

if(opus) {
snd = read_opus_chunk(opus, vfspath);

if(!snd) {
// error message printed by read_opus_chunk
return NULL;
}
} else {
snd = Mix_LoadWAV_RW(rw, true);

if(!snd) {
log_error("Mix_LoadWAV_RW() failed: %s (%s)", Mix_GetError(), vfspath);
return NULL;
}
}

SoundImpl *isnd = calloc(1, sizeof(*isnd));
Expand All @@ -492,8 +599,8 @@ static bool audio_sdl2mixer_sound_set_volume(SoundImpl *isnd, double gain) {
AudioBackend _a_backend_sdl2mixer = {
.name = "sdl2mixer",
.funcs = {
.get_supported_music_exts = audio_sdl2mixer_get_supported_bgm_exts,
.get_supported_sound_exts = audio_sdl2mixer_get_supported_sfx_exts,
.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,
Expand Down
7 changes: 4 additions & 3 deletions src/audio/sdl2mixer/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ a_sdl2mixer_src = files(
)

a_sdl2mixer_deps = []
a_sdl2mixer_libdeps = [dep_sdl2_mixer]
a_sdl2mixer_libdeps = [dep_sdl2_mixer, dep_opusfile]

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')
if get_option('a_sdl2mixer')
assert(dep_sdl2_mixer.found(), 'sdl2mixer backend enabled, but SDL2_mixer was not found. Install SDL2_mixer or disable a_sdl2mixer')
assert(dep_opusfile.found(), 'sdl2mixer backend enabled, but opusfile was not found. Install opusfile or disable a_sdl2mixer')
endif
4 changes: 2 additions & 2 deletions src/credits.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ static void credits_fill(void) {
"https://zlib.net/\n\n"
"libpng\n"
"http://www.libpng.org/\n\n"
"Ogg Vorbis\n"
"https://xiph.org/vorbis/"
"Ogg Opus\n"
"https://www.opus-codec.org/"
), 350);

credits_add((
Expand Down
4 changes: 4 additions & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ taisei_deps += [
util_deps,
]

if taisei_deps.contains(dep_opusfile)
taisei_src += util_opus_src
endif

taisei_basename = (macos_app_bundle ? 'Taisei' : 'taisei')

if host_machine.system() == 'emscripten'
Expand Down
4 changes: 4 additions & 0 deletions src/util/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ util_src = files(
'stringops.c',
)

util_opus_src = files(
'opuscruft.c',
)

sse42_src += files(
'sse42.c',
)
Expand Down
41 changes: 41 additions & 0 deletions src/util/opuscruft.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 "opuscruft.h"

#include <SDL.h>
#include <opusfile.h>

static int opusutil_rwops_read(void *_stream, unsigned char *_ptr, int _nbytes) {
SDL_RWops *rw = _stream;
return SDL_RWread(rw, _ptr, 1, _nbytes);
}

static int opusutil_rwops_seek(void *_stream, opus_int64 _offset, int _whence) {
SDL_RWops *rw = _stream;
return SDL_RWseek(rw, _offset, _whence) < 0 ? -1 : 0;
}

static opus_int64 opusutil_rwops_tell(void *_stream) {
SDL_RWops *rw = _stream;
return SDL_RWtell(rw);
}

static int opusutil_rwops_close(void *_stream) {
SDL_RWops *rw = _stream;
return SDL_RWclose(rw) < 0 ? EOF : 0;
}

OpusFileCallbacks opusutil_rwops_callbacks = {
.read = opusutil_rwops_read,
.seek = opusutil_rwops_seek,
.tell = opusutil_rwops_tell,
.close = opusutil_rwops_close,
};
18 changes: 18 additions & 0 deletions src/util/opuscruft.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* 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_util_opuscruft_h
#define IGUARD_util_opuscruft_h

#include "taisei.h"

#include <opusfile.h>

extern OpusFileCallbacks opusutil_rwops_callbacks;

#endif // IGUARD_util_opuscruft_h