Merge commit '105c459' into tentative-merge-cpp14
This commit is contained in:
commit
0eb960f028
29 changed files with 308 additions and 253 deletions
|
|
@ -9,11 +9,10 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) # To override the policy in abseil and ben
|
||||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
||||||
|
set(USE_LIBCPP ON CACHE BOOL "Use libc++ with clang")
|
||||||
add_compile_options(-stdlib=libc++)
|
add_compile_options(-stdlib=libc++)
|
||||||
# Presumably need the above for linking too, maybe other options missing as well
|
# Presumably need the above for linking too, maybe other options missing as well
|
||||||
add_link_options(-stdlib=libc++) # New command on CMake master, not in 3.12 release
|
add_link_options(-lc++abi) # New command on CMake master, not in 3.12 release
|
||||||
else()
|
|
||||||
add_link_options(-lstdc++fs)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (UNIX)
|
if (UNIX)
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,14 @@ void lowpass(absl::Span<const float> input, absl::Span<float> lowpass, float gai
|
||||||
float state = 0.0f;
|
float state = 0.0f;
|
||||||
float intermediate;
|
float intermediate;
|
||||||
const auto G = gain / (1 - gain);
|
const auto G = gain / (1 - gain);
|
||||||
for (auto [in, out] = std::make_pair(input.begin(), lowpass.begin());
|
auto in = input.begin();
|
||||||
in < input.end() && out < lowpass.end();
|
auto out = lowpass.begin();
|
||||||
in++, out++)
|
while (in < input.end()) {
|
||||||
{
|
|
||||||
intermediate = G * (*in - state);
|
intermediate = G * (*in - state);
|
||||||
*out = intermediate + state;
|
*out = intermediate + state;
|
||||||
state = *out + intermediate;
|
state = *out + intermediate;
|
||||||
|
in++;
|
||||||
|
out++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,32 +57,23 @@ void highpass(absl::Span<const float> input, absl::Span<float> highpass, float g
|
||||||
float state = 0.0f;
|
float state = 0.0f;
|
||||||
float intermediate;
|
float intermediate;
|
||||||
const auto G = gain / (1 - gain);
|
const auto G = gain / (1 - gain);
|
||||||
for (auto [in, out] = std::make_pair(input.begin(), highpass.begin());
|
auto in = input.begin();
|
||||||
in < input.end() && out < highpass.end();
|
auto out = highpass.begin();
|
||||||
in++, out++)
|
while (in < input.end()) {
|
||||||
{
|
|
||||||
intermediate = G * (*in - state);
|
intermediate = G * (*in - state);
|
||||||
*out = *in - intermediate - state;
|
*out = *in - intermediate - state;
|
||||||
state += 2*intermediate;
|
state += 2*intermediate;
|
||||||
|
in++;
|
||||||
|
out++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void highpass_foreach(absl::Span<const float> input, absl::Span<float> highpass, float gain)
|
|
||||||
{
|
|
||||||
lowpass(input, highpass, gain);
|
|
||||||
for (auto [in, out] = std::make_pair(input.begin(), highpass.begin());
|
|
||||||
in < input.end() && out < highpass.end();
|
|
||||||
in++, out++)
|
|
||||||
*out = *in - *out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void highpass_raw(absl::Span<const float> input, absl::Span<float> highpass, float gain)
|
void highpass_raw(absl::Span<const float> input, absl::Span<float> highpass, float gain)
|
||||||
{
|
{
|
||||||
lowpass(input, highpass, gain);
|
lowpass(input, highpass, gain);
|
||||||
auto in = input.data();
|
auto in = input.data();
|
||||||
auto out = highpass.data();
|
auto out = highpass.data();
|
||||||
while (in < input.end() && out < highpass.end())
|
while (in < input.end()) {
|
||||||
{
|
|
||||||
*out = *in - *out;
|
*out = *in - *out;
|
||||||
out++;
|
out++;
|
||||||
in++;
|
in++;
|
||||||
|
|
@ -144,21 +136,6 @@ static void High(benchmark::State& state) {
|
||||||
highpass(input, absl::MakeSpan(output), filterGain);
|
highpass(input, absl::MakeSpan(output), filterGain);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void High_ForEach(benchmark::State& state) {
|
|
||||||
std::vector<float> input(state.range(0));
|
|
||||||
std::vector<float> output(state.range(0));
|
|
||||||
std::random_device rd { };
|
|
||||||
std::mt19937 gen { rd() };
|
|
||||||
|
|
||||||
std::normal_distribution<float> dist { };
|
|
||||||
std::generate(input.begin(), input.end(), [&]() {
|
|
||||||
return dist(gen);
|
|
||||||
});
|
|
||||||
|
|
||||||
for (auto _ : state)
|
|
||||||
highpass_foreach(input, absl::MakeSpan(output), filterGain);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void High_Raw(benchmark::State& state) {
|
static void High_Raw(benchmark::State& state) {
|
||||||
std::vector<float> input(state.range(0));
|
std::vector<float> input(state.range(0));
|
||||||
std::vector<float> output(state.range(0));
|
std::vector<float> output(state.range(0));
|
||||||
|
|
@ -192,7 +169,6 @@ static void High_SSE(benchmark::State& state) {
|
||||||
|
|
||||||
BENCHMARK(Low)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
BENCHMARK(Low)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
||||||
BENCHMARK(High)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
BENCHMARK(High)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
||||||
BENCHMARK(High_ForEach)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
|
||||||
BENCHMARK(High_Raw)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
BENCHMARK(High_Raw)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
||||||
BENCHMARK(High_SSE)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
BENCHMARK(High_SSE)->RangeMultiplier(2)->Range((2<<5), (2<<10));
|
||||||
BENCHMARK_MAIN();
|
BENCHMARK_MAIN();
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
#include "../sfizz/SIMDHelpers.h"
|
#include "../sfizz/SIMDHelpers.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
|
|
||||||
inline constexpr int bigNumber { 2399132 };
|
constexpr int bigNumber { 2399132 };
|
||||||
|
|
||||||
class IterOffset : public benchmark::Fixture {
|
class IterOffset : public benchmark::Fixture {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,7 @@ int main(int argc, char** argv)
|
||||||
std::cout << '\n';
|
std::cout << '\n';
|
||||||
|
|
||||||
PrintingParser parser;
|
PrintingParser parser;
|
||||||
std::filesystem::path filename { filesToParse[0] };
|
parser.loadSfzFile(filesToParse[0]);
|
||||||
parser.loadSfzFile(filename);
|
|
||||||
std::cout << "==========" << '\n';
|
std::cout << "==========" << '\n';
|
||||||
std::cout << "Total:" << '\n';
|
std::cout << "Total:" << '\n';
|
||||||
std::cout << "\tMasters: " << parser.getNumMasters() << '\n';
|
std::cout << "\tMasters: " << parser.getNumMasters() << '\n';
|
||||||
|
|
|
||||||
|
|
@ -36,24 +36,59 @@ endif()
|
||||||
|
|
||||||
set(SFIZZ_SOURCES ${SFIZZ_SOURCES} ${SFIZZ_SIMD_SOURCES})
|
set(SFIZZ_SOURCES ${SFIZZ_SOURCES} ${SFIZZ_SIMD_SOURCES})
|
||||||
|
|
||||||
|
# include(CheckCXXSourceCompiles)
|
||||||
|
# check_cxx_source_compiles("
|
||||||
|
# #include <filesystem>
|
||||||
|
|
||||||
|
# int main(int argc, char** argv)
|
||||||
|
# {
|
||||||
|
# std::filesystem::path p;
|
||||||
|
# return 0;
|
||||||
|
# }
|
||||||
|
# " HAVE_STD_FILESYSTEM)
|
||||||
|
|
||||||
|
check_include_file_cxx("filesystem" HAVE_STD_FILESYSTEM)
|
||||||
|
if (HAVE_STD_FILESYSTEM)
|
||||||
|
# if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)
|
||||||
|
# add_compile_options(-DUSE_STD_FILESYSTEM)
|
||||||
|
# endif()
|
||||||
|
if (WIN32)
|
||||||
|
add_compile_options(/DUSE_STD_FILESYSTEM)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
add_library(sfizz_parser STATIC)
|
add_library(sfizz_parser STATIC)
|
||||||
target_sources(sfizz_parser PRIVATE Parser.cpp Opcode.cpp)
|
target_sources(sfizz_parser PRIVATE Parser.cpp Opcode.cpp)
|
||||||
target_include_directories(sfizz_parser PUBLIC .)
|
target_include_directories(sfizz_parser PUBLIC .)
|
||||||
if(UNIX)
|
|
||||||
target_link_libraries(sfizz_parser PUBLIC stdc++fs)
|
|
||||||
# target_compile_options(sfizz_parser PUBLIC -fno-rtti -fno-exceptions)
|
|
||||||
endif(UNIX)
|
|
||||||
target_link_libraries(sfizz_parser PRIVATE absl::strings)
|
target_link_libraries(sfizz_parser PRIVATE absl::strings)
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
||||||
|
target_link_libraries(sfizz_parser PUBLIC c++fs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
target_link_libraries(sfizz_parser PUBLIC stdc++fs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
add_library(sfizz STATIC ${SFIZZ_SOURCES})
|
add_library(sfizz STATIC ${SFIZZ_SOURCES})
|
||||||
target_link_libraries(sfizz PRIVATE sfizz_parser)
|
target_link_libraries(sfizz PRIVATE sfizz_parser)
|
||||||
target_include_directories(sfizz PUBLIC .)
|
target_include_directories(sfizz PUBLIC .)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
target_link_libraries(sfizz PRIVATE Threads::Threads)
|
target_link_libraries(sfizz PRIVATE Threads::Threads)
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
target_link_libraries(sfizz PUBLIC stdc++fs atomic)
|
target_link_libraries(sfizz PUBLIC atomic)
|
||||||
# target_compile_options(sfizz PUBLIC -fno-rtti -fno-exceptions)
|
|
||||||
endif(UNIX)
|
endif(UNIX)
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT ANDROID)
|
||||||
|
target_link_libraries(sfizz PUBLIC c++fs)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
target_link_libraries(sfizz PUBLIC stdc++fs)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(sfizz PUBLIC absl::strings)
|
target_link_libraries(sfizz PUBLIC absl::strings)
|
||||||
target_link_libraries(sfizz PRIVATE sndfile absl::flat_hash_map)
|
target_link_libraries(sfizz PRIVATE sndfile absl::flat_hash_map)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ std::unique_ptr<AudioBuffer<T>> readFromFile(SndfileHandle& sndFile, int numFram
|
||||||
|
|
||||||
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename, uint32_t offset) noexcept
|
absl::optional<sfz::FilePool::FileInformation> sfz::FilePool::getFileInformation(const std::string& filename, uint32_t offset) noexcept
|
||||||
{
|
{
|
||||||
std::filesystem::path file { rootDirectory / filename };
|
fs::path file { rootDirectory / filename };
|
||||||
if (!std::filesystem::exists(file))
|
if (!fs::exists(file))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
SndfileHandle sndFile(reinterpret_cast<const char*>(file.c_str()));
|
||||||
|
|
@ -128,8 +128,8 @@ void sfz::FilePool::loadingThread() noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
DBG("Background loading of: " << *fileToLoad.sample);
|
DBG("Background loading of: " << *fileToLoad.sample);
|
||||||
std::filesystem::path file { rootDirectory / *fileToLoad.sample };
|
fs::path file { rootDirectory / *fileToLoad.sample };
|
||||||
if (!std::filesystem::exists(file)) {
|
if (!fs::exists(file)) {
|
||||||
DBG("Background thread: no file " << *fileToLoad.sample << " exists.");
|
DBG("Background thread: no file " << *fileToLoad.sample << " exists.");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ public:
|
||||||
fileLoadingThread.join();
|
fileLoadingThread.join();
|
||||||
garbageCollectionThread.join();
|
garbageCollectionThread.join();
|
||||||
}
|
}
|
||||||
void setRootDirectory(const std::filesystem::path& directory) noexcept { rootDirectory = directory; }
|
void setRootDirectory(const fs::path& directory) noexcept { rootDirectory = directory; }
|
||||||
size_t getNumPreloadedSamples() const noexcept { return preloadedData.size(); }
|
size_t getNumPreloadedSamples() const noexcept { return preloadedData.size(); }
|
||||||
|
|
||||||
struct FileInformation {
|
struct FileInformation {
|
||||||
|
|
@ -60,7 +60,7 @@ public:
|
||||||
void enqueueLoading(Voice* voice, const std::string* sample, int numFrames, unsigned ticket) noexcept;
|
void enqueueLoading(Voice* voice, const std::string* sample, int numFrames, unsigned ticket) noexcept;
|
||||||
void clear();
|
void clear();
|
||||||
private:
|
private:
|
||||||
std::filesystem::path rootDirectory;
|
fs::path rootDirectory;
|
||||||
struct FileLoadingInformation {
|
struct FileLoadingInformation {
|
||||||
Voice* voice;
|
Voice* voice;
|
||||||
const std::string* sample;
|
const std::string* sample;
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ public:
|
||||||
void getBlock(absl::Span<Type> output);
|
void getBlock(absl::Span<Type> output);
|
||||||
private:
|
private:
|
||||||
std::function<Type(Type)> function { [](Type input) { return input; } };
|
std::function<Type(Type)> function { [](Type input) { return input; } };
|
||||||
static_assert(std::is_arithmetic<Type>::value);
|
static_assert(std::is_arithmetic<Type>::value, "Type should be arithmetic");
|
||||||
std::vector<std::pair<int, Type>> events;
|
std::vector<std::pair<int, Type>> events;
|
||||||
int maxCapacity { config::defaultSamplesPerBlock };
|
int maxCapacity { config::defaultSamplesPerBlock };
|
||||||
Type currentValue { 0.0 };
|
Type currentValue { 0.0 };
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ inline constexpr Type mag2db(Type in)
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Random {
|
namespace Random {
|
||||||
static SFZ_INLINE std::random_device randomDevice;
|
static SFZ_INLINE std::random_device randomDevice;
|
||||||
static SFZ_INLINE std::mt19937 randomGenerator { randomDevice() };
|
static SFZ_INLINE std::mt19937 randomGenerator { randomDevice() };
|
||||||
} // namespace Random
|
} // namespace Random
|
||||||
|
|
||||||
inline float midiNoteFrequency(const int noteNumber)
|
inline float midiNoteFrequency(const int noteNumber)
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
|
#pragma once
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include "SfzHelpers.h"
|
||||||
#include "compat/utils.h"
|
#include "compat/utils.h"
|
||||||
|
|
||||||
namespace sfz
|
namespace sfz
|
||||||
{
|
{
|
||||||
SFZ_INLINE std::array<std::chrono::steady_clock::time_point, 128> noteOnTimes { };
|
struct MidiState
|
||||||
SFZ_INLINE std::array<uint8_t, 128> lastNoteVelocities { };
|
{
|
||||||
inline void noteOn(int noteNumber, uint8_t velocity)
|
inline void noteOn(int noteNumber, uint8_t velocity)
|
||||||
{
|
{
|
||||||
if (noteNumber >= 0 && noteNumber < 128) {
|
if (noteNumber >= 0 && noteNumber < 128) {
|
||||||
|
|
@ -13,7 +16,7 @@ namespace sfz
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float getNoteDuration(int noteNumber)
|
inline float getNoteDuration(int noteNumber) const
|
||||||
{
|
{
|
||||||
if (noteNumber >= 0 && noteNumber < 128) {
|
if (noteNumber >= 0 && noteNumber < 128) {
|
||||||
const auto noteOffTime = std::chrono::steady_clock::now();
|
const auto noteOffTime = std::chrono::steady_clock::now();
|
||||||
|
|
@ -24,11 +27,15 @@ namespace sfz
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t getNoteVelocity(int noteNumber)
|
inline uint8_t getNoteVelocity(int noteNumber) const
|
||||||
{
|
{
|
||||||
if (noteNumber >= 0 && noteNumber < 128)
|
if (noteNumber >= 0 && noteNumber < 128)
|
||||||
return lastNoteVelocities[noteNumber];
|
return lastNoteVelocities[noteNumber];
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
std::array<std::chrono::steady_clock::time_point, 128> noteOnTimes { };
|
||||||
|
std::array<uint8_t, 128> lastNoteVelocities { };
|
||||||
|
CCValueArray cc;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,9 @@ public:
|
||||||
while (in < sentinel) {
|
while (in < sentinel) {
|
||||||
setGain(*g);
|
setGain(*g);
|
||||||
oneLowpass(in, out);
|
oneLowpass(in, out);
|
||||||
|
in++;
|
||||||
|
out++;
|
||||||
|
g++;
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +105,10 @@ public:
|
||||||
auto sentinel = in + size;
|
auto sentinel = in + size;
|
||||||
while (in < sentinel) {
|
while (in < sentinel) {
|
||||||
setGain(*g);
|
setGain(*g);
|
||||||
oneLowpass(in, out);
|
oneHighpass(in, out);
|
||||||
|
in++;
|
||||||
|
out++;
|
||||||
|
g++;
|
||||||
}
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@
|
||||||
#include "StringViewHelpers.h"
|
#include "StringViewHelpers.h"
|
||||||
#include "absl/strings/str_join.h"
|
#include "absl/strings/str_join.h"
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "Regexes.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <regex>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
using svregex_iterator = std::regex_iterator<absl::string_view::const_iterator>;
|
using svregex_iterator = std::regex_iterator<absl::string_view::const_iterator>;
|
||||||
|
|
@ -39,10 +41,10 @@ void removeCommentOnLine(absl::string_view& line)
|
||||||
line.remove_suffix(line.size() - position);
|
line.remove_suffix(line.size() - position);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sfz::Parser::loadSfzFile(const std::filesystem::path& file)
|
bool sfz::Parser::loadSfzFile(const fs::path& file)
|
||||||
{
|
{
|
||||||
const auto sfzFile = file.is_absolute() ? file : rootDirectory / file;
|
const auto sfzFile = file.is_absolute() ? file : rootDirectory / file;
|
||||||
if (!std::filesystem::exists(sfzFile))
|
if (!fs::exists(sfzFile))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
rootDirectory = file.parent_path();
|
rootDirectory = file.parent_path();
|
||||||
|
|
@ -79,7 +81,7 @@ bool sfz::Parser::loadSfzFile(const std::filesystem::path& file)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Parser::readSfzFile(const std::filesystem::path& fileName, std::vector<std::string>& lines) noexcept
|
void sfz::Parser::readSfzFile(const fs::path& fileName, std::vector<std::string>& lines) noexcept
|
||||||
{
|
{
|
||||||
std::ifstream fileStream(fileName.c_str());
|
std::ifstream fileStream(fileName.c_str());
|
||||||
if (!fileStream)
|
if (!fileStream)
|
||||||
|
|
@ -105,7 +107,7 @@ void sfz::Parser::readSfzFile(const std::filesystem::path& fileName, std::vector
|
||||||
std::replace(includePath.begin(), includePath.end(), '\\', '/');
|
std::replace(includePath.begin(), includePath.end(), '\\', '/');
|
||||||
const auto newFile = rootDirectory / includePath;
|
const auto newFile = rootDirectory / includePath;
|
||||||
auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile);
|
auto alreadyIncluded = std::find(includedFiles.begin(), includedFiles.end(), newFile);
|
||||||
if (std::filesystem::exists(newFile)) {
|
if (fs::exists(newFile)) {
|
||||||
if (alreadyIncluded == includedFiles.end()) {
|
if (alreadyIncluded == includedFiles.end()) {
|
||||||
includedFiles.push_back(newFile);
|
includedFiles.push_back(newFile);
|
||||||
readSfzFile(newFile, lines);
|
readSfzFile(newFile, lines);
|
||||||
|
|
|
||||||
|
|
@ -25,36 +25,27 @@
|
||||||
#include "Opcode.h"
|
#include "Opcode.h"
|
||||||
#include "compat/filesystem.h"
|
#include "compat/filesystem.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <regex>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include "absl/strings/string_view.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
namespace Regexes {
|
|
||||||
SFZ_INLINE static std::regex includes { R"V(#include\s*"(.*?)".*$)V", std::regex::optimize };
|
|
||||||
SFZ_INLINE static std::regex defines { R"(#define\s*(\$[a-zA-Z0-9]+)\s+([a-zA-Z0-9]+)(?=\s|$))", std::regex::optimize };
|
|
||||||
SFZ_INLINE static std::regex headers { R"(<(.*?)>(.*?)(?=<|$))", std::regex::optimize };
|
|
||||||
SFZ_INLINE static std::regex members { R"(([a-zA-Z0-9_]+)=([a-zA-Z0-9-_#.&\/\s\\\(\),\*]+)(?![a-zA-Z0-9_]*=))", std::regex::optimize };
|
|
||||||
SFZ_INLINE static std::regex opcodeParameters { R"(([a-zA-Z0-9_]+?)([0-9]+)$)", std::regex::optimize };
|
|
||||||
}
|
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
virtual bool loadSfzFile(const std::filesystem::path& file);
|
virtual bool loadSfzFile(const fs::path& file);
|
||||||
const std::map<std::string, std::string>& getDefines() const noexcept { return defines; }
|
const std::map<std::string, std::string>& getDefines() const noexcept { return defines; }
|
||||||
const std::vector<std::filesystem::path>& getIncludedFiles() const noexcept { return includedFiles; }
|
const std::vector<fs::path>& getIncludedFiles() const noexcept { return includedFiles; }
|
||||||
void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; }
|
void disableRecursiveIncludeGuard() { recursiveIncludeGuard = false; }
|
||||||
void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; }
|
void enableRecursiveIncludeGuard() { recursiveIncludeGuard = true; }
|
||||||
protected:
|
protected:
|
||||||
virtual void callback(absl::string_view header, const std::vector<Opcode>& members) = 0;
|
virtual void callback(absl::string_view header, const std::vector<Opcode>& members) = 0;
|
||||||
std::filesystem::path rootDirectory { std::filesystem::current_path() };
|
fs::path rootDirectory { fs::current_path() };
|
||||||
private:
|
private:
|
||||||
bool recursiveIncludeGuard { false };
|
bool recursiveIncludeGuard { false };
|
||||||
std::map<std::string, std::string> defines;
|
std::map<std::string, std::string> defines;
|
||||||
std::vector<std::filesystem::path> includedFiles;
|
std::vector<fs::path> includedFiles;
|
||||||
std::string aggregatedContent {};
|
std::string aggregatedContent {};
|
||||||
void readSfzFile(const std::filesystem::path& fileName, std::vector<std::string>& lines) noexcept;
|
void readSfzFile(const fs::path& fileName, std::vector<std::string>& lines) noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
13
sfizz/Regexes.h
Normal file
13
sfizz/Regexes.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
namespace sfz
|
||||||
|
{
|
||||||
|
namespace Regexes
|
||||||
|
{
|
||||||
|
static std::regex includes { R"V(#include\s*"(.*?)".*$)V", std::regex::optimize };
|
||||||
|
static std::regex defines { R"(#define\s*(\$[a-zA-Z0-9]+)\s+([a-zA-Z0-9]+)(?=\s|$))", std::regex::optimize };
|
||||||
|
static std::regex headers { R"(<(.*?)>(.*?)(?=<|$))", std::regex::optimize };
|
||||||
|
static std::regex members { R"(([a-zA-Z0-9_]+)=([a-zA-Z0-9-_#.&\/\s\\\(\),\*]+)(?![a-zA-Z0-9_]*=))", std::regex::optimize };
|
||||||
|
static std::regex opcodeParameters { R"(([a-zA-Z0-9_]+?)([0-9]+)$)", std::regex::optimize };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -635,7 +635,7 @@ float sfz::Region::getBaseVolumedB(int noteNumber) noexcept
|
||||||
{
|
{
|
||||||
auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator);
|
auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator);
|
||||||
if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key)
|
if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key)
|
||||||
baseVolumedB -= rtDecay * getNoteDuration(noteNumber);
|
baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber);
|
||||||
return baseVolumedB;
|
return baseVolumedB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "EGDescription.h"
|
#include "EGDescription.h"
|
||||||
#include "Opcode.h"
|
#include "Opcode.h"
|
||||||
#include "AudioBuffer.h"
|
#include "AudioBuffer.h"
|
||||||
|
#include "MidiState.h"
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
@ -36,7 +37,8 @@
|
||||||
|
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
struct Region {
|
struct Region {
|
||||||
Region()
|
Region(const MidiState& midiState)
|
||||||
|
: midiState(midiState)
|
||||||
{
|
{
|
||||||
ccSwitched.set();
|
ccSwitched.set();
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +156,7 @@ struct Region {
|
||||||
double sampleRate { config::defaultSampleRate };
|
double sampleRate { config::defaultSampleRate };
|
||||||
std::shared_ptr<AudioBuffer<float>> preloadedData { nullptr };
|
std::shared_ptr<AudioBuffer<float>> preloadedData { nullptr };
|
||||||
private:
|
private:
|
||||||
|
const MidiState& midiState;
|
||||||
bool keySwitched { true };
|
bool keySwitched { true };
|
||||||
bool previousKeySwitched { true };
|
bool previousKeySwitched { true };
|
||||||
bool sequenceSwitched { true };
|
bool sequenceSwitched { true };
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ inline constexpr float centsFactor(T cents, T centsPerOctave = 1200)
|
||||||
template<class T>
|
template<class T>
|
||||||
inline constexpr float normalizeCC(T ccValue)
|
inline constexpr float normalizeCC(T ccValue)
|
||||||
{
|
{
|
||||||
static_assert(std::is_integral<T>::value);
|
static_assert(std::is_integral<T>::value, "Requires an integral T");
|
||||||
return static_cast<float>(std::min(std::max(ccValue, static_cast<T>(0)), static_cast<T>(127))) / 127.0f;
|
return static_cast<float>(std::min(std::max(ccValue, static_cast<T>(0)), static_cast<T>(127))) / 127.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ using namespace std::literals;
|
||||||
sfz::Synth::Synth()
|
sfz::Synth::Synth()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < config::numVoices; ++i)
|
for (int i = 0; i < config::numVoices; ++i)
|
||||||
voices.push_back(std::make_unique<Voice>(ccState));
|
voices.push_back(std::make_unique<Voice>(midiState));
|
||||||
voiceViewArray.reserve(config::numVoices);
|
voiceViewArray.reserve(config::numVoices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ void sfz::Synth::callback(absl::string_view header, const std::vector<Opcode>& m
|
||||||
|
|
||||||
void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||||
{
|
{
|
||||||
auto lastRegion = std::make_unique<Region>();
|
auto lastRegion = std::make_unique<Region>(midiState);
|
||||||
|
|
||||||
auto parseOpcodes = [&](const auto& opcodes) {
|
auto parseOpcodes = [&](const auto& opcodes) {
|
||||||
for (auto& opcode : opcodes) {
|
for (auto& opcode : opcodes) {
|
||||||
|
|
@ -114,7 +114,7 @@ void sfz::Synth::clear()
|
||||||
numCurves = 0;
|
numCurves = 0;
|
||||||
fileTicket = -1;
|
fileTicket = -1;
|
||||||
defaultSwitch = absl::nullopt;
|
defaultSwitch = absl::nullopt;
|
||||||
for (auto& state : ccState)
|
for (auto& state : midiState.cc)
|
||||||
state = 0;
|
state = 0;
|
||||||
ccNames.clear();
|
ccNames.clear();
|
||||||
globalOpcodes.clear();
|
globalOpcodes.clear();
|
||||||
|
|
@ -142,7 +142,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
||||||
case hash("Set_cc"): [[fallthrough]];
|
case hash("Set_cc"): [[fallthrough]];
|
||||||
case hash("set_cc"):
|
case hash("set_cc"):
|
||||||
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
|
if (member.parameter && Default::ccRange.containsWithEnd(*member.parameter))
|
||||||
setValueFromOpcode(member, ccState[*member.parameter], Default::ccRange);
|
setValueFromOpcode(member, midiState.cc[*member.parameter], Default::ccRange);
|
||||||
break;
|
break;
|
||||||
case hash("Label_cc"): [[fallthrough]];
|
case hash("Label_cc"): [[fallthrough]];
|
||||||
case hash("label_cc"):
|
case hash("label_cc"):
|
||||||
|
|
@ -152,8 +152,8 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
||||||
case hash("Default_path"): [[fallthrough]];
|
case hash("Default_path"): [[fallthrough]];
|
||||||
case hash("default_path"): {
|
case hash("default_path"): {
|
||||||
auto stringPath = std::string(member.value.begin(), member.value.end());
|
auto stringPath = std::string(member.value.begin(), member.value.end());
|
||||||
auto newPath = std::filesystem::path(stringPath);
|
auto newPath = fs::path(stringPath);
|
||||||
if (std::filesystem::exists(newPath))
|
if (fs::exists(newPath))
|
||||||
rootDirectory = newPath;
|
rootDirectory = newPath;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ void addEndpointsToVelocityCurve(sfz::Region& region)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
|
bool sfz::Synth::loadSfzFile(const fs::path& filename)
|
||||||
{
|
{
|
||||||
canEnterCallback = false;
|
canEnterCallback = false;
|
||||||
while (inCallback) {
|
while (inCallback) {
|
||||||
|
|
@ -230,7 +230,7 @@ bool sfz::Synth::loadSfzFile(const std::filesystem::path& filename)
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
for (int ccIndex = 1; ccIndex < 128; ccIndex++)
|
for (int ccIndex = 1; ccIndex < 128; ccIndex++)
|
||||||
region->registerCC(region->channelRange.getStart(), ccIndex, ccState[ccIndex]);
|
region->registerCC(region->channelRange.getStart(), ccIndex, midiState.cc[ccIndex]);
|
||||||
|
|
||||||
if (defaultSwitch) {
|
if (defaultSwitch) {
|
||||||
region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0);
|
region->registerNoteOn(region->channelRange.getStart(), *defaultSwitch, 127, 1.0);
|
||||||
|
|
@ -333,7 +333,7 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
|
||||||
ASSERT(noteNumber < 128);
|
ASSERT(noteNumber < 128);
|
||||||
ASSERT(noteNumber >= 0);
|
ASSERT(noteNumber >= 0);
|
||||||
|
|
||||||
sfz::noteOn(noteNumber, velocity);
|
midiState.noteOn(noteNumber, velocity);
|
||||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||||
|
|
||||||
for (auto& region : noteActivationLists[noteNumber]) {
|
for (auto& region : noteActivationLists[noteNumber]) {
|
||||||
|
|
@ -364,7 +364,7 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
|
||||||
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
|
// FIXME: Some keyboards (e.g. Casio PX5S) can send a real note-off velocity. In this case, do we have a
|
||||||
// way in sfz to specify that a release trigger should NOT use the note-on velocity?
|
// way in sfz to specify that a release trigger should NOT use the note-on velocity?
|
||||||
// auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity);
|
// auto replacedVelocity = (velocity == 0 ? sfz::getNoteVelocity(noteNumber) : velocity);
|
||||||
auto replacedVelocity = sfz::getNoteVelocity(noteNumber);
|
auto replacedVelocity = midiState.getNoteVelocity(noteNumber);
|
||||||
auto randValue = randNoteDistribution(Random::randomGenerator);
|
auto randValue = randNoteDistribution(Random::randomGenerator);
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity);
|
voice->registerNoteOff(delay, channel, noteNumber, replacedVelocity);
|
||||||
|
|
@ -392,7 +392,7 @@ void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexc
|
||||||
for (auto& voice : voices)
|
for (auto& voice : voices)
|
||||||
voice->registerCC(delay, channel, ccNumber, ccValue);
|
voice->registerCC(delay, channel, ccNumber, ccValue);
|
||||||
|
|
||||||
ccState[ccNumber] = ccValue;
|
midiState.cc[ccNumber] = ccValue;
|
||||||
|
|
||||||
for (auto& region : ccActivationLists[ccNumber]) {
|
for (auto& region : ccActivationLists[ccNumber]) {
|
||||||
if (region->registerCC(channel, ccNumber, ccValue)) {
|
if (region->registerCC(channel, ccNumber, ccValue)) {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
#include "Region.h"
|
#include "Region.h"
|
||||||
#include "LeakDetector.h"
|
#include "LeakDetector.h"
|
||||||
|
#include "MidiState.h"
|
||||||
#include "AudioSpan.h"
|
#include "AudioSpan.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include <absl/types/optional.h>
|
#include <absl/types/optional.h>
|
||||||
|
|
@ -39,8 +40,7 @@ namespace sfz {
|
||||||
class Synth : public Parser {
|
class Synth : public Parser {
|
||||||
public:
|
public:
|
||||||
Synth();
|
Synth();
|
||||||
|
bool loadSfzFile(const fs::path& file) final;
|
||||||
bool loadSfzFile(const std::filesystem::path& file) final;
|
|
||||||
int getNumRegions() const noexcept;
|
int getNumRegions() const noexcept;
|
||||||
int getNumGroups() const noexcept;
|
int getNumGroups() const noexcept;
|
||||||
int getNumMasters() const noexcept;
|
int getNumMasters() const noexcept;
|
||||||
|
|
@ -80,7 +80,7 @@ private:
|
||||||
std::vector<Opcode> groupOpcodes;
|
std::vector<Opcode> groupOpcodes;
|
||||||
|
|
||||||
FilePool filePool;
|
FilePool filePool;
|
||||||
CCValueArray ccState;
|
MidiState midiState;
|
||||||
Voice* findFreeVoice() noexcept;
|
Voice* findFreeVoice() noexcept;
|
||||||
std::vector<CCNamePair> ccNames;
|
std::vector<CCNamePair> ccNames;
|
||||||
absl::optional<uint8_t> defaultSwitch;
|
absl::optional<uint8_t> defaultSwitch;
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
sfz::Voice::Voice(const CCValueArray& ccState)
|
sfz::Voice::Voice(const MidiState& midiState)
|
||||||
: ccState(ccState)
|
: midiState(midiState)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,39 +59,39 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
|
||||||
|
|
||||||
auto volumedB { baseVolumedB };
|
auto volumedB { baseVolumedB };
|
||||||
if (region->volumeCC)
|
if (region->volumeCC)
|
||||||
volumedB += normalizeCC(ccState[region->volumeCC->first]) * region->volumeCC->second;
|
volumedB += normalizeCC(midiState.cc[region->volumeCC->first]) * region->volumeCC->second;
|
||||||
volumeEnvelope.reset(db2mag(volumedB));
|
volumeEnvelope.reset(db2mag(volumedB));
|
||||||
// DBG("Base volume: " << baseVolumedB << " dB - with modifier: " << volumedB << " dB");
|
// DBG("Base volume: " << baseVolumedB << " dB - with modifier: " << volumedB << " dB");
|
||||||
|
|
||||||
baseGain = region->getBaseGain();
|
baseGain = region->getBaseGain();
|
||||||
baseGain *= region->getCrossfadeGain(ccState);
|
baseGain *= region->getCrossfadeGain(midiState.cc);
|
||||||
if (triggerType != TriggerType::CC)
|
if (triggerType != TriggerType::CC)
|
||||||
baseGain *= region->getNoteGain(number, value);
|
baseGain *= region->getNoteGain(number, value);
|
||||||
|
|
||||||
float gain { baseGain };
|
float gain { baseGain };
|
||||||
if (region->amplitudeCC)
|
if (region->amplitudeCC)
|
||||||
gain *= normalizeCC(ccState[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second);
|
gain *= normalizeCC(midiState.cc[region->amplitudeCC->first]) * normalizePercents(region->amplitudeCC->second);
|
||||||
amplitudeEnvelope.reset(gain);
|
amplitudeEnvelope.reset(gain);
|
||||||
// DBG("Base gain: " << baseGain << " - with modifier: " << gain);
|
// DBG("Base gain: " << baseGain << " - with modifier: " << gain);
|
||||||
|
|
||||||
basePan = normalizeNegativePercents(region->pan);
|
basePan = normalizeNegativePercents(region->pan);
|
||||||
auto pan { basePan };
|
auto pan { basePan };
|
||||||
if (region->panCC)
|
if (region->panCC)
|
||||||
pan += normalizeCC(ccState[region->panCC->first]) * normalizeNegativePercents(region->panCC->second);
|
pan += normalizeCC(midiState.cc[region->panCC->first]) * normalizeNegativePercents(region->panCC->second);
|
||||||
panEnvelope.reset(pan);
|
panEnvelope.reset(pan);
|
||||||
// DBG("Base pan: " << basePan << " - with modifier: " << pan);
|
// DBG("Base pan: " << basePan << " - with modifier: " << pan);
|
||||||
|
|
||||||
basePosition = normalizeNegativePercents(region->position);
|
basePosition = normalizeNegativePercents(region->position);
|
||||||
auto position { basePosition };
|
auto position { basePosition };
|
||||||
if (region->positionCC)
|
if (region->positionCC)
|
||||||
position += normalizeCC(ccState[region->positionCC->first]) * normalizeNegativePercents(region->positionCC->second);
|
position += normalizeCC(midiState.cc[region->positionCC->first]) * normalizeNegativePercents(region->positionCC->second);
|
||||||
positionEnvelope.reset(position);
|
positionEnvelope.reset(position);
|
||||||
// DBG("Base position: " << basePosition << " - with modifier: " << position);
|
// DBG("Base position: " << basePosition << " - with modifier: " << position);
|
||||||
|
|
||||||
baseWidth = normalizeNegativePercents(region->width);
|
baseWidth = normalizeNegativePercents(region->width);
|
||||||
auto width { baseWidth };
|
auto width { baseWidth };
|
||||||
if (region->widthCC)
|
if (region->widthCC)
|
||||||
width += normalizeCC(ccState[region->widthCC->first]) * normalizeNegativePercents(region->widthCC->second);
|
width += normalizeCC(midiState.cc[region->widthCC->first]) * normalizeNegativePercents(region->widthCC->second);
|
||||||
widthEnvelope.reset(width);
|
widthEnvelope.reset(width);
|
||||||
// DBG("Base width: " << baseWidth << " - with modifier: " << width);
|
// DBG("Base width: " << baseWidth << " - with modifier: " << width);
|
||||||
|
|
||||||
|
|
@ -109,13 +109,13 @@ void sfz::Voice::prepareEGEnvelope(int delay, uint8_t velocity) noexcept
|
||||||
};
|
};
|
||||||
|
|
||||||
egEnvelope.reset(
|
egEnvelope.reset(
|
||||||
secondsToSamples(region->amplitudeEG.getAttack(ccState, velocity)),
|
secondsToSamples(region->amplitudeEG.getAttack(midiState.cc, velocity)),
|
||||||
secondsToSamples(region->amplitudeEG.getRelease(ccState, velocity)),
|
secondsToSamples(region->amplitudeEG.getRelease(midiState.cc, velocity)),
|
||||||
normalizePercents(region->amplitudeEG.getSustain(ccState, velocity)),
|
normalizePercents(region->amplitudeEG.getSustain(midiState.cc, velocity)),
|
||||||
delay + secondsToSamples(region->amplitudeEG.getDelay(ccState, velocity)),
|
delay + secondsToSamples(region->amplitudeEG.getDelay(midiState.cc, velocity)),
|
||||||
secondsToSamples(region->amplitudeEG.getDecay(ccState, velocity)),
|
secondsToSamples(region->amplitudeEG.getDecay(midiState.cc, velocity)),
|
||||||
secondsToSamples(region->amplitudeEG.getHold(ccState, velocity)),
|
secondsToSamples(region->amplitudeEG.getHold(midiState.cc, velocity)),
|
||||||
normalizePercents(region->amplitudeEG.getStart(ccState, velocity)));
|
normalizePercents(region->amplitudeEG.getStart(midiState.cc, velocity)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void sfz::Voice::setFileData(std::shared_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept
|
void sfz::Voice::setFileData(std::shared_ptr<AudioBuffer<float>> file, unsigned ticket) noexcept
|
||||||
|
|
@ -154,7 +154,7 @@ void sfz::Voice::registerNoteOff(int delay, int channel, int noteNumber, uint8_t
|
||||||
if (region->loopMode == SfzLoopMode::one_shot)
|
if (region->loopMode == SfzLoopMode::one_shot)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!region->checkSustain || ccState[config::sustainCC] < config::halfCCThreshold)
|
if (!region->checkSustain || midiState.cc[config::sustainCC] < config::halfCCThreshold)
|
||||||
release(delay);
|
release(delay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include "HistoricalBuffer.h"
|
#include "HistoricalBuffer.h"
|
||||||
#include "Region.h"
|
#include "Region.h"
|
||||||
#include "AudioBuffer.h"
|
#include "AudioBuffer.h"
|
||||||
|
#include "MidiState.h"
|
||||||
#include "AudioSpan.h"
|
#include "AudioSpan.h"
|
||||||
#include "LeakDetector.h"
|
#include "LeakDetector.h"
|
||||||
#include <absl/types/span.h>
|
#include <absl/types/span.h>
|
||||||
|
|
@ -38,7 +39,7 @@ namespace sfz {
|
||||||
class Voice {
|
class Voice {
|
||||||
public:
|
public:
|
||||||
Voice() = delete;
|
Voice() = delete;
|
||||||
Voice(const CCValueArray& ccState);
|
Voice(const MidiState& midiState);
|
||||||
enum class TriggerType {
|
enum class TriggerType {
|
||||||
NoteOn,
|
NoteOn,
|
||||||
NoteOff,
|
NoteOff,
|
||||||
|
|
@ -124,7 +125,7 @@ private:
|
||||||
int samplesPerBlock { config::defaultSamplesPerBlock };
|
int samplesPerBlock { config::defaultSamplesPerBlock };
|
||||||
float sampleRate { config::defaultSampleRate };
|
float sampleRate { config::defaultSampleRate };
|
||||||
|
|
||||||
const CCValueArray& ccState;
|
const MidiState& midiState;
|
||||||
ADSREnvelope<float> egEnvelope;
|
ADSREnvelope<float> egEnvelope;
|
||||||
LinearEnvelope<float> volumeEnvelope; // dB events but the envelope output is linear gain
|
LinearEnvelope<float> volumeEnvelope; // dB events but the envelope output is linear gain
|
||||||
LinearEnvelope<float> amplitudeEnvelope; // linear events
|
LinearEnvelope<float> amplitudeEnvelope; // linear events
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#ifdef USE_STD_FILESYSTEM
|
||||||
#ifdef __cplusplus
|
#warning FS
|
||||||
#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
#include <filesystem>
|
||||||
#include <filesystem>
|
namespace fs = std::filesystem;
|
||||||
#elif (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
|
|
||||||
#warning std::experimental::filesystem in use
|
|
||||||
#include <experimental/filesystem>
|
|
||||||
namespace std {
|
|
||||||
namespace filesystem = std::experimental::filesystem;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
#else
|
||||||
#error no filesystem support
|
#warning EXPFS
|
||||||
|
#include <experimental/filesystem>
|
||||||
|
namespace fs = std::experimental::filesystem;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #if __cplusplus < 201703L
|
||||||
|
// #warning EXPFSNS
|
||||||
|
// #else
|
||||||
|
// #warning FSNS
|
||||||
|
// #endif
|
||||||
|
|
@ -23,13 +23,13 @@
|
||||||
|
|
||||||
#include "Synth.h"
|
#include "Synth.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
#include <filesystem>
|
#include "../sfizz/compat/filesystem.h"
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
|
|
||||||
TEST_CASE("[Files] Single region (regions_one.sfz)")
|
TEST_CASE("[Files] Single region (regions_one.sfz)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_one.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_one.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 1);
|
REQUIRE(synth.getNumRegions() == 1);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
||||||
}
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ TEST_CASE("[Files] Single region (regions_one.sfz)")
|
||||||
TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
|
TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_many.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_many.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 3);
|
REQUIRE(synth.getNumRegions() == 3);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "dummy.1.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "dummy.1.wav");
|
||||||
|
|
@ -48,7 +48,7 @@ TEST_CASE("[Files] Multiple regions (regions_many.sfz)")
|
||||||
TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
|
TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/regions_opcodes.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 1);
|
REQUIRE(synth.getNumRegions() == 1);
|
||||||
REQUIRE(synth.getRegionView(0)->channelRange == Range<uint8_t>(2, 14));
|
REQUIRE(synth.getRegionView(0)->channelRange == Range<uint8_t>(2, 14));
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ TEST_CASE("[Files] Basic opcodes (regions_opcodes.sfz)")
|
||||||
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
|
TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Regions/underscore_opcodes.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 1);
|
REQUIRE(synth.getNumRegions() == 1);
|
||||||
REQUIRE(synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain);
|
REQUIRE(synth.getRegionView(0)->loopMode == SfzLoopMode::loop_sustain);
|
||||||
}
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ TEST_CASE("[Files] Underscore opcodes (underscore_opcodes.sfz)")
|
||||||
TEST_CASE("[Files] Local include")
|
TEST_CASE("[Files] Local include")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_local.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_local.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 1);
|
REQUIRE(synth.getNumRegions() == 1);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
||||||
}
|
}
|
||||||
|
|
@ -72,7 +72,7 @@ TEST_CASE("[Files] Local include")
|
||||||
TEST_CASE("[Files] Multiple includes")
|
TEST_CASE("[Files] Multiple includes")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 2);
|
REQUIRE(synth.getNumRegions() == 2);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav");
|
||||||
|
|
@ -81,7 +81,7 @@ TEST_CASE("[Files] Multiple includes")
|
||||||
TEST_CASE("[Files] Multiple includes with comments")
|
TEST_CASE("[Files] Multiple includes with comments")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/multiple_includes_with_comments.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 2);
|
REQUIRE(synth.getNumRegions() == 2);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "dummy2.wav");
|
||||||
|
|
@ -90,7 +90,7 @@ TEST_CASE("[Files] Multiple includes with comments")
|
||||||
TEST_CASE("[Files] Subdir include")
|
TEST_CASE("[Files] Subdir include")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_subdir.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 1);
|
REQUIRE(synth.getNumRegions() == 1);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav");
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,7 @@ TEST_CASE("[Files] Subdir include")
|
||||||
TEST_CASE("[Files] Subdir include Win")
|
TEST_CASE("[Files] Subdir include Win")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_subdir_win.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 1);
|
REQUIRE(synth.getNumRegions() == 1);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy_subdir.wav");
|
||||||
}
|
}
|
||||||
|
|
@ -107,7 +107,7 @@ TEST_CASE("[Files] Recursive include (with include guard)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.enableRecursiveIncludeGuard();
|
synth.enableRecursiveIncludeGuard();
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_recursive.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_recursive.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 2);
|
REQUIRE(synth.getNumRegions() == 2);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy_recursive2.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy_recursive2.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "dummy_recursive1.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "dummy_recursive1.wav");
|
||||||
|
|
@ -117,7 +117,7 @@ TEST_CASE("[Files] Include loops (with include guard)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.enableRecursiveIncludeGuard();
|
synth.enableRecursiveIncludeGuard();
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/Includes/root_loop.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/Includes/root_loop.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 2);
|
REQUIRE(synth.getNumRegions() == 2);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "dummy_loop2.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "dummy_loop2.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "dummy_loop1.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "dummy_loop1.wav");
|
||||||
|
|
@ -126,7 +126,7 @@ TEST_CASE("[Files] Include loops (with include guard)")
|
||||||
TEST_CASE("[Files] Define test")
|
TEST_CASE("[Files] Define test")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/defines.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/defines.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 3);
|
REQUIRE(synth.getNumRegions() == 3);
|
||||||
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36));
|
REQUIRE(synth.getRegionView(0)->keyRange == Range<uint8_t>(36, 36));
|
||||||
REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38));
|
REQUIRE(synth.getRegionView(1)->keyRange == Range<uint8_t>(38, 38));
|
||||||
|
|
@ -136,7 +136,7 @@ TEST_CASE("[Files] Define test")
|
||||||
TEST_CASE("[Files] Group from AVL")
|
TEST_CASE("[Files] Group from AVL")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/groups_avl.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/groups_avl.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 5);
|
REQUIRE(synth.getNumRegions() == 5);
|
||||||
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
||||||
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
|
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
|
||||||
|
|
@ -152,7 +152,7 @@ TEST_CASE("[Files] Group from AVL")
|
||||||
TEST_CASE("[Files] Full hierarchy")
|
TEST_CASE("[Files] Full hierarchy")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 8);
|
REQUIRE(synth.getNumRegions() == 8);
|
||||||
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
||||||
REQUIRE(synth.getRegionView(i)->width == 40.0f);
|
REQUIRE(synth.getRegionView(i)->width == 40.0f);
|
||||||
|
|
@ -193,9 +193,9 @@ TEST_CASE("[Files] Full hierarchy")
|
||||||
TEST_CASE("[Files] Reloading files")
|
TEST_CASE("[Files] Reloading files")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 8);
|
REQUIRE(synth.getNumRegions() == 8);
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 8);
|
REQUIRE(synth.getNumRegions() == 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -203,7 +203,7 @@ TEST_CASE("[Files] Full hierarchy with antislashes")
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 8);
|
REQUIRE(synth.getNumRegions() == 8);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav");
|
||||||
|
|
@ -217,7 +217,7 @@ TEST_CASE("[Files] Full hierarchy with antislashes")
|
||||||
|
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/basic_hierarchy_antislash.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 8);
|
REQUIRE(synth.getNumRegions() == 8);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "Regions/dummy.wav");
|
||||||
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav");
|
REQUIRE(synth.getRegionView(1)->sample == "Regions/dummy.1.wav");
|
||||||
|
|
@ -233,7 +233,7 @@ TEST_CASE("[Files] Full hierarchy with antislashes")
|
||||||
TEST_CASE("[Files] Pizz basic")
|
TEST_CASE("[Files] Pizz basic")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/MeatBassPizz/Programs/pizz.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 4);
|
REQUIRE(synth.getNumRegions() == 4);
|
||||||
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
||||||
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
|
REQUIRE(synth.getRegionView(i)->keyRange == Range<uint8_t>(12, 22));
|
||||||
|
|
@ -254,7 +254,7 @@ TEST_CASE("[Files] Pizz basic")
|
||||||
TEST_CASE("[Files] Channels (channels.sfz)")
|
TEST_CASE("[Files] Channels (channels.sfz)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/channels.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/channels.sfz");
|
||||||
REQUIRE(synth.getNumRegions() == 2);
|
REQUIRE(synth.getNumRegions() == 2);
|
||||||
REQUIRE(synth.getRegionView(0)->sample == "mono_sample.wav");
|
REQUIRE(synth.getRegionView(0)->sample == "mono_sample.wav");
|
||||||
REQUIRE(!synth.getRegionView(0)->isStereo());
|
REQUIRE(!synth.getRegionView(0)->isStereo());
|
||||||
|
|
@ -265,7 +265,7 @@ TEST_CASE("[Files] Channels (channels.sfz)")
|
||||||
TEST_CASE("[Files] sw_default")
|
TEST_CASE("[Files] sw_default")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/sw_default.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
|
||||||
REQUIRE( synth.getNumRegions() == 4 );
|
REQUIRE( synth.getNumRegions() == 4 );
|
||||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||||
|
|
@ -276,7 +276,7 @@ TEST_CASE("[Files] sw_default")
|
||||||
TEST_CASE("[Files] sw_default and playing with switches")
|
TEST_CASE("[Files] sw_default and playing with switches")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/sw_default.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz");
|
||||||
REQUIRE( synth.getNumRegions() == 4 );
|
REQUIRE( synth.getNumRegions() == 4 );
|
||||||
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
REQUIRE( !synth.getRegionView(0)->isSwitchedOn() );
|
||||||
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
REQUIRE( synth.getRegionView(1)->isSwitchedOn() );
|
||||||
|
|
@ -306,7 +306,7 @@ TEST_CASE("[Files] sw_default and playing with switches")
|
||||||
TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
TEST_CASE("[Files] wrong (overlapping) replacement for defines")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzFile(std::filesystem::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz");
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/SpecificBugs/wrong-replacements.sfz");
|
||||||
REQUIRE( synth.getNumRegions() == 3 );
|
REQUIRE( synth.getNumRegions() == 3 );
|
||||||
REQUIRE( synth.getRegionView(0)->keyRange.getStart() == 52 );
|
REQUIRE( synth.getRegionView(0)->keyRange.getStart() == 52 );
|
||||||
REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 52 );
|
REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 52 );
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ inline bool approxEqual(const std::vector<Type>& lhs, const std::vector<Type>& r
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Type>
|
template <class Type>
|
||||||
void testLowpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain)
|
void testLowpass(const fs::path& inputNumpyFile, const fs::path& outputNumpyFile, Type gain)
|
||||||
{
|
{
|
||||||
const auto input = cnpy::npy_load(inputNumpyFile.string());
|
const auto input = cnpy::npy_load(inputNumpyFile.string());
|
||||||
REQUIRE(input.word_size == 8);
|
REQUIRE(input.word_size == 8);
|
||||||
|
|
@ -81,7 +81,7 @@ void testLowpass(const std::filesystem::path& inputNumpyFile, const std::filesys
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Type>
|
template <class Type>
|
||||||
void testHighpass(const std::filesystem::path& inputNumpyFile, const std::filesystem::path& outputNumpyFile, Type gain)
|
void testHighpass(const fs::path& inputNumpyFile, const fs::path& outputNumpyFile, Type gain)
|
||||||
{
|
{
|
||||||
const auto input = cnpy::npy_load(inputNumpyFile.string());
|
const auto input = cnpy::npy_load(inputNumpyFile.string());
|
||||||
REQUIRE(input.word_size == 8);
|
REQUIRE(input.word_size == 8);
|
||||||
|
|
@ -118,95 +118,95 @@ void testHighpass(const std::filesystem::path& inputNumpyFile, const std::filesy
|
||||||
TEST_CASE("[OnePoleFilter] Lowpass Float")
|
TEST_CASE("[OnePoleFilter] Lowpass Float")
|
||||||
{
|
{
|
||||||
testLowpass<float>(
|
testLowpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy",
|
||||||
0.1f);
|
0.1f);
|
||||||
testLowpass<float>(
|
testLowpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy",
|
||||||
0.3f);
|
0.3f);
|
||||||
testLowpass<float>(
|
testLowpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy",
|
||||||
0.5f);
|
0.5f);
|
||||||
testLowpass<float>(
|
testLowpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy",
|
||||||
0.7f);
|
0.7f);
|
||||||
testLowpass<float>(
|
testLowpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy",
|
||||||
0.9f);
|
0.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[OnePoleFilter] Lowpass Double")
|
TEST_CASE("[OnePoleFilter] Lowpass Double")
|
||||||
{
|
{
|
||||||
testLowpass<double>(
|
testLowpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.1.npy",
|
||||||
0.1f);
|
0.1f);
|
||||||
testLowpass<double>(
|
testLowpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.3.npy",
|
||||||
0.3f);
|
0.3f);
|
||||||
testLowpass<double>(
|
testLowpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.5.npy",
|
||||||
0.5f);
|
0.5f);
|
||||||
testLowpass<double>(
|
testLowpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.7.npy",
|
||||||
0.7f);
|
0.7f);
|
||||||
testLowpass<double>(
|
testLowpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_low_gain_0.9.npy",
|
||||||
0.9f);
|
0.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[OnePoleFilter] Highpass Float")
|
TEST_CASE("[OnePoleFilter] Highpass Float")
|
||||||
{
|
{
|
||||||
testHighpass<float>(
|
testHighpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy",
|
||||||
0.1f);
|
0.1f);
|
||||||
testHighpass<float>(
|
testHighpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy",
|
||||||
0.3f);
|
0.3f);
|
||||||
testHighpass<float>(
|
testHighpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy",
|
||||||
0.5f);
|
0.5f);
|
||||||
testHighpass<float>(
|
testHighpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy",
|
||||||
0.7f);
|
0.7f);
|
||||||
testHighpass<float>(
|
testHighpass<float>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy",
|
||||||
0.9f);
|
0.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[OnePoleFilter] Highpass Double")
|
TEST_CASE("[OnePoleFilter] Highpass Double")
|
||||||
{
|
{
|
||||||
testHighpass<double>(
|
testHighpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.1.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.1.npy",
|
||||||
0.1f);
|
0.1f);
|
||||||
testHighpass<double>(
|
testHighpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.3.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.3.npy",
|
||||||
0.3f);
|
0.3f);
|
||||||
testHighpass<double>(
|
testHighpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.5.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.5.npy",
|
||||||
0.5f);
|
0.5f);
|
||||||
testHighpass<double>(
|
testHighpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.7.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.7.npy",
|
||||||
0.7f);
|
0.7f);
|
||||||
testHighpass<double>(
|
testHighpass<double>(
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_input_gain_0.9.npy",
|
||||||
std::filesystem::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy",
|
fs::current_path() / "tests/TestFiles/OnePoleFilter/OPF_high_gain_0.9.npy",
|
||||||
0.9f);
|
0.9f);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "Parser.h"
|
#include "Regexes.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ using namespace Catch::literals;
|
||||||
|
|
||||||
TEST_CASE("Region activation", "Region tests")
|
TEST_CASE("Region activation", "Region tests")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
|
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
SECTION("Basic state")
|
SECTION("Basic state")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ using namespace Catch::literals;
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on key")
|
TEST_CASE("[Region] Crossfade in on key")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_lokey", "1" });
|
region.parseOpcode({ "xfin_lokey", "1" });
|
||||||
region.parseOpcode({ "xfin_hikey", "3" });
|
region.parseOpcode({ "xfin_hikey", "3" });
|
||||||
|
|
@ -43,7 +44,8 @@ TEST_CASE("[Region] Crossfade in on key")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on key - 2")
|
TEST_CASE("[Region] Crossfade in on key - 2")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_lokey", "1" });
|
region.parseOpcode({ "xfin_lokey", "1" });
|
||||||
region.parseOpcode({ "xfin_hikey", "5" });
|
region.parseOpcode({ "xfin_hikey", "5" });
|
||||||
|
|
@ -57,7 +59,8 @@ TEST_CASE("[Region] Crossfade in on key - 2")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on key - gain")
|
TEST_CASE("[Region] Crossfade in on key - gain")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_lokey", "1" });
|
region.parseOpcode({ "xfin_lokey", "1" });
|
||||||
region.parseOpcode({ "xfin_hikey", "5" });
|
region.parseOpcode({ "xfin_hikey", "5" });
|
||||||
|
|
@ -71,7 +74,8 @@ TEST_CASE("[Region] Crossfade in on key - gain")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade out on key")
|
TEST_CASE("[Region] Crossfade out on key")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfout_lokey", "51" });
|
region.parseOpcode({ "xfout_lokey", "51" });
|
||||||
region.parseOpcode({ "xfout_hikey", "55" });
|
region.parseOpcode({ "xfout_hikey", "55" });
|
||||||
|
|
@ -86,7 +90,8 @@ TEST_CASE("[Region] Crossfade out on key")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade out on key - gain")
|
TEST_CASE("[Region] Crossfade out on key - gain")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfout_lokey", "51" });
|
region.parseOpcode({ "xfout_lokey", "51" });
|
||||||
region.parseOpcode({ "xfout_hikey", "55" });
|
region.parseOpcode({ "xfout_hikey", "55" });
|
||||||
|
|
@ -102,7 +107,8 @@ TEST_CASE("[Region] Crossfade out on key - gain")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on velocity")
|
TEST_CASE("[Region] Crossfade in on velocity")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_lovel", "20" });
|
region.parseOpcode({ "xfin_lovel", "20" });
|
||||||
region.parseOpcode({ "xfin_hivel", "24" });
|
region.parseOpcode({ "xfin_hivel", "24" });
|
||||||
|
|
@ -118,7 +124,8 @@ TEST_CASE("[Region] Crossfade in on velocity")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on vel - gain")
|
TEST_CASE("[Region] Crossfade in on vel - gain")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_lovel", "20" });
|
region.parseOpcode({ "xfin_lovel", "20" });
|
||||||
region.parseOpcode({ "xfin_hivel", "24" });
|
region.parseOpcode({ "xfin_hivel", "24" });
|
||||||
|
|
@ -135,7 +142,8 @@ TEST_CASE("[Region] Crossfade in on vel - gain")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade out on vel")
|
TEST_CASE("[Region] Crossfade out on vel")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfout_lovel", "51" });
|
region.parseOpcode({ "xfout_lovel", "51" });
|
||||||
region.parseOpcode({ "xfout_hivel", "55" });
|
region.parseOpcode({ "xfout_hivel", "55" });
|
||||||
|
|
@ -151,7 +159,8 @@ TEST_CASE("[Region] Crossfade out on vel")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade out on vel - gain")
|
TEST_CASE("[Region] Crossfade out on vel - gain")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfout_lovel", "51" });
|
region.parseOpcode({ "xfout_lovel", "51" });
|
||||||
region.parseOpcode({ "xfout_hivel", "55" });
|
region.parseOpcode({ "xfout_hivel", "55" });
|
||||||
|
|
@ -168,76 +177,77 @@ TEST_CASE("[Region] Crossfade out on vel - gain")
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on CC")
|
TEST_CASE("[Region] Crossfade in on CC")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
sfz::CCValueArray ccState;
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_locc24", "20" });
|
region.parseOpcode({ "xfin_locc24", "20" });
|
||||||
region.parseOpcode({ "xfin_hicc24", "24" });
|
region.parseOpcode({ "xfin_hicc24", "24" });
|
||||||
region.parseOpcode({ "amp_veltrack", "0" });
|
region.parseOpcode({ "amp_veltrack", "0" });
|
||||||
ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a );
|
midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a );
|
||||||
ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.70711_a );
|
midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.70711_a );
|
||||||
ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.86603_a );
|
midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.86603_a );
|
||||||
ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade in on CC - gain")
|
TEST_CASE("[Region] Crossfade in on CC - gain")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
sfz::CCValueArray ccState;
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfin_locc24", "20" });
|
region.parseOpcode({ "xfin_locc24", "20" });
|
||||||
region.parseOpcode({ "xfin_hicc24", "24" });
|
region.parseOpcode({ "xfin_hicc24", "24" });
|
||||||
region.parseOpcode({ "amp_veltrack", "0" });
|
region.parseOpcode({ "amp_veltrack", "0" });
|
||||||
region.parseOpcode({ "xf_cccurve", "gain" });
|
region.parseOpcode({ "xf_cccurve", "gain" });
|
||||||
ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.25_a );
|
midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.25_a );
|
||||||
ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a );
|
midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a );
|
||||||
ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.75_a );
|
midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.75_a );
|
||||||
ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
}
|
}
|
||||||
TEST_CASE("[Region] Crossfade out on CC")
|
TEST_CASE("[Region] Crossfade out on CC")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
sfz::CCValueArray ccState;
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfout_locc24", "20" });
|
region.parseOpcode({ "xfout_locc24", "20" });
|
||||||
region.parseOpcode({ "xfout_hicc24", "24" });
|
region.parseOpcode({ "xfout_hicc24", "24" });
|
||||||
region.parseOpcode({ "amp_veltrack", "0" });
|
region.parseOpcode({ "amp_veltrack", "0" });
|
||||||
ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.86603_a );
|
midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.86603_a );
|
||||||
ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.70711_a );
|
midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.70711_a );
|
||||||
ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a );
|
midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a );
|
||||||
ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Region] Crossfade out on CC - gain")
|
TEST_CASE("[Region] Crossfade out on CC - gain")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
sfz::CCValueArray ccState;
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "xfout_locc24", "20" });
|
region.parseOpcode({ "xfout_locc24", "20" });
|
||||||
region.parseOpcode({ "xfout_hicc24", "24" });
|
region.parseOpcode({ "xfout_hicc24", "24" });
|
||||||
region.parseOpcode({ "amp_veltrack", "0" });
|
region.parseOpcode({ "amp_veltrack", "0" });
|
||||||
region.parseOpcode({ "xf_cccurve", "gain" });
|
region.parseOpcode({ "xf_cccurve", "gain" });
|
||||||
ccState[24] = 19; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 19; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
ccState[24] = 20; REQUIRE( region.getCrossfadeGain(ccState) == 1.0_a );
|
midiState.cc[24] = 20; REQUIRE( region.getCrossfadeGain(midiState.cc) == 1.0_a );
|
||||||
ccState[24] = 21; REQUIRE( region.getCrossfadeGain(ccState) == 0.75_a );
|
midiState.cc[24] = 21; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.75_a );
|
||||||
ccState[24] = 22; REQUIRE( region.getCrossfadeGain(ccState) == 0.5_a );
|
midiState.cc[24] = 22; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.5_a );
|
||||||
ccState[24] = 23; REQUIRE( region.getCrossfadeGain(ccState) == 0.25_a );
|
midiState.cc[24] = 23; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.25_a );
|
||||||
ccState[24] = 24; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 24; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
ccState[24] = 25; REQUIRE( region.getCrossfadeGain(ccState) == 0.0_a );
|
midiState.cc[24] = 25; REQUIRE( region.getCrossfadeGain(midiState.cc) == 0.0_a );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
|
TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "amp_veltrack", "0" });
|
region.parseOpcode({ "amp_veltrack", "0" });
|
||||||
REQUIRE( region.getNoteGain(64, 127) == 1.0_a );
|
REQUIRE( region.getNoteGain(64, 127) == 1.0_a );
|
||||||
|
|
@ -247,7 +257,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
|
||||||
|
|
||||||
TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
|
TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "amp_veltrack", "100" });
|
region.parseOpcode({ "amp_veltrack", "100" });
|
||||||
REQUIRE( region.getNoteGain(64, 127) == 1.0_a );
|
REQUIRE( region.getNoteGain(64, 127) == 1.0_a );
|
||||||
|
|
@ -256,7 +267,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
|
||||||
|
|
||||||
TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
|
TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "amp_veltrack", "-100" });
|
region.parseOpcode({ "amp_veltrack", "-100" });
|
||||||
REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) );
|
REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) );
|
||||||
|
|
@ -265,19 +277,20 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
|
||||||
|
|
||||||
TEST_CASE("[Region] rt_decay")
|
TEST_CASE("[Region] rt_decay")
|
||||||
{
|
{
|
||||||
sfz::Region region {};
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
region.parseOpcode({ "trigger", "release" });
|
region.parseOpcode({ "trigger", "release" });
|
||||||
region.parseOpcode({ "rt_decay", "10" });
|
region.parseOpcode({ "rt_decay", "10" });
|
||||||
sfz::noteOn(64, 64);
|
midiState.noteOn(64, 64);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
|
||||||
region.parseOpcode({ "rt_decay", "20" });
|
region.parseOpcode({ "rt_decay", "20" });
|
||||||
sfz::noteOn(64, 64);
|
midiState.noteOn(64, 64);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
|
||||||
region.parseOpcode({ "trigger", "attack" });
|
region.parseOpcode({ "trigger", "attack" });
|
||||||
sfz::noteOn(64, 64);
|
midiState.noteOn(64, 64);
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
|
||||||
}
|
}
|
||||||
|
|
@ -21,13 +21,16 @@
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "MidiState.h"
|
||||||
#include "Region.h"
|
#include "Region.h"
|
||||||
#include "catch2/catch.hpp"
|
#include "catch2/catch.hpp"
|
||||||
using namespace Catch::literals;
|
using namespace Catch::literals;
|
||||||
|
|
||||||
TEST_CASE("[Region] Parsing opcodes")
|
TEST_CASE("[Region] Parsing opcodes")
|
||||||
{
|
{
|
||||||
sfz::Region region;
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
|
|
||||||
SECTION("sample")
|
SECTION("sample")
|
||||||
{
|
{
|
||||||
REQUIRE(region.sample == "");
|
REQUIRE(region.sample == "");
|
||||||
|
|
@ -168,10 +171,10 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
REQUIRE(!region.offBy);
|
REQUIRE(!region.offBy);
|
||||||
region.parseOpcode({ "off_by", "5" });
|
region.parseOpcode({ "off_by", "5" });
|
||||||
REQUIRE(region.offBy);
|
REQUIRE(region.offBy);
|
||||||
REQUIRE(region.offBy == 5);
|
REQUIRE(*region.offBy == 5);
|
||||||
region.parseOpcode({ "off_by", "-1" });
|
region.parseOpcode({ "off_by", "-1" });
|
||||||
REQUIRE(region.offBy);
|
REQUIRE(region.offBy);
|
||||||
REQUIRE(region.offBy == 0);
|
REQUIRE(*region.offBy == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("off_mode")
|
SECTION("off_mode")
|
||||||
|
|
@ -1036,7 +1039,8 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
// Specific region bugs
|
// Specific region bugs
|
||||||
TEST_CASE("[Region] Non-conforming floating point values in integer opcodes")
|
TEST_CASE("[Region] Non-conforming floating point values in integer opcodes")
|
||||||
{
|
{
|
||||||
sfz::Region region;
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "offset", "2014.5" });
|
region.parseOpcode({ "offset", "2014.5" });
|
||||||
REQUIRE(region.offset == 2014);
|
REQUIRE(region.offset == 2014);
|
||||||
region.parseOpcode({ "pitch_keytrack", "-2.1" });
|
region.parseOpcode({ "pitch_keytrack", "-2.1" });
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@ using namespace Catch::literals;
|
||||||
|
|
||||||
TEST_CASE("Basic triggers", "Region triggers")
|
TEST_CASE("Basic triggers", "Region triggers")
|
||||||
{
|
{
|
||||||
sfz::Region region;
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
|
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
SECTION("key")
|
SECTION("key")
|
||||||
{
|
{
|
||||||
|
|
@ -136,7 +138,8 @@ TEST_CASE("Basic triggers", "Region triggers")
|
||||||
|
|
||||||
TEST_CASE("Legato triggers", "Region triggers")
|
TEST_CASE("Legato triggers", "Region triggers")
|
||||||
{
|
{
|
||||||
sfz::Region region;
|
sfz::MidiState midiState;
|
||||||
|
sfz::Region region { midiState };
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
SECTION("First note playing")
|
SECTION("First note playing")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue