diff --git a/.gitmodules b/.gitmodules index 1247ca5f..c47fa23b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,11 @@ path = editor/external/vstgui4 url = https://github.com/sfztools/vstgui.git shallow = true +[submodule "external/st_audiofile/thirdparty/dr_libs"] + path = external/st_audiofile/thirdparty/dr_libs + url = https://github.com/mackron/dr_libs.git + shallow = true +[submodule "external/st_audiofile/thirdparty/stb_vorbis"] + path = external/st_audiofile/thirdparty/stb_vorbis + url = https://github.com/sfztools/stb_vorbis.git + shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index 32455b1b..d3896ef5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ option (SFIZZ_BENCHMARKS "Enable benchmarks build [default: OFF]" OFF) option (SFIZZ_TESTS "Enable tests build [default: OFF]" OFF) option (SFIZZ_DEVTOOLS "Enable developer tools build [default: OFF]" OFF) option (SFIZZ_SHARED "Enable shared library build [default: ON]" ON) +option (SFIZZ_USE_SNDFILE "Enable use of the sndfile library [default: ON]" ON) option (SFIZZ_USE_VCPKG "Assume that sfizz is build using vcpkg [default: OFF]" OFF) option (SFIZZ_STATIC_DEPENDENCIES "Link dependencies statically [default: OFF]" OFF) option (SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds [default: OFF]" OFF) diff --git a/benchmarks/BM_audioReaders.cpp b/benchmarks/BM_audioReaders.cpp index 569a8810..98483a8d 100644 --- a/benchmarks/BM_audioReaders.cpp +++ b/benchmarks/BM_audioReaders.cpp @@ -146,16 +146,12 @@ static void doReaderBenchmark(const fs::path& path, std::vector &buffer, static void doEntireRead(const fs::path& path) { -#if !defined(_WIN32) - SndfileHandle handle(path.c_str()); -#else - SndfileHandle handle(path.wstring().c_str()); -#endif - if (handle.error()) - throw std::runtime_error("cannot open sound file for reading"); + sfz::AudioReaderPtr reader = sfz::createAudioReader(path, false); + if (!reader) + return; - std::vector buffer(static_cast(2 * handle.frames())); - handle.read(buffer.data(), buffer.size()); + std::vector buffer(static_cast(2 * reader->frames())); + reader->readNextBlock(buffer.data(), buffer.size()); } BENCHMARK_DEFINE_F(AudioReaderFixture, EntireWav)(benchmark::State& state) @@ -214,12 +210,14 @@ BENCHMARK_DEFINE_F(AudioReaderFixture, ForwardOgg)(benchmark::State& state) } } -//BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseOgg)(benchmark::State& state) -//{ -// for (auto _ : state) { -// doReaderBenchmark(fileOgg.path(), workBuffer, sfz::AudioReaderType::Reverse); -// } -//} +#if !defined(ST_AUDIO_FILE_USE_SNDFILE) +BENCHMARK_DEFINE_F(AudioReaderFixture, ReverseOgg)(benchmark::State& state) +{ + for (auto _ : state) { + doReaderBenchmark(fileOgg.path(), workBuffer, sfz::AudioReaderType::Reverse); + } +} +#endif BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseWav)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); @@ -228,6 +226,8 @@ BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardFlac)->RangeMultiplier(2)->Range BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseFlac)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); BENCHMARK_REGISTER_F(AudioReaderFixture, EntireFlac)->Range(1, 1); BENCHMARK_REGISTER_F(AudioReaderFixture, ForwardOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); -//BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +#if !defined(ST_AUDIO_FILE_USE_SNDFILE) +BENCHMARK_REGISTER_F(AudioReaderFixture, ReverseOgg)->RangeMultiplier(2)->Range((1 << 6), (1 << 10)); +#endif BENCHMARK_REGISTER_F(AudioReaderFixture, EntireOgg)->Range(1, 1); BENCHMARK_MAIN(); diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index d53ff15f..2f33e648 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -86,7 +86,7 @@ sfizz_add_benchmark(bm_flacfile BM_flacfile.cpp) target_link_libraries(bm_flacfile PRIVATE sfizz-sndfile) sfizz_add_benchmark(bm_audioReaders BM_audioReaders.cpp ../src/sfizz/AudioReader.cpp) -target_link_libraries(bm_audioReaders PRIVATE sfizz-sndfile) +target_link_libraries(bm_audioReaders PRIVATE st_audiofile sfizz-sndfile) sfizz_add_benchmark(bm_readChunk BM_readChunk.cpp) target_link_libraries(bm_readChunk PRIVATE sfizz-sndfile) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index b488b4db..f9387a09 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -79,30 +79,40 @@ function(sfizz_enable_fast_math NAME) endif() endfunction() -# The sndfile library -add_library(sfizz-sndfile INTERFACE) - # The jsl utility library for C++ add_library(sfizz-jsl INTERFACE) target_include_directories(sfizz-jsl INTERFACE "external/jsl/include") -if (SFIZZ_USE_VCPKG OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - find_package(SndFile CONFIG REQUIRED) - find_path(SNDFILE_INCLUDE_DIR sndfile.hh) - target_include_directories(sfizz-sndfile INTERFACE "${SNDFILE_INCLUDE_DIR}") - target_link_libraries(sfizz-sndfile INTERFACE SndFile::sndfile) -else() - find_package(PkgConfig REQUIRED) - pkg_check_modules(SNDFILE "sndfile" REQUIRED) - target_include_directories(sfizz-sndfile INTERFACE ${SNDFILE_INCLUDE_DIRS}) - if (SFIZZ_STATIC_DEPENDENCIES) - target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_STATIC_LIBRARIES}) +# The sndfile library +if (SFIZZ_USE_SNDFILE OR SFIZZ_TESTS OR SFIZZ_BENCHMARKS OR SFIZZ_RENDER) + add_library(sfizz-sndfile INTERFACE) + if (SFIZZ_USE_VCPKG OR CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + find_package(SndFile CONFIG REQUIRED) + find_path(SNDFILE_INCLUDE_DIR "sndfile.hh") + target_include_directories(sfizz-sndfile INTERFACE "${SNDFILE_INCLUDE_DIR}") + target_link_libraries(sfizz-sndfile INTERFACE SndFile::sndfile) else() - target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_LIBRARIES}) + find_package(PkgConfig REQUIRED) + pkg_check_modules(SNDFILE "sndfile" REQUIRED) + target_include_directories(sfizz-sndfile INTERFACE ${SNDFILE_INCLUDE_DIRS}) + if (SFIZZ_STATIC_DEPENDENCIES) + target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_STATIC_LIBRARIES}) + else() + target_link_libraries(sfizz-sndfile INTERFACE ${SNDFILE_LIBRARIES}) + endif() + link_directories(${SNDFILE_LIBRARY_DIRS}) endif() - link_directories(${SNDFILE_LIBRARY_DIRS}) endif() +# The st_audiofile library +if (SFIZZ_USE_SNDFILE) + set(ST_AUDIO_FILE_USE_SNDFILE ON CACHE BOOL "" FORCE) + set(ST_AUDIO_FILE_EXTERNAL_SNDFILE "sfizz-sndfile" CACHE STRING "" FORCE) +else() + set(ST_AUDIO_FILE_USE_SNDFILE OFF CACHE BOOL "" FORCE) + set(ST_AUDIO_FILE_EXTERNAL_SNDFILE "" CACHE STRING "" FORCE) +endif() +add_subdirectory("external/st_audiofile" EXCLUDE_FROM_ALL) # If we build with Clang, optionally use libc++. Enabled by default on Apple OS. cmake_dependent_option(USE_LIBCPP "Use libc++ with clang" "${APPLE}" @@ -154,6 +164,7 @@ Build VST plug-in: ${SFIZZ_VST} Build AU plug-in: ${SFIZZ_AU} Build benchmarks: ${SFIZZ_BENCHMARKS} Build tests: ${SFIZZ_TESTS} +Use sndfile: ${SFIZZ_USE_SNDFILE} Use vcpkg: ${SFIZZ_USE_VCPKG} Statically link dependencies: ${SFIZZ_STATIC_DEPENDENCIES} Link libatomic: ${SFIZZ_LINK_LIBATOMIC} diff --git a/common.mk b/common.mk index 99a83ee1..e7ada2c1 100644 --- a/common.mk +++ b/common.mk @@ -4,6 +4,10 @@ ifndef SFIZZ_DIR $(error sfizz: The source directory must be set before including) endif +### Options + +SFIZZ_USE_SNDFILE ?= 1 + ### SFIZZ_MACHINE := $(shell $(CC) -dumpmachine) @@ -125,13 +129,32 @@ SFIZZ_PKG_CONFIG ?= pkg-config # Sndfile dependency +ifeq ($(SFIZZ_USE_SNDFILE),1) SFIZZ_SNDFILE_C_FLAGS ?= $(shell $(SFIZZ_PKG_CONFIG) --cflags sndfile) SFIZZ_SNDFILE_CXX_FLAGS ?= $(SFIZZ_SNDFILE_C_FLAGS) SFIZZ_SNDFILE_LINK_FLAGS ?= $(shell $(SFIZZ_PKG_CONFIG) --libs sndfile) +endif -SFIZZ_C_FLAGS += $(SFIZZ_SNDFILE_C_FLAGS) -SFIZZ_CXX_FLAGS += $(SFIZZ_SNDFILE_CXX_FLAGS) +# st_audiofile dependency + +SFIZZ_SOURCES += \ + $(SFIZZ_DIR)/external/st_audiofile/src/st_audiofile.c \ + $(SFIZZ_DIR)/external/st_audiofile/src/st_audiofile_common.c \ + $(SFIZZ_DIR)/external/st_audiofile/src/st_audiofile_libs.c \ + $(SFIZZ_DIR)/external/st_audiofile/src/st_audiofile_sndfile.c + +SFIZZ_C_FLAGS += \ + -I$(SFIZZ_DIR)/external/st_audiofile/src \ + -I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/dr_libs +SFIZZ_CXX_FLAGS += \ + -I$(SFIZZ_DIR)/external/st_audiofile/src \ + -I$(SFIZZ_DIR)/external/st_audiofile/thirdparty/dr_libs + +ifeq ($(SFIZZ_USE_SNDFILE),1) +SFIZZ_C_FLAGS += $(SFIZZ_SNDFILE_C_FLAGS) -DST_AUDIO_FILE_USE_SNDFILE=1 +SFIZZ_CXX_FLAGS += $(SFIZZ_SNDFILE_CXX_FLAGS) -DST_AUDIO_FILE_USE_SNDFILE=1 SFIZZ_LINK_FLAGS += $(SFIZZ_SNDFILE_LINK_FLAGS) +endif ### Abseil dependency diff --git a/external/st_audiofile/CMakeLists.txt b/external/st_audiofile/CMakeLists.txt new file mode 100644 index 00000000..e0fb1e4e --- /dev/null +++ b/external/st_audiofile/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.5) +project(st_audiofile) + +option(ST_AUDIO_FILE_USE_SNDFILE "Use sndfile" OFF) +set(ST_AUDIO_FILE_EXTERNAL_SNDFILE "" CACHE STRING "Name of external sndfile target") + +add_library(st_audiofile STATIC + "src/st_audiofile.c" + "src/st_audiofile_common.c" + "src/st_audiofile_libs.c" + "src/st_audiofile_sndfile.c") +target_include_directories(st_audiofile + PUBLIC "src" + PUBLIC "thirdparty/dr_libs" + PUBLIC "thirdparty/stb_vorbis") + +add_executable(st_info + "src/st_info.c") +target_link_libraries(st_info + PRIVATE st_audiofile) + +if(ST_AUDIO_FILE_USE_SNDFILE) + target_compile_definitions(st_audiofile + PUBLIC "ST_AUDIO_FILE_USE_SNDFILE=1") + if(ST_AUDIO_FILE_EXTERNAL_SNDFILE) + target_link_libraries(st_audiofile + PUBLIC "${ST_AUDIO_FILE_EXTERNAL_SNDFILE}") + else() + find_package(PkgConfig REQUIRED) + pkg_check_modules(Sndfile "sndfile" REQUIRED) + target_include_directories(st_audiofile + PUBLIC ${Sndfile_INCLUDE_DIRS}) + target_link_libraries(st_audiofile + PUBLIC ${Sndfile_LIBRARIES}) + link_directories( + ${Sndfile_LIBRARY_DIRS}) + endif() +endif() diff --git a/external/st_audiofile/LICENSE.md b/external/st_audiofile/LICENSE.md new file mode 100644 index 00000000..bb52be90 --- /dev/null +++ b/external/st_audiofile/LICENSE.md @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2020, Jean-Pierre Cimalando +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/st_audiofile/src/st_audiofile.c b/external/st_audiofile/src/st_audiofile.c new file mode 100644 index 00000000..81eb5d47 --- /dev/null +++ b/external/st_audiofile/src/st_audiofile.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "st_audiofile.h" +#if !defined(ST_AUDIO_FILE_USE_SNDFILE) +#include "st_audiofile_libs.h" +#include + +struct st_audio_file { + int type; + union { + drwav *wav; + drflac *flac; + drmp3 *mp3; + stb_vorbis* ogg; + }; + + union { + struct { uint64_t frames; } mp3; + struct { uint32_t channels; float sample_rate; uint64_t frames; } ogg; + } cache; +}; + +enum { + st_audio_file_null = -1, +}; + +static st_audio_file* st_generic_open_file(const void* filename, int widepath) +{ +#if !defined(_WIN32) + if (widepath) + return NULL; +#endif + + st_audio_file* af = (st_audio_file*)malloc(sizeof(st_audio_file)); + if (!af) + return NULL; + + // Try WAV + { + af->wav = (drwav*)malloc(sizeof(drwav)); + if (!af->wav) { + free(af); + return NULL; + } + drwav_bool32 ok = +#if defined(_WIN32) + widepath ? drwav_init_file_w(af->wav, (const wchar_t*)filename, NULL) : +#endif + drwav_init_file(af->wav, (const char*)filename, NULL); + if (!ok) + free(af->wav); + else { + af->type = st_audio_file_wav; + return af; + } + } + + // Try FLAC + { + af->flac = +#if defined(_WIN32) + widepath ? drflac_open_file_w((const wchar_t*)filename, NULL) : +#endif + drflac_open_file((const char*)filename, NULL); + if (af->flac) { + af->type = st_audio_file_flac; + return af; + } + } + + // Try OGG + { + af->ogg = +#if defined(_WIN32) + widepath ? stb_vorbis_open_filename_w((const wchar_t*)filename, NULL, NULL) : +#endif + stb_vorbis_open_filename((const char*)filename, NULL, NULL); + if (af->ogg) { + af->cache.ogg.frames = stb_vorbis_stream_length_in_samples(af->ogg); + if (af->cache.ogg.frames == 0) { + stb_vorbis_close(af->ogg); + free(af); + return NULL; + } + stb_vorbis_info info = stb_vorbis_get_info(af->ogg); + af->cache.ogg.channels = info.channels; + af->cache.ogg.sample_rate = info.sample_rate; + af->type = st_audio_file_ogg; + return af; + } + } + + // Try MP3 + if (af->type == st_audio_file_null) { + af->mp3 = (drmp3*)malloc(sizeof(drmp3)); + if (!af->mp3) { + free(af); + return NULL; + } + drmp3_bool32 ok = +#if defined(_WIN32) + widepath ? drmp3_init_file_w(af->mp3, (const wchar_t*)filename, NULL) : +#endif + drmp3_init_file(af->mp3, (const char*)filename, NULL); + if (!ok) + free(af->mp3); + else { + af->cache.mp3.frames = drmp3_get_pcm_frame_count(af->mp3); + if (af->cache.mp3.frames == 0) { + free(af->mp3); + free(af); + return NULL; + } + af->type = st_audio_file_mp3; + return af; + } + } + + free(af); + return NULL; +} + +st_audio_file* st_open_file(const char* filename) +{ + return st_generic_open_file(filename, 0); +} + +#if defined(_WIN32) +st_audio_file* st_open_file_w(const wchar_t* filename) +{ + return st_generic_open_file(filename, 1); +} +#endif + +void st_close(st_audio_file* af) +{ + switch (af->type) { + case st_audio_file_wav: + drwav_uninit(af->wav); + free(af->wav); + break; + case st_audio_file_flac: + drflac_close(af->flac); + break; + case st_audio_file_ogg: + stb_vorbis_close(af->ogg); + break; + case st_audio_file_mp3: + drmp3_uninit(af->mp3); + free(af->mp3); + break; + } + + af->type = st_audio_file_null; +} + +int st_get_type(st_audio_file* af) +{ + return af->type; +} + +uint32_t st_get_channels(st_audio_file* af) +{ + uint32_t channels = 0; + + switch (af->type) { + case st_audio_file_wav: + channels = af->wav->channels; + break; + case st_audio_file_flac: + channels = af->flac->channels; + break; + case st_audio_file_ogg: + channels = af->cache.ogg.channels; + break; + case st_audio_file_mp3: + channels = af->mp3->channels; + break; + } + + return channels; +} + +float st_get_sample_rate(st_audio_file* af) +{ + float sample_rate = 0; + + switch (af->type) { + case st_audio_file_wav: + sample_rate = af->wav->sampleRate; + break; + case st_audio_file_flac: + sample_rate = af->flac->sampleRate; + break; + case st_audio_file_ogg: + sample_rate = af->cache.ogg.sample_rate; + break; + case st_audio_file_mp3: + sample_rate = af->mp3->sampleRate; + break; + } + + return sample_rate; +} + +uint64_t st_get_frame_count(st_audio_file* af) +{ + uint64_t frames = 0; + + switch (af->type) { + case st_audio_file_wav: + frames = af->wav->totalPCMFrameCount; + break; + case st_audio_file_flac: + frames = af->flac->totalPCMFrameCount; + break; + case st_audio_file_ogg: + frames = af->cache.ogg.frames; + break; + case st_audio_file_mp3: + frames = af->cache.mp3.frames; + break; + } + + return frames; +} + +bool st_seek(st_audio_file* af, uint64_t frame) +{ + bool success = false; + + switch (af->type) { + case st_audio_file_wav: + success = drwav_seek_to_pcm_frame(af->wav, frame); + break; + case st_audio_file_flac: + success = drflac_seek_to_pcm_frame(af->flac, frame); + break; + case st_audio_file_ogg: + success = stb_vorbis_seek(af->ogg, (unsigned)frame) != 0; + break; + case st_audio_file_mp3: + success = drmp3_seek_to_pcm_frame(af->mp3, frame); + break; + } + + return success; +} + +uint64_t st_read_s16(st_audio_file* af, int16_t* buffer, uint64_t count) +{ + switch (af->type) { + case st_audio_file_wav: + count = drwav_read_pcm_frames_s16(af->wav, count, buffer); + break; + case st_audio_file_flac: + count = drflac_read_pcm_frames_s16(af->flac, count, buffer); + break; + case st_audio_file_ogg: + count = stb_vorbis_get_samples_short_interleaved( + af->ogg, af->cache.ogg.channels, buffer, + count * af->cache.ogg.channels); + break; + case st_audio_file_mp3: + count = drmp3_read_pcm_frames_s16(af->mp3, count, buffer); + break; + } + + return count; +} + +uint64_t st_read_f32(st_audio_file* af, float* buffer, uint64_t count) +{ + switch (af->type) { + case st_audio_file_wav: + count = drwav_read_pcm_frames_f32(af->wav, count, buffer); + break; + case st_audio_file_flac: + count = drflac_read_pcm_frames_f32(af->flac, count, buffer); + break; + case st_audio_file_ogg: + count = stb_vorbis_get_samples_float_interleaved( + af->ogg, af->cache.ogg.channels, buffer, + count * af->cache.ogg.channels); + break; + case st_audio_file_mp3: + count = drmp3_read_pcm_frames_f32(af->mp3, count, buffer); + break; + } + + return count; +} + +#endif // !defined(ST_AUDIO_FILE_USE_SNDFILE) diff --git a/external/st_audiofile/src/st_audiofile.h b/external/st_audiofile/src/st_audiofile.h new file mode 100644 index 00000000..fd62c77f --- /dev/null +++ b/external/st_audiofile/src/st_audiofile.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#if defined(ST_AUDIO_FILE_USE_SNDFILE) +#include +#endif +#include +#include +#if defined(_WIN32) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_audio_file st_audio_file; + +typedef enum st_audio_file_type { + st_audio_file_wav, + st_audio_file_flac, + st_audio_file_ogg, + st_audio_file_mp3, + st_audio_file_other, +} st_audio_file_type; + +st_audio_file* st_open_file(const char* filename); +#if defined(_WIN32) +st_audio_file* st_open_file_w(const wchar_t* filename); +#endif +void st_close(st_audio_file* af); +int st_get_type(st_audio_file* af); +const char* st_get_type_string(st_audio_file* af); +const char* st_type_string(int type); +uint32_t st_get_channels(st_audio_file* af); +float st_get_sample_rate(st_audio_file* af); +uint64_t st_get_frame_count(st_audio_file* af); +bool st_seek(st_audio_file* af, uint64_t frame); +uint64_t st_read_s16(st_audio_file* af, int16_t* buffer, uint64_t count); +uint64_t st_read_f32(st_audio_file* af, float* buffer, uint64_t count); + +#if defined(ST_AUDIO_FILE_USE_SNDFILE) +SNDFILE* st_get_sndfile_handle(st_audio_file* af); +int st_get_sndfile_format(st_audio_file* af); +#endif + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/external/st_audiofile/src/st_audiofile.hpp b/external/st_audiofile/src/st_audiofile.hpp new file mode 100644 index 00000000..c30da975 --- /dev/null +++ b/external/st_audiofile/src/st_audiofile.hpp @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "st_audiofile.h" + +class ST_AudioFile { +public: + constexpr ST_AudioFile() noexcept; + ~ST_AudioFile() noexcept; + + ST_AudioFile(ST_AudioFile&&) noexcept; + ST_AudioFile& operator=(ST_AudioFile&&) noexcept; + + ST_AudioFile(const ST_AudioFile&) = delete; + ST_AudioFile& operator=(const ST_AudioFile&) = delete; + + explicit operator bool() const noexcept; + void reset(st_audio_file* new_af = nullptr) noexcept; + + bool open_file(const char* filename); +#if defined(_WIN32) + bool open_file_w(const wchar_t* filename); +#endif + + int get_type() const noexcept; + const char* get_type_string() const noexcept; + static const char* type_string(int type) noexcept; + uint32_t get_channels() const noexcept; + float get_sample_rate() const noexcept; + uint64_t get_frame_count() const noexcept; + + bool seek(uint64_t frame) noexcept; + uint64_t read_s16(int16_t* buffer, uint64_t count) noexcept; + uint64_t read_f32(float* buffer, uint64_t count) noexcept; + + st_audio_file* get_handle() const noexcept; + +#if defined(ST_AUDIO_FILE_USE_SNDFILE) + void* get_sndfile_handle() const noexcept; + int get_sndfile_format() const noexcept; +#endif + +private: + st_audio_file* af_ = nullptr; +}; + +//------------------------------------------------------------------------------ + +inline constexpr ST_AudioFile::ST_AudioFile() noexcept +{ +} + +inline ST_AudioFile::~ST_AudioFile() noexcept +{ + reset(); +} + +ST_AudioFile::ST_AudioFile(ST_AudioFile&& other) noexcept + : af_(other.af_) +{ + other.af_ = nullptr; +} + +ST_AudioFile& ST_AudioFile::operator=(ST_AudioFile&& other) noexcept +{ + if (this != &other) { + if (af_) + st_close(af_); + af_ = other.af_; + other.af_ = nullptr; + } + return *this; +} + +inline ST_AudioFile::operator bool() const noexcept +{ + return af_ != nullptr; +} + +inline void ST_AudioFile::reset(st_audio_file* new_af) noexcept +{ + if (af_ != new_af) { + if (af_) + st_close(af_); + af_ = new_af; + } +} + +bool ST_AudioFile::open_file(const char* filename) +{ + st_audio_file* new_af = st_open_file(filename); + reset(new_af); + return new_af != nullptr; +} +#if defined(_WIN32) +inline bool ST_AudioFile::open_file_w(const wchar_t* filename) +{ + st_audio_file* new_af = st_open_file_w(filename); + reset(new_af); + return new_af != nullptr; +} +#endif + +inline int ST_AudioFile::get_type() const noexcept +{ + return st_get_type(af_); +} + +inline const char* ST_AudioFile::get_type_string() const noexcept +{ + return st_get_type_string(af_); +} + +inline const char* ST_AudioFile::type_string(int type) noexcept +{ + return st_type_string(type); +} + +inline uint32_t ST_AudioFile::get_channels() const noexcept +{ + return st_get_channels(af_); +} + +inline float ST_AudioFile::get_sample_rate() const noexcept +{ + return st_get_sample_rate(af_); +} + +inline uint64_t ST_AudioFile::get_frame_count() const noexcept +{ + return st_get_frame_count(af_); +} + +inline bool ST_AudioFile::seek(uint64_t frame) noexcept +{ + return st_seek(af_, frame); +} + +inline uint64_t ST_AudioFile::read_s16(int16_t* buffer, uint64_t count) noexcept +{ + return st_read_s16(af_, buffer, count); +} + +inline uint64_t ST_AudioFile::read_f32(float* buffer, uint64_t count) noexcept +{ + return st_read_f32(af_, buffer, count); +} + +inline st_audio_file* ST_AudioFile::get_handle() const noexcept +{ + return af_; +} + +#if defined(ST_AUDIO_FILE_USE_SNDFILE) +inline void* ST_AudioFile::get_sndfile_handle() const noexcept +{ + return st_get_sndfile_handle(af_); +} + +int ST_AudioFile::get_sndfile_format() const noexcept +{ + return st_get_sndfile_format(af_); +} +#endif diff --git a/external/st_audiofile/src/st_audiofile_common.c b/external/st_audiofile/src/st_audiofile_common.c new file mode 100644 index 00000000..d8e2ac6e --- /dev/null +++ b/external/st_audiofile/src/st_audiofile_common.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "st_audiofile.h" +#include + +const char* st_get_type_string(st_audio_file* af) +{ + return st_type_string(st_get_type(af)); +} + +const char* st_type_string(int type) +{ + const char *type_string = NULL; + + switch (type) { + case st_audio_file_wav: + type_string = "WAV"; + break; + case st_audio_file_flac: + type_string = "FLAC"; + break; + case st_audio_file_ogg: + type_string = "OGG"; + break; + case st_audio_file_mp3: + type_string = "MP3"; + break; + case st_audio_file_other: + type_string = "other"; + break; + } + + return type_string; +} diff --git a/external/st_audiofile/src/st_audiofile_libs.c b/external/st_audiofile/src/st_audiofile_libs.c new file mode 100644 index 00000000..0bbcccca --- /dev/null +++ b/external/st_audiofile/src/st_audiofile_libs.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#define DR_WAV_IMPLEMENTATION +#define DR_FLAC_IMPLEMENTATION +#define DR_MP3_IMPLEMENTATION +#define STB_VORBIS_HEADER_ONLY 0 +#include "st_audiofile_libs.h" + +#if defined(_WIN32) +stb_vorbis* stb_vorbis_open_filename_w(const wchar_t* filename, int* error, const stb_vorbis_alloc* alloc) +{ + FILE* f; +#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) + if (0 != _wfopen_s(&f, filename, L"rb")) + f = NULL; +#else + f = _wfopen(filename, L"rb"); +#endif + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) + *error = VORBIS_file_open_failure; + return NULL; +} +#endif diff --git a/external/st_audiofile/src/st_audiofile_libs.h b/external/st_audiofile/src/st_audiofile_libs.h new file mode 100644 index 00000000..21727c6b --- /dev/null +++ b/external/st_audiofile/src/st_audiofile_libs.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "dr_wav.h" +#include "dr_flac.h" +#include "dr_mp3.h" +#if !defined(STB_VORBIS_HEADER_ONLY) +# define STB_VORBIS_HEADER_ONLY 1 +#elif STB_VORBIS_HEADER_ONLY == 0 +# undef STB_VORBIS_HEADER_ONLY +#endif +#include "stb_vorbis.c" + +#if defined(_WIN32) +#include +stb_vorbis* stb_vorbis_open_filename_w(const wchar_t* filename, int* error, const stb_vorbis_alloc* alloc); +#endif diff --git a/external/st_audiofile/src/st_audiofile_sndfile.c b/external/st_audiofile/src/st_audiofile_sndfile.c new file mode 100644 index 00000000..5b3e9b9e --- /dev/null +++ b/external/st_audiofile/src/st_audiofile_sndfile.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "st_audiofile.h" +#if defined(ST_AUDIO_FILE_USE_SNDFILE) +#if defined(_WIN32) +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif +#include +#include + +struct st_audio_file { + SNDFILE* snd; + SF_INFO info; +}; + +st_audio_file* st_open_file(const char* filename) +{ + st_audio_file* af = (st_audio_file*)malloc(sizeof(st_audio_file)); + if (!af) + return NULL; + + af->snd = sf_open(filename, SFM_READ, &af->info); + if (!af->snd) { + free(af); + return NULL; + } + + return af; +} + +#if defined(_WIN32) +st_audio_file* st_open_file_w(const wchar_t* filename) +{ + st_audio_file* af = (st_audio_file*)malloc(sizeof(st_audio_file)); + if (!af) + return NULL; + + af->snd = sf_wchar_open(filename, SFM_READ, &af->info); + if (!af->snd) { + free(af); + return NULL; + } + + return af; +} +#endif + +void st_close(st_audio_file* af) +{ + if (af->snd) { + sf_close(af->snd); + af->snd = NULL; + } +} + +int st_get_type(st_audio_file* af) +{ + int type = st_audio_file_other; + + switch (af->info.format & SF_FORMAT_TYPEMASK) { + case SF_FORMAT_WAV: + type = st_audio_file_wav; + break; + case SF_FORMAT_FLAC: + type = st_audio_file_flac; + break; + case SF_FORMAT_OGG: + type = st_audio_file_ogg; + break; + } + + return type; +} + +uint32_t st_get_channels(st_audio_file* af) +{ + return af->info.channels; +} + +float st_get_sample_rate(st_audio_file* af) +{ + return af->info.samplerate; +} + +uint64_t st_get_frame_count(st_audio_file* af) +{ + return af->info.frames; +} + +bool st_seek(st_audio_file* af, uint64_t frame) +{ + return sf_seek(af->snd, frame, SEEK_SET) != -1; +} + +uint64_t st_read_s16(st_audio_file* af, int16_t* buffer, uint64_t count) +{ + return sf_readf_short(af->snd, buffer, count); +} + +uint64_t st_read_f32(st_audio_file* af, float* buffer, uint64_t count) +{ + return sf_readf_float(af->snd, buffer, count); +} + +SNDFILE* st_get_sndfile_handle(st_audio_file* af) +{ + return af->snd; +} + +int st_get_sndfile_format(st_audio_file* af) +{ + return af->info.format; +} + +#endif // defined(ST_AUDIO_FILE_USE_SNDFILE) diff --git a/external/st_audiofile/src/st_info.c b/external/st_audiofile/src/st_info.c new file mode 100644 index 00000000..dc5680f4 --- /dev/null +++ b/external/st_audiofile/src/st_info.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "st_audiofile.h" +#include +#include + +int main(int argc, char* argv[]) +{ + if (argc != 2) { + fprintf(stderr, "Please indicate a sound file.\n"); + return 1; + } + + const char* filename = argv[1]; + + st_audio_file* af = st_open_file(filename); + if (!af) { + fprintf(stderr, "Could not open the sound file.\n"); + return 1; + } + + printf("File name : %s\n", filename); + printf("File type : %s\n", st_get_type_string(af)); + printf("Channels : %u\n", st_get_channels(af)); + printf("Sample rate : %f\n", st_get_sample_rate(af)); + printf("Frames : %" PRIu64 "\n", st_get_frame_count(af)); + + return 0; +} diff --git a/external/st_audiofile/thirdparty/dr_libs b/external/st_audiofile/thirdparty/dr_libs new file mode 160000 index 00000000..cac1785c --- /dev/null +++ b/external/st_audiofile/thirdparty/dr_libs @@ -0,0 +1 @@ +Subproject commit cac1785cee4abb455817b43d5dee33b49d61be2f diff --git a/external/st_audiofile/thirdparty/stb_vorbis b/external/st_audiofile/thirdparty/stb_vorbis new file mode 160000 index 00000000..fc0bd698 --- /dev/null +++ b/external/st_audiofile/thirdparty/stb_vorbis @@ -0,0 +1 @@ +Subproject commit fc0bd698b26888da0a632da33f4c49b90763e69b diff --git a/rack.mk b/rack.mk index 4f8d2512..7c483ca6 100644 --- a/rack.mk +++ b/rack.mk @@ -29,6 +29,7 @@ # # SFIZZ_RACK_PLUGIN_DIR = # SFIZZ_PKG_CONFIG = +# SFIZZ_USE_SNDFILE = <0 disabled, 1 enabled (default)> # SFIZZ_SNDFILE_C_FLAGS = # SFIZZ_SNDFILE_CXX_FLAGS = # SFIZZ_SNDFILE_LINK_FLAGS = diff --git a/scripts/run_clang_tidy.sh b/scripts/run_clang_tidy.sh index 08ceb36c..18d0b08f 100755 --- a/scripts/run_clang_tidy.sh +++ b/scripts/run_clang_tidy.sh @@ -31,6 +31,7 @@ clang-tidy \ vst/SfizzVstEditor.cpp \ vst/SfizzVstState.cpp \ -- -Iexternal/abseil-cpp -Iexternal/jsl/include -Isrc/external -Isrc/external/pugixml/src \ + -Iexternal/st_audiofile/src -Iexternal/st_audiofile/thirdparty/dr_libs \ -Isrc/sfizz -Isrc -Isrc/external/spline -Isrc/external/cpuid/src \ -Ivst -Ivst/external/VST_SDK/VST3_SDK -Ivst/external/VST_SDK/VST3_SDK/vstgui4 -Ivst/external/ring_buffer \ -Ieditor/src \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b4a55653..d005c794 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -218,7 +218,7 @@ target_sources(sfizz_static PRIVATE target_include_directories (sfizz_static PUBLIC .) target_include_directories (sfizz_static PUBLIC external) target_link_libraries (sfizz_static PUBLIC absl::strings absl::span) -target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic) +target_link_libraries (sfizz_static PRIVATE sfizz_parser absl::flat_hash_map Threads::Threads st_audiofile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic) set_target_properties (sfizz_static PROPERTIES OUTPUT_NAME sfizz PUBLIC_HEADER "sfizz.h;sfizz.hpp") if (WIN32) target_compile_definitions (sfizz_static PRIVATE _USE_MATH_DEFINES) @@ -245,7 +245,7 @@ if (SFIZZ_SHARED) ${SFIZZ_HEADERS} ${SFIZZ_SOURCES} ${FAUST_FILES} sfizz/sfizz_wrapper.cpp sfizz/sfizz.cpp) target_include_directories (sfizz_shared PRIVATE .) target_include_directories (sfizz_shared PRIVATE external) - target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads sfizz-sndfile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic) + target_link_libraries (sfizz_shared PRIVATE absl::strings absl::span sfizz_parser absl::flat_hash_map Threads::Threads st_audiofile sfizz-pugixml sfizz-spline sfizz-tunings sfizz-kissfft sfizz-cpuid sfizz-jsl sfizz-atomic) if (WIN32) target_compile_definitions (sfizz_shared PRIVATE _USE_MATH_DEFINES) endif() diff --git a/src/sfizz/AudioReader.cpp b/src/sfizz/AudioReader.cpp index e906e3ae..6f9fe1b9 100644 --- a/src/sfizz/AudioReader.cpp +++ b/src/sfizz/AudioReader.cpp @@ -5,51 +5,60 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "AudioReader.h" -#include +#include "FileMetadata.h" +#include +#if defined(ST_AUDIO_FILE_USE_SNDFILE) +#include +#endif #include namespace sfz { class BasicSndfileReader : public AudioReader { public: - explicit BasicSndfileReader(SndfileHandle handle) : handle_(handle) {} + explicit BasicSndfileReader(ST_AudioFile handle) : handle_(std::move(handle)) {} virtual ~BasicSndfileReader() {} int format() const override; int64_t frames() const override; unsigned channels() const override; unsigned sampleRate() const override; - bool getInstrument(SF_INSTRUMENT* instrument) override; + bool getInstrument(InstrumentInfo* instrument) override; protected: - SndfileHandle handle_; + ST_AudioFile handle_; }; int BasicSndfileReader::format() const { - return handle_.format(); + return handle_.get_type(); } int64_t BasicSndfileReader::frames() const { - return handle_.frames(); + return handle_.get_frame_count(); } unsigned BasicSndfileReader::channels() const { - return handle_.channels(); + return handle_.get_channels(); } unsigned BasicSndfileReader::sampleRate() const { - return handle_.samplerate(); + return handle_.get_sample_rate(); } -bool BasicSndfileReader::getInstrument(SF_INSTRUMENT* instrument) +bool BasicSndfileReader::getInstrument(InstrumentInfo* instrument) { - if (handle_.command(SFC_GET_INSTRUMENT, instrument, sizeof(SF_INSTRUMENT)) == SF_FALSE) - return false; - return true; +#if defined(ST_AUDIO_FILE_USE_SNDFILE) + SNDFILE* sndfile = reinterpret_cast(handle_.get_sndfile_handle()); + if (sf_command(sndfile, SFC_GET_INSTRUMENT, &instrument, sizeof(instrument)) == SF_TRUE) + return true; +#else + (void)instrument; +#endif + return false; } //------------------------------------------------------------------------------ @@ -59,13 +68,13 @@ bool BasicSndfileReader::getInstrument(SF_INSTRUMENT* instrument) */ class ForwardReader : public BasicSndfileReader { public: - explicit ForwardReader(SndfileHandle handle); + explicit ForwardReader(ST_AudioFile handle); AudioReaderType type() const override; size_t readNextBlock(float* buffer, size_t frames) override; }; -ForwardReader::ForwardReader(SndfileHandle handle) - : BasicSndfileReader(handle) +ForwardReader::ForwardReader(ST_AudioFile handle) + : BasicSndfileReader(std::move(handle)) { } @@ -76,7 +85,7 @@ AudioReaderType ForwardReader::type() const size_t ForwardReader::readNextBlock(float* buffer, size_t frames) { - sf_count_t readFrames = handle_.readf(buffer, frames); + uint64_t readFrames = handle_.read_f32(buffer, frames); if (frames <= 0) return 0; @@ -93,7 +102,7 @@ struct AudioFrame { /** * @brief Reorder a sequence of frames in reverse */ -static void reverse_frames(float* data, sf_count_t frames, unsigned channels) +static void reverse_frames(float* data, size_t frames, unsigned channels) { switch (channels) { @@ -108,8 +117,8 @@ static void reverse_frames(float* data, sf_count_t frames, unsigned channels) SPECIALIZE_FOR(2); default: - for (sf_count_t i = 0; i < frames / 2; ++i) { - sf_count_t j = frames - 1 - i; + for (size_t i = 0; i < frames / 2; ++i) { + size_t j = frames - 1 - i; float* frame1 = &data[i * channels]; float* frame2 = &data[j * channels]; for (unsigned c = 0; c < channels; ++c) @@ -128,18 +137,18 @@ static void reverse_frames(float* data, sf_count_t frames, unsigned channels) */ class ReverseReader : public BasicSndfileReader { public: - explicit ReverseReader(SndfileHandle handle); + explicit ReverseReader(ST_AudioFile handle); AudioReaderType type() const override; size_t readNextBlock(float* buffer, size_t frames) override; private: - sf_count_t position_ {}; + uint64_t position_ {}; }; -ReverseReader::ReverseReader(SndfileHandle handle) - : BasicSndfileReader(handle) +ReverseReader::ReverseReader(ST_AudioFile handle) + : BasicSndfileReader(std::move(handle)) { - position_ = handle.seek(0, SEEK_END); + position_ = handle_.get_frame_count(); } AudioReaderType ReverseReader::type() const @@ -149,16 +158,16 @@ AudioReaderType ReverseReader::type() const size_t ReverseReader::readNextBlock(float* buffer, size_t frames) { - sf_count_t position = position_; - const unsigned channels = handle_.channels(); + uint64_t position = position_; + const unsigned channels = handle_.get_channels(); - const sf_count_t readFrames = std::min(frames, position); + const uint64_t readFrames = std::min(frames, position); if (readFrames <= 0) return false; position -= readFrames; - if (handle_.seek(position, SEEK_SET) != position || - handle_.readf(buffer, readFrames) != readFrames) + if (!handle_.seek(position) || + handle_.read_f32(buffer, readFrames) != readFrames) return false; position_ = position; @@ -173,7 +182,7 @@ size_t ReverseReader::readNextBlock(float* buffer, size_t frames) */ class NoSeekReverseReader : public BasicSndfileReader { public: - explicit NoSeekReverseReader(SndfileHandle handle); + explicit NoSeekReverseReader(ST_AudioFile handle); AudioReaderType type() const override; size_t readNextBlock(float* buffer, size_t frames) override; @@ -182,11 +191,11 @@ private: private: std::unique_ptr fileBuffer_; - sf_count_t fileFramesLeft_ { 0 }; + uint64_t fileFramesLeft_ { 0 }; }; -NoSeekReverseReader::NoSeekReverseReader(SndfileHandle handle) - : BasicSndfileReader(handle) +NoSeekReverseReader::NoSeekReverseReader(ST_AudioFile handle) + : BasicSndfileReader(std::move(handle)) { } @@ -203,9 +212,9 @@ size_t NoSeekReverseReader::readNextBlock(float* buffer, size_t frames) fileBuffer = fileBuffer_.get(); } - const unsigned channels = handle_.channels(); - const sf_count_t fileFramesLeft = fileFramesLeft_; - sf_count_t readFrames = std::min(frames, fileFramesLeft); + const unsigned channels = handle_.get_channels(); + const uint64_t fileFramesLeft = fileFramesLeft_; + uint64_t readFrames = std::min(frames, fileFramesLeft); if (readFrames <= 0) return 0; @@ -220,15 +229,16 @@ size_t NoSeekReverseReader::readNextBlock(float* buffer, size_t frames) void NoSeekReverseReader::readWholeFile() { - const sf_count_t frames = handle_.frames(); - const unsigned channels = handle_.channels(); + const uint64_t frames = handle_.get_frame_count(); + const unsigned channels = handle_.get_channels(); float* fileBuffer = new float[channels * frames]; fileBuffer_.reset(fileBuffer); - fileFramesLeft_ = handle_.readf(fileBuffer, frames); + fileFramesLeft_ = handle_.read_f32(fileBuffer, frames); } //------------------------------------------------------------------------------ +#if defined(ST_AUDIO_FILE_USE_SNDFILE) const std::error_category& sndfile_category() { class sndfile_category : public std::error_category { @@ -248,6 +258,26 @@ const std::error_category& sndfile_category() static const sndfile_category cat; return cat; } +#endif + +const std::error_category& undetailed_category() +{ + class undetailed_category : public std::error_category { + public: + const char* name() const noexcept override + { + return "undetailed"; + } + + std::string message(int condition) const override + { + return (condition == 0) ? "success" : "failure"; + } + }; + + static const undetailed_category cat; + return cat; +} //------------------------------------------------------------------------------ @@ -260,7 +290,7 @@ public: unsigned channels() const override { return 1; } unsigned sampleRate() const override { return 44100; } size_t readNextBlock(float*, size_t) override { return 0; } - bool getInstrument(SF_INSTRUMENT* ) override { return false; } + bool getInstrument(InstrumentInfo* ) override { return false; } private: AudioReaderType type_ {}; @@ -268,6 +298,7 @@ private: //------------------------------------------------------------------------------ +#if defined(ST_AUDIO_FILE_USE_SNDFILE) static bool formatHasFastSeeking(int format) { bool fast; @@ -300,8 +331,9 @@ static bool formatHasFastSeeking(int format) return fast; } +#endif -static AudioReaderPtr createAudioReaderWithHandle(SndfileHandle handle, bool reverse, std::error_code* ec) +static AudioReaderPtr createAudioReaderWithHandle(ST_AudioFile handle, bool reverse, std::error_code* ec) { AudioReaderPtr reader; @@ -310,30 +342,38 @@ static AudioReaderPtr createAudioReaderWithHandle(SndfileHandle handle, bool rev if (!handle) { if (ec) - *ec = std::error_code(handle.error(), sndfile_category()); + *ec = std::error_code(1, undetailed_category()); reader.reset(new DummyAudioReader(reverse ? AudioReaderType::Reverse : AudioReaderType::Forward)); } else if (!reverse) - reader.reset(new ForwardReader(handle)); - else if (formatHasFastSeeking(handle.format())) - reader.reset(new ReverseReader(handle)); - else - reader.reset(new NoSeekReverseReader(handle)); + reader.reset(new ForwardReader(std::move(handle))); + else { +#if defined(ST_AUDIO_FILE_USE_SNDFILE) + bool hasFastSeeking = formatHasFastSeeking(handle.get_sndfile_format()); +#else + bool hasFastSeeking = true; +#endif + if (hasFastSeeking) + reader.reset(new ReverseReader(std::move(handle))); + else + reader.reset(new NoSeekReverseReader(std::move(handle))); + } return reader; } AudioReaderPtr createAudioReader(const fs::path& path, bool reverse, std::error_code* ec) { + ST_AudioFile handle; #if defined(_WIN32) - SndfileHandle handle(path.wstring().c_str()); + handle.open_file_w(path.wstring().c_str()); #else - SndfileHandle handle(path.c_str()); + handle.open_file(path.c_str()); #endif - return createAudioReaderWithHandle(handle, reverse, ec); + return createAudioReaderWithHandle(std::move(handle), reverse, ec); } -static AudioReaderPtr createExplicitAudioReaderWithHandle(SndfileHandle handle, AudioReaderType type, std::error_code* ec) +static AudioReaderPtr createExplicitAudioReaderWithHandle(ST_AudioFile handle, AudioReaderType type, std::error_code* ec) { AudioReaderPtr reader; @@ -342,19 +382,19 @@ static AudioReaderPtr createExplicitAudioReaderWithHandle(SndfileHandle handle, if (!handle) { if (ec) - *ec = std::error_code(handle.error(), sndfile_category()); + *ec = std::error_code(1, undetailed_category()); reader.reset(new DummyAudioReader(type)); } else { switch (type) { case AudioReaderType::Forward: - reader.reset(new ForwardReader(handle)); + reader.reset(new ForwardReader(std::move(handle))); break; case AudioReaderType::Reverse: - reader.reset(new ReverseReader(handle)); + reader.reset(new ReverseReader(std::move(handle))); break; case AudioReaderType::NoSeekReverse: - reader.reset(new NoSeekReverseReader(handle)); + reader.reset(new NoSeekReverseReader(std::move(handle))); break; } } @@ -364,12 +404,13 @@ static AudioReaderPtr createExplicitAudioReaderWithHandle(SndfileHandle handle, AudioReaderPtr createExplicitAudioReader(const fs::path& path, AudioReaderType type, std::error_code* ec) { + ST_AudioFile handle; #if defined(_WIN32) - SndfileHandle handle(path.wstring().c_str()); + handle.open_file_w(path.wstring().c_str()); #else - SndfileHandle handle(path.c_str()); + handle.open_file(path.c_str()); #endif - return createExplicitAudioReaderWithHandle(handle, type, ec); + return createExplicitAudioReaderWithHandle(std::move(handle), type, ec); } } // namespace sfz diff --git a/src/sfizz/AudioReader.h b/src/sfizz/AudioReader.h index 64c661f1..8e138c2e 100644 --- a/src/sfizz/AudioReader.h +++ b/src/sfizz/AudioReader.h @@ -7,16 +7,13 @@ #pragma once #include "absl/types/span.h" #include "ghc/fs_std.hpp" +#include #include #include #include -#if defined(_WIN32) -#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 -#include -#endif -#include namespace sfz { +struct InstrumentInfo; /** * @brief Designation of a particular kind of audio reader @@ -42,7 +39,7 @@ public: virtual unsigned channels() const = 0; virtual unsigned sampleRate() const = 0; virtual size_t readNextBlock(float* buffer, size_t frames) = 0; - virtual bool getInstrument(SF_INSTRUMENT* instrument) = 0; + virtual bool getInstrument(InstrumentInfo* instrument) = 0; }; typedef std::unique_ptr AudioReaderPtr; diff --git a/src/sfizz/FileMetadata.cpp b/src/sfizz/FileMetadata.cpp index 60f6d64e..bb1d3d0c 100644 --- a/src/sfizz/FileMetadata.cpp +++ b/src/sfizz/FileMetadata.cpp @@ -242,7 +242,7 @@ size_t FileMetadataReader::Impl::readRiffData(size_t index, void* buffer, size_t return fread(buffer, 1, count, stream); } -bool FileMetadataReader::extractRiffInstrument(SF_INSTRUMENT& ins) +bool FileMetadataReader::extractRiffInstrument(InstrumentInfo& ins) { const RiffChunkInfo* riff = riffChunkById(RiffChunkId{'s', 'm', 'p', 'l'}); if (!riff) @@ -278,16 +278,16 @@ bool FileMetadataReader::extractRiffInstrument(SF_INSTRUMENT& ins) switch (extractU32(loopOffset + 0x04)) { default: - ins.loops[i].mode = SF_LOOP_NONE; + ins.loops[i].mode = LoopNone; break; case 0: - ins.loops[i].mode = SF_LOOP_FORWARD; + ins.loops[i].mode = LoopForward; break; case 1: - ins.loops[i].mode = SF_LOOP_ALTERNATING; + ins.loops[i].mode = LoopAlternating; break; case 2: - ins.loops[i].mode = SF_LOOP_BACKWARD; + ins.loops[i].mode = LoopBackward; break; } diff --git a/src/sfizz/FileMetadata.h b/src/sfizz/FileMetadata.h index 9e93be2e..74c6d9ad 100644 --- a/src/sfizz/FileMetadata.h +++ b/src/sfizz/FileMetadata.h @@ -5,15 +5,13 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #pragma once +#if defined(ST_AUDIO_FILE_USE_SNDFILE) +#include +#endif #include "ghc/fs_std.hpp" #include #include #include -#if defined(_WIN32) -#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 -#include -#endif -#include namespace sfz { @@ -26,6 +24,44 @@ struct RiffChunkInfo { uint32_t length; }; +#if !defined(ST_AUDIO_FILE_USE_SNDFILE) +/** + @brief Loop mode, like SF_LOOP_* + */ +enum LoopMode { + LoopNone, + LoopForward, + LoopBackward, + LoopAlternating, +}; + +/** + @brief Instrument information, like SF_INSTRUMENT + */ +struct InstrumentInfo { + int gain; + int8_t basenote, detune; + int8_t velocity_lo, velocity_hi; + int8_t key_lo, key_hi; + int loop_count; + struct { + int mode; + uint32_t start; + uint32_t end; + uint32_t count; + } loops[16]; +}; +#else +enum LoopMode { + LoopNone = SF_LOOP_NONE, + LoopForward = SF_LOOP_FORWARD, + LoopBackward = SF_LOOP_BACKWARD, + LoopAlternating = SF_LOOP_ALTERNATING, +}; + +struct InstrumentInfo : SF_INSTRUMENT {}; +#endif + struct WavetableInfo { /** @brief Size of each successive table in the file @@ -79,7 +115,7 @@ public: /** * @brief Extract the RIFF 'smpl' data and convert it to sndfile instrument */ - bool extractRiffInstrument(SF_INSTRUMENT& ins); + bool extractRiffInstrument(InstrumentInfo& ins); /** * @brief Extract the wavetable information from various relevant RIFF chunks diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index bb66bb31..5bea534c 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #if defined(_WIN32) #include #else @@ -235,7 +234,7 @@ absl::optional sfz::FilePool::getFileInformation(const Fil returnedValue.sampleRate = static_cast(reader->sampleRate()); returnedValue.numChannels = reader->channels(); - SF_INSTRUMENT instrumentInfo {}; + InstrumentInfo instrumentInfo {}; bool haveInstrumentInfo = reader->getInstrument(&instrumentInfo); FileMetadataReader mdReader; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index eba64aea..522042dd 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -88,10 +88,10 @@ if(JACK_FOUND AND TARGET Qt5::Widgets) endif() add_executable(eq_apply EQ.cpp) -target_link_libraries(eq_apply PRIVATE sfizz::sfizz) +target_link_libraries(eq_apply PRIVATE sfizz::sfizz sfizz-sndfile) add_executable(filter_apply Filter.cpp) -target_link_libraries(filter_apply PRIVATE sfizz::sfizz) +target_link_libraries(filter_apply PRIVATE sfizz::sfizz sfizz-sndfile) add_executable(sfizz_plot_curve PlotCurve.cpp) target_link_libraries(sfizz_plot_curve PRIVATE sfizz::sfizz) @@ -100,10 +100,10 @@ add_executable(sfizz_plot_wavetables PlotWavetables.cpp) target_link_libraries(sfizz_plot_wavetables PRIVATE sfizz::sfizz) add_executable(sfizz_plot_lfo PlotLFO.cpp) -target_link_libraries(sfizz_plot_lfo PRIVATE sfizz::sfizz) +target_link_libraries(sfizz_plot_lfo PRIVATE sfizz::sfizz sfizz-sndfile) add_executable(sfizz_file_instrument FileInstrument.cpp) -target_link_libraries(sfizz_file_instrument PRIVATE sfizz::sfizz) +target_link_libraries(sfizz_file_instrument PRIVATE sfizz::sfizz sfizz-sndfile) add_executable(sfizz_file_wavetable FileWavetable.cpp) target_link_libraries(sfizz_file_wavetable PRIVATE sfizz::sfizz) diff --git a/tests/FileInstrument.cpp b/tests/FileInstrument.cpp index 98643be2..e759f481 100644 --- a/tests/FileInstrument.cpp +++ b/tests/FileInstrument.cpp @@ -12,20 +12,21 @@ static const char* modeString(int mode, const char* valueFallback = nullptr) { switch (mode) { - case SF_LOOP_NONE: + case sfz::LoopNone: return "none"; - case SF_LOOP_FORWARD: + case sfz::LoopForward: return "forward"; - case SF_LOOP_BACKWARD: + case sfz::LoopBackward: return "backward"; - case SF_LOOP_ALTERNATING: + case sfz::LoopAlternating: return "alternating"; default: return valueFallback; } } -static void printInstrument(const SF_INSTRUMENT& ins) +template +static void printInstrument(const Instrument& ins) { printf("Gain: %d\n", ins.gain); printf("Base note: %d\n", ins.basenote); @@ -34,7 +35,7 @@ static void printInstrument(const SF_INSTRUMENT& ins) printf("Key: %d:%d\n", ins.key_lo, ins.key_hi); printf("Loop count: %d\n", ins.loop_count); - for (int i = 0; i < ins.loop_count; ++i) { + for (unsigned i = 0, n = ins.loop_count; i < n; ++i) { printf("\nLoop %d:\n", i + 1); printf("\tMode: %s\n", modeString(ins.loops[i].mode, "(unknown)")); printf("\tStart: %u\n", ins.loops[i].start); @@ -83,18 +84,18 @@ int main(int argc, char *argv[]) return 1; } - SF_INSTRUMENT ins {}; - if (method == kMethodRiff) { sfz::FileMetadataReader reader; if (!reader.open(path)) { fprintf(stderr, "Cannot open file\n"); return 1; } + sfz::InstrumentInfo ins {}; if (!reader.extractRiffInstrument(ins)) { fprintf(stderr, "Cannot get instrument\n"); return 1; } + printInstrument(ins); } else { SndfileHandle sndFile(path); @@ -102,13 +103,13 @@ int main(int argc, char *argv[]) fprintf(stderr, "Cannot open file\n"); return 1; } + SF_INSTRUMENT ins {}; if (sndFile.command(SFC_GET_INSTRUMENT, &ins, sizeof(ins)) != 1) { fprintf(stderr, "Cannot get instrument\n"); return 1; } + printInstrument(ins); } - printInstrument(ins); - return 0; }