changeset 379:67c1c46af2c8

core: replace SDL2_mixer with OpenAL, closes #2528 @3h
author David Demelier <markand@malikania.fr>
date Wed, 05 Jan 2022 12:45:17 +0100
parents 460c78706989
children 31e2f6d35c34
files CMakeLists.txt cmake/FindSndFile.cmake examples/example-audio/main.c src/libmlk-core/CMakeLists.txt src/libmlk-core/core/music.c src/libmlk-core/core/music.h src/libmlk-core/core/sound.c src/libmlk-core/core/sound.h src/libmlk-core/core/sys.c src/libmlk-core/core/sys_p.h src/libmlk-rpg/rpg/battle-state-lost.c
diffstat 11 files changed, 332 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Sun Jan 02 10:22:48 2022 +0100
+++ b/CMakeLists.txt	Wed Jan 05 12:45:17 2022 +0100
@@ -27,7 +27,7 @@
 set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
 
 if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
-	set(CMAKE_C_FLAGS "-Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L ${CMAKE_C_FLAGS}")
+	set(CMAKE_C_FLAGS "-Wall -Wextra -Wno-deprecated-declarations -Wno-unknown-pragmas -pedantic -D_POSIX_C_SOURCE=200809L ${CMAKE_C_FLAGS}")
 
 	if (CMAKE_C_COMPILER_ID MATCHES "GNU")
 		set(CMAKE_C_FLAGS "-Wno-format-truncation ${CMAKE_C_FLAGS}")
@@ -57,7 +57,9 @@
 
 include(GNUInstallDirs)
 
-find_package(SDL2 REQUIRED COMPONENTS image mixer ttf)
+find_package(SDL2 REQUIRED COMPONENTS image ttf)
+find_package(OpenAL REQUIRED)
+find_package(SndFile REQUIRED)
 find_package(Jansson REQUIRED)
 
 # POSIX math library isn't available everywhere.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cmake/FindSndFile.cmake	Wed Jan 05 12:45:17 2022 +0100
@@ -0,0 +1,42 @@
+# FindSndFile
+# -----------
+#
+# Find SndFile library, this modules defines:
+#
+# SndFile_INCLUDE_DIRS, where to find sndfile.h
+# SndFile_LIBRARIES, where to find library
+# SndFile_FOUND, if it is found
+#
+# The following imported targets will be available:
+#
+# SndFile::SndFile, if found.
+#
+
+find_path(SndFile_INCLUDE_DIR NAMES sndfile.h)
+find_library(SndFile_LIBRARY NAMES libsndfile sndfile)
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(
+	SndFile
+	FOUND_VAR SndFile_FOUND
+	REQUIRED_VARS SndFile_LIBRARY SndFile_INCLUDE_DIR
+)
+
+if (SndFile_FOUND)
+	set(SndFile_LIBRARIES ${SndFile_LIBRARY})
+	set(SndFile_INCLUDE_DIRS ${SndFile_INCLUDE_DIR})
+
+	if (NOT TARGET SndFile::SndFile)
+		add_library(SndFile::SndFile UNKNOWN IMPORTED)
+		set_target_properties(
+			SndFile::SndFile
+			PROPERTIES
+				IMPORTED_LINK_INTERFACE_LANGUAGES "C"
+				IMPORTED_LOCATION "${SndFile_LIBRARY}"
+				INTERFACE_INCLUDE_DIRECTORIES "${SndFile_INCLUDE_DIRS}"
+		)
+	endif ()
+endif ()
+
+mark_as_advanced(SndFile_INCLUDE_DIR SndFile_LIBRARY)
--- a/examples/example-audio/main.c	Sun Jan 02 10:22:48 2022 +0100
+++ b/examples/example-audio/main.c	Wed Jan 05 12:45:17 2022 +0100
@@ -42,7 +42,7 @@
 static struct sound sound;
 
 static struct label label_music = {
-	.text = "Music: <Space> play, <f> fade in, <s> fade out, <p> pause, <r> resume, <q> stop, <l> loop.",
+	.text = "Music: <Space> play, <p> pause, <r> resume, <q> stop, <l> loop.",
 	.x = 10,
 	.y = 10,
 	.flags = LABEL_FLAGS_SHADOW
@@ -82,31 +82,25 @@
 
 	switch (ev->type) {
 	case EVENT_CLICKDOWN:
-		if (sound_play(&sound, -1, 0) < 0)
+		if (sound_play(&sound) < 0)
 			panic();
 		break;
 	case EVENT_KEYDOWN:
 		switch (ev->key.key) {
-		case KEY_f:
-			music_play(&music, 0, 500);
-			break;
-		case KEY_s:
-			music_stop(500);
-			break;
 		case KEY_p:
-			music_pause();
+			music_pause(&music);
 			break;
 		case KEY_r:
-			music_resume();
+			music_resume(&music);
 			break;
 		case KEY_q:
-			music_stop(0);
+			music_stop(&music);
 			break;
 		case KEY_l:
-			music_play(&music, MUSIC_LOOP, 0);
+			music_play(&music, MUSIC_LOOP);
 			break;
 		case KEY_SPACE:
-			music_play(&music, 0, 0);
+			music_play(&music, 0);
 			break;
 		default:
 			break;
--- a/src/libmlk-core/CMakeLists.txt	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-core/CMakeLists.txt	Wed Jan 05 12:45:17 2022 +0100
@@ -71,6 +71,7 @@
 	${libmlk-core_SOURCE_DIR}/core/state.h
 	${libmlk-core_SOURCE_DIR}/core/sys.c
 	${libmlk-core_SOURCE_DIR}/core/sys.h
+	${libmlk-core_SOURCE_DIR}/core/sys_p.h
 	${libmlk-core_SOURCE_DIR}/core/texture.c
 	${libmlk-core_SOURCE_DIR}/core/texture.h
 	${libmlk-core_SOURCE_DIR}/core/texture_p.h
@@ -125,13 +126,16 @@
 	LIBRARIES
 		PUBLIC
 			${LIBRARIES}
+			${OPENAL_LIBRARY}
+			SndFile::SndFile
 			SDL2::SDL2
 			SDL2::image
 			SDL2::ttf
-			SDL2::mixer
 			libmlk-port
 	INCLUDES
-		PUBLIC $<BUILD_INTERFACE:${libmlk-core_SOURCE_DIR}>
+		PUBLIC
+			${OPENAL_INCLUDE_DIR}
+			$<BUILD_INTERFACE:${libmlk-core_SOURCE_DIR}>
 )
 
 source_group(TREE ${libmlk-core_SOURCE_DIR} FILES ${SOURCES} ${NLS})
--- a/src/libmlk-core/core/music.c	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-core/core/music.c	Wed Jan 05 12:45:17 2022 +0100
@@ -19,12 +19,13 @@
 #include <assert.h>
 #include <string.h>
 
-#include <SDL_mixer.h>
-
 #include "error.h"
 #include "music.h"
 #include "vfs.h"
 #include "vfs_p.h"
+#include "sys_p.h"
+
+#define SOURCE(mus) ((const struct audiostream *)mus->handle)->source
 
 int
 music_open(struct music *mus, const char *path)
@@ -32,8 +33,8 @@
 	assert(mus);
 	assert(path);
 
-	if (!(mus->handle = Mix_LoadMUS(path)))
-		return errorf("%s", SDL_GetError());
+	if (!(mus->handle = audiostream_open(path)))
+		return -1;
 
 	return 0;
 }
@@ -44,11 +45,8 @@
 	assert(mus);
 	assert(buffer);
 
-	SDL_RWops *ops;
-
-	if (!(ops = SDL_RWFromConstMem(buffer, buffersz)) ||
-	    !(mus->handle = Mix_LoadMUS_RW(ops, 1)))
-		return errorf("%s", SDL_GetError());
+	if (!(mus->handle = audiostream_openmem(buffer, buffersz)))
+		return -1;
 
 	return 0;
 }
@@ -59,13 +57,6 @@
 	assert(mus);
 	assert(vfs_file_ok(file));
 
-	SDL_RWops *ops;
-
-	if (!(ops = vfs_to_rw(file)))
-		return -1;
-	if (!(mus->handle = Mix_LoadMUS_RW(ops, 1)))
-		return errorf("%s", SDL_GetError());
-
 	return 0;
 }
 
@@ -76,49 +67,43 @@
 }
 
 int
-music_play(struct music *mus, enum music_flags flags, unsigned int fadein)
+music_play(struct music *mus, enum music_flags flags)
 {
 	assert(mus);
 
-	int loops = flags & MUSIC_LOOP ? -1 : 1;
-	int ret;
+	if (flags & MUSIC_LOOP)
+		alSourcei(SOURCE(mus), AL_LOOPING, AL_TRUE);
+	else
+		alSourcei(SOURCE(mus), AL_LOOPING, AL_TRUE);
 
-	if (fadein > 0)
-		ret = Mix_FadeInMusic(mus->handle, loops, fadein);
-	else
-		ret = Mix_PlayMusic(mus->handle, loops);
-
-	if (ret < 0)
-		return errorf("%s", SDL_GetError());
+	alSourceRewind(SOURCE(mus));
+	alSourcePlay(SOURCE(mus));
 
 	return 0;
 }
 
-int
-music_playing(void)
+void
+music_pause(struct music *mus)
 {
-	return Mix_PlayingMusic();
-}
+	assert(music_ok(mus));
 
-void
-music_pause(void)
-{
-	Mix_PauseMusic();
+	alSourcePause(SOURCE(mus));
 }
 
 void
-music_resume(void)
+music_resume(struct music *mus)
 {
-	Mix_ResumeMusic();
+	assert(music_ok(mus));
+
+	alSourcePlay(SOURCE(mus));
 }
 
 void
-music_stop(unsigned int fadeout)
+music_stop(struct music *mus)
 {
-	if (fadeout > 0)
-		Mix_FadeOutMusic(fadeout);
-	else
-		Mix_HaltMusic();
+	assert(music_ok(mus));
+
+	alSourceStop(SOURCE(mus));
 }
 
 void
@@ -127,8 +112,8 @@
 	assert(mus);
 
 	if (mus->handle) {
-		Mix_HaltMusic();
-		Mix_FreeMusic(mus->handle);
+		music_stop(mus);
+		audiostream_finish(mus->handle);
 	}
 
 	memset(mus, 0, sizeof (*mus));
--- a/src/libmlk-core/core/music.h	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-core/core/music.h	Wed Jan 05 12:45:17 2022 +0100
@@ -49,19 +49,16 @@
 music_ok(const struct music *);
 
 int
-music_play(struct music *, enum music_flags, unsigned int);
-
-int
-music_playing(void);
+music_play(struct music *, enum music_flags);
 
 void
-music_pause(void);
+music_pause(struct music *);
 
 void
-music_resume(void);
+music_resume(struct music *);
 
 void
-music_stop(unsigned int);
+music_stop(struct music *);
 
 void
 music_finish(struct music *);
--- a/src/libmlk-core/core/sound.c	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-core/core/sound.c	Wed Jan 05 12:45:17 2022 +0100
@@ -20,12 +20,13 @@
 #include <stdio.h>
 #include <string.h>
 
-#include <SDL_mixer.h>
-
 #include "error.h"
 #include "sound.h"
 #include "vfs.h"
 #include "vfs_p.h"
+#include "sys_p.h"
+
+#define SOURCE(snd) ((const struct audiostream *)snd->handle)->source
 
 int
 sound_open(struct sound *snd, const char *path)
@@ -33,8 +34,8 @@
 	assert(snd);
 	assert(path);
 
-	if (!(snd->handle = Mix_LoadWAV(path)))
-		return errorf("%s", SDL_GetError());
+	if (!(snd->handle = audiostream_open(path)))
+		return -1;
 
 	return 0;
 }
@@ -45,11 +46,8 @@
 	assert(snd);
 	assert(buffer);
 
-	SDL_RWops *ops;
-
-	if (!(ops = SDL_RWFromConstMem(buffer, buffersz)) ||
-	    !(snd->handle = Mix_LoadWAV_RW(ops, 1)))
-		return errorf("%s", SDL_GetError());
+	if (!(snd->handle = audiostream_openmem(buffer, buffersz)))
+		return -1;
 
 	return 0;
 }
@@ -60,14 +58,18 @@
 	assert(snd);
 	assert(vfs_file_ok(file));
 
-	SDL_RWops *ops;
+	char *data;
+	size_t datasz;
+	int ret = 0;
 
-	if (!(ops = vfs_to_rw(file)))
+	if (!(data = vfs_file_aread(file, &datasz)))
 		return -1;
-	if (!(snd->handle = Mix_LoadWAV_RW(ops, 1)))
-		return errorf("%s", SDL_GetError());
+	if (!(snd->handle = audiostream_openmem(data, datasz)))
+		ret = -1;
 
-	return 0;
+	free(data);
+
+	return ret;
 }
 
 int
@@ -77,21 +79,11 @@
 }
 
 int
-sound_play(struct sound *snd, int channel, unsigned int fadein)
+sound_play(struct sound *snd)
 {
 	assert(sound_ok(snd));
 
-	int ret;
-
-	if (fadein > 0)
-		ret = Mix_FadeInChannel(channel, snd->handle, 0, fadein);
-	else
-		ret = Mix_PlayChannel(channel, snd->handle, 0);
-
-	if (ret < 0)
-		return errorf("%s", SDL_GetError());
-
-	snd->channel = channel;
+	alSourcePlay(SOURCE(snd));
 
 	return 0;
 }
@@ -99,22 +91,25 @@
 void
 sound_pause(struct sound *snd)
 {
-	Mix_Pause(snd ? snd->channel : -1);
+	assert(sound_ok(snd));
+
+	alSourcePause(SOURCE(snd));
 }
 
 void
 sound_resume(struct sound *snd)
 {
-	Mix_Resume(snd ? snd->channel : -1);
+	assert(sound_ok(snd));
+
+	alSourcePlay(SOURCE(snd));
 }
 
 void
-sound_stop(struct sound *snd, unsigned int fadeout)
+sound_stop(struct sound *snd)
 {
-	if (fadeout > 0)
-		Mix_FadeOutChannel(snd ? snd->channel : -1, fadeout);
-	else
-		Mix_HaltChannel(snd ? snd->channel : -1);
+	assert(sound_ok(snd));
+
+	alSourceStop(SOURCE(snd));
 }
 
 void
@@ -123,8 +118,8 @@
 	assert(snd);
 
 	if (snd->handle) {
-		Mix_HaltChannel(snd->channel);
-		Mix_FreeChunk(snd->handle);
+		sound_stop(snd);
+		audiostream_finish(snd->handle);
 	}
 
 	memset(snd, 0, sizeof (*snd));
--- a/src/libmlk-core/core/sound.h	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-core/core/sound.h	Wed Jan 05 12:45:17 2022 +0100
@@ -47,7 +47,7 @@
 sound_ok(const struct sound *);
 
 int
-sound_play(struct sound *, int, unsigned int);
+sound_play(struct sound *);
 
 void
 sound_pause(struct sound *);
@@ -56,7 +56,7 @@
 sound_resume(struct sound *);
 
 void
-sound_stop(struct sound *, unsigned int);
+sound_stop(struct sound *);
 
 void
 sound_finish(struct sound *);
--- a/src/libmlk-core/core/sys.c	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-core/core/sys.c	Wed Jan 05 12:45:17 2022 +0100
@@ -20,8 +20,10 @@
 
 #include <sys/stat.h>
 #include <assert.h>
+#include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <limits.h>
 
 #if defined(_WIN32)
@@ -34,14 +36,21 @@
 
 #include <SDL.h>
 #include <SDL_image.h>
-#include <SDL_mixer.h>
 #include <SDL_ttf.h>
 
+#include <sndfile.h>
+
 #include <port/port.h>
 
+#include "alloc.h"
 #include "error.h"
+#include "panic.h"
 #include "sound.h"
 #include "sys.h"
+#include "sys_p.h"
+
+ALCdevice *audio_dev = NULL;
+ALCcontext *audio_ctx = NULL;
 
 static struct {
 	char organization[128];
@@ -192,18 +201,21 @@
 	port_strlcpy(info.organization, organization, sizeof (info.organization));
 	port_strlcpy(info.name, name, sizeof (info.name));
 
+	/* SDL2. */
 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
 		return errorf("%s", SDL_GetError());
 	if (IMG_Init(IMG_INIT_PNG) != IMG_INIT_PNG)
 		return errorf("%s", SDL_GetError());
 	if (TTF_Init() < 0)
 		return errorf("%s", SDL_GetError());
-	if (Mix_Init(MIX_INIT_OGG) != MIX_INIT_OGG)
-		return errorf("%s", SDL_GetError());
-	if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0)
-		return errorf("%s", SDL_GetError());
 
-	Mix_AllocateChannels(SOUND_CHANNELS_MAX);
+	/* OpenAL. */
+	if (!(audio_dev = alcOpenDevice(NULL)))
+		return errorf("unable to create audio device");
+	if (!(audio_ctx = alcCreateContext(audio_dev, NULL)))
+		return errorf("unable to create audio context");
+
+	alcMakeContextCurrent(audio_ctx);
 
 	return 0;
 }
@@ -259,8 +271,149 @@
 void
 sys_finish(void)
 {
-	Mix_Quit();
 	TTF_Quit();
 	IMG_Quit();
 	SDL_Quit();
+
+	alcMakeContextCurrent(NULL);
+
+	if (audio_ctx) {
+		alcDestroyContext(audio_ctx);
+		audio_ctx = NULL;
+	}
+	if (audio_dev) {
+		alcCloseDevice(audio_dev);
+		audio_dev = NULL;
+	}
 }
+
+struct audiostream *
+audiostream_create(SNDFILE *file, const SF_INFO *info)
+{
+	struct audiostream *stream;
+
+	stream = alloc_new(sizeof (*stream));
+	stream->samplerate = info->samplerate;
+	stream->samplesz = info->frames * info->channels;
+	stream->samples = alloc_array(stream->samplesz, sizeof (*stream->samples));
+	stream->format = info->channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
+
+	if (sf_read_short(file, stream->samples, stream->samplesz) != stream->samplesz) {
+		free(stream->samples);
+		free(stream);
+		stream = NULL;
+	}
+
+	alGenBuffers(1, &stream->buffer);
+	alBufferData(stream->buffer, stream->format, stream->samples,
+	    stream->samplesz * sizeof (ALushort), stream->samplerate);
+	alGenSources(1, &stream->source);
+	alSourcei(stream->source, AL_BUFFER, stream->buffer);
+	
+	sf_close(file);
+
+	return stream;
+}
+
+struct audiostream *
+audiostream_open(const char *path)
+{
+	assert(path);
+
+	SF_INFO info;
+	SNDFILE *file;
+
+	if (!(file = sf_open(path, SFM_READ, &info)))
+		return NULL;
+
+	return audiostream_create(file, &info);
+}
+
+struct viodata {
+	const unsigned char *data;
+	const size_t datasz;
+	sf_count_t offset;
+};
+
+static sf_count_t
+vio_get_filelen(void *data)
+{
+	const struct viodata *vio = data;
+
+	return (sf_count_t)vio->datasz;
+}
+
+static sf_count_t
+vio_seek(sf_count_t offset, int whence, void *data)
+{
+	struct viodata *vio = data;
+
+	switch (whence) {
+	case SEEK_SET:
+		vio->offset = offset;
+		break;
+	case SEEK_CUR:
+		vio->offset += offset;
+		break;
+	case SEEK_END:
+		vio->offset = vio->datasz - offset;
+		break;
+	default:
+		break;
+	}
+
+	return vio->offset;
+}
+
+static sf_count_t
+vio_read(void *ptr, sf_count_t count, void *data)
+{
+	struct viodata *vio = data;
+
+	memcpy(ptr, vio->data + vio->offset, count);
+	vio->offset += count;
+
+	return count;
+}
+
+static sf_count_t
+vio_tell(void *data)
+{
+	const struct viodata *vio = data;
+
+	return vio->offset;
+}
+
+struct audiostream *
+audiostream_openmem(const void *data, size_t datasz)
+{
+	assert(data);
+
+	SF_VIRTUAL_IO io = {
+		.get_filelen = vio_get_filelen,
+		.seek = vio_seek,
+		.read = vio_read,
+		.tell = vio_tell
+	};
+	SF_INFO info;
+	SNDFILE *file;
+	struct viodata viodata = {
+		.data = data,
+		.datasz = datasz,
+	};
+	
+	if (!(file = sf_open_virtual(&io, SFM_READ, &info, &viodata)))
+		return NULL;
+
+	return audiostream_create(file, &info);
+}
+
+void
+audiostream_finish(struct audiostream *s)
+{
+	assert(s);
+
+	alDeleteBuffers(1, &s->buffer);
+	alSourcei(s->source, AL_BUFFER, 0);
+	alDeleteSources(1, &s->source);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/libmlk-core/core/sys_p.h	Wed Jan 05 12:45:17 2022 +0100
@@ -0,0 +1,48 @@
+/*
+ * sys_p.h -- libcore private definitions
+ *
+ * Copyright (c) 2020-2022 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef MLK_CORE_SYS_P_H
+#define MLK_CORE_SYS_P_H
+
+#include <stddef.h>
+
+#include <al.h>
+#include <alc.h>
+
+extern ALCdevice *audio_dev;
+extern ALCcontext *audio_ctx;
+
+struct audiostream {
+	ALshort *samples;
+	ALsizei samplesz;
+	ALsizei samplerate;
+	ALuint buffer;
+	ALuint source;
+	ALenum format;
+};
+
+struct audiostream *
+audiostream_open(const char *);
+
+struct audiostream *
+audiostream_openmem(const void *, size_t);
+
+void
+audiostream_finish(struct audiostream *);
+
+#endif /* !MLK_CORE_SYS_P_H */
--- a/src/libmlk-rpg/rpg/battle-state-lost.c	Sun Jan 02 10:22:48 2022 +0100
+++ b/src/libmlk-rpg/rpg/battle-state-lost.c	Wed Jan 05 12:45:17 2022 +0100
@@ -101,5 +101,5 @@
 	battle_switch(bt, &lost->self);
 
 	if (bt->music[2])
-		music_play(bt->music[2], MUSIC_NONE, 0);
+		music_play(bt->music[2], MUSIC_NONE);
 }