Reenable and fix tests

- Add ASAN workflows
- Remove flag messages from configure; these are misleading as they exclude `COMPILE_OPTIONS`
- Fix non-working tests
- Fix WavPack file loading on Windows
- Fix ASAN traps in LoFi code due to a SIMD `loadu` call in HIIR resamplers
- Add tests for st_audiofile
-
This commit is contained in:
Paul Fd 2023-08-07 21:50:58 +02:00 committed by Paul Ferrand
parent 507c61d458
commit 1e771a45e7
14 changed files with 228 additions and 105 deletions

View file

@ -20,13 +20,8 @@ env:
# shell: pwsh on windows, bash all the rest # shell: pwsh on windows, bash all the rest
# working-directory: ${{ github.workspace }} # working-directory: ${{ github.workspace }}
# FIXME:
# Tests disabled (again); they were working one day and broken the day after
# without changes: ./build/tests/sfizz_tests: No such file or directory
# It didn't work either by specifying the full path (that was OK in previous builds)
jobs: jobs:
clang_tidy: clang_tidy:
# if: ${{ false }}
name: Clang Tidy name: Clang Tidy
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
@ -39,8 +34,57 @@ jobs:
- name: Run - name: Run
run: ./scripts/run_clang_tidy.sh run: ./scripts/run_clang_tidy.sh
test_with_asan:
name: ASAN
runs-on: ubuntu-22.04 # abseil (libabsl) is not available in 20.04 repository
env:
install_name: sfizz-${{ github.ref_name }}-linux
strategy:
matrix:
build_type: [Debug, RelWithDebInfo]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install dependencies
run: |
sudo apt-get update && sudo apt-get install \
ninja-build \
libjack-jackd2-dev \
libabsl-dev \
libabsl20210324
- name: Configure CMake
run: |
options=(
-G Ninja
-B build
-S .
-D CMAKE_BUILD_TYPE=${{ matrix.build_type }}
-D SFIZZ_TESTS=ON
-D SFIZZ_USE_SYSTEM_ABSEIL=ON
-D SFIZZ_ASAN=ON
)
cmake "${options[@]}"
- name: Build tests
run: |
options=(
--build build
--config ${{ matrix.build_type }}
--parallel 2
--target sfizz_tests
)
cmake "${options[@]}"
- name: Run tests
run: |
options=(
--build-config ${{ matrix.build_type }}
--output-on-failure
--test-dir build
)
ctest "${options[@]}"
build_for_linux: build_for_linux:
# if: ${{ false }}
name: Linux Ubuntu 22.04 name: Linux Ubuntu 22.04
runs-on: ubuntu-22.04 # abseil (libabsl) is not available in 20.04 repository runs-on: ubuntu-22.04 # abseil (libabsl) is not available in 20.04 repository
env: env:
@ -83,7 +127,6 @@ jobs:
echo "-- github.workspace: ${{ github.workspace }}" echo "-- github.workspace: ${{ github.workspace }}"
echo "-- runner.workspace: ${{ runner.workspace }}" echo "-- runner.workspace: ${{ runner.workspace }}"
- name: Build tests - name: Build tests
if: ${{ false }}
run: | run: |
options=( options=(
--build build --build build
@ -94,15 +137,10 @@ jobs:
) )
cmake "${options[@]}" cmake "${options[@]}"
- name: Run tests - name: Run tests
if: ${{ false }}
run: | run: |
./build/tests/sfizz_tests
options=( options=(
--build-config ${{ env.build_type }} --build-config ${{ env.build_type }}
--debug
--extra-verbose
--output-on-failure --output-on-failure
--parallel 2
--test-dir build --test-dir build
) )
ctest "${options[@]}" ctest "${options[@]}"
@ -129,7 +167,6 @@ jobs:
path: "${{ github.workspace }}/${{ env.install_name }}.tar.gz" path: "${{ github.workspace }}/${{ env.install_name }}.tar.gz"
build_for_macos: build_for_macos:
# if: ${{ false }}
name: macOS 11 name: macOS 11
runs-on: macos-11 runs-on: macos-11
env: env:
@ -161,7 +198,6 @@ jobs:
echo "-- runner.workspace: ${{ runner.workspace }}" echo "-- runner.workspace: ${{ runner.workspace }}"
echo "-- github.workspace: ${{ github.workspace }}" echo "-- github.workspace: ${{ github.workspace }}"
- name: Build tests - name: Build tests
if: ${{ false }}
run: | run: |
options=( options=(
--build build --build build
@ -172,15 +208,10 @@ jobs:
) )
cmake "${options[@]}" cmake "${options[@]}"
- name: Run tests - name: Run tests
if: ${{ false }}
run: | run: |
./build/tests/sfizz_tests
options=( options=(
--build-config ${{ env.build_type }} --build-config ${{ env.build_type }}
--debug
--extra-verbose
--output-on-failure --output-on-failure
--parallel 2
--test-dir build --test-dir build
) )
ctest "${options[@]}" ctest "${options[@]}"
@ -207,7 +238,6 @@ jobs:
path: "${{ github.workspace }}/${{ env.install_name }}.tar.gz" path: "${{ github.workspace }}/${{ env.install_name }}.tar.gz"
build_for_windows: build_for_windows:
# if: ${{ false }}
name: Windows 2019 name: Windows 2019
runs-on: windows-2019 runs-on: windows-2019
strategy: strategy:
@ -235,12 +265,10 @@ jobs:
-A "${{ matrix.release_arch }}" ` -A "${{ matrix.release_arch }}" `
-B build ` -B build `
-S . ` -S . `
-D CMAKE_BUILD_TYPE=${{ env.build_type }} `
-D SFIZZ_TESTS=ON -D SFIZZ_TESTS=ON
echo "-- runner.workspace: ${{ runner.workspace }}" echo "-- runner.workspace: ${{ runner.workspace }}"
echo "-- github.workspace: ${{ github.workspace }}" echo "-- github.workspace: ${{ github.workspace }}"
- name: Build tests - name: Build tests
if: ${{ false }}
run: | run: |
cmake ` cmake `
--build build ` --build build `
@ -249,15 +277,10 @@ jobs:
--target sfizz_tests ` --target sfizz_tests `
--verbose ` --verbose `
- name: Run tests - name: Run tests
if: ${{ false }}
run: | run: |
.\build\tests\${{ env.build_type }}\sfizz_tests.exe
ctest ` ctest `
--build-config ${{ env.build_type }} ` --build-config ${{ env.build_type }} `
--debug `
--extra-verbose `
--output-on-failure ` --output-on-failure `
--parallel 2 `
--test-dir build ` --test-dir build `
- name: Build all - name: Build all
run: | run: |
@ -277,7 +300,6 @@ jobs:
path: "${{ github.workspace }}/${{ env.install_name }}.zip" path: "${{ github.workspace }}/${{ env.install_name }}.zip"
archive_source_code: archive_source_code:
# if: startsWith(github.ref, 'refs/tags/')
if: ${{ github.ref_type == 'tag' }} if: ${{ github.ref_type == 'tag' }}
name: Source code archive name: Source code archive
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
@ -304,7 +326,6 @@ jobs:
path: "${{ github.workspace }}/${{ env.install_name }}.tar.gz" path: "${{ github.workspace }}/${{ env.install_name }}.tar.gz"
build_with_libsndfile: build_with_libsndfile:
# if: ${{ false }}
name: Linux libsndfile name: Linux libsndfile
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:
@ -332,7 +353,6 @@ jobs:
) )
cmake "${options[@]}" cmake "${options[@]}"
- name: Build tests - name: Build tests
if: ${{ false }}
run: | run: |
options=( options=(
--build build --build build
@ -343,11 +363,15 @@ jobs:
) )
cmake "${options[@]}" cmake "${options[@]}"
- name: Run tests - name: Run tests
if: ${{ false }} run: |
run: ./build/tests/sfizz_tests options=(
--build-config ${{ env.build_type }}
--output-on-failure
--test-dir build
)
ctest "${options[@]}"
build_with_makefile: build_with_makefile:
# if: ${{ false }}
name: Linux makefile name: Linux makefile
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
steps: steps:

1
.gitignore vendored
View file

@ -26,3 +26,4 @@ clients/sfzprint
*.code-* *.code-*
.kak.tags.namecache .kak.tags.namecache
.clangd .clangd
venv

View file

@ -41,6 +41,7 @@ option_ex(SFIZZ_USE_SYSTEM_CATCH "Use Catch libraries preinstalled on system"
option_ex(SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF) option_ex(SFIZZ_RELEASE_ASSERTS "Forced assertions in release builds" OFF)
option_ex(SFIZZ_PROFILE_BUILD "Profile the build time" OFF) option_ex(SFIZZ_PROFILE_BUILD "Profile the build time" OFF)
option_ex(SFIZZ_SNDFILE_STATIC "Link the sndfile library statically" OFF) option_ex(SFIZZ_SNDFILE_STATIC "Link the sndfile library statically" OFF)
option_ex(SFIZZ_ASAN "Use address sanitizer on all sfizz targets" OFF)
# Continuous Controller count (0 to 511) # Continuous Controller count (0 to 511)
set(MIDI_CC_COUNT 512 CACHE STRING "Maximum number of managed Control Change messages") set(MIDI_CC_COUNT 512 CACHE STRING "Maximum number of managed Control Change messages")

View file

@ -116,6 +116,11 @@ function(sfizz_enable_fast_math NAME)
endif() endif()
endfunction() endfunction()
function(sfizz_enable_release_asserts NAME)
target_compile_definitions("${NAME}" PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
target_compile_definitions("${NAME}" PUBLIC "SFIZZ_ENABLE_RELEASE_DBG=1")
endfunction()
# If we build with Clang, optionally use libc++. Enabled by default on Apple OS. # If we build with Clang, optionally use libc++. Enabled by default on Apple OS.
cmake_dependent_option(USE_LIBCPP "Use libc++ with clang" "${APPLE}" cmake_dependent_option(USE_LIBCPP "Use libc++ with clang" "${APPLE}"
"CMAKE_CXX_COMPILER_ID MATCHES Clang" OFF) "CMAKE_CXX_COMPILER_ID MATCHES Clang" OFF)
@ -133,6 +138,13 @@ else()
set(PROJECT_IS_MAIN FALSE) set(PROJECT_IS_MAIN FALSE)
endif() endif()
if(SFIZZ_ASAN)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-fsanitize=address>)
add_link_options($<$<COMPILE_LANGUAGE:C,CXX>:-fsanitize=address>)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-fno-omit-frame-pointer>)
add_link_options($<$<COMPILE_LANGUAGE:C,CXX>:-fno-omit-frame-pointer>)
endif()
# Don't show build information when building a different project # Don't show build information when building a different project
function(show_build_info_if_needed) function(show_build_info_if_needed)
if(PROJECT_IS_MAIN) if(PROJECT_IS_MAIN)
@ -155,6 +167,7 @@ Statically link sndfile: ${SFIZZ_SNDFILE_STATIC}
Use clang libc++: ${USE_LIBCPP} Use clang libc++: ${USE_LIBCPP}
Release asserts: ${SFIZZ_RELEASE_ASSERTS} Release asserts: ${SFIZZ_RELEASE_ASSERTS}
Use ASAN: ${SFIZZ_ASAN}
Use system abseil-cpp: ${SFIZZ_USE_SYSTEM_ABSEIL} Use system abseil-cpp: ${SFIZZ_USE_SYSTEM_ABSEIL}
Use system catch: ${SFIZZ_USE_SYSTEM_CATCH} Use system catch: ${SFIZZ_USE_SYSTEM_CATCH}
@ -182,11 +195,6 @@ VST3 destination directory: ${VST3_PLUGIN_INSTALL_DIR}")
endif() endif()
message(STATUS " message(STATUS "
Install prefix: ${CMAKE_INSTALL_PREFIX} Install prefix: ${CMAKE_INSTALL_PREFIX}
CXX Debug flags: ${CMAKE_CXX_FLAGS_DEBUG}
CXX Release flags: ${CMAKE_CXX_FLAGS_RELEASE}
CXX MinSize flags: ${CMAKE_CXX_FLAGS_MINSIZEREL}
CXX RelWithDebInfo flags: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}
") ")
endif() endif()
endfunction() endfunction()

View file

@ -9,6 +9,9 @@
#include "st_audiofile_libs.h" #include "st_audiofile_libs.h"
#include <stdlib.h> #include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#endif
#define WAVPACK_MEMORY_ASSUMED_VERSION 5 #define WAVPACK_MEMORY_ASSUMED_VERSION 5
struct st_audio_file { struct st_audio_file {
@ -179,10 +182,18 @@ static st_audio_file* st_generic_open_file(const void* filename, int widepath)
// Try WV // Try WV
{ {
af->wv =
#if defined(_WIN32) #if defined(_WIN32)
WavpackOpenFileInput((const char*)filename, NULL, OPEN_FILE_UTF8, 0); // WavPack expects an UTF8 input and has no widechar api, so we convert the filename back...
unsigned wsize = wcslen(filename);
unsigned size = WideCharToMultiByte(CP_UTF8, 0, filename, wsize, NULL, 0, NULL, NULL);
char *buffer = (char*)malloc((size+1) * sizeof(char));
WideCharToMultiByte(CP_UTF8, 0, filename, wsize, buffer, size, NULL, NULL);
buffer[size] = '\0';
af->wv =
WavpackOpenFileInput(buffer, NULL, OPEN_FILE_UTF8, 0);
free(buffer);
#else #else
af->wv =
WavpackOpenFileInput((const char*)filename, NULL, 0, 0); WavpackOpenFileInput((const char*)filename, NULL, 0, 0);
#endif #endif
if (af->wv) { if (af->wv) {

View file

@ -60,13 +60,13 @@ inline ST_AudioFile::~ST_AudioFile() noexcept
reset(); reset();
} }
ST_AudioFile::ST_AudioFile(ST_AudioFile&& other) noexcept inline ST_AudioFile::ST_AudioFile(ST_AudioFile&& other) noexcept
: af_(other.af_) : af_(other.af_)
{ {
other.af_ = nullptr; other.af_ = nullptr;
} }
ST_AudioFile& ST_AudioFile::operator=(ST_AudioFile&& other) noexcept inline ST_AudioFile& ST_AudioFile::operator=(ST_AudioFile&& other) noexcept
{ {
if (this != &other) { if (this != &other) {
if (af_) if (af_)
@ -91,14 +91,14 @@ inline void ST_AudioFile::reset(st_audio_file* new_af) noexcept
} }
} }
bool ST_AudioFile::open_file(const char* filename) inline bool ST_AudioFile::open_file(const char* filename)
{ {
st_audio_file* new_af = st_open_file(filename); st_audio_file* new_af = st_open_file(filename);
reset(new_af); reset(new_af);
return new_af != nullptr; return new_af != nullptr;
} }
bool ST_AudioFile::open_memory(const void* memory, size_t length) inline bool ST_AudioFile::open_memory(const void* memory, size_t length)
{ {
st_audio_file* new_af = st_open_memory(memory, length); st_audio_file* new_af = st_open_memory(memory, length);
reset(new_af); reset(new_af);

View file

@ -231,9 +231,7 @@ target_include_directories(sfizz_parser PUBLIC sfizz)
target_link_libraries(sfizz_parser target_link_libraries(sfizz_parser
PUBLIC sfizz::filesystem sfizz::simde absl::strings PUBLIC sfizz::filesystem sfizz::simde absl::strings
PRIVATE absl::flat_hash_map) PRIVATE absl::flat_hash_map)
if(SFIZZ_RELEASE_ASSERTS) sfizz_enable_release_asserts(sfizz_parser)
target_compile_definitions(sfizz_parser PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
endif()
# OSC messaging library # OSC messaging library
set(SFIZZ_MESSAGING_HEADERS set(SFIZZ_MESSAGING_HEADERS
@ -250,9 +248,8 @@ target_sources(sfizz_messaging PRIVATE
${SFIZZ_MESSAGING_HEADERS} ${SFIZZ_MESSAGING_SOURCES}) ${SFIZZ_MESSAGING_HEADERS} ${SFIZZ_MESSAGING_SOURCES})
target_include_directories(sfizz_messaging PUBLIC ".") target_include_directories(sfizz_messaging PUBLIC ".")
target_link_libraries(sfizz_messaging PUBLIC absl::strings) target_link_libraries(sfizz_messaging PUBLIC absl::strings)
if(SFIZZ_RELEASE_ASSERTS) sfizz_enable_release_asserts(sfizz_messaging)
target_compile_definitions(sfizz_messaging PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
endif()
# Import library # Import library
set(SFIZZ_IMPORT_HEADERS set(SFIZZ_IMPORT_HEADERS
@ -303,9 +300,8 @@ if(SFIZZ_USE_SNDFILE)
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1") target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1")
target_link_libraries(sfizz_internal PUBLIC st_audiofile) target_link_libraries(sfizz_internal PUBLIC st_audiofile)
endif() endif()
if(SFIZZ_RELEASE_ASSERTS) sfizz_enable_release_asserts(sfizz_internal)
target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_ENABLE_RELEASE_ASSERT=1")
endif()
if(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT) if(SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT)
target_compile_definitions(sfizz_internal PRIVATE "SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT=1") target_compile_definitions(sfizz_internal PRIVATE "SFIZZ_IMPLEMENT_CXX17_ALIGNED_NEW_SUPPORT=1")
endif() endif()

View file

@ -477,7 +477,7 @@ void sfz::FilePool::loadingJob(const QueuedFileData& data) noexcept
AudioReaderPtr reader = createAudioReader(file, id->isReverse(), &readError); AudioReaderPtr reader = createAudioReader(file, id->isReverse(), &readError);
if (readError) { if (readError) {
DBG("[sfizz] libsndfile errored for " << *id << " with message " << readError.message()); DBG("[sfizz] reading the file errored for " << *id << " with code " << readError << ": " << readError.message());
return; return;
} }

View file

@ -1069,6 +1069,10 @@ void Voice::Impl::fillWithData(AudioSpan<float> buffer) noexcept
} }
auto source = currentPromise_->getData(); auto source = currentPromise_->getData();
if (source.getNumFrames() == 0) {
DBG("[Voice] Empty source in promise");
return;
}
BufferPool& bufferPool = resources_.getBufferPool(); BufferPool& bufferPool = resources_.getBufferPool();
const CurveSet& curves = resources_.getCurves(); const CurveSet& curves = resources_.getCurves();

View file

@ -128,13 +128,13 @@ namespace fx {
const float steps = (1.0f + (100.0f - fDepth)) * 0.75f; const float steps = (1.0f + (100.0f - fDepth)) * 0.75f;
const float invSteps = 1.0f / steps; const float invSteps = 1.0f / steps;
float y2x[4] { 0.0f, 0.0f, 0.0f, 0.0f };
for (uint32_t i = 0; i < nframes; ++i) { for (uint32_t i = 0; i < nframes; ++i) {
float x = in[i]; float x = in[i];
float y = std::copysign((int)(0.5f + std::fabs(x * steps)), x) * invSteps; // NOLINT float y = std::copysign((int)(0.5f + std::fabs(x * steps)), x) * invSteps; // NOLINT
float y2x[2];
y2x[0] = (y != lastValue) ? (0.5f * (y + lastValue)) : y; y2x[0] = (y != lastValue) ? (0.5f * (y + lastValue)) : y;
y2x[1] = y; y2x[1] = y;
@ -185,6 +185,7 @@ namespace fx {
float phase = fPhase; float phase = fPhase;
float lastValue = fLastValue; float lastValue = fLastValue;
float y2x[4] { 0.0f, 0.0f, 0.0f, 0.0f };
for (uint32_t i = 0; i < nframes; ++i) { for (uint32_t i = 0; i < nframes; ++i) {
float x = in[i]; float x = in[i];
@ -193,7 +194,6 @@ namespace fx {
float y = (phase > 1.0f) ? x : lastValue; float y = (phase > 1.0f) ? x : lastValue;
phase -= static_cast<float>(static_cast<int>(phase)); phase -= static_cast<float>(static_cast<int>(phase));
float y2x[2];
y2x[0] = (y != lastValue) ? (0.5f * (y + lastValue)) : y; y2x[0] = (y != lastValue) ? (0.5f * (y + lastValue)) : y;
y2x[1] = y; y2x[1] = y;

View file

@ -9,63 +9,137 @@
#include "sfizz/Synth.h" #include "sfizz/Synth.h"
#include "TestHelpers.h" #include "TestHelpers.h"
#include "catch2/catch.hpp" #include "catch2/catch.hpp"
#include "st_audiofile.hpp"
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
using namespace Catch::literals; using namespace Catch::literals;
TEST_CASE("[AudioFiles] No leakage on right") void compareFiles(const fs::path& lFile, const fs::path& rFile)
{ {
sfz::Synth synth; auto l_file = ST_AudioFile();
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) }; auto r_file = ST_AudioFile();
std::vector<float> zeros(synth.getSamplesPerBlock(), 0); #if defined(_WIN32)
sfz::AudioSpan<float> span { buffer }; l_file.open_file_w(lFile.wstring().c_str());
synth.loadSfzString(fs::current_path() / "tests/TestFiles/wavpack.sfz", R"( r_file.open_file_w(rFile.wstring().c_str());
<region> sample=kick.wav key=60 pan=-100 #else
)"); l_file.open_file(lFile.string().c_str());
synth.noteOn(0, 60, 127); r_file.open_file(rFile.string().c_str());
synth.renderBlock(buffer); #endif
REQUIRE( numPlayingVoices(synth) == 1 ); CHECK(l_file.get_channels() == 1);
REQUIRE( approxEqual(span.getConstSpan(1), absl::MakeConstSpan(zeros), 1e-2f) ); CHECK(r_file.get_channels() == 1);
while (numPlayingVoices(synth) > 0) { std::vector<float> wav (l_file.get_frame_count());
synth.renderBlock(buffer); std::vector<float> flac (r_file.get_frame_count());
REQUIRE( approxEqual(span.getConstSpan(1), absl::MakeConstSpan(zeros), 1e-2f) ); l_file.read_f32(wav.data(), l_file.get_frame_count());
r_file.read_f32(flac.data(), r_file.get_frame_count());
REQUIRE( approxEqual<float>(wav, flac) );
}
TEST_CASE("[AudioFiles] Compare Flac and WAV")
{
compareFiles(fs::current_path() / "tests/TestFiles/kick.wav",
fs::current_path() / "tests/TestFiles/kick.flac");
}
#if !defined(SFIZZ_USE_SNDFILE)
TEST_CASE("[AudioFiles] Compare WV and WAV")
{
compareFiles(fs::current_path() / "tests/TestFiles/kick.wav",
fs::current_path() / "tests/TestFiles/kick.wv");
}
#endif
struct CompareOutputOpts
{
int note { 60 };
int delay { 0 };
int velocity { 127 };
float sampleRate { 48000.0f };
int samplesPerBlock { 1024 };
};
/**
* @brief Compare the outputs of 2 sfz files with a given set of options, for a single note pressed.
*
* @param lFile
* @param rFile
* @param opts
*/
void compareOutputs(const std::string& lFile, const std::string& rFile, CompareOutputOpts opts)
{
sfz::Synth lSynth;
sfz::Synth rSynth;
lSynth.enableFreeWheeling();
rSynth.enableFreeWheeling();
lSynth.setSampleRate(opts.sampleRate);
rSynth.setSampleRate(opts.sampleRate);
lSynth.setSamplesPerBlock(opts.samplesPerBlock);
rSynth.setSamplesPerBlock(opts.samplesPerBlock);
sfz::AudioBuffer<float> lBuffer { 2, static_cast<unsigned>(lSynth.getSamplesPerBlock()) };
sfz::AudioBuffer<float> rBuffer { 2, static_cast<unsigned>(rSynth.getSamplesPerBlock()) };
sfz::AudioSpan<float> lSpan { lBuffer };
sfz::AudioSpan<float> rSpan { rBuffer };
lSynth.loadSfzString(fs::current_path() / "tests/TestFiles/l.sfz", lFile);
rSynth.loadSfzString(fs::current_path() / "tests/TestFiles/r.sfz", rFile);
lSynth.noteOn(opts.delay, opts.note, opts.velocity);
rSynth.noteOn(opts.delay, opts.note, opts.velocity);
lSynth.renderBlock(lSpan);
rSynth.renderBlock(rSpan);
REQUIRE( numPlayingVoices(lSynth) == 1 );
REQUIRE( numPlayingVoices(rSynth) == 1 );
REQUIRE( approxEqual(lSpan.getConstSpan(0), rSpan.getConstSpan(0)) );
REQUIRE( approxEqual(lSpan.getConstSpan(1), rSpan.getConstSpan(1)) );
while (numPlayingVoices(lSynth) == 1) {
REQUIRE( numPlayingVoices(rSynth) == 1 );
lSynth.renderBlock(lSpan);
rSynth.renderBlock(rSpan);
REQUIRE( approxEqual(lSpan.getConstSpan(0), rSpan.getConstSpan(0)) );
REQUIRE( approxEqual(lSpan.getConstSpan(1), rSpan.getConstSpan(1)) );
} }
} }
TEST_CASE("[AudioFiles] WavPack file") TEST_CASE("[AudioFiles] Sanity check (native sample rate)")
{ {
sfz::Synth synth; std::string lFile = "<region> sample=kick.wav key=60";
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) }; std::string rFile = "<region> sample=kick.wav key=60";
sfz::AudioSpan<float> span { buffer }; CompareOutputOpts opts;
synth.loadSfzString(fs::current_path() / "tests/TestFiles/wavpack.sfz", R"( opts.sampleRate = 44100.0f;
<region> sample=kick.wav key=60 pan=-100 compareOutputs(lFile, rFile, opts);
<region> sample=kick.wv key=60 pan=100
)");
synth.noteOn(0, 60, 127);
synth.renderBlock(buffer);
REQUIRE( numPlayingVoices(synth) == 2 );
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) );
while (numPlayingVoices(synth) > 0) {
synth.renderBlock(buffer);
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) );
}
} }
TEST_CASE("[AudioFiles] Flac file") #if !defined(SFIZZ_USE_SNDFILE)
TEST_CASE("[AudioFiles] Wavpack file (native sample rate)")
{ {
sfz::Synth synth; std::string lFile = "<region> sample=kick.wav key=60";
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) }; std::string rFile = "<region> sample=kick.wv key=60";
sfz::AudioSpan<float> span { buffer }; CompareOutputOpts opts;
synth.loadSfzString(fs::current_path() / "tests/TestFiles/wavpack.sfz", R"( opts.sampleRate = 44100.0f;
<region> sample=kick.wav key=60 pan=-100 compareOutputs(lFile, rFile, opts);
<region> sample=kick.flac key=60 pan=100 }
)");
synth.noteOn(0, 60, 127); TEST_CASE("[AudioFiles] Wavpack file (resampled)")
synth.renderBlock(buffer); {
REQUIRE( numPlayingVoices(synth) == 2 ); std::string lFile = "<region> sample=kick.wav key=60";
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) ); std::string rFile = "<region> sample=kick.wv key=60";
while (numPlayingVoices(synth) > 0) { CompareOutputOpts opts;
synth.renderBlock(buffer); opts.sampleRate = 48000.0f;
REQUIRE( approxEqual(span.getConstSpan(0), span.getConstSpan(1)) ); compareOutputs(lFile, rFile, opts);
} }
#endif
TEST_CASE("[AudioFiles] Flac file (native sample rate)")
{
std::string lFile = "<region> sample=kick.wav key=60";
std::string rFile = "<region> sample=kick.flac key=60";
CompareOutputOpts opts;
opts.sampleRate = 44100.0f;
compareOutputs(lFile, rFile, opts);
}
TEST_CASE("[AudioFiles] Flac file (resampled)")
{
std::string lFile = "<region> sample=kick.wav key=60";
std::string rFile = "<region> sample=kick.flac key=60";
CompareOutputOpts opts;
opts.sampleRate = 48000.0f;
compareOutputs(lFile, rFile, opts);
} }

View file

@ -64,7 +64,7 @@ set(SFIZZ_TEST_SOURCES
) )
add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES}) add_executable(sfizz_tests ${SFIZZ_TEST_SOURCES})
target_link_libraries(sfizz_tests PRIVATE sfizz::internal sfizz::static sfizz::spin_mutex sfizz::jsl sfizz::filesystem) target_link_libraries(sfizz_tests PRIVATE sfizz::internal sfizz::static sfizz::spin_mutex sfizz::jsl sfizz::filesystem st_audiofile)
if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.12") if(APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS "10.12")
# workaround for incomplete C++17 runtime on macOS # workaround for incomplete C++17 runtime on macOS
target_compile_definitions(sfizz_tests PRIVATE "CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS") target_compile_definitions(sfizz_tests PRIVATE "CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS")

View file

@ -116,8 +116,10 @@ TEST_CASE("[Messaging] Type-safe client API")
REQUIRE(args[index++].f == f); REQUIRE(args[index++].f == f);
REQUIRE(args[index++].d == d); REQUIRE(args[index++].d == d);
REQUIRE(!strcmp(args[index++].s, s)); REQUIRE(!strcmp(args[index++].s, s));
#if !defined(__SANITIZE_ADDRESS__)
REQUIRE(args[index ].b->data == b.data); REQUIRE(args[index ].b->data == b.data);
REQUIRE(args[index++].b->size == b.size); REQUIRE(args[index++].b->size == b.size);
#endif
}); });
client.receive<'i', 'm', 'h', 'f', 'd', 's', 'b', 'T', 'F', 'N', 'I'>( client.receive<'i', 'm', 'h', 'f', 'd', 's', 'b', 'T', 'F', 'N', 'I'>(

View file

@ -152,8 +152,10 @@ std::string createModulationDotGraph(std::vector<std::string> lines);
template <class Type> template <class Type>
bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3) bool approxEqual(absl::Span<const Type> lhs, absl::Span<const Type> rhs, Type eps = 1e-3)
{ {
if (lhs.size() != rhs.size()) if (lhs.size() != rhs.size()) {
std::cerr << "Span size are different: "<< lhs.size() << " != " << rhs.size() << '\n';
return false; return false;
}
bool different = false; bool different = false;
for (size_t i = 0; i < rhs.size(); ++i) for (size_t i = 0; i < rhs.size(); ++i)