From c8582d2b3b34c0b24d92e300856ca5a61ee17d19 Mon Sep 17 00:00:00 2001
From: Fabian Greffrath <[EMAIL REDACTED]>
Date: Sun, 26 Feb 2023 07:45:53 +0100
Subject: [PATCH] load sounds via libsndfile
---
CMakeLists.txt | 34 ++++++
cmake/FindSndFile.cmake | 32 +++++
cmake/SDL3_mixerConfig.cmake.in | 6 +
src/codecs/load_sndfile.c | 210 ++++++++++++++++++++++++++++++++
src/codecs/load_sndfile.h | 39 ++++++
src/mixer.c | 22 ++--
6 files changed, 335 insertions(+), 8 deletions(-)
create mode 100644 cmake/FindSndFile.cmake
create mode 100644 src/codecs/load_sndfile.c
create mode 100644 src/codecs/load_sndfile.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4746bdd9..9eb62ba5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,6 +77,9 @@ else()
endif()
option(SDL3MIXER_CMD "Support an external music player" ${sdl3mixer_cmd_default})
+option(SDL3MIXER_SNDFILE "Support loading sounds via libsndfile" OFF)
+option(SDL3MIXER_SNDFILE_SHARED "Dynamically load libsndfile" "${SDL3MIXER_DEPS_SHARED}")
+
option(SDL3MIXER_FLAC "Enable FLAC music" ON)
cmake_dependent_option(SDL3MIXER_FLAC_LIBFLAC "Enable FLAC music using libFLAC" ON SDL3MIXER_FLAC OFF)
@@ -210,6 +213,7 @@ set(BUILD_SHARED_LIBS ${SDL3MIXER_BUILD_SHARED_LIBS})
add_library(SDL3_mixer
src/codecs/load_aiff.c
src/codecs/load_voc.c
+ src/codecs/load_sndfile.c
src/codecs/mp3utils.c
src/codecs/music_cmd.c
src/codecs/music_drflac.c
@@ -329,6 +333,35 @@ if(SDL3MIXER_CMD)
endif()
endif()
+if(SDL3MIXER_SNDFILE)
+ target_compile_definitions(SDL3_mixer PRIVATE LOAD_SNDFILE)
+ if(SDL3MIXER_VENDORED)
+ message(STATUS "Using vendored libsndfile")
+ message(FATAL_ERROR "libsndfile is not vendored.")
+ else()
+ message(STATUS "Using system libsndfile")
+ find_package(SndFile REQUIRED)
+ if(NOT SDL3MIXER_SNDFILE_SHARED)
+ list(APPEND PC_REQUIRES sndfile)
+ endif()
+ endif()
+ if(SDL3MIXER_SNDFILE_SHARED)
+ target_include_directories(SDL3_mixer PRIVATE
+ $<TARGET_PROPERTY:SndFile::sndfile,INCLUDE_DIRECTORIES>
+ $<TARGET_PROPERTY:SndFile::sndfile,INTERFACE_INCLUDE_DIRECTORIES>
+ $<TARGET_PROPERTY:SndFile::sndfile,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>
+ )
+ target_get_dynamic_library(dynamic_sndfile SndFile::sndfile)
+ message(STATUS "Dynamic libsndfile: ${dynamic_sndfile}")
+ target_compile_definitions(SDL3_mixer PRIVATE "SNDFILE_DYNAMIC=\"${dynamic_sndfile}\"")
+ if(SDL3MIXER_VENDORED)
+ add_dependencies(SDL3_mixer SndFile::sndfile)
+ endif()
+ else()
+ target_link_libraries(SDL3_mixer PRIVATE SndFile::sndfile)
+ endif()
+endif()
+
if(SDL3MIXER_OGG)
# libogg is a requirement of libflac, libtremor and libvorbisfile, so only need this library when vendoring
if(SDL3MIXER_VENDORED)
@@ -906,6 +939,7 @@ if(SDL3MIXER_INSTALL)
cmake/FindVorbis.cmake
cmake/Findtremor.cmake
cmake/Findwavpack.cmake
+ cmake/FindSndFile.cmake
DESTINATION "${SDL3MIXER_INSTALL_CMAKEDIR}"
COMPONENT devel
)
diff --git a/cmake/FindSndFile.cmake b/cmake/FindSndFile.cmake
new file mode 100644
index 00000000..e277c54b
--- /dev/null
+++ b/cmake/FindSndFile.cmake
@@ -0,0 +1,32 @@
+include(FindPackageHandleStandardArgs)
+
+find_library(SndFile_LIBRARY
+ NAMES sndfile sndfile-1
+)
+
+find_path(SndFile_INCLUDE_PATH
+ NAMES sndfile.h
+)
+
+set(SndFile_COMPILE_OPTIONS "" CACHE STRING "Extra compile options of libsndfile")
+
+set(SndFile_LINK_LIBRARIES "" CACHE STRING "Extra link libraries of libsndfile")
+
+set(SndFile_LINK_FLAGS "" CACHE STRING "Extra link flags of libsndfile")
+
+find_package_handle_standard_args(SndFile
+ REQUIRED_VARS SndFile_LIBRARY SndFile_INCLUDE_PATH
+)
+
+if(SndFile_FOUND)
+ if(NOT TARGET SndFile::sndfile)
+ add_library(SndFile::sndfile UNKNOWN IMPORTED)
+ set_target_properties(SndFile::sndfile PROPERTIES
+ IMPORTED_LOCATION "${SndFile_LIBRARY}"
+ INTERFACE_INCLUDE_DIRECTORIES "${SndFile_INCLUDE_PATH}"
+ INTERFACE_COMPILE_OPTIONS "${SndFile_COMPILE_OPTIONS}"
+ INTERFACE_LINK_LIBRARIES "${SndFile_LINK_LIBRARIES}"
+ INTERFACE_LINK_FLAGS "${SndFile_LINK_FLAGS}"
+ )
+ endif()
+endif()
diff --git a/cmake/SDL3_mixerConfig.cmake.in b/cmake/SDL3_mixerConfig.cmake.in
index 8534e78d..f8969ac1 100644
--- a/cmake/SDL3_mixerConfig.cmake.in
+++ b/cmake/SDL3_mixerConfig.cmake.in
@@ -12,6 +12,8 @@ set(SDL3MIXER_VENDORED @SDL3MIXER_VENDORED@)
set(SDL3MIXER_CMD @SDL3MIXER_CMD@)
+set(SDL3MIXER_SNDFILE @SDL3MIXER_SNDFILE@)
+
set(SDL3MIXER_FLAC_LIBFLAC @SDL3MIXER_FLAC_LIBFLAC@)
set(SDL3MIXER_FLAC_DRFLAC @SDL3MIXER_FLAC_DRFLAC@)
@@ -57,6 +59,10 @@ if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/SDL3_mixer-static-targets.cmake")
include(CMakeFindDependencyMacro)
+ if(SDL3MIXER_SNDFILE AND NOT SDL3MIXER_VENDORED AND NOT TARGET SndFile::sndfile)
+ find_dependency(SndFile)
+ endif()
+
if(SDL3MIXER_FLAC_LIBFLAC AND NOT SDL3MIXER_VENDORED AND NOT TARGET FLAC::FLAC)
find_dependency(FLAC)
endif()
diff --git a/src/codecs/load_sndfile.c b/src/codecs/load_sndfile.c
new file mode 100644
index 00000000..0b53d453
--- /dev/null
+++ b/src/codecs/load_sndfile.c
@@ -0,0 +1,210 @@
+/*
+ SDL_mixer: An audio mixer library based on the SDL library
+ Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ This is the source needed to decode a file in any format supported by
+ libsndfile. The only externally-callable function is Mix_LoadSndFile_RW(),
+ which is meant to act as identically to SDL_LoadWAV_RW() as possible.
+
+ This file by Fabian Greffrath (fabian@greffrath.com).
+*/
+
+#include <SDL3/SDL_audio.h>
+
+#include "load_sndfile.h"
+
+#ifdef LOAD_SNDFILE
+
+#include <SDL3/SDL_loadso.h>
+
+#include <sndfile.h>
+
+static SNDFILE* (*SF_sf_open_virtual) (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data);
+static int (*SF_sf_close) (SNDFILE *sndfile);
+static sf_count_t (*SF_sf_readf_short) (SNDFILE *sndfile, short *ptr, sf_count_t frames);
+static const char* (*SF_sf_strerror) (SNDFILE *sndfile);
+
+static int SNDFILE_init (void)
+{
+ static int SNDFILE_loaded;
+
+ if (SNDFILE_loaded == 0)
+ {
+#ifdef SNDFILE_DYNAMIC
+ static void *SNDFILE_lib;
+
+ SNDFILE_lib = SDL_LoadObject(SNDFILE_DYNAMIC);
+ if (SNDFILE_lib == NULL) {
+ return -1;
+ }
+
+ /* *INDENT-OFF* */ /* clang-format off */
+ SF_sf_open_virtual = (SNDFILE* (*)(SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data))SDL_LoadFunction(SNDFILE_lib, "sf_open_virtual");
+ SF_sf_close = (int (*)(SNDFILE *sndfile))SDL_LoadFunction(SNDFILE_lib, "sf_close");
+ SF_sf_readf_short = (sf_count_t(*)(SNDFILE *sndfile, short *ptr, sf_count_t frames))SDL_LoadFunction(SNDFILE_lib, "sf_readf_short");
+ SF_sf_strerror = (const char* (*)(SNDFILE *sndfile))SDL_LoadFunction(SNDFILE_lib, "sf_strerror");
+ /* *INDENT-ON* */ /* clang-format on */
+
+ if (SF_sf_open_virtual == NULL || SF_sf_close == NULL ||
+ SF_sf_readf_short == NULL || SF_sf_strerror == NULL) {
+ SDL_UnloadObject(SNDFILE_lib);
+ SNDFILE_lib = NULL;
+ return -1;
+ }
+#else
+ SF_sf_open_virtual = sf_open_virtual;
+ SF_sf_close = sf_close;
+ SF_sf_readf_short = sf_readf_short;
+ SF_sf_strerror = sf_strerror;
+#endif
+
+ SNDFILE_loaded = 1;
+ }
+
+ return 0;
+}
+
+static sf_count_t sfvio_size(void *user_data)
+{
+ SDL_RWops *RWops = user_data;
+
+ return SDL_RWsize(RWops);
+}
+
+static sf_count_t sfvio_seek(sf_count_t offset, int whence, void *user_data)
+{
+ SDL_RWops *RWops = user_data;
+
+ return SDL_RWseek(RWops, offset, whence);
+}
+
+static sf_count_t sfvio_read(void *ptr, sf_count_t count, void *user_data)
+{
+ SDL_RWops *RWops = user_data;
+
+ return SDL_RWread(RWops, ptr, count);
+}
+
+static sf_count_t sfvio_tell(void *user_data)
+{
+ SDL_RWops *RWops = user_data;
+
+ return SDL_RWtell(RWops);
+}
+
+SDL_AudioSpec *Mix_LoadSndFile_RW (SDL_RWops *src, int freesrc,
+ SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
+{
+ SNDFILE *sndfile = NULL;
+ SF_INFO sfinfo;
+ SF_VIRTUAL_IO sfvio =
+ {
+ sfvio_size,
+ sfvio_seek,
+ sfvio_read,
+ NULL,
+ sfvio_tell
+ };
+ Uint32 len;
+ short *buf = NULL;
+
+ int was_error = 1;
+
+ if (src == NULL || spec == NULL ||
+ audio_buf == NULL || audio_len == NULL) {
+ goto done;
+ }
+
+ *audio_buf = NULL;
+ *audio_len = 0;
+ SDL_memset(spec, 0, sizeof(*spec));
+
+ if (SNDFILE_init() != 0) {
+ goto done;
+ }
+
+ SDL_memset(&sfinfo, 0, sizeof(sfinfo));
+
+ sndfile = SF_sf_open_virtual(&sfvio, SFM_READ, &sfinfo, src);
+
+ if (sndfile == NULL) {
+ Mix_SetError("sf_open_virtual: %s", SF_sf_strerror(sndfile));
+ goto done;
+ }
+
+ if (sfinfo.frames <= 0) {
+ Mix_SetError("Invalid number of frames: %ld", (long)sfinfo.frames);
+ goto done;
+ }
+
+ if (sfinfo.channels <= 0) {
+ Mix_SetError("Invalid number of channels: %d", sfinfo.channels);
+ goto done;
+ }
+
+ len = sfinfo.frames * sfinfo.channels * sizeof(short);
+ buf = SDL_malloc(len);
+
+ if (buf == NULL) {
+ Mix_OutOfMemory();
+ goto done;
+ }
+
+ if (SF_sf_readf_short(sndfile, buf, sfinfo.frames) < sfinfo.frames) {
+ SDL_free(buf);
+ Mix_SetError("sf_readf_short: %s", SF_sf_strerror(sndfile));
+ goto done;
+ }
+
+ was_error = 0;
+
+ spec->channels = sfinfo.channels;
+ spec->freq = sfinfo.samplerate;
+ spec->format = AUDIO_S16;
+
+ *audio_buf = (Uint8 *)buf;
+ *audio_len = len;
+
+ if (freesrc && src) {
+ SDL_RWclose(src);
+ }
+
+done:
+ if (sndfile) {
+ SF_sf_close(sndfile);
+ }
+
+ if (was_error) {
+ spec = NULL;
+ }
+
+ return spec;
+}
+
+#else
+
+SDL_AudioSpec *Mix_LoadSndFile_RW (SDL_RWops *src, int freesrc,
+ SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
+{
+ return NULL;
+}
+
+#endif
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/codecs/load_sndfile.h b/src/codecs/load_sndfile.h
new file mode 100644
index 00000000..6f5e6020
--- /dev/null
+++ b/src/codecs/load_sndfile.h
@@ -0,0 +1,39 @@
+/*
+ SDL_mixer: An audio mixer library based on the SDL library
+ Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ This is the source needed to decode a file in any format supported by
+ libsndfile. The only externally-callable function is Mix_LoadSndFile_RW(),
+ which is meant to act as identically to SDL_LoadWAV_RW() as possible.
+
+ This file by Fabian Greffrath (fabian@greffrath.com).
+*/
+
+#ifndef LOAD_SNDFILE_H
+#define LOAD_SNDFILE_H
+
+#include <SDL3/SDL_mixer.h>
+
+/* Don't call this directly; use Mix_LoadWAV_RW() for now. */
+SDL_AudioSpec *Mix_LoadSndFile_RW (SDL_RWops *src, int freesrc,
+ SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len);
+
+#endif
+
+/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/mixer.c b/src/mixer.c
index 8ac43f45..0af6ed7a 100644
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -26,6 +26,7 @@
#include "music.h"
#include "load_aiff.h"
#include "load_voc.h"
+#include "load_sndfile.h"
#define MIX_INTERNAL_EFFECT__
#include "effects_internal.h"
@@ -808,14 +809,19 @@ Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
/* Seek backwards for compatibility with older loaders */
SDL_RWseek(src, -4, SDL_RW_SEEK_CUR);
- if (SDL_memcmp(magic, "WAVE", 4) == 0 || SDL_memcmp(magic, "RIFF", 4) == 0) {
- loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
- } else if (SDL_memcmp(magic, "FORM", 4) == 0) {
- loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
- } else if (SDL_memcmp(magic, "Crea", 4) == 0) {
- loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
- } else {
- loaded = Mix_LoadMusic_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
+ /* First try loading via libsndfile */
+ loaded = Mix_LoadSndFile_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
+
+ if (!loaded) {
+ if (SDL_memcmp(magic, "WAVE", 4) == 0 || SDL_memcmp(magic, "RIFF", 4) == 0) {
+ loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
+ } else if (SDL_memcmp(magic, "FORM", 4) == 0) {
+ loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
+ } else if (SDL_memcmp(magic, "Crea", 4) == 0) {
+ loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
+ } else {
+ loaded = Mix_LoadMusic_RW(src, freesrc, &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen);
+ }
}
if (!loaded) {
/* The individual loaders have closed src if needed */