diff --git a/.gitmodules b/.gitmodules index c3f223211..a2184e24a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "external/st_audiofile/thirdparty/wavpack"] path = external/st_audiofile/thirdparty/wavpack url = https://github.com/dbry/WavPack.git +[submodule "external/invoke.hpp"] + path = external/invoke.hpp + url = https://github.com/BlackMATov/invoke.hpp.git diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index ff0ab5ebb..1b43ed78b 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -51,7 +51,7 @@ endif() # Set macOS compatibility level if(APPLE) - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9") + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13") endif() # If using C++17, check if aligned-new has runtime support on the platform; diff --git a/cmake/SfizzDeps.cmake b/cmake/SfizzDeps.cmake index daf3cdee6..94e5418b6 100644 --- a/cmake/SfizzDeps.cmake +++ b/cmake/SfizzDeps.cmake @@ -83,6 +83,11 @@ else() sfizz_add_vendor_abseil() endif() +# C++14 std::invoke +# add_subdirectory("external/invoke.hpp") +add_library(invoke_hpp INTERFACE) +target_include_directories(invoke_hpp INTERFACE "external/invoke.hpp/headers") + # The jsl utility library for C++ add_library(sfizz_jsl INTERFACE) add_library(sfizz::jsl ALIAS sfizz_jsl) diff --git a/common.mk b/common.mk index 27576171d..2475a9b90 100644 --- a/common.mk +++ b/common.mk @@ -243,6 +243,10 @@ SFIZZ_CXX_FLAGS += -I$(SFIZZ_DIR)/external/atomic_queue/include SFIZZ_CXX_FLAGS += -I$(SFIZZ_DIR)/external/filesystem/include +# invoke.hpp dependency + +SFIZZ_CXX_FLAGS += -I$(SFIZZ_DIR)/external/invoke.hpp/headers + ### Abseil dependency SFIZZ_C_FLAGS += -I$(SFIZZ_DIR)/external/abseil-cpp @@ -293,7 +297,11 @@ SFIZZ_SOURCES += \ external/abseil-cpp/absl/synchronization/blocking_counter.cc \ external/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc \ external/abseil-cpp/absl/synchronization/internal/per_thread_sem.cc \ - external/abseil-cpp/absl/synchronization/internal/waiter.cc \ + external/abseil-cpp/absl/synchronization/internal/waiter_base.cc \ + external/abseil-cpp/absl/synchronization/internal/win32_waiter.cc \ + external/abseil-cpp/absl/synchronization/internal/stdcpp_waiter.cc \ + external/abseil-cpp/absl/synchronization/internal/sem_waiter.cc \ + external/abseil-cpp/absl/synchronization/internal/pthread_waiter.cc \ external/abseil-cpp/absl/synchronization/notification.cc \ external/abseil-cpp/absl/synchronization/mutex.cc # absl::graphcycles_internal diff --git a/external/abseil-cpp b/external/abseil-cpp index c2435f834..ad73c6dc1 160000 --- a/external/abseil-cpp +++ b/external/abseil-cpp @@ -1 +1 @@ -Subproject commit c2435f8342c2d0ed8101cb43adfd605fdc52dca2 +Subproject commit ad73c6dc1a253203c1c8b529cda18f2138d49df0 diff --git a/external/invoke.hpp b/external/invoke.hpp new file mode 160000 index 000000000..2c1eabc2e --- /dev/null +++ b/external/invoke.hpp @@ -0,0 +1 @@ +Subproject commit 2c1eabc2e20ab02961f95c704ff0c0818671ddd1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b68e750c7..f7aab0f7d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -295,7 +295,7 @@ target_sources(sfizz_internal PRIVATE ${SFIZZ_HEADERS} ${SFIZZ_SOURCES} ${FAUST_ target_include_directories(sfizz_internal PUBLIC "." "sfizz") target_link_libraries(sfizz_internal PUBLIC absl::strings absl::span sfizz::filesystem sfizz::atomic_queue sfizz::spin_mutex sfizz::bit_array sfizz::simde sfizz::hiir sfizz::jsl - PRIVATE sfizz::parser sfizz::messaging absl::flat_hash_map Threads::Threads st_audiofile sfizz::pugixml sfizz::spline sfizz::tunings sfizz::kissfft sfizz::cephes sfizz::cpuid sfizz::threadpool sfizz::atomic) + PRIVATE sfizz::parser sfizz::messaging absl::flat_hash_map Threads::Threads st_audiofile sfizz::pugixml sfizz::spline sfizz::tunings sfizz::kissfft sfizz::cephes sfizz::cpuid sfizz::threadpool sfizz::atomic invoke_hpp) if(SFIZZ_USE_SNDFILE) target_compile_definitions(sfizz_internal PUBLIC "SFIZZ_USE_SNDFILE=1") target_link_libraries(sfizz_internal PUBLIC st_audiofile) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 4382cd36a..acd774672 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -70,6 +70,7 @@ FloatSpec loBipolar { -1.0f, {-1.0f, 1.0f}, kPermissiveBounds }; FloatSpec hiBipolar { 1.0f, {-1.0f, 1.0f}, kPermissiveBounds }; UInt16Spec ccNumber { 0, {0, config::numCCs}, 0 }; UInt16Spec smoothCC { 0, {0, 100}, kPermissiveUpperBound }; +FloatSpec stepCC { 0.0f, {0.0f, 127.0f}, kPermissiveUpperBound }; UInt8Spec curveCC { 0, {0, 255}, 0 }; UInt8Spec sustainCC { 64, {0, 127}, 0 }; UInt8Spec sostenutoCC { 66, {0, 127}, 0 }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 87614b6cd..b3fac82e1 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -228,6 +228,7 @@ namespace Default extern const OpcodeSpec hiPolyAftertouch; extern const OpcodeSpec ccNumber; extern const OpcodeSpec curveCC; + extern const OpcodeSpec stepCC; extern const OpcodeSpec smoothCC; extern const OpcodeSpec sustainCC; extern const OpcodeSpec sostenutoCC; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index cd9510981..be799ae61 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1126,11 +1126,11 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg) eg.vel2sustain = opcode.read(Default::egPercentMod); break; case_any_eg("attack_oncc&"): // also attackcc& - if (!setModifier(eg.ccAttack, Default::egTimeMod)) + if (!setModifier(eg.ccAttack, Default::egTimeMod)) return false; break; case_any_eg("attack_curvecc&"): - if (!setCurve(eg.ccAttack)) + if (!setCurve(eg.ccAttack)) return false; break; case_any_eg("decay_oncc&"): // also decaycc& @@ -1138,47 +1138,47 @@ bool sfz::Region::parseEGOpcode(const Opcode& opcode, EGDescription& eg) return false; break; case_any_eg("decay_curvecc&"): - if (!setCurve(eg.ccDecay)) + if (!setCurve(eg.ccDecay)) return false; break; case_any_eg("delay_oncc&"): // also delaycc& - if (!setModifier(eg.ccDelay, Default::egTimeMod)) + if (!setModifier(eg.ccDelay, Default::egTimeMod)) return false; break; case_any_eg("delay_curvecc&"): - if (!setCurve(eg.ccDelay)) + if (!setCurve(eg.ccDelay)) return false; break; case_any_eg("hold_oncc&"): // also holdcc& - if (!setModifier(eg.ccHold, Default::egTimeMod)) + if (!setModifier(eg.ccHold, Default::egTimeMod)) return false; break; case_any_eg("hold_curvecc&"): // also attackcc& - if (!setCurve(eg.ccHold)) + if (!setCurve(eg.ccHold)) return false; break; case_any_eg("release_oncc&"): // also releasecc& - if (!setModifier(eg.ccRelease, Default::egTimeMod)) + if (!setModifier(eg.ccRelease, Default::egTimeMod)) return false; break; case_any_eg("release_curvecc&"): // also attackcc& - if (!setCurve(eg.ccRelease)) + if (!setCurve(eg.ccRelease)) return false; break; case_any_eg("start_oncc&"): // also startcc& - if (!setModifier(eg.ccStart, Default::egPercentMod)) + if (!setModifier(eg.ccStart, Default::egPercentMod)) return false; break; case_any_eg("start_curvecc&"): // also startcc& - if (!setCurve(eg.ccStart)) + if (!setCurve(eg.ccStart)) return false; break; case_any_eg("sustain_oncc&"): // also sustaincc& - if (!setModifier(eg.ccSustain, Default::egPercentMod)) + if (!setModifier(eg.ccSustain, Default::egPercentMod)) return false; break; case_any_eg("sustain_curvecc&"): // also attackcc& - if (!setCurve(eg.ccSustain)) + if (!setCurve(eg.ccSustain)) return false; break; @@ -1757,10 +1757,7 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec spec, p.curve = opcode.read(Default::curveCC); break; case kOpcodeStepCcN: - { - const OpcodeSpec stepCC { 0.0f, {}, kPermissiveBounds }; - p.step = spec.normalizeInput(opcode.read(stepCC)); - } + p.step = spec.normalizeInput(opcode.read(Default::stepCC)); break; case kOpcodeSmoothCcN: p.smooth = opcode.read(Default::smoothCC); diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index de7cc37f1..b882c44ab 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -290,8 +290,8 @@ struct Region { VelocityOverride velocityOverride { Default::velocityOverride }; // sw_vel bool checkSustain { Default::checkSustain }; // sustain_sw bool checkSostenuto { Default::checkSostenuto }; // sostenuto_sw - uint16_t sustainCC { Default::sustainCC }; // sustain_cc - uint16_t sostenutoCC { Default::sostenutoCC }; // sustain_cc + uint8_t sustainCC { Default::sustainCC }; // sustain_cc + uint8_t sostenutoCC { Default::sostenutoCC }; // sostenuto_cc float sustainThreshold { Default::sustainThreshold }; // sustain_cc float sostenutoThreshold { Default::sostenutoThreshold }; // sustain_cc diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index d6bfc670d..b871c24ae 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -283,22 +283,21 @@ inline CXX14_CONSTEXPR bool ccModulationIsPerVoice(int cc) { } namespace literals { - inline float operator""_norm(unsigned long long int value) + inline float operator""_norm(unsigned long long int value) { - if (value > 127) - value = 127; - - return normalize7Bits(value); + return static_cast(value) / 127.0f; } - - inline float operator""_norm(long double value) + inline float operator""_norm(long double value) { - if (value < 0) - value = 0; - if (value > 127) - value = 127; - - return normalize7Bits(value); + return static_cast(value) / 127.0f; + } + inline float operator""_bend(unsigned long long int value) + { + return static_cast(value) / 8191.0f; + } + inline float operator""_bend(long double value) + { + return static_cast(value) / 8191.0f; } } diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 162a0bc74..4ad783dca 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -33,6 +33,7 @@ #include "parser/Parser.h" #include #include +#include #include #include #include diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 8b69c679e..a605420ce 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -4,554 +4,16 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "CCMap.h" -#include "Config.h" #include "Defaults.h" -#include "EQDescription.h" -#include "FileId.h" -#include "FilterDescription.h" -#include "FlexEGDescription.h" -#include "LFOCommon.h" -#include "LFODescription.h" -#include "Range.h" -#include "SfzFilter.h" -#include "SfzHelpers.h" -#include "SynthPrivate.h" -#include "FilePool.h" -#include "Curve.h" -#include "MidiState.h" -#include "SynthConfig.h" -#include "TriggerEvent.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "absl/types/span.h" -#include "modulations/ModId.h" -#include "modulations/ModKey.h" -#include "modulations/ModKeyHash.h" -#include "utility/StringViewHelpers.h" -#include -#include "utility/Size.h" -#include -#include -#include -#include - -// TODO: `ccModDepth` and `ccModParameters` are O(N), need better implementation +#include "Region.h" +#include "SynthMessagingHelper.hpp" namespace sfz { -static constexpr unsigned maxIndices = 8; - -static uint64_t hashMessagePath(const char* path, const char* sig) -{ - uint64_t h = Fnv1aBasis; - while (unsigned char c = *path++) { - if (!absl::ascii_isdigit(c)) - h = hashByte(c, h); - else { - h = hashByte('&', h); - while (absl::ascii_isdigit(*path)) - ++path; - } - } - h = hashByte(',', h); - while (unsigned char c = *sig++) - h = hashByte(c, h); - return h; -} - -class MessagingHelper -{ -public: - MessagingHelper(Client& client, int delay, const char* path, const char* sig, sfz::Synth::Impl& impl) - : client(client), impl(impl), delay(delay), path(path), sig(sig) - { - indices.reserve(maxIndices); - } - - enum class ModParam { Depth, Curve, Smooth, Step }; - - bool match(const char* pattern, const char* sig) - { - indices.clear(); - const char* path_ = this->path; - - while (const char *endp = strchr(pattern, '&')) { - if (indices.size() == maxIndices) - return false; - - size_t length = endp - pattern; - if (strncmp(pattern, path_, length) != 0) - return false; - pattern += length; - path_ += length; - - length = 0; - while (absl::ascii_isdigit(path_[length])) - ++length; - - indices.push_back(0); - if (!absl::SimpleAtoi(absl::string_view(path_, length), &indices.back())) - return false; - - pattern += 1; - path_ += length; - } - - return !strcmp(path_, pattern) && !strcmp(this->sig, sig); - } - - // These are the reply/reply2 overloads for the (almost) concrete types, that should - // translate to actual calls to the client.receive(...) method. - - void reply(const std::string& value) { client.receive<'s'>(delay, path, value.data()); } - void reply(const char* value) { client.receive<'s'>(delay, path, value); } - void reply(float value) { client.receive<'f'>(delay, path, value); } - void reply(absl::nullopt_t) { client.receive<'N'>(delay, path, {}); } - - void reply(bool value) - { - if (value) - client.receive<'T'>(delay, path, {}); - else - client.receive<'F'>(delay, path, {}); - } - - template::value>> - void reply(T value) - { - if (sizeof(value) <= 4) - client.receive<'i'>(delay, path, static_cast(value)); - else - client.receive<'h'>(delay, path, static_cast(value)); - } - template - void reply(const BitArray& array) - { - sfizz_blob_t blob { array.data(), static_cast(array.byte_size()) }; - client.receive<'b'>(delay, path, &blob); - } - - // Call reply but denormalizes the input if needed by the opcode spec - template - void reply(T value, OpcodeSpec spec) { reply(spec.denormalizeInput(value)); } - - // sfizz specific types - void reply(sfz::LFOWave wave) { reply(static_cast(wave)); } - void reply(sfz::SelfMask mode) { reply(mode == SelfMask::mask); } - void reply(sfz::LoopMode mode) - { - switch (mode) { - case LoopMode::no_loop: reply("no_loop"); break; - case LoopMode::loop_continuous: reply("loop_continuous"); break; - case LoopMode::loop_sustain: reply("loop_sustain"); break; - case LoopMode::one_shot: reply("one_shot"); break; - } - } - - void reply(sfz::CrossfadeCurve curve) - { - switch (curve) { - case CrossfadeCurve::gain: reply("gain"); break; - case CrossfadeCurve::power: reply("power"); break; - } - } - - void reply(sfz::Trigger mode) - { - switch (mode) { - case Trigger::attack: reply("attack"); break; - case Trigger::first: reply("first"); break; - case Trigger::legato: reply("legato"); break; - case Trigger::release: reply("release"); break; - case Trigger::release_key: reply("release_key"); break; - } - } - - void reply(sfz::VelocityOverride mode) - { - switch (mode) { - case VelocityOverride::current: reply("current"); break; - case VelocityOverride::previous: reply("previous"); break; - } - } - - void reply(sfz::OffMode mode) - { - switch (mode) { - case OffMode::fast: reply("fast"); break; - case OffMode::time: reply("time"); break; - case OffMode::normal: reply("normal"); break; - } - } - - void reply(sfz::FilterType type) - { - switch (type) { - case FilterType::kFilterLpf1p: reply("lpf_1p"); break; - case FilterType::kFilterHpf1p: reply("hpf_1p"); break; - case FilterType::kFilterLpf2p: reply("lpf_2p"); break; - case FilterType::kFilterHpf2p: reply("hpf_2p"); break; - case FilterType::kFilterBpf2p: reply("bpf_2p"); break; - case FilterType::kFilterBrf2p: reply("brf_2p"); break; - case FilterType::kFilterBpf1p: reply("bpf_1p"); break; - case FilterType::kFilterBrf1p: reply("brf_1p"); break; - case FilterType::kFilterApf1p: reply("apf_1p"); break; - case FilterType::kFilterLpf2pSv: reply("lpf_2p_sv"); break; - case FilterType::kFilterHpf2pSv: reply("hpf_2p_sv"); break; - case FilterType::kFilterBpf2pSv: reply("bpf_2p_sv"); break; - case FilterType::kFilterBrf2pSv: reply("brf_2p_sv"); break; - case FilterType::kFilterLpf4p: reply("lpf_4p"); break; - case FilterType::kFilterHpf4p: reply("hpf_4p"); break; - case FilterType::kFilterLpf6p: reply("lpf_6p"); break; - case FilterType::kFilterHpf6p: reply("hpf_6p"); break; - case FilterType::kFilterPink: reply("pink"); break; - case FilterType::kFilterLsh: reply("lsh"); break; - case FilterType::kFilterHsh: reply("hsh"); break; - case FilterType::kFilterPeq: reply("peq"); break; - case FilterType::kFilterBpf4p: reply("bpf_4p"); break; - case FilterType::kFilterBpf6p: reply("bpf_6p"); break; - case FilterType::kFilterNone: reply("none"); break; - } - } - - void reply(sfz::EqType type) - { - switch(type) { - case EqType::kEqNone: reply("none"); break; - case EqType::kEqPeak: reply("peak"); break; - case EqType::kEqLshelf: reply("lshelf"); break; - case EqType::kEqHshelf: reply("hshelf"); break; - } - } - - void reply(sfz::TriggerEventType type) - { - switch(type) { - case TriggerEventType::NoteOff: reply("note_off"); break; - case TriggerEventType::NoteOn: reply("note_on"); break; - case TriggerEventType::CC: reply("cc"); break; - } - } - - // reply2 for pairs (Usually only for float/int ranges) - template::value>> - void reply2(T value1, T value2) - { - sfizz_arg_t args[2]; - if (sizeof(value1) <= 4) { - args[0].i = value1; - args[1].i = value2; - client.receive(delay, path, "ii", args); - } else { - args[0].h = value1; - args[1].h = value2; - client.receive(delay, path, "hh", args); - } - } - - void reply2(float value1, float value2) - { - sfizz_arg_t args[2]; - args[0].f = value1; - args[1].f = value2; - client.receive(delay, path, "ff", args); - } - - // Now we have some templated reply overloads that decay into "concrete" reply calls - // but add some logic (e.g. an optional can either reply the value, or send 'N' for - // null) - template - void reply(absl::optional value, Args... args) - { - if (!value) { - client.receive<'N'>(delay, path, {}); - return; - } - - reply(*value, args...); - } - - - template - void reply(T* value, Args... args) - { - if (!value) { - client.receive<'N'>(delay, path, {}); - return; - } - - reply(*value, args...); - } - - template - void reply(std::shared_ptr value) { reply(value.get()); } - - template - void reply(sfz::UncheckedRange range) { reply2(range.getStart(), range.getEnd()); } - - template - void reply(absl::optional value, T def) { reply(value.value_or(def)); } - - template - void reply(const sfz::ModifierCurvePair& modCurve, ModParam which, Args...args) - { - if (auto region = getRegion()) { - switch (which) { - case ModParam::Curve: reply(modCurve.curve); break; - default: reply(modCurve.modifier, args...); break; - } - } - } - - template - void reply(const sfz::ModKey::Parameters& params, ModParam which, Args...args) - { - if (auto region = getRegion()) { - switch (which) { - case ModParam::Depth: break; - case ModParam::Curve: reply(params.curve); break; - case ModParam::Smooth: reply(params.smooth); break; - case ModParam::Step: reply(params.step, args...); break; - } - } - } - - template - void reply(CCMap map, Args...args) { reply(map, true, args...); } - - template - void reply(CCMap map, bool useDefault, Args...args) - { - if (useDefault) - reply(map.getWithDefault(indices.back()), args...); - else if (map.contains(indices.back())) - reply(map.get(indices.back()), args...); - else - client.receive<'N'>(delay, path, {}); - } - - // Below are all methods that will fetch various data structure elements (regions, - // egs, ...), check that they exist, and call an actual "concrete" reply - // implementation. They use pointer-to-member variables, which allow the compiler to - // dispatch to the correct logic. For example, if the pointer-to-member is - // `sfz::Region::*`, we aim at sending the value of a member of the sfz::Region - // struct. The first thing is to read the index of the region and fetch it from the - // sfizz data structure using `getRegion(...)`, and then apply the pointer-to-member - // to this particular region to send the value. - - // Adding new dispatching overloads seemed simple enough to this point, although I - // haven't found a nice way to the same with pointer-to-member function. - // TODO: maybe a semi-templated overload that check that if the member is a function, - // TODO: and call it with `args...` could be an approach to try. - - template - void reply(T sfz::Region::* member, Args... args) - { - if (auto region = getRegion()) - reply((*region).*member, args...); - } - - template - void reply(const sfz::EGDescription& eg, T sfz::EGDescription::* member, Args...args) - { - reply(eg.*member, args...); - } - - template - void reply(T sfz::FilterDescription::* member, Args... args) - { - if (auto region = getRegion()) - if (auto filter = getFilter(*region)) - reply((*filter).*member, args...); - } - - template - void reply(T sfz::EQDescription::* member, Args... args) - { - if (auto region = getRegion()) - if (auto eq = getEQ(*region)) - reply((*eq).*member, args...); - } - - template - void reply(T sfz::LFODescription::* member, Args... args) - { - if (auto region = getRegion()) - if (auto lfo = getLFO(*region)) - reply((*lfo).*member, args...); - } - - template - void reply(T sfz::LFODescription::Sub::* member, Args... args) - { - if (auto region = getRegion()) - if (auto lfo = getLFO(*region)) - if (!lfo->sub.empty()) - reply((lfo->sub[0]).*member, args...); - } - - template - void reply(T sfz::FlexEGPoint::* member, Args... args) - { - if (auto region = getRegion()) - if (auto eg = getEG(*region)) - if (auto point = getEGPoint(*eg)) - reply((*point).*member, args...); - } - - template - void reply(T sfz::TriggerEvent::* member, Args... args) - { - if (auto voice = getVoice()) - reply((*voice).getTriggerEvent().*member, args...); - } - - template - void reply(T sfz::Voice::* member, Args... args) - { - if (auto voice = getVoice()) { - reply((*voice.*member)(args...)); // Voice only has callables - } - } - - template - void reply(ModId id, ModParam param, Args...args) - { - if (auto region = getRegion()) { - int cc = static_cast (indices.back()); - switch (id){ - case ModId::FilCutoff: - case ModId::FilGain: - switch (param) { - case ModParam::Depth: reply(region->ccModDepth(cc, id, indices[1]), args...); break; - default: reply(region->ccModParameters(cc, id, indices[1]), param, args...); break; - } break; - default: - switch (param) { - case ModParam::Depth: reply(region->ccModDepth(cc, id), args...); break; - default: reply(region->ccModParameters(cc, id), param, args...); break; - } - } - } - } - - // Validate and fetch elements from the sfizz data structures. By default, we kind of - // assume that regions/voices will be the first index, CCs will be the last, and - // EQ/Filter/.. will be in-between. - sfz::Region* getRegion(absl::optional index = {}) - { - const auto idx = index.value_or(indices[0]); - if (idx >= impl.layers_.size()) - return {}; - - Layer& layer = *impl.layers_[idx]; - return &layer.getRegion(); - } - - sfz::FilterDescription* getFilter(sfz::Region& region, absl::optional index = {}) - { - const auto idx = index.value_or(indices[1]); - if (region.filters.size() <= idx) - return {}; - - return ®ion.filters[idx]; - } - - sfz::EQDescription* getEQ(sfz::Region& region, absl::optional index = {}) - { - const auto idx = index.value_or(indices[1]); - if (region.equalizers.size() <= idx) - return {}; - - return ®ion.equalizers[idx]; - } - - sfz::LFODescription* getLFO(sfz::Region& region, absl::optional index = {}) - { - const auto idx = index.value_or(indices[1]); - if (region.lfos.size() <= idx) - return {}; - - return ®ion.lfos[idx]; - } - - sfz::LFODescription::Sub* getLFOSub(sfz::LFODescription& lfo, absl::optional index = {}) - { - const auto idx = index.value_or(indices[2]); - if (lfo.sub.size() <= idx) - return {}; - - return &lfo.sub[idx]; - } - - sfz::FlexEGDescription* getEG(sfz::Region& region, absl::optional index = {}) - { - const auto idx = index.value_or(indices[1]); - if (region.flexEGs.size() <= idx) - return {}; - - return ®ion.flexEGs[idx]; - } - - sfz::FlexEGPoint* getEGPoint(sfz::FlexEGDescription& desc, absl::optional index = {}) - { - const auto idx = index.value_or(indices[2]) + 1; - if (desc.points.size() <= idx) - return {}; - - return &desc.points[idx]; - } - - sfz::Voice* getVoice(absl::optional index = {}) - { - const auto idx = index.value_or(indices[0]); - if (static_cast(idx) >= impl.numVoices_) - return {}; - - auto& voice = impl.voiceManager_[idx]; - if (voice.isFree()) - return {}; - - return &voice; - } - - // Helpers to get and check the values of the indices - template - absl::optional index(int i) - { - if (i <= ssize(indices)) - return static_cast(indices[i]); - - return absl::nullopt; - } - - absl::optional sindex(int i) { return index(i); } - - absl::optional checkCC(int i = 0) - { - auto cc = index(i); - return cc <= config::numCCs ? cc : absl::nullopt; - } - - absl::optional checkNote(int i = 0) - { - auto note = sindex(i); - return note <= 127 ? note : absl::nullopt; - } - -private: - Client& client; - std::vector indices; - sfz::Synth::Impl& impl; - int delay; - const char* path; - const char* sig; -}; void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, const char* sig, const sfizz_arg_t* args) { Impl& impl = *impl_; - MessagingHelper m {client, delay, path, sig, impl}; + MessagingHelper m {client, delay, path, sig, args, impl}; using ModParam = MessagingHelper::ModParam; switch (hashMessagePath(path, sig)) { @@ -567,6 +29,18 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/note_offset", "") { m.reply(impl.noteOffset_); } break; MATCH("/num_outputs", "") { m.reply(impl.numOutputs_); } break; MATCH("/num_active_voices", "") { m.reply(uint32_t(impl.voiceManager_.getNumActiveVoices())); } break; + MATCH("/sustain_cancels_release", "") { m.reply(&SynthConfig::sustainCancelsRelease); } break; + MATCH("/sample_quality", "") { m.reply(&SynthConfig::liveSampleQuality); } break; + MATCH("/sustain_cancels_release", "s") { m.set(&SynthConfig::sustainCancelsRelease, Default::sustainCancelsRelease); } break; + MATCH("/sustain_cancels_release", "T") { m.set(&SynthConfig::sustainCancelsRelease, Default::sustainCancelsRelease); } break; + MATCH("/sustain_cancels_release", "F") { m.set(&SynthConfig::sustainCancelsRelease, Default::sustainCancelsRelease); } break; + MATCH("/sample_quality", "i") { m.set(&SynthConfig::liveSampleQuality, Default::sampleQuality); } break; + MATCH("/oscillator_quality", "") { m.reply(&SynthConfig::liveOscillatorQuality); } break; + MATCH("/oscillator_quality", "i") { m.set(&SynthConfig::liveOscillatorQuality, Default::oscillatorQuality); } break; + MATCH("/freewheeling_sample_quality", "") { m.reply(&SynthConfig::freeWheelingSampleQuality); } break; + MATCH("/freewheeling_sample_quality", "i") { m.set(&SynthConfig::freeWheelingSampleQuality, Default::sampleQuality); } break; + MATCH("/freewheeling_oscillator_quality", "") { m.reply(&SynthConfig::freeWheelingOscillatorQuality); } break; + MATCH("/freewheeling_oscillator_quality", "i") { m.set(&SynthConfig::freeWheelingOscillatorQuality, Default::oscillatorQuality); } break; //---------------------------------------------------------------------- MATCH("/key/slots", "") { m.reply(impl.keySlots_); } break; MATCH("/key&/label", "") { if (auto k = m.sindex(0)) m.reply(impl.getKeyLabel(*k)); } break; @@ -594,125 +68,220 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/mem/buffers", "") { m.reply(BufferCounter::counter().getTotalBytes()); } break; //---------------------------------------------------------------------- MATCH("/region&/delay", "") { m.reply(&Region::delay); } break; + MATCH("/region&/delay", "f") { m.set(&Region::delay, Default::delay); } break; MATCH("/region&/delay_random", "") { m.reply(&Region::delayRandom); } break; + MATCH("/region&/delay_random", "f") { m.set(&Region::delayRandom, Default::delayRandom); } break; MATCH("/region&/sample", "") { if (auto region = m.getRegion()) { m.reply(region->sampleId->filename()); } } break; MATCH("/region&/direction", "") { if (auto region = m.getRegion()) { m.reply(region->sampleId->isReverse() ? "reverse" : "forward"); } } break; MATCH("/region&/delay_cc&", "") { m.reply(&Region::delayCC); } break; + MATCH("/region&/delay_cc&", "f") { m.set(&Region::delayCC, Default::delayMod); } break; MATCH("/region&/offset", "") { m.reply(&Region::offset); } break; + MATCH("/region&/offset", "h") { m.set(&Region::offset, Default::offset); } break; MATCH("/region&/offset_random", "") { m.reply(&Region::offsetRandom); } break; + MATCH("/region&/offset_random", "h") { m.set(&Region::offsetRandom, Default::offsetRandom); } break; MATCH("/region&/offset_cc&", "") { m.reply(&Region::offsetCC); } break; + MATCH("/region&/offset_cc&", "h") { m.set(&Region::offsetCC, Default::offsetMod); } break; MATCH("/region&/end", "") { m.reply(&Region::sampleEnd); } break; + MATCH("/region&/end", "h") { m.set(&Region::sampleEnd, Default::sampleEnd); } break; MATCH("/region&/end_cc&", "") { m.reply(&Region::endCC); } break; + MATCH("/region&/end_cc&", "h") { m.set(&Region::endCC, Default::sampleEndMod); } break; MATCH("/region&/enabled", "") { if (auto region = m.getRegion()) { m.reply(!region->disabled()); } } break; MATCH("/region&/trigger_on_note", "") { m.reply(&Region::triggerOnNote); } break; MATCH("/region&/trigger_on_cc", "") { m.reply(&Region::triggerOnCC); } break; MATCH("/region&/use_timer_range", "") { m.reply(&Region::useTimerRange); } break; MATCH("/region&/count", "") { m.reply(&Region::sampleCount); } break; + MATCH("/region&/count", "i") { m.set(&Region::sampleCount, Default::sampleCount); } break; + MATCH("/region&/count", "N") { m.set(&Region::sampleCount, Default::sampleCount); } break; MATCH("/region&/loop_range", "") { m.reply(&Region::loopRange); } break; + MATCH("/region&/loop_range", "hh") { m.set(&Region::loopRange, Default::loopStart, Default::loopEnd); } break; MATCH("/region&/loop_start_cc&", "") { m.reply(&Region::loopStartCC); } break; + MATCH("/region&/loop_start_cc&", "h") { m.set(&Region::loopStartCC, Default::loopStart); } break; MATCH("/region&/loop_end_cc&", "") { m.reply(&Region::loopEndCC); } break; + MATCH("/region&/loop_end_cc&", "h") { m.set(&Region::loopEndCC, Default::loopEnd); } break; MATCH("/region&/loop_mode", "") { m.reply(&Region::loopMode, LoopMode::no_loop); } break; + MATCH("/region&/loop_mode", "s") { m.set(&Region::loopMode, Default::loopMode); } break; MATCH("/region&/loop_crossfade", "") { m.reply(&Region::loopCrossfade); } break; + MATCH("/region&/loop_crossfade", "f") { m.set(&Region::loopCrossfade, Default::loopCrossfade); } break; MATCH("/region&/loop_count", "") { m.reply(&Region::loopCount); } break; + MATCH("/region&/loop_count", "i") { m.set(&Region::loopCount, Default::loopCount); } break; + MATCH("/region&/loop_count", "N") { m.set(&Region::loopCount, Default::loopCount); } break; MATCH("/region&/output", "") { m.reply(&Region::output); } break; + MATCH("/region&/output", "i") { m.set(&Region::output, Default::output); } break; MATCH("/region&/group", "") { m.reply(&Region::group); } break; + MATCH("/region&/group", "h") { m.set(&Region::group, Default::group); } break; MATCH("/region&/off_by", "") { m.reply(&Region::offBy); } break; + MATCH("/region&/off_by", "h") { m.set(&Region::offBy, Default::group); } break; + MATCH("/region&/off_by", "N") { m.set(&Region::offBy, Default::group); } break; MATCH("/region&/off_mode", "") { m.reply(&Region::offMode); } break; + MATCH("/region&/off_mode", "s") { m.set(&Region::offMode, Default::offMode); } break; MATCH("/region&/key_range", "") { m.reply(&Region::keyRange); } break; + MATCH("/region&/key_range", "ii") { m.set(&Region::keyRange, Default::loKey, Default::hiKey); } break; MATCH("/region&/off_time", "") { m.reply(&Region::offTime); } break; + MATCH("/region&/off_time", "f") { m.set(&Region::offTime, Default::offTime); } break; MATCH("/region&/pitch_keycenter", "") { m.reply(&Region::pitchKeycenter); } break; + MATCH("/region&/pitch_keycenter", "i") { m.set(&Region::pitchKeycenter, Default::key); } break; MATCH("/region&/vel_range", "") { m.reply(&Region::velocityRange); } break; + MATCH("/region&/vel_range", "ff") { m.set(&Region::velocityRange); } break; MATCH("/region&/bend_range", "") { m.reply(&Region::bendRange); } break; + MATCH("/region&/bend_range", "ff") { m.set(&Region::bendRange); } break; MATCH("/region&/program_range", "") { m.reply(&Region::programRange); } break; + MATCH("/region&/program_range", "ii") { m.set(&Region::programRange); } break; MATCH("/region&/cc_range&", "") { m.reply(&Region::ccConditions); } break; + MATCH("/region&/cc_range&", "ff") { m.set(&Region::ccConditions); } break; MATCH("/region&/sw_last", "") { if (auto region = m.getRegion()) { if (region->lastKeyswitch) m.reply(region->lastKeyswitch); else m.reply(region->lastKeyswitchRange); } } break; + MATCH("/region&/sw_last", "i") { + if (auto region = m.getRegion()) { + m.set(&Region::lastKeyswitch, Default::key); + region->lastKeyswitchRange.reset(); + } + } break; + MATCH("/region&/sw_last", "ii") { + if (auto region = m.getRegion()) { + region->lastKeyswitch.reset(); + region->lastKeyswitchRange.emplace(args[0].i, args[1].i); + } + } break; MATCH("/region&/sw_label", "") { m.reply(&Region::keyswitchLabel); } break; + MATCH("/region&/sw_label", "s") { m.set(&Region::keyswitchLabel); } break; MATCH("/region&/sw_up", "") { m.reply(&Region::upKeyswitch); } break; + MATCH("/region&/sw_up", "i") { m.set(&Region::upKeyswitch, Default::key); } break; + MATCH("/region&/sw_up", "s") { m.set(&Region::upKeyswitch, Default::key); } break; MATCH("/region&/sw_down", "") { m.reply(&Region::downKeyswitch); } break; + MATCH("/region&/sw_down", "i") { m.set(&Region::downKeyswitch, Default::key); } break; + MATCH("/region&/sw_down", "s") { m.set(&Region::downKeyswitch, Default::key); } break; MATCH("/region&/sw_previous", "") { m.reply(&Region::previousKeyswitch); } break; + MATCH("/region&/sw_previous", "i") { m.set(&Region::previousKeyswitch, Default::key); } break; + MATCH("/region&/sw_previous", "s") { m.set(&Region::previousKeyswitch, Default::key); } break; MATCH("/region&/sw_vel", "") { m.reply(&Region::velocityOverride); } break; + MATCH("/region&/sw_vel", "s") { m.set(&Region::velocityOverride, Default::velocityOverride); } break; MATCH("/region&/chanaft_range", "") { m.reply(&Region::aftertouchRange); } break; + MATCH("/region&/chanaft_range", "ff") { m.set(&Region::aftertouchRange); } break; MATCH("/region&/polyaft_range", "") { m.reply(&Region::polyAftertouchRange); } break; + MATCH("/region&/polyaft_range", "ff") { m.set(&Region::polyAftertouchRange); } break; MATCH("/region&/bpm_range", "") { m.reply(&Region::bpmRange); } break; + MATCH("/region&/bpm_range", "ff") { m.set(&Region::bpmRange, Default::loBPM, Default::hiBPM); } break; MATCH("/region&/rand_range", "") { m.reply(&Region::randRange); } break; + MATCH("/region&/rand_range", "ff") { m.set(&Region::randRange, Default::loNormalized, Default::hiNormalized); } break; MATCH("/region&/seq_length", "") { m.reply(&Region::sequenceLength); } break; + MATCH("/region&/seq_length", "i") { m.set(&Region::sequenceLength, Default::sequence); } break; MATCH("/region&/seq_position", "") { m.reply(&Region::sequencePosition); } break; + MATCH("/region&/seq_position", "i") { m.set(&Region::sequencePosition, Default::sequence); } break; MATCH("/region&/trigger", "") { m.reply(&Region::trigger); } break; + MATCH("/region&/trigger", "s") { m.set(&Region::trigger, Default::trigger); } break; MATCH("/region&/start_cc_range&", "") { m.reply(&Region::ccTriggers, false); } break; + MATCH("/region&/start_cc_range&", "ff") { m.set(&Region::ccTriggers); } break; MATCH("/region&/volume", "") { m.reply(&Region::volume); } break; + MATCH("/region&/volume", "f") { m.set(&Region::volume, Default::volume); } break; + // Probably need to rethink the way we set these in both the Region parsing and here before making changes MATCH("/region&/volume_cc&", "") { m.reply(ModId::Volume, ModParam::Depth); } break; MATCH("/region&/volume_stepcc&", "") { m.reply(ModId::Volume, ModParam::Step); } break; MATCH("/region&/volume_smoothcc&", "") { m.reply(ModId::Volume, ModParam::Smooth); } break; MATCH("/region&/volume_curvecc&", "") { m.reply(ModId::Volume, ModParam::Curve); } break; MATCH("/region&/pan", "") { m.reply(&Region::pan, Default::pan); } break; + MATCH("/region&/pan", "f") { m.set(&Region::pan, Default::pan); } break; MATCH("/region&/pan_cc&", "") { m.reply(ModId::Pan, ModParam::Depth, Default::pan); } break; MATCH("/region&/pan_stepcc&", "") { m.reply(ModId::Pan, ModParam::Step, Default::pan); } break; MATCH("/region&/pan_smoothcc&", "") { m.reply(ModId::Pan, ModParam::Smooth, Default::pan); } break; MATCH("/region&/pan_curvecc&", "") { m.reply(ModId::Pan, ModParam::Curve, Default::pan); } break; MATCH("/region&/width", "") { m.reply(&Region::width, Default::width); } break; + MATCH("/region&/width", "f") { m.set(&Region::width, Default::width); } break; MATCH("/region&/width_cc&", "") { m.reply(ModId::Width, ModParam::Depth, Default::width); } break; MATCH("/region&/width_stepcc&", "") { m.reply(ModId::Width, ModParam::Step, Default::width); } break; MATCH("/region&/width_smoothcc&", "") { m.reply(ModId::Width, ModParam::Smooth, Default::width); } break; MATCH("/region&/width_curvecc&", "") { m.reply(ModId::Width, ModParam::Curve, Default::width); } break; MATCH("/region&/timer_range", "") { m.reply(&Region::timerRange); } break; MATCH("/region&/position", "") { m.reply(&Region::position, Default::position); } break; + MATCH("/region&/position", "f") { m.set(&Region::position, Default::position); } break; MATCH("/region&/position_cc&", "") { m.reply(ModId::Position, ModParam::Depth, Default::position); } break; MATCH("/region&/position_stepcc&", "") { m.reply(ModId::Position, ModParam::Step, Default::position); } break; MATCH("/region&/position_smoothcc&", "") { m.reply(ModId::Position, ModParam::Smooth, Default::position); } break; MATCH("/region&/position_curvecc&", "") { m.reply(ModId::Position, ModParam::Curve, Default::position); } break; MATCH("/region&/amplitude", "") { m.reply(&Region::amplitude, Default::amplitude); } break; + MATCH("/region&/amplitude", "f") { m.set(&Region::amplitude, Default::amplitude); } break; MATCH("/region&/amplitude_cc&", "") { m.reply(ModId::Amplitude, ModParam::Depth, Default::amplitude); } break; MATCH("/region&/amplitude_stepcc&", "") { m.reply(ModId::Amplitude, ModParam::Step, Default::amplitude); } break; MATCH("/region&/amplitude_smoothcc&", "") { m.reply(ModId::Amplitude, ModParam::Smooth, Default::amplitude); } break; MATCH("/region&/amplitude_curvecc&", "") { m.reply(ModId::Amplitude, ModParam::Curve, Default::amplitude); } break; MATCH("/region&/amp_keycenter", "") { m.reply(&Region::ampKeycenter); } break; + MATCH("/region&/amp_keycenter", "i") { m.set(&Region::ampKeycenter, Default::key); } break; MATCH("/region&/amp_keytrack", "") { m.reply(&Region::ampKeytrack); } break; + MATCH("/region&/amp_keytrack", "f") { m.set(&Region::ampKeytrack, Default::ampKeytrack); } break; MATCH("/region&/amp_veltrack", "") { m.reply(&Region::ampVeltrack, Default::ampVeltrack); } break; + MATCH("/region&/amp_veltrack", "f") { m.set(&Region::ampVeltrack, Default::ampVeltrack); } break; MATCH("/region&/amp_veltrack_cc&", "") { m.reply(&Region::ampVeltrackCC, false, ModParam::Depth, Default::ampVeltrackMod); } break; + MATCH("/region&/amp_veltrack_cc&", "f") { m.set(&Region::ampVeltrackCC, ModParam::Depth, Default::ampVeltrackMod); } break; MATCH("/region&/amp_veltrack_curvecc&", "") { m.reply(&Region::ampVeltrackCC, false, ModParam::Curve, Default::ampVeltrackMod); } break; + MATCH("/region&/amp_veltrack_curvecc&", "i") { m.set(&Region::ampVeltrackCC, ModParam::Curve, Default::ampVeltrackMod); } break; MATCH("/region&/amp_random", "") { m.reply(&Region::ampRandom); } break; + MATCH("/region&/amp_random", "f") { m.set(&Region::ampRandom, Default::ampRandom); } break; MATCH("/region&/xfin_key_range", "") { m.reply(&Region::crossfadeKeyInRange); } break; + MATCH("/region&/xfin_key_range", "ii") { m.set(&Region::crossfadeKeyInRange, Default::loKey, Default::hiKey); } break; MATCH("/region&/xfout_key_range", "") { m.reply(&Region::crossfadeKeyOutRange); } break; + MATCH("/region&/xfout_key_range", "ii") { m.set(&Region::crossfadeKeyOutRange, Default::loKey, Default::hiKey); } break; MATCH("/region&/xfin_vel_range", "") { m.reply(&Region::crossfadeVelInRange); } break; + MATCH("/region&/xfin_vel_range", "ff") { m.set(&Region::crossfadeVelInRange); } break; MATCH("/region&/xfout_vel_range", "") { m.reply(&Region::crossfadeVelOutRange); } break; + MATCH("/region&/xfout_vel_range", "ff") { m.set(&Region::crossfadeVelOutRange); } break; MATCH("/region&/xfin_cc_range&", "") { m.reply(&Region::crossfadeCCInRange, false); } break; + MATCH("/region&/xfin_cc_range&", "ff") { m.set(&Region::crossfadeCCInRange); } break; MATCH("/region&/xfout_cc_range&", "") { m.reply(&Region::crossfadeCCOutRange, false); } break; + MATCH("/region&/xfout_cc_range&", "ff") { m.set(&Region::crossfadeCCOutRange); } break; MATCH("/region&/xf_keycurve", "") { m.reply(&Region::crossfadeKeyCurve); } break; + MATCH("/region&/xf_keycurve", "s") { m.set(&Region::crossfadeKeyCurve, Default::crossfadeCurve); } break; MATCH("/region&/xf_velcurve", "") { m.reply(&Region::crossfadeVelCurve); } break; + MATCH("/region&/xf_velcurve", "s") { m.set(&Region::crossfadeVelCurve, Default::crossfadeCurve); } break; MATCH("/region&/xf_cccurve", "") { m.reply(&Region::crossfadeCCCurve); } break; + MATCH("/region&/xf_cccurve", "s") { m.set(&Region::crossfadeCCCurve, Default::crossfadeCurve); } break; MATCH("/region&/global_volume", "") { m.reply(&Region::globalVolume); } break; + MATCH("/region&/global_volume", "f") { m.set(&Region::globalVolume, Default::volume); } break; MATCH("/region&/master_volume", "") { m.reply(&Region::masterVolume); } break; + MATCH("/region&/master_volume", "f") { m.set(&Region::masterVolume, Default::volume); } break; MATCH("/region&/group_volume", "") { m.reply(&Region::groupVolume); } break; + MATCH("/region&/group_volume", "f") { m.set(&Region::groupVolume, Default::volume); } break; MATCH("/region&/global_amplitude", "") { m.reply(&Region::globalAmplitude, Default::amplitude); } break; + MATCH("/region&/global_amplitude", "f") { m.set(&Region::globalAmplitude, Default::amplitude); } break; MATCH("/region&/master_amplitude", "") { m.reply(&Region::masterAmplitude, Default::amplitude); } break; + MATCH("/region&/master_amplitude", "f") { m.set(&Region::masterAmplitude, Default::amplitude); } break; MATCH("/region&/group_amplitude", "") { m.reply(&Region::groupAmplitude, Default::amplitude); } break; + MATCH("/region&/group_amplitude", "f") { m.set(&Region::groupAmplitude, Default::amplitude); } break; MATCH("/region&/pitch_keytrack", "") { m.reply(&Region::pitchKeytrack); } break; + MATCH("/region&/pitch_keytrack", "f") { m.set(&Region::pitchKeytrack, Default::pitchKeytrack); } break; MATCH("/region&/pitch_veltrack", "") { m.reply(&Region::pitchVeltrack); } break; + MATCH("/region&/pitch_veltrack", "f") { m.set(&Region::pitchVeltrack, Default::pitchVeltrack); } break; MATCH("/region&/pitch_veltrack_cc&", "") { m.reply(&Region::pitchVeltrackCC, false, ModParam::Depth); } break; + MATCH("/region&/pitch_veltrack_cc&", "f") { m.set(&Region::pitchVeltrackCC, ModParam::Depth, Default::pitchVeltrackMod); } break; MATCH("/region&/pitch_veltrack_curvecc&", "") { m.reply(&Region::pitchVeltrackCC, false, ModParam::Curve); } break; + MATCH("/region&/pitch_veltrack_curvecc&", "i") { m.set(&Region::pitchVeltrackCC, ModParam::Curve, Default::pitchVeltrackMod); } break; MATCH("/region&/pitch_random", "") { m.reply(&Region::pitchRandom); } break; + MATCH("/region&/pitch_random", "f") { m.set(&Region::pitchRandom, Default::pitchRandom); } break; MATCH("/region&/transpose", "") { m.reply(&Region::transpose); } break; + MATCH("/region&/transpose", "f") { m.set(&Region::transpose, Default::transpose); } break; MATCH("/region&/pitch", "") { m.reply(&Region::pitch); } break; + MATCH("/region&/pitch", "f") { m.set(&Region::pitch, Default::pitch); } break; MATCH("/region&/pitch_cc&", "") { m.reply(ModId::Pitch, ModParam::Depth, Default::pitch); } break; MATCH("/region&/pitch_stepcc&", "") { m.reply(ModId::Pitch, ModParam::Step, Default::pitch); } break; MATCH("/region&/pitch_smoothcc&", "") { m.reply(ModId::Pitch, ModParam::Smooth, Default::pitch); } break; MATCH("/region&/pitch_curvecc&", "") { m.reply(ModId::Pitch, ModParam::Curve, Default::pitch); } break; MATCH("/region&/bend_up", "") { m.reply(&Region::bendUp); } break; + MATCH("/region&/bend_up", "f") { m.set(&Region::bendUp, Default::bendUp); } break; MATCH("/region&/bend_down", "") { m.reply(&Region::bendDown); } break; + MATCH("/region&/bend_down", "f") { m.set(&Region::bendDown, Default::bendDown); } break; MATCH("/region&/bend_step", "") { m.reply(&Region::bendStep); } break; + MATCH("/region&/bend_step", "f") { m.set(&Region::bendStep, Default::bendStep); } break; MATCH("/region&/bend_smooth", "") { m.reply(&Region::bendSmooth); } break; + MATCH("/region&/bend_smooth", "i") { m.set(&Region::bendSmooth, Default::smoothCC); } break; MATCH("/region&/ampeg_attack", "") { m.reply(&Region::amplitudeEG, &EGDescription::attack); } break; MATCH("/region&/ampeg_delay", "") { m.reply(&Region::amplitudeEG, &EGDescription::delay); } break; MATCH("/region&/ampeg_decay", "") { m.reply(&Region::amplitudeEG, &EGDescription::decay); } break; MATCH("/region&/ampeg_hold", "") { m.reply(&Region::amplitudeEG, &EGDescription::hold); } break; MATCH("/region&/ampeg_release", "") { m.reply(&Region::amplitudeEG, &EGDescription::release); } break; - MATCH("/region&/ampeg_start", "") { m.reply(&Region::amplitudeEG, &EGDescription::start, Default::egPercentMod); } break; - MATCH("/region&/ampeg_sustain", "") { m.reply(&Region::amplitudeEG, &EGDescription::sustain, Default::egPercentMod); } break; + MATCH("/region&/ampeg_start", "") { m.reply(&Region::amplitudeEG, &EGDescription::start, Default::egPercent); } break; + MATCH("/region&/ampeg_sustain", "") { m.reply(&Region::amplitudeEG, &EGDescription::sustain, Default::egPercent); } break; MATCH("/region&/ampeg_depth", "") { m.reply(&Region::amplitudeEG, &EGDescription::depth); } break; MATCH("/region&/ampeg_attack_cc&", "") { m.reply(&Region::amplitudeEG, &EGDescription::ccAttack, ModParam::Depth); } break; MATCH("/region&/ampeg_attack_curvecc&", "") { m.reply(&Region::amplitudeEG, &EGDescription::ccAttack, ModParam::Curve); } break; @@ -736,6 +305,38 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/region&/ampeg_vel&sustain", "") { m.reply(&Region::amplitudeEG, &EGDescription::vel2sustain, Default::egPercentMod); } break; MATCH("/region&/ampeg_vel&depth", "") { m.reply(&Region::amplitudeEG, &EGDescription::vel2depth); } break; MATCH("/region&/ampeg_dynamic", "") { m.reply(&Region::amplitudeEG, &EGDescription::dynamic); } break; + MATCH("/region&/ampeg_attack", "f") { m.set(&Region::amplitudeEG, &EGDescription::attack, Default::egTime); } break; + MATCH("/region&/ampeg_delay", "f") { m.set(&Region::amplitudeEG, &EGDescription::delay, Default::egTime); } break; + MATCH("/region&/ampeg_decay", "f") { m.set(&Region::amplitudeEG, &EGDescription::decay, Default::egTime); } break; + MATCH("/region&/ampeg_hold", "f") { m.set(&Region::amplitudeEG, &EGDescription::hold, Default::egTime); } break; + MATCH("/region&/ampeg_release", "f") { m.set(&Region::amplitudeEG, &EGDescription::release, Default::egTime); } break; + MATCH("/region&/ampeg_start", "f") { m.set(&Region::amplitudeEG, &EGDescription::start, Default::egPercent); } break; + MATCH("/region&/ampeg_sustain", "f") { m.set(&Region::amplitudeEG, &EGDescription::sustain, Default::egPercent); } break; + MATCH("/region&/ampeg_depth", "f") { m.set(&Region::amplitudeEG, &EGDescription::depth, Default::egDepth); } break; + MATCH("/region&/ampeg_attack_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccAttack, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/ampeg_attack_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccAttack, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/ampeg_decay_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccDecay, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/ampeg_decay_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccDecay, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/ampeg_delay_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccDelay, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/ampeg_delay_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccDelay, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/ampeg_hold_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccHold, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/ampeg_hold_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccHold, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/ampeg_release_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccRelease, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/ampeg_release_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccRelease, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/ampeg_sustain_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccSustain, ModParam::Depth, Default::egPercentMod); } break; + MATCH("/region&/ampeg_sustain_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccSustain, ModParam::Curve, Default::egPercentMod); } break; + MATCH("/region&/ampeg_start_cc&", "f") { m.set(&Region::amplitudeEG, &EGDescription::ccStart, ModParam::Depth, Default::egPercentMod); } break; + MATCH("/region&/ampeg_start_curvecc&", "i") { m.set(&Region::amplitudeEG, &EGDescription::ccStart, ModParam::Curve, Default::egPercentMod); } break; + MATCH("/region&/ampeg_vel&attack", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2attack, Default::egTimeMod); } break; + MATCH("/region&/ampeg_vel&delay", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2delay, Default::egTimeMod); } break; + MATCH("/region&/ampeg_vel&decay", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2decay, Default::egTimeMod); } break; + MATCH("/region&/ampeg_vel&hold", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2hold, Default::egTimeMod); } break; + MATCH("/region&/ampeg_vel&release", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2release, Default::egTimeMod); } break; + MATCH("/region&/ampeg_vel&sustain", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2sustain, Default::egPercentMod); } break; + MATCH("/region&/ampeg_vel&depth", "f") { m.set(&Region::amplitudeEG, &EGDescription::vel2depth, Default::egDepth); } break; + MATCH("/region&/ampeg_dynamic", "T") { m.set(&Region::amplitudeEG, &EGDescription::dynamic, Default::egDynamic); } break; + MATCH("/region&/ampeg_dynamic", "F") { m.set(&Region::amplitudeEG, &EGDescription::dynamic, Default::egDynamic); } break; + MATCH("/region&/ampeg_dynamic", "s") { m.set(&Region::amplitudeEG, &EGDescription::dynamic, Default::egDynamic); } break; MATCH("/region&/fileg_attack", "") { m.reply(&Region::filterEG, &EGDescription::attack); } break; MATCH("/region&/fileg_delay", "") { m.reply(&Region::filterEG, &EGDescription::delay); } break; MATCH("/region&/fileg_decay", "") { m.reply(&Region::filterEG, &EGDescription::decay); } break; @@ -759,6 +360,31 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/region&/fileg_start_cc&", "") { m.reply(&Region::filterEG, &EGDescription::ccStart, ModParam::Depth, Default::egPercentMod); } break; MATCH("/region&/fileg_start_curvecc&", "") { m.reply(&Region::filterEG, &EGDescription::ccStart, ModParam::Curve, Default::egPercentMod); } break; MATCH("/region&/fileg_dynamic", "") { m.reply(&Region::filterEG, &EGDescription::dynamic); } break; + MATCH("/region&/fileg_attack", "f") { m.set(&Region::filterEG, &EGDescription::attack, Default::egTime); } break; + MATCH("/region&/fileg_delay", "f") { m.set(&Region::filterEG, &EGDescription::delay, Default::egTime); } break; + MATCH("/region&/fileg_decay", "f") { m.set(&Region::filterEG, &EGDescription::decay, Default::egTime); } break; + MATCH("/region&/fileg_hold", "f") { m.set(&Region::filterEG, &EGDescription::hold, Default::egTime); } break; + MATCH("/region&/fileg_release", "f") { m.set(&Region::filterEG, &EGDescription::release, Default::egTime); } break; + MATCH("/region&/fileg_start", "f") { m.set(&Region::filterEG, &EGDescription::start, Default::egPercent); } break; + MATCH("/region&/fileg_sustain", "f") { m.set(&Region::filterEG, &EGDescription::sustain, Default::egPercent); } break; + MATCH("/region&/fileg_depth", "f") { m.set(&Region::filterEG, &EGDescription::depth, Default::egDepth); } break; + MATCH("/region&/fileg_attack_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccAttack, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/fileg_attack_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccAttack, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/fileg_decay_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccDecay, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/fileg_decay_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccDecay, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/fileg_delay_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccDelay, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/fileg_delay_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccDelay, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/fileg_hold_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccHold, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/fileg_hold_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccHold, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/fileg_release_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccRelease, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/fileg_release_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccRelease, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/fileg_sustain_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccSustain, ModParam::Depth, Default::egPercentMod); } break; + MATCH("/region&/fileg_sustain_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccSustain, ModParam::Curve, Default::egPercentMod); } break; + MATCH("/region&/fileg_start_cc&", "f") { m.set(&Region::filterEG, &EGDescription::ccStart, ModParam::Depth, Default::egPercentMod); } break; + MATCH("/region&/fileg_start_curvecc&", "i") { m.set(&Region::filterEG, &EGDescription::ccStart, ModParam::Curve, Default::egPercentMod); } break; + MATCH("/region&/fileg_dynamic", "T") { m.set(&Region::filterEG, &EGDescription::dynamic, Default::egDynamic); } break; + MATCH("/region&/fileg_dynamic", "F") { m.set(&Region::filterEG, &EGDescription::dynamic, Default::egDynamic); } break; + MATCH("/region&/fileg_dynamic", "s") { m.set(&Region::filterEG, &EGDescription::dynamic, Default::egDynamic); } break; MATCH("/region&/pitcheg_attack", "") { m.reply(&Region::pitchEG, &EGDescription::attack); } break; MATCH("/region&/pitcheg_delay", "") { m.reply(&Region::pitchEG, &EGDescription::delay); } break; MATCH("/region&/pitcheg_decay", "") { m.reply(&Region::pitchEG, &EGDescription::decay); } break; @@ -782,21 +408,67 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/region&/pitcheg_start_cc&", "") { m.reply(&Region::pitchEG, &EGDescription::ccStart, ModParam::Depth, Default::egPercentMod); } break; MATCH("/region&/pitcheg_start_curvecc&", "") { m.reply(&Region::pitchEG, &EGDescription::ccStart, ModParam::Curve, Default::egPercentMod); } break; MATCH("/region&/pitcheg_dynamic", "") { m.reply(&Region::pitchEG, &EGDescription::dynamic); } break; + MATCH("/region&/pitcheg_attack", "f") { m.set(&Region::pitchEG, &EGDescription::attack, Default::egTime); } break; + MATCH("/region&/pitcheg_delay", "f") { m.set(&Region::pitchEG, &EGDescription::delay, Default::egTime); } break; + MATCH("/region&/pitcheg_decay", "f") { m.set(&Region::pitchEG, &EGDescription::decay, Default::egTime); } break; + MATCH("/region&/pitcheg_hold", "f") { m.set(&Region::pitchEG, &EGDescription::hold, Default::egTime); } break; + MATCH("/region&/pitcheg_release", "f") { m.set(&Region::pitchEG, &EGDescription::release, Default::egTime); } break; + MATCH("/region&/pitcheg_start", "f") { m.set(&Region::pitchEG, &EGDescription::start, Default::egPercent); } break; + MATCH("/region&/pitcheg_sustain", "f") { m.set(&Region::pitchEG, &EGDescription::sustain, Default::egPercent); } break; + MATCH("/region&/pitcheg_depth", "f") { m.set(&Region::pitchEG, &EGDescription::depth, Default::egDepth); } break; + MATCH("/region&/pitcheg_attack_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccAttack, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_attack_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccAttack, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_decay_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccDecay, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_decay_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccDecay, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_delay_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccDelay, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_delay_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccDelay, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_hold_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccHold, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_hold_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccHold, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_release_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccRelease, ModParam::Depth, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_release_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccRelease, ModParam::Curve, Default::egTimeMod); } break; + MATCH("/region&/pitcheg_sustain_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccSustain, ModParam::Depth, Default::egPercentMod); } break; + MATCH("/region&/pitcheg_sustain_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccSustain, ModParam::Curve, Default::egPercentMod); } break; + MATCH("/region&/pitcheg_start_cc&", "f") { m.set(&Region::pitchEG, &EGDescription::ccStart, ModParam::Depth, Default::egPercentMod); } break; + MATCH("/region&/pitcheg_start_curvecc&", "i") { m.set(&Region::pitchEG, &EGDescription::ccStart, ModParam::Curve, Default::egPercentMod); } break; + MATCH("/region&/pitcheg_dynamic", "T") { if (auto region = m.getRegion()) { if (region->pitchEG) { region->pitchEG->dynamic = true;} } } break; + MATCH("/region&/pitcheg_dynamic", "F") { if (auto region = m.getRegion()) { if (region->pitchEG) { region->pitchEG->dynamic = false;} } } break; + MATCH("/region&/pitcheg_dynamic", "s") { m.set(&Region::pitchEG, &EGDescription::dynamic, Default::egDynamic); } break; MATCH("/region&/note_polyphony", "") { m.reply(&Region::notePolyphony); } break; + MATCH("/region&/note_polyphony", "i") { m.set(&Region::notePolyphony, Default::notePolyphony); } break; MATCH("/region&/rt_dead", "") { m.reply(&Region::rtDead); } break; + MATCH("/region&/rt_dead", "s") { m.set(&Region::rtDead, Default::rtDead); } break; + MATCH("/region&/rt_dead", "T") { m.set(&Region::rtDead, Default::rtDead); } break; + MATCH("/region&/rt_dead", "F") { m.set(&Region::rtDead, Default::rtDead); } break; MATCH("/region&/sustain_sw", "") { m.reply(&Region::checkSustain); } break; + MATCH("/region&/sustain_sw", "s") { m.set(&Region::checkSustain, Default::checkSustain); } break; + MATCH("/region&/sustain_sw", "T") { m.set(&Region::checkSustain, Default::checkSustain); } break; + MATCH("/region&/sustain_sw", "F") { m.set(&Region::checkSustain, Default::checkSustain); } break; MATCH("/region&/sostenuto_sw", "") { m.reply(&Region::checkSostenuto); } break; + MATCH("/region&/sostenuto_sw", "s") { m.set(&Region::checkSostenuto, Default::checkSostenuto); } break; + MATCH("/region&/sostenuto_sw", "T") { m.set(&Region::checkSostenuto, Default::checkSostenuto); } break; + MATCH("/region&/sostenuto_sw", "F") { m.set(&Region::checkSostenuto, Default::checkSostenuto); } break; MATCH("/region&/sustain_cc", "") { m.reply(&Region::sustainCC); } break; + MATCH("/region&/sustain_cc", "i") { m.set(&Region::sustainCC, Default::sustainCC); } break; MATCH("/region&/sostenuto_cc", "") { m.reply(&Region::sostenutoCC); } break; + MATCH("/region&/sostenuto_cc", "i") { m.set(&Region::sostenutoCC, Default::sostenutoCC); } break; MATCH("/region&/sustain_lo", "") { m.reply(&Region::sustainThreshold); } break; + MATCH("/region&/sustain_lo", "f") { m.set(&Region::sustainThreshold); } break; MATCH("/region&/sostenuto_lo", "") { m.reply(&Region::sostenutoThreshold); } break; + MATCH("/region&/sostenuto_lo", "f") { m.set(&Region::sostenutoThreshold); } break; MATCH("/region&/note_selfmask", "") { m.reply(&Region::selfMask); } break; + MATCH("/region&/note_selfmask", "s") { m.set(&Region::selfMask, Default::selfMask); } break; MATCH("/region&/oscillator_phase", "") { m.reply(&Region::oscillatorPhase); } break; + MATCH("/region&/oscillator_phase", "f") { m.set(&Region::oscillatorPhase, Default::oscillatorPhase); } break; MATCH("/region&/oscillator_quality", "") { m.reply(&Region::oscillatorQuality); } break; + MATCH("/region&/oscillator_quality", "i") { m.set(&Region::oscillatorQuality, Default::oscillatorQuality); } break; MATCH("/region&/oscillator_mode", "") { m.reply(&Region::oscillatorMode); } break; + MATCH("/region&/oscillator_mode", "i") { m.set(&Region::oscillatorMode, Default::oscillatorMode); } break; MATCH("/region&/oscillator_multi", "") { m.reply(&Region::oscillatorMulti); } break; + MATCH("/region&/oscillator_multi", "i") { m.set(&Region::oscillatorMulti, Default::oscillatorMulti); } break; MATCH("/region&/oscillator_detune", "") { m.reply(&Region::oscillatorDetune); } break; + MATCH("/region&/oscillator_detune", "f") { m.set(&Region::oscillatorDetune, Default::oscillatorDetune); } break; MATCH("/region&/oscillator_mod_depth", "") { m.reply(&Region::oscillatorModDepth, Default::oscillatorModDepth); } break; + MATCH("/region&/oscillator_mod_depth", "f") { m.set(&Region::oscillatorModDepth, Default::oscillatorModDepth); } break; // TODO: detune cc, mod depth cc MATCH("/region&/effect&", "") { @@ -805,102 +477,104 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co if (effectIdx > 0 && effectIdx < ssize(region->gainToEffect)) m.reply(region->gainToEffect[*effectIdx], Default::effect); } break; + MATCH("/region&/effect&", "f") { + if (auto region = m.getRegion()) + if (auto effectIdx = m.sindex(1)) + if (effectIdx > 0 && effectIdx < ssize(region->gainToEffect)) + m.set(region->gainToEffect[*effectIdx], Default::effect); + } break; + MATCH("/region&/add_filter", "") { + if (auto region = m.getRegion()) { + region->filters.emplace_back(); + int32_t index = region->filters.size() - 1; + impl.settingsPerVoice_.maxFilters = max(impl.settingsPerVoice_.maxFilters, region->flexEGs.size()); + impl.applySettingsPerVoice(); + m.reply(index); + } + } break; MATCH("/region&/filter&/cutoff", "") { m.reply(&FilterDescription::cutoff); } break; + MATCH("/region&/filter&/cutoff", "f") { m.set(&FilterDescription::cutoff, Default::filterCutoff); } break; MATCH("/region&/filter&/cutoff_cc&", "") { m.reply(ModId::FilCutoff, ModParam::Depth); } break; MATCH("/region&/filter&/cutoff_curvecc&", "") { m.reply(ModId::FilCutoff, ModParam::Curve); } break; MATCH("/region&/filter&/cutoff_stepcc&", "") { m.reply(ModId::FilCutoff, ModParam::Step); } break; MATCH("/region&/filter&/cutoff_smoothcc&", "") { m.reply(ModId::FilCutoff, ModParam::Smooth); } break; MATCH("/region&/filter&/resonance", "") { m.reply(&FilterDescription::resonance); } break; + MATCH("/region&/filter&/resonance", "f") { m.set(&FilterDescription::resonance, Default::filterResonance); } break; MATCH("/region&/filter&/gain", "") { m.reply(&FilterDescription::gain); } break; + MATCH("/region&/filter&/gain", "f") { m.set(&FilterDescription::gain, Default::filterGain); } break; MATCH("/region&/filter&/keycenter", "") { m.reply(&FilterDescription::keycenter); } break; + MATCH("/region&/filter&/keycenter", "i") { m.set(&FilterDescription::keycenter, Default::key); } break; MATCH("/region&/filter&/keytrack", "") { m.reply(&FilterDescription::keytrack); } break; + MATCH("/region&/filter&/keytrack", "f") { m.set(&FilterDescription::keytrack, Default::filterKeytrack); } break; MATCH("/region&/filter&/veltrack", "") { m.reply(&FilterDescription::veltrack); } break; + MATCH("/region&/filter&/veltrack", "f") { m.set(&FilterDescription::veltrack, Default::filterVeltrack); } break; MATCH("/region&/filter&/veltrack_cc&", "") { m.reply(&FilterDescription::veltrackCC, ModParam::Depth); } break; + MATCH("/region&/filter&/veltrack_cc&", "f") { m.set(&FilterDescription::veltrackCC, ModParam::Depth, Default::filterVeltrackMod); } break; MATCH("/region&/filter&/veltrack_curvecc&", "") { m.reply(&FilterDescription::veltrackCC, ModParam::Curve); } break; + MATCH("/region&/filter&/veltrack_curvecc&", "i") { m.set(&FilterDescription::veltrackCC, ModParam::Curve, Default::filterVeltrackMod); } break; MATCH("/region&/filter&/type", "") { m.reply(&FilterDescription::type); } break; + MATCH("/region&/filter&/type", "s") { m.set(&FilterDescription::type, Default::filter); } break; //---------------------------------------------------------------------- + MATCH("/region&/add_eq", "") { + if (auto region = m.getRegion()) { + region->equalizers.emplace_back(); + int32_t index = region->equalizers.size() - 1; + impl.settingsPerVoice_.maxEQs = max(impl.settingsPerVoice_.maxEQs, region->flexEGs.size()); + impl.applySettingsPerVoice(); + m.reply(index); + } + } break; MATCH("/region&/eq&/gain", "") { m.reply(&EQDescription::gain); } break; + MATCH("/region&/eq&/gain", "f") { m.set(&EQDescription::gain, Default::eqGain); } break; MATCH("/region&/eq&/bandwidth", "") { m.reply(&EQDescription::bandwidth); } break; + MATCH("/region&/eq&/bandwidth", "f") { m.set(&EQDescription::bandwidth, Default::eqBandwidth); } break; MATCH("/region&/eq&/frequency", "") { m.reply(&EQDescription::frequency); } break; + MATCH("/region&/eq&/frequency", "f") { m.set(&EQDescription::frequency, Default::eqFrequency); } break; MATCH("/region&/eq&/vel&freq", "") { m.reply(&EQDescription::vel2frequency); } break; + MATCH("/region&/eq&/vel&freq", "f") { m.set(&EQDescription::vel2frequency, Default::eqVel2Frequency); } break; MATCH("/region&/eq&/vel&gain", "") { m.reply(&EQDescription::vel2gain); } break; + MATCH("/region&/eq&/vel&gain", "f") { m.set(&EQDescription::vel2gain, Default::eqVel2Gain); } break; MATCH("/region&/eq&/type", "") { m.reply(&EQDescription::type); } break; + MATCH("/region&/eq&/type", "s") { m.set(&EQDescription::type, Default::eq); } break; //---------------------------------------------------------------------- MATCH("/region&/lfo&/wave", "") { m.reply(&LFODescription::Sub::wave); } break; + MATCH("/region&/lfo&/wave", "i") { m.set(&LFODescription::Sub::wave, Default::lfoWave); } break; + MATCH("/region&/lfo&/wave&", "") { m.reply(&LFODescription::Sub::wave); } break; + MATCH("/region&/lfo&/wave&", "i") { m.set(&LFODescription::Sub::wave, Default::lfoWave); } break; //---------------------------------------------------------------------- + MATCH("/region&/add_eg", "") { + if (auto region = m.getRegion()) { + region->flexEGs.emplace_back(); + region->flexEGs.back().points.emplace_back(); + int32_t index = region->flexEGs.size() - 1; + impl.settingsPerVoice_.maxFlexEGs = max(impl.settingsPerVoice_.maxFlexEGs, region->flexEGs.size()); + impl.applySettingsPerVoice(); + m.reply(index); + } + } break; + MATCH("/region&/eg&/add_point", "") { + if (auto region = m.getRegion()) { + if (auto eg = m.getEG(*region)) { + eg->points.emplace_back(); + int32_t index = eg->points.size() - 2; + m.reply(index); + } + } + } break; MATCH("/region&/eg&/point&/time", "") { m.reply(&FlexEGPoint::time); } break; + MATCH("/region&/eg&/point&/time", "f") { m.set(&FlexEGPoint::time, Default::flexEGPointTime); } break; MATCH("/region&/eg&/point&/time_cc&", "") { m.reply(&FlexEGPoint::ccTime); } break; + MATCH("/region&/eg&/point&/time_cc&", "f") { m.set(&FlexEGPoint::ccTime, Default::flexEGPointTimeMod); } break; MATCH("/region&/eg&/point&/level", "") { m.reply(&FlexEGPoint::level); } break; + MATCH("/region&/eg&/point&/level", "f") { m.set(&FlexEGPoint::level, Default::flexEGPointLevel); } break; MATCH("/region&/eg&/point&/level_cc&", "") { m.reply(&FlexEGPoint::ccLevel); } break; + MATCH("/region&/eg&/point&/level_cc&", "f") { m.set(&FlexEGPoint::ccLevel, Default::flexEGPointLevelMod); } break; //---------------------------------------------------------------------- MATCH("/voice&/trigger_value", "") { m.reply(&TriggerEvent::value); } break; MATCH("/voice&/trigger_number", "") { m.reply(&TriggerEvent::number); } break; MATCH("/voice&/trigger_type", "") { m.reply(&TriggerEvent::type); } break; MATCH("/voice&/remaining_delay", "") { m.reply(&Voice::getRemainingDelay); } break; MATCH("/voice&/source_position", "") { m.reply(&Voice::getSourcePosition); } break; - //---------------------------------------------------------------------- - - // Setting values - // Note: all these must be rt-safe within the parseOpcode method in region - - MATCH("/sample_quality", "i") { - impl.resources_.getSynthConfig().liveSampleQuality = - Opcode::transform(Default::sampleQuality, static_cast(args[0].i)); - } break; - - MATCH("/oscillator_quality", "i") { - impl.resources_.getSynthConfig().liveOscillatorQuality = - Opcode::transform(Default::oscillatorQuality, static_cast(args[0].i)); - } break; - - MATCH("/freewheeling_sample_quality", "i") { - impl.resources_.getSynthConfig().freeWheelingSampleQuality = - Opcode::transform(Default::freewheelingSampleQuality, static_cast(args[0].i)); - } break; - - MATCH("/freewheeling_oscillator_quality", "i") { - impl.resources_.getSynthConfig().freeWheelingOscillatorQuality = - Opcode::transform(Default::freewheelingOscillatorQuality, static_cast(args[0].i)); - } break; - - MATCH("/sustain_cancels_release", "T") { - impl.resources_.getSynthConfig().sustainCancelsRelease = true; - } break; - - MATCH("/sustain_cancels_release", "F") { - impl.resources_.getSynthConfig().sustainCancelsRelease = false; - } break; - - MATCH("/region&/pitch_keycenter", "i") { - if (auto region = m.getRegion()) - region->pitchKeycenter = Opcode::transform(Default::key, args[0].i); - } break; - - MATCH("/region&/loop_mode", "s") { - if (auto region = m.getRegion()) - region->loopMode = Opcode::readOptional(Default::loopMode, args[0].s); - } break; - - MATCH("/region&/filter&/type", "s") { - if (auto region = m.getRegion()) - if (auto filter = m.getFilter(*region)) - filter->type = Opcode::read(Default::filter, args[0].s); - } break; - - MATCH("/region&/lfo&/wave", "i") { - if (auto region = m.getRegion()) - if (auto lfo = m.getLFO(*region)) - if (!lfo->sub.empty()) - lfo->sub[0].wave = Opcode::transform(Default::lfoWave, args[0].i); - } break; - - MATCH("/region&/lfo&/wave&", "i") { - if (auto region = m.getRegion()) - if (auto lfo = m.getLFO(*region)) - if (auto sub = m.getLFOSub(*lfo)) - sub->wave = Opcode::transform(Default::lfoWave, args[0].i); - } break; - #undef MATCH } } diff --git a/src/sfizz/SynthMessagingHelper.hpp b/src/sfizz/SynthMessagingHelper.hpp new file mode 100644 index 000000000..81916504c --- /dev/null +++ b/src/sfizz/SynthMessagingHelper.hpp @@ -0,0 +1,708 @@ +#pragma once + +#include "CCMap.h" +#include "Config.h" +#include "Defaults.h" +#include "EQDescription.h" +#include "FileId.h" +#include "FilterDescription.h" +#include "FlexEGDescription.h" +#include "LFOCommon.h" +#include "LFODescription.h" +#include "Opcode.h" +#include "Range.h" +#include "SfzFilter.h" +#include "SfzHelpers.h" +#include "SynthPrivate.h" +#include "FilePool.h" +#include "Curve.h" +#include "MidiState.h" +#include "SynthConfig.h" +#include "TriggerEvent.h" +#include "SynthPrivate.h" +#include "utility/Size.h" +#include +#include + +namespace inv = invoke_hpp; + +static uint64_t hashMessagePath(const char* path, const char* sig) +{ + uint64_t h = Fnv1aBasis; + while (unsigned char c = *path++) { + if (!absl::ascii_isdigit(c)) + h = hashByte(c, h); + else { + h = hashByte('&', h); + while (absl::ascii_isdigit(*path)) + ++path; + } + } + h = hashByte(',', h); + while (unsigned char c = *sig++) + h = hashByte(c, h); + return h; +} + +template +using IntegralNotBool = std::enable_if_t::value && !std::is_same::value>; + +template +using BoolOrNotIntegral = std::enable_if_t::value || std::is_same::value>; + +namespace sfz { + +static constexpr unsigned maxIndices = 8; + +class MessagingHelper { +public: + MessagingHelper(Client& client, int delay, const char* path, const char* sig, const sfizz_arg_t* args, Synth::Impl& impl) + : client(client) + , impl(impl) + , delay(delay) + , path(path) + , sig(sig) + , request_args(args) + { + indices.reserve(maxIndices); + } + + enum class ModParam { Depth, Curve, Smooth, Step }; + + bool match(const char* pattern, const char* sig) + { + indices.clear(); + const char* path_ = this->path; + + while (const char* endp = strchr(pattern, '&')) { + if (indices.size() == maxIndices) + return false; + + size_t length = endp - pattern; + if (strncmp(pattern, path_, length) != 0) + return false; + pattern += length; + path_ += length; + + length = 0; + while (absl::ascii_isdigit(path_[length])) + ++length; + + indices.push_back(0); + if (!absl::SimpleAtoi(absl::string_view(path_, length), &indices.back())) + return false; + + pattern += 1; + path_ += length; + } + + return !strcmp(path_, pattern) && !strcmp(this->sig, sig); + } + + // These are the reply/reply2 overloads for the (almost) concrete types, that should + // translate to actual calls to the client.receive(...) method. + + void reply(const std::string& value) { client.receive<'s'>(delay, path, value.data()); } + void reply(const char* value) { client.receive<'s'>(delay, path, value); } + // void reply(float value) { client.receive<'f'>(delay, path, value); } + void reply(const float& value) { client.receive<'f'>(delay, path, value); } + void reply(absl::nullopt_t) { client.receive<'N'>(delay, path, {}); } + + void reply(const bool& value) + { + if (value) + client.receive<'T'>(delay, path, {}); + else + client.receive<'F'>(delay, path, {}); + } + + template ::value>> + void reply(const T& value) + { + if (sizeof(value) <= 4) + client.receive<'i'>(delay, path, static_cast(value)); + else + client.receive<'h'>(delay, path, static_cast(value)); + } + template + void reply(const BitArray& array) + { + sfizz_blob_t blob { array.data(), static_cast(array.byte_size()) }; + client.receive<'b'>(delay, path, &blob); + } + + // Call reply but denormalizes the input if needed by the opcode spec + template + void reply(const T& value, const OpcodeSpec& spec) { reply(spec.denormalizeInput(value)); } + + template + BoolOrNotIntegral set(T& target, const OpcodeSpec& spec) { target = Opcode::read(spec, request_args[0].s); } + + // sfizz specific types + void reply(const LFOWave& wave) { reply(static_cast(wave)); } + void reply(const SelfMask& mode) { reply(mode == SelfMask::mask); } + void reply(const LoopMode& mode) + { + switch (mode) { + case LoopMode::no_loop: reply("no_loop"); break; + case LoopMode::loop_continuous: reply("loop_continuous"); break; + case LoopMode::loop_sustain: reply("loop_sustain"); break; + case LoopMode::one_shot: reply("one_shot"); break; + } + } + + void reply(const CrossfadeCurve& curve) + { + switch (curve) { + case CrossfadeCurve::gain: reply("gain"); break; + case CrossfadeCurve::power: reply("power"); break; + } + } + + void reply(const Trigger& mode) + { + switch (mode) { + case Trigger::attack: reply("attack"); break; + case Trigger::first: reply("first"); break; + case Trigger::legato: reply("legato"); break; + case Trigger::release: reply("release"); break; + case Trigger::release_key: reply("release_key"); break; + } + } + + void reply(const VelocityOverride& mode) + { + switch (mode) { + case VelocityOverride::current: reply("current"); break; + case VelocityOverride::previous: reply("previous"); break; + } + } + + void reply(const OffMode& mode) + { + switch (mode) { + case OffMode::fast: reply("fast"); break; + case OffMode::time: reply("time"); break; + case OffMode::normal: reply("normal"); break; + } + } + + void reply(const FilterType& type) + { + switch (type) { + case FilterType::kFilterLpf1p: reply("lpf_1p"); break; + case FilterType::kFilterHpf1p: reply("hpf_1p"); break; + case FilterType::kFilterLpf2p: reply("lpf_2p"); break; + case FilterType::kFilterHpf2p: reply("hpf_2p"); break; + case FilterType::kFilterBpf2p: reply("bpf_2p"); break; + case FilterType::kFilterBrf2p: reply("brf_2p"); break; + case FilterType::kFilterBpf1p: reply("bpf_1p"); break; + case FilterType::kFilterBrf1p: reply("brf_1p"); break; + case FilterType::kFilterApf1p: reply("apf_1p"); break; + case FilterType::kFilterLpf2pSv: reply("lpf_2p_sv"); break; + case FilterType::kFilterHpf2pSv: reply("hpf_2p_sv"); break; + case FilterType::kFilterBpf2pSv: reply("bpf_2p_sv"); break; + case FilterType::kFilterBrf2pSv: reply("brf_2p_sv"); break; + case FilterType::kFilterLpf4p: reply("lpf_4p"); break; + case FilterType::kFilterHpf4p: reply("hpf_4p"); break; + case FilterType::kFilterLpf6p: reply("lpf_6p"); break; + case FilterType::kFilterHpf6p: reply("hpf_6p"); break; + case FilterType::kFilterPink: reply("pink"); break; + case FilterType::kFilterLsh: reply("lsh"); break; + case FilterType::kFilterHsh: reply("hsh"); break; + case FilterType::kFilterPeq: reply("peq"); break; + case FilterType::kFilterBpf4p: reply("bpf_4p"); break; + case FilterType::kFilterBpf6p: reply("bpf_6p"); break; + case FilterType::kFilterNone: reply("none"); break; + } + } + + void reply(const EqType& type) + { + switch (type) { + case EqType::kEqNone: reply("none"); break; + case EqType::kEqPeak: reply("peak"); break; + case EqType::kEqLshelf: reply("lshelf"); break; + case EqType::kEqHshelf: reply("hshelf"); break; + } + } + + void reply(const TriggerEventType& type) + { + switch (type) { + case TriggerEventType::NoteOff: reply("note_off"); break; + case TriggerEventType::NoteOn: reply("note_on"); break; + case TriggerEventType::CC: reply("cc"); break; + } + } + + // reply2 for pairs (Usually only for float/int ranges) + template ::value>> + void reply2(T value1, T value2) + { + sfizz_arg_t response_args[2]; + if (sizeof(value1) <= 4) { + response_args[0].i = value1; + response_args[1].i = value2; + client.receive(delay, path, "ii", response_args); + } else { + response_args[0].h = value1; + response_args[1].h = value2; + client.receive(delay, path, "hh", response_args); + } + } + + void reply2(float value1, float value2) + { + sfizz_arg_t response_args[2]; + response_args[0].f = value1; + response_args[1].f = value2; + client.receive(delay, path, "ff", response_args); + } + + void set(std::string& target) { target = request_args[0].s; } + void set(float& target, const OpcodeSpec& spec) { target = Opcode::transform(spec, request_args[0].f); } + void set(float& target) { target = request_args[0].f; } + void set(LFOWave& target, const OpcodeSpec& spec) { target = Opcode::transform(spec, request_args[0].i); } + void set(bool& target, const OpcodeSpec& spec) + { + if (sig[0] == 'T') { + target = true; + } else if (sig[0] == 'F') { + target = false; + } else { + target = Opcode::read(spec, request_args[0].s); + } + } + + template + IntegralNotBool set(T& target, const OpcodeSpec& spec) + { + if (sizeof(target) <= 4) + target = Opcode::transform(spec, request_args[0].i); + else + target = Opcode::transform(spec, request_args[0].h); + } + + // Now we have some templated reply overloads that decay into "concrete" reply calls + // but add some logic (e.g. an optional can either reply the value, or send 'N' for + // null) + template + void reply(const absl::optional& value, Args... args) + { + if (!value) { + client.receive<'N'>(delay, path, {}); + return; + } + + reply(*value, std::forward(args)...); + } + + template + void reply(T* value, Args... args) + { + if (!value) { + client.receive<'N'>(delay, path, {}); + return; + } + + reply(*value, std::forward(args)...); + } + + template + void reply(const std::shared_ptr& value) { reply(value.get()); } + + template + void reply(const UncheckedRange& range) { reply2(range.getStart(), range.getEnd()); } + template + void reply(const UncheckedRange& range, const OpcodeSpec& startSpec, const OpcodeSpec& endSpec) + { + reply2(startSpec.denormalizeInput(range.getStart()), endSpec.denormalizeInput(range.getEnd())); + } + + template + IntegralNotBool set(UncheckedRange& range, const OpcodeSpec& startSpec, const OpcodeSpec& endSpec) + { + if (sizeof(range.getStart()) <= 4) { + range.setStart(Opcode::transform(startSpec, request_args[0].i)); + range.setEnd(Opcode::transform(endSpec, request_args[1].i)); + } else { + range.setStart(Opcode::transform(startSpec, request_args[0].h)); + range.setEnd(Opcode::transform(endSpec, request_args[1].h)); + } + } + + template + BoolOrNotIntegral set(UncheckedRange& range, const OpcodeSpec& startSpec, const OpcodeSpec& endSpec) + { + if (sizeof(range.getStart()) <= 4) { + range.setStart(Opcode::transform(startSpec, request_args[0].f)); + range.setEnd(Opcode::transform(endSpec, request_args[1].f)); + } else { + range.setStart(Opcode::transform(startSpec, request_args[0].d)); + range.setEnd(Opcode::transform(endSpec, request_args[1].d)); + } + } + + template + IntegralNotBool set(UncheckedRange& range) + { + if (sizeof(range.getStart()) <= 4) { + range.setStart(request_args[0].i); + range.setEnd(request_args[1].i); + } else { + range.setStart(request_args[0].h); + range.setEnd(request_args[1].h); + } + } + + template + BoolOrNotIntegral set(UncheckedRange& range) + { + if (sizeof(range.getStart()) <= 4) { + range.setStart(request_args[0].f); + range.setEnd(request_args[1].f); + } else { + range.setStart(request_args[0].d); + range.setEnd(request_args[1].d); + } + } + + template + void reply(const absl::optional& value, T&& def) { reply(value.value_or(def)); } + + template + void set(absl::optional& member, Args&&...args) + { + if (sig[0] == 'N') { + member.reset(); + } else if (member.has_value()) { + set(*member, std::forward(args)...); + } else { + set(member.emplace(), std::forward(args)...); + } + } + + template + void reply(const ModifierCurvePair& modCurve, ModParam which, Args&&... args) + { + switch (which) { + case ModParam::Curve: reply(modCurve.curve); break; + default: reply(modCurve.modifier, std::forward(args)...); break; + } + } + + template + void set(ModifierCurvePair& modCurve, ModParam which, Args&&... args) + { + switch (which) { + case ModParam::Curve: modCurve.curve = request_args[0].i; break; + default: set(modCurve.modifier, args...); break; + } + } + + template + void reply(const ModKey::Parameters& params, ModParam which, Args&&... args) + { + switch (which) { + case ModParam::Depth: break; + case ModParam::Curve: reply(params.curve); break; + case ModParam::Smooth: reply(params.smooth); break; + case ModParam::Step: reply(params.step, std::forward(args)...); break; + } + } + + template + void reply(const CCMap& map, Args... args) { reply(map, true, args...); } + + template + void reply(const CCMap& map, bool&& useDefault, Args... args) + { + if (useDefault) + reply(map.getWithDefault(indices.back()), args...); + else + reply(map.get(indices.back()), args...); + } + + template + void set(const CCMap& map, Args... args) + { + set(map[indices.back()], args...); + } + + template + void reply(const EGDescription& eg, T EGDescription::*&& member, Args&&... args) + { + reply(eg.*member, std::forward(args)...); + } + + template + void set(EGDescription& eg, T EGDescription::*&& member, Args&&... args) + { + set(eg.*member, std::forward(args)...); + } + + template + void set(CCMap& map, Args&&... args) { set(map[indices.back()], std::forward(args)...); } + + // Below are all methods that will fetch various data structure elements (regions, + // egs, ...), check that they exist, and call an actual "concrete" reply + // implementation. They use pointer-to-member variables, which allow the compiler to + // dispatch to the correct logic. For example, if the pointer-to-member is + // `Region::*`, we aim at sending the value of a member of the Region + // struct. The first thing is to read the index of the region and fetch it from the + // sfizz data structure using `getRegion(...)`, and then apply the pointer-to-member + // to this particular region to send the value. + + // Adding new dispatching overloads seemed simple enough to this point, although I + // haven't found a nice way to the same with pointer-to-member function. + // TODO: could do something with the c++14 compatible `invoke` maybe? + + template + void set(T M::*member, Args&&... args) + { + dispatch( + static_cast(&MessagingHelper::set), + member, + std::forward(args)...); + } + + // MSVC require this because it finds T M::* and T Voice::* ambiguous in the overload + // resolution of `reply`, so we "disable" the reply/set dispatching for Voice and + // TriggerEvent since they're read-only components. + template + using DispatchedType = typename std::enable_if< + !std::is_same::value && !std::is_same::value, void>::type; + + template + DispatchedType reply(T M::*member, Args&&... args) + { + dispatch( + static_cast(&MessagingHelper::reply), + member, + std::forward(args)...); + } + + + template + void dispatch(F&& f, T FilterDescription::*member, Args&&... args) + { + if (auto region = getRegion()) + if (auto filter = getFilter(*region)) + inv::invoke(std::forward(f), this, (*filter).*member, std::forward(args)...); + } + + template + void dispatch(F&& f, T SynthConfig::*member, Args&&... args) + { + inv::invoke(std::forward(f), this, (impl.resources_.getSynthConfig()).*member, std::forward(args)...); + } + + template + void dispatch(F&& f, T LFODescription::Sub::*member, Args&&... args) + { + if (auto region = getRegion()) + if (auto lfo = getLFO(*region)) + if (auto sub = getLFOSub(*lfo)) + inv::invoke(std::forward(f), this, (*sub).*member, std::forward(args)...); + } + + template + void dispatch(F&& f, T Region::*member, Args&&... args) + { + if (auto region = getRegion()) + inv::invoke(std::forward(f), this, (*region).*member, std::forward(args)...); + } + + template + void dispatch(F&& f, T EQDescription::*member, Args&&... args) + { + if (auto region = getRegion()) + if (auto eq = getEQ(*region)) + inv::invoke(std::forward(f), this, (*eq).*member, std::forward(args)...); + } + + template + void dispatch(F&& f, T LFODescription::*member, Args&&... args) + { + if (auto region = getRegion()) + if (auto lfo = getLFO(*region)) + inv::invoke(std::forward(f), this, (*lfo).*member, std::forward(args)...); + } + + template + void dispatch(F&& f, T FlexEGPoint::*member, Args&&... args) + { + if (auto region = getRegion()) + if (auto eg = getEG(*region)) + if (auto point = getEGPoint(*eg)) + inv::invoke(std::forward(f), this, (*point).*member, std::forward(args)...); + } + + // No need to dispatch for voices, they are read-only for now + + template + void reply(T TriggerEvent::*member, Args&&... args) + { + if (auto voice = getVoice()) + reply((*voice).getTriggerEvent().*member, std::forward(args)...); + } + + template + void reply(T Voice::*member, Args&&... args) + { + if (auto voice = getVoice()) { + reply((*voice.*member)(std::forward(args)...)); // Voice only has callables + } + } + + template + void reply(ModId id, ModParam param, Args&&... args) + { + if (auto region = getRegion()) { + int cc = static_cast(indices.back()); + switch (id) { + case ModId::FilCutoff: + case ModId::FilGain: + switch (param) { + case ModParam::Depth: reply(region->ccModDepth(cc, id, indices[1]), std::forward(args)...); + break; + default: + reply(region->ccModParameters(cc, id, indices[1]), param, std::forward(args)...); + break; + } + break; + default: + switch (param) { + case ModParam::Depth: reply(region->ccModDepth(cc, id), std::forward(args)...); + break; + default: + reply(region->ccModParameters(cc, id), param, std::forward(args)...); + break; + } + } + } + } + + // Validate and fetch elements from the sfizz data structures. By default, we kind of + // assume that regions/voices will be the first index, CCs will be the last, and + // EQ/Filter/.. will be in-between. + Region* getRegion(absl::optional index = {}) + { + const auto idx = index.value_or(indices[0]); + if (idx >= impl.layers_.size()) + return {}; + + Layer& layer = *impl.layers_[idx]; + return &layer.getRegion(); + } + + FilterDescription* getFilter(Region& region, absl::optional index = {}) + { + const auto idx = index.value_or(indices[1]); + if (region.filters.size() <= idx) + return {}; + + return ®ion.filters[idx]; + } + + EQDescription* getEQ(Region& region, absl::optional index = {}) + { + const auto idx = index.value_or(indices[1]); + if (region.equalizers.size() <= idx) + return {}; + + return ®ion.equalizers[idx]; + } + + LFODescription* getLFO(Region& region, absl::optional index = {}) + { + const auto idx = index.value_or(indices[1]); + if (region.lfos.size() <= idx) + return {}; + + return ®ion.lfos[idx]; + } + + LFODescription::Sub* getLFOSub(LFODescription& lfo, absl::optional index = {}) + { + if (indices.size() == 2) { + if (lfo.sub.empty()) + return {}; + else + return &lfo.sub.front(); + } + + const auto idx = index.value_or(indices[2]); + if (lfo.sub.size() <= idx) + return {}; + + return &lfo.sub[idx]; + } + + FlexEGDescription* getEG(Region& region, absl::optional index = {}) + { + const auto idx = index.value_or(indices[1]); + if (region.flexEGs.size() <= idx) + return {}; + + return ®ion.flexEGs[idx]; + } + + FlexEGPoint* getEGPoint(FlexEGDescription& desc, absl::optional index = {}) + { + const auto idx = index.value_or(indices[2]) + 1; + if (desc.points.size() <= idx) + return {}; + + return &desc.points[idx]; + } + + Voice* getVoice(absl::optional index = {}) + { + const auto idx = index.value_or(indices[0]); + if (static_cast(idx) >= impl.numVoices_) + return {}; + + auto& voice = impl.voiceManager_[idx]; + if (voice.isFree()) + return {}; + + return &voice; + } + + // Helpers to get and check the values of the indices + template + absl::optional index(int i) + { + if (i <= ssize(indices)) + return static_cast(indices[i]); + + return absl::nullopt; + } + + absl::optional sindex(int i) { return index(i); } + + absl::optional checkCC(int i = 0) + { + auto cc = index(i); + return cc <= config::numCCs ? cc : absl::nullopt; + } + + absl::optional checkNote(int i = 0) + { + auto note = sindex(i); + return note <= 127 ? note : absl::nullopt; + } + +private: + Client& client; + std::vector indices; + Synth::Impl& impl; + int delay; + const char* path; + const char* sig; + const sfizz_arg_t* request_args; +}; + +} diff --git a/src/sfizz/modulations/ModMatrix.cpp b/src/sfizz/modulations/ModMatrix.cpp index 98a92c866..54e7a0131 100644 --- a/src/sfizz/modulations/ModMatrix.cpp +++ b/src/sfizz/modulations/ModMatrix.cpp @@ -13,6 +13,7 @@ #include "SIMDHelpers.h" #include "utility/Debug.h" #include +#include #include #include #include diff --git a/tests/ParsingT.cpp b/tests/ParsingT.cpp index 36f01dd4a..6e3877641 100644 --- a/tests/ParsingT.cpp +++ b/tests/ParsingT.cpp @@ -10,6 +10,7 @@ #include #include "catch2/catch.hpp" #include "absl/strings/string_view.h" +#include "absl/strings/str_cat.h" using namespace Catch::literals; struct ParsingMocker: sfz::Parser::Listener diff --git a/tests/RegionValuesSetT.cpp b/tests/RegionValuesSetT.cpp index e3af3497a..c565dde77 100644 --- a/tests/RegionValuesSetT.cpp +++ b/tests/RegionValuesSetT.cpp @@ -4,119 +4,509 @@ // license. You should have receive a LICENSE.md file along with the code. // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz -#include "TestHelpers.h" -#include "sfizz/Synth.h" +#include "SynthDiscussion.h" +#include "SfzHelpers.h" #include "catch2/catch.hpp" -#include -#include -#include + using namespace Catch::literals; using namespace sfz; +using namespace sfz::literals; +using namespace std::literals; -TEST_CASE("[Set values] Pitch keycenter") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - AudioBuffer buffer { 2, 256 }; - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/values_set.sfz", R"( - sample=*sine pitch_keycenter=48 - )"); - synth.dispatchMessage(client, 0, "/region0/pitch_keycenter", "", nullptr); - - // Update value - sfizz_arg_t args; - args.i = 60; - synth.dispatchMessage(client, 1, "/region0/pitch_keycenter", "i", &args); - synth.renderBlock(buffer); - - synth.dispatchMessage(client, 0, "/region0/pitch_keycenter", "", nullptr); - std::vector expected { - "/region0/pitch_keycenter,i : { 48 }", - "/region0/pitch_keycenter,i : { 60 }", - }; - REQUIRE(messageList == expected); -} +using OSC = OSCValueLess; -TEST_CASE("[Set values] LFO wave") +TEST_CASE("Set values", "[parsing][OSC]") { - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - AudioBuffer buffer { 2, 256 }; - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/values_set.sfz", R"( - sample=*sine lfo1_wave=5 - )"); - synth.dispatchMessage(client, 0, "/region0/lfo0/wave", "", nullptr); - - // Update value - sfizz_arg_t args; - args.i = 2; - synth.dispatchMessage(client, 1, "/region0/lfo0/wave", "i", &args); - synth.renderBlock(buffer); - - synth.dispatchMessage(client, 0, "/region0/lfo0/wave", "", nullptr); - std::vector expected { - "/region0/lfo0/wave,i : { 5 }", - "/region0/lfo0/wave,i : { 2 }", - }; - REQUIRE(messageList == expected); -} + SynthDiscussion d; -TEST_CASE("[Set values] Filter type") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - AudioBuffer buffer { 2, 256 }; - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/values_set.sfz", R"( - sample=*sine fil2_type=lpf_1p - )"); - synth.dispatchMessage(client, 0, "/region0/filter1/type", "", nullptr); - - // Update value - sfizz_arg_t args; - args.s = "hpf_2p"; - synth.dispatchMessage(client, 1, "/region0/filter1/type", "s", &args); - synth.renderBlock(buffer); - - synth.dispatchMessage(client, 0, "/region0/filter1/type", "", nullptr); - std::vector expected { - "/region0/filter1/type,s : { lpf_1p }", - "/region0/filter1/type,s : { hpf_2p }", - }; - REQUIRE(messageList == expected); -} + SECTION("Pitch keycenter") { + d.load(R"( sample=*sine pitch_keycenter=48 )"); + REQUIRE( d.read("/region0/pitch_keycenter") == 48); + REQUIRE( d.sendAndRead("/region0/pitch_keycenter", 60) == 60); + } -TEST_CASE("[Set values] Loop mode") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - AudioBuffer buffer { 2, 256 }; - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/values_set.sfz", R"( - sample=looped_flute.wav - )"); - synth.dispatchMessage(client, 0, "/region0/loop_mode", "", nullptr); - - // Update value - sfizz_arg_t args; - args.s = "one_shot"; - synth.dispatchMessage(client, 1, "/region0/loop_mode", "s", &args); - synth.renderBlock(buffer); - - synth.dispatchMessage(client, 0, "/region0/loop_mode", "", nullptr); - std::vector expected { - "/region0/loop_mode,s : { loop_continuous }", - "/region0/loop_mode,s : { one_shot }", - }; - REQUIRE(messageList == expected); + SECTION("LFO Wave") { + d.load(R"( sample=*sine lfo1_wave=5 lfo1_wave2=4 )"); + REQUIRE( d.read("/region0/lfo0/wave") == 5); + REQUIRE( d.read("/region0/lfo0/wave1") == 4); + REQUIRE( d.sendAndRead("/region0/lfo0/wave", 3) == 3); + REQUIRE( d.sendAndRead("/region0/lfo0/wave1", 2) == 2); + } + + SECTION("Loop mode") { + d.load(R"( sample=looped_flute.wav )"); + REQUIRE( d.read("/region0/loop_mode") == "loop_continuous"); + REQUIRE( d.sendAndRead("/region0/loop_mode", "one_shot") == "one_shot"); + } + + SECTION("Sample quality") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.read("/sample_quality") == 2); + REQUIRE( d.read("/oscillator_quality") == 1); + REQUIRE( d.read("/freewheeling_sample_quality") == 10); + REQUIRE( d.read("/freewheeling_oscillator_quality") == 3); + REQUIRE( d.sendAndRead("/sample_quality", 3) == 3); + REQUIRE( d.sendAndRead("/oscillator_quality", 2) == 2); + REQUIRE( d.sendAndRead("/freewheeling_sample_quality", 6) == 6); + REQUIRE( d.sendAndRead("/freewheeling_oscillator_quality", 2) == 2); + } + + SECTION("Sustain cancels release") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.read("/sustain_cancels_release") == OSC::False ); + d.send("/sustain_cancels_release", true); + REQUIRE( d.read("/sustain_cancels_release") == OSC::True ); + d.send("/sustain_cancels_release", false); + REQUIRE( d.read("/sustain_cancels_release") == OSC::False ); + d.send("/sustain_cancels_release", "on"s); + REQUIRE( d.read("/sustain_cancels_release") == OSC::True ); + } + + SECTION("Delay") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/delay", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/delay_random", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/delay_cc1", 10.0f) == 10.0f); + } + + SECTION("Offset") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/offset", 10) == 10); + REQUIRE( d.sendAndRead("/region0/offset_random", 10) == 10); + REQUIRE( d.sendAndRead("/region0/offset_cc1", 10) == 10); + } + + SECTION("End") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/end", 10) == 10); + REQUIRE( d.sendAndRead("/region0/end_cc1", 10) == 10); + } + + SECTION("Count") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/count", 3) == 3); + } + + SECTION("Loop range") { + d.load(R"( sample=kick.wav )"); + std::vector v {13, 2000}; + REQUIRE( d.sendAndReadAll("/region0/loop_range", v) == v); + REQUIRE( d.sendAndRead("/region0/loop_start_cc1", 10) == 10); + REQUIRE( d.sendAndRead("/region0/loop_end_cc1", 1000) == 1000); + } + + SECTION("Loop count") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/loop_count", 3) == 3); + d.send("/region0/loop_count", nullptr); + REQUIRE( d.read("/region0/loop_count") == OSC::None); + } + + SECTION("Output") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/output", 3) == 3); + } + + SECTION("Off by") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.read("/region0/off_by") == OSC::None); + REQUIRE( d.sendAndRead("/region0/off_by", 2) == 2); + d.send("/region0/off_by", nullptr); + REQUIRE( d.read("/region0/off_by") == OSC::None); + } + + SECTION("Off mode") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/off_mode", "time") =="time"); + REQUIRE( d.sendAndRead("/region0/off_mode", "fast") =="fast"); + } + + SECTION("Key range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5, 67}; + REQUIRE( d.sendAndReadAll("/region0/key_range", v) == v); + } + + SECTION("Off time") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/off_time", 0.1f) == 0.1f); + } + + SECTION("Velocity range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/vel_range", v) == v); + } + + SECTION("Bend range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_bend, 67_bend}; + REQUIRE( d.sendAndReadAll("/region0/bend_range", v) == v); + } + + SECTION("Program range") { + d.load(R"( sample=kick.wav )"); + std::vector v {2, 10}; + REQUIRE( d.sendAndReadAll("/region0/program_range", v) == v); + } + + SECTION("CC range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/cc_range2", v) == v); + } + + SECTION("Last keyswitch") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/sw_last", 24) == 24); + std::vector v {10, 15}; + REQUIRE( d.sendAndReadAll("/region0/sw_last", v) == v); + } + + SECTION("Keyswitch label") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/sw_label", "hello") == "hello"); + } + + SECTION("Keyswitch up") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/sw_up", 12) == 12); + d.send("/region0/sw_up", "c4"s); + REQUIRE( d.read("/region0/sw_up") == 60); + } + + SECTION("Keyswitch down") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/sw_down", 12) == 12); + d.send("/region0/sw_down", "c4"s); + REQUIRE( d.read("/region0/sw_down") == 60); + } + + SECTION("Keyswitch down") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/sw_previous", 12) == 12); + d.send("/region0/sw_previous", "c4"s); + REQUIRE( d.read("/region0/sw_previous") == 60); + } + + SECTION("Velocity override") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/sw_vel", "previous") == "previous"); + } + + SECTION("Channel aftertouch range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/chanaft_range", v) == v); + } + + SECTION("Poly aftertouch range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/polyaft_range", v) == v); + } + + SECTION("BPM range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5, 67}; + REQUIRE( d.sendAndReadAll("/region0/bpm_range", v) == v); + } + + SECTION("Rand range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/rand_range", v) == v); + } + + SECTION("Sequences") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/seq_length", 2) == 2); + REQUIRE( d.sendAndRead("/region0/seq_position", 2) == 2); + } + + SECTION("Trigger") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/trigger", "release") == "release"); + } + + SECTION("Start CC range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/start_cc_range2", v) == v); + } + + SECTION("Volume etc") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/volume", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/pan", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/width", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/position", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/amplitude", 10.0f) == 10.0f); + } + + SECTION("Amp key something") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/amp_keycenter", 48) == 48); + REQUIRE( d.sendAndRead("/region0/amp_keytrack", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/amp_veltrack", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/amp_veltrack_cc3", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/amp_veltrack_curvecc3", 2) == 2); + } + + SECTION("Amp random") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/amp_random", 10.0f) == 10.0f); + } + + SECTION("Crossfade key range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5, 67}; + REQUIRE( d.sendAndReadAll("/region0/xfin_key_range", v) == v); + REQUIRE( d.sendAndReadAll("/region0/xfout_key_range", v) == v); + } + + SECTION("Other crossfade range") { + d.load(R"( sample=kick.wav )"); + std::vector v {5_norm, 67_norm}; + REQUIRE( d.sendAndReadAll("/region0/xfin_vel_range", v) == v); + REQUIRE( d.sendAndReadAll("/region0/xfout_vel_range", v) == v); + REQUIRE( d.sendAndReadAll("/region0/xfin_cc_range3", v) == v); + REQUIRE( d.sendAndReadAll("/region0/xfout_cc_range3", v) == v); + } + + SECTION("Crossfade curves") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/xf_keycurve", "power") == "power"); + REQUIRE( d.sendAndRead("/region0/xf_velcurve", "power") == "power"); + REQUIRE( d.sendAndRead("/region0/xf_cccurve", "power") == "power"); + } + + SECTION("Global amps and volumes curves") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/global_volume", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/master_volume", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/group_volume", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/global_amplitude", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/master_amplitude", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/group_amplitude", 10.0f) == 10.0f); + } + + SECTION("Pitch and transpose") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/pitch", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/transpose", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/pitch_random", 10.0f) == 10.0f); + } + + SECTION("Pitch key something") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/pitch_keycenter", 48) == 48); + REQUIRE( d.sendAndRead("/region0/pitch_keytrack", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/pitch_veltrack", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/pitch_veltrack_cc3", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/pitch_veltrack_curvecc3", 2) == 2); + } + + SECTION("Bends") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/bend_up", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/bend_down", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/bend_step", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/bend_smooth", 10) == 10); + } + + SECTION("Ampeg") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/ampeg_attack", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_delay", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_decay", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_hold", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_release", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_start", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_sustain", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_depth", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_attack_cc1", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_decay_cc2", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_delay_cc3", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_hold_cc4", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_release_cc5", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_sustain_cc6", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_start_cc7", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_attack_curvecc1", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_decay_curvecc2", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_delay_curvecc3", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_hold_curvecc4", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_release_curvecc5", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_sustain_curvecc6", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_start_curvecc7", 2) == 2); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2attack", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2delay", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2decay", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2hold", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2release", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2sustain", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/ampeg_vel2depth", 1.0f) == 1.0f); + REQUIRE( d.read("/region0/ampeg_dynamic") == OSC::False); + d.send("/region0/ampeg_dynamic", true); + REQUIRE( d.read("/region0/ampeg_dynamic") == OSC::True); + d.send("/region0/ampeg_dynamic", "off"s); + REQUIRE( d.read("/region0/ampeg_dynamic") == OSC::False); + } + + SECTION("Fileg") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/fileg_attack", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_delay", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_decay", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_hold", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_release", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_start", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_sustain", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_depth", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/fileg_attack_cc1", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_decay_cc2", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_delay_cc3", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_hold_cc4", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_release_cc5", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_sustain_cc6", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_start_cc7", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/fileg_attack_curvecc1", 2) == 2); + REQUIRE( d.sendAndRead("/region0/fileg_decay_curvecc2", 2) == 2); + REQUIRE( d.sendAndRead("/region0/fileg_delay_curvecc3", 2) == 2); + REQUIRE( d.sendAndRead("/region0/fileg_hold_curvecc4", 2) == 2); + REQUIRE( d.sendAndRead("/region0/fileg_release_curvecc5", 2) == 2); + REQUIRE( d.sendAndRead("/region0/fileg_sustain_curvecc6", 2) == 2); + REQUIRE( d.sendAndRead("/region0/fileg_start_curvecc7", 2) == 2); + REQUIRE( d.read("/region0/fileg_dynamic") == OSC::False); + d.send("/region0/fileg_dynamic", true); + REQUIRE( d.read("/region0/fileg_dynamic") == OSC::True); + d.send("/region0/fileg_dynamic", "off"s); + REQUIRE( d.read("/region0/fileg_dynamic") == OSC::False); + } + + SECTION("PitchEG") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/pitcheg_attack", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_delay", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_decay", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_hold", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_release", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_start", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_sustain", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_depth", 2.0f) == 2.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_attack_cc1", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_decay_cc2", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_delay_cc3", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_hold_cc4", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_release_cc5", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_sustain_cc6", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_start_cc7", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/pitcheg_attack_curvecc1", 2) == 2); + REQUIRE( d.sendAndRead("/region0/pitcheg_decay_curvecc2", 2) == 2); + REQUIRE( d.sendAndRead("/region0/pitcheg_delay_curvecc3", 2) == 2); + REQUIRE( d.sendAndRead("/region0/pitcheg_hold_curvecc4", 2) == 2); + REQUIRE( d.sendAndRead("/region0/pitcheg_release_curvecc5", 2) == 2); + REQUIRE( d.sendAndRead("/region0/pitcheg_sustain_curvecc6", 2) == 2); + REQUIRE( d.sendAndRead("/region0/pitcheg_start_curvecc7", 2) == 2); + REQUIRE( d.read("/region0/pitcheg_dynamic") == OSC::False); + d.send("/region0/pitcheg_dynamic", true); + REQUIRE( d.read("/region0/pitcheg_dynamic") == OSC::True); + d.send("/region0/pitcheg_dynamic", "off"s); + REQUIRE( d.read("/region0/pitcheg_dynamic") == OSC::False); + } + + SECTION("Note polyphony") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/note_polyphony", 3) == 3); + } + + SECTION("RT dead") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.read("/region0/rt_dead") == OSC::False ); + d.send("/region0/rt_dead", true); + REQUIRE( d.read("/region0/rt_dead") == OSC::True ); + d.send("/region0/rt_dead", false); + REQUIRE( d.read("/region0/rt_dead") == OSC::False ); + d.send("/region0/rt_dead", "on"s); + REQUIRE( d.read("/region0/rt_dead") == OSC::True ); + } + + SECTION("Sustain/sostenuto") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.read("/region0/sustain_sw") == OSC::True ); + d.send("/region0/sustain_sw", false); + REQUIRE( d.read("/region0/sustain_sw") == OSC::False ); + d.send("/region0/sustain_sw", true); + REQUIRE( d.read("/region0/sustain_sw") == OSC::True ); + d.send("/region0/sustain_sw", "off"s); + REQUIRE( d.read("/region0/sustain_sw") == OSC::False ); + REQUIRE( d.read("/region0/sostenuto_sw") == OSC::True ); + d.send("/region0/sostenuto_sw", false); + REQUIRE( d.read("/region0/sostenuto_sw") == OSC::False ); + d.send("/region0/sostenuto_sw", true); + REQUIRE( d.read("/region0/sostenuto_sw") == OSC::True ); + d.send("/region0/sostenuto_sw", "off"s); + REQUIRE( d.read("/region0/sostenuto_sw") == OSC::False ); + REQUIRE( d.sendAndRead("/region0/sustain_cc", 23) == 23); + REQUIRE( d.sendAndRead("/region0/sostenuto_cc", 23) == 23); + REQUIRE( d.sendAndRead("/region0/sustain_lo", 0.1f) == 0.1f); + REQUIRE( d.sendAndRead("/region0/sostenuto_lo", 0.1f) == 0.1f); + } + + SECTION("Note selfmask") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.read("/region0/note_selfmask") == OSC::True ); + d.send("/region0/note_selfmask", "off"s); + REQUIRE( d.read("/region0/note_selfmask") == OSC::False ); + d.send("/region0/note_selfmask", "mask"s); + REQUIRE( d.read("/region0/note_selfmask") == OSC::True ); + } + + SECTION("Oscillator stuff") { + d.load(R"( sample=kick.wav )"); + REQUIRE( d.sendAndRead("/region0/oscillator_phase", 0.1f) == 0.1f); + REQUIRE( d.sendAndRead("/region0/oscillator_quality", 2) == 2); + REQUIRE( d.sendAndRead("/region0/oscillator_mode", 1) == 1); + REQUIRE( d.sendAndRead("/region0/oscillator_multi", 5) == 5); + REQUIRE( d.sendAndRead("/region0/oscillator_detune", 0.2f) == 0.2f); + REQUIRE( d.sendAndRead("/region0/oscillator_mod_depth", 0.2f) == 0.2f); + } + + SECTION("Effect") { + d.load(R"( sample=kick.wav effect1=10)"); + REQUIRE( d.sendAndRead("/region0/effect1", 1.0f) == 1.0f); + } + + SECTION("Filters") { + d.load(R"( sample=kick.wav)"); + REQUIRE(d.read("/region0/add_filter") == 0); + REQUIRE( d.sendAndRead("/region0/filter0/cutoff", 100.0f) == 100.0f); + REQUIRE( d.sendAndRead("/region0/filter0/resonance", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/filter0/gain", 4.0f) == 4.0f); + REQUIRE( d.sendAndRead("/region0/filter0/keycenter", 42) == 42); + REQUIRE( d.sendAndRead("/region0/filter0/keytrack", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/filter0/veltrack", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/filter0/veltrack_cc1", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/filter0/veltrack_curvecc2", 3) == 3); + REQUIRE( d.sendAndRead("/region0/filter0/type", "lpf_2p") == "lpf_2p"); + } + + SECTION("EQs") { + d.load(R"( sample=kick.wav)"); + REQUIRE(d.read("/region0/add_eq") == 0); + REQUIRE( d.sendAndRead("/region0/eq0/gain", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/eq0/bandwidth", 100.0f) == 100.0f); + REQUIRE( d.sendAndRead("/region0/eq0/frequency", 500.0f) == 500.0f); + REQUIRE( d.sendAndRead("/region0/eq0/vel2freq", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/eq0/vel2gain", 10.0f) == 10.0f); + REQUIRE( d.sendAndRead("/region0/eq0/type", "hshelf") == "hshelf"); + } + + SECTION("EGs") { + d.load(R"( sample=kick.wav)"); + REQUIRE(d.read("/region0/add_eg") == 0); + REQUIRE(d.read("/region0/eg0/add_point") == 0); + REQUIRE( d.sendAndRead("/region0/eg0/point0/time", 1.0f) == 1.0f); + REQUIRE( d.sendAndRead("/region0/eg0/point0/level", 0.5f) == 0.5f); + } } diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 2e0a09887..b40ff82a9 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -10,349 +10,196 @@ #include #include #include +#include "SynthDiscussion.h" + using namespace Catch::literals; using namespace sfz; +using namespace sfz::literals; +using OSC = OSCValueLess; -TEST_CASE("[Values] Delay") +TEST_CASE("Read values", "[parsing][OSC]") { - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SynthDiscussion d; - SECTION("Basic") + SECTION("Delay basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=*sine sample=*sine delay=1 sample=*sine delay=-1 sample=*sine delay=1 delay=-1 )"); - synth.dispatchMessage(client, 0, "/region0/delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/delay", "", nullptr); - std::vector expected { - "/region0/delay,f : { 0 }", - "/region1/delay,f : { 1 }", - "/region2/delay,f : { -1 }", - "/region3/delay,f : { -1 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/delay") == 0); + REQUIRE(d.read("/region1/delay") == 1); + REQUIRE(d.read("/region2/delay") == -1); + REQUIRE(d.read("/region3/delay") == -1); } - SECTION("Random") + SECTION("Delay random") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=*sine sample=*sine delay_random=1 sample=*sine delay_random=-1 sample=*sine delay_random=1 delay_random=-1 )"); - synth.dispatchMessage(client, 0, "/region0/delay_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/delay_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/delay_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/delay_random", "", nullptr); - std::vector expected { - "/region0/delay_random,f : { 0 }", - "/region1/delay_random,f : { 1 }", - "/region2/delay_random,f : { -1 }", - "/region3/delay_random,f : { -1 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/delay_random") == 0); + REQUIRE(d.read("/region1/delay_random") == 1); + REQUIRE(d.read("/region2/delay_random") == -1); + REQUIRE(d.read("/region3/delay_random") == -1); } - SECTION("CC") + SECTION("Delay CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav delay_cc12=1.5 sample=kick.wav delay_cc12=-1.5 sample=kick.wav delay_cc14=3 delay_cc12=2 delay_cc12=-12 )"); - synth.dispatchMessage(client, 0, "/region0/delay_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/delay_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/delay_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/delay_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/delay_cc12", "", nullptr); - std::vector expected { - "/region0/delay_cc12,f : { 0 }", - "/region1/delay_cc12,f : { 1.5 }", - "/region2/delay_cc12,f : { -1.5 }", - "/region3/delay_cc14,f : { 3 }", - "/region3/delay_cc12,f : { -12 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/delay_cc12") == 0 ); + REQUIRE( d.read("/region1/delay_cc12") == 1.5 ); + REQUIRE( d.read("/region2/delay_cc12") == -1.5 ); + REQUIRE( d.read("/region3/delay_cc14") == 3 ); + REQUIRE( d.read("/region3/delay_cc12") == -12 ); } -} - -TEST_CASE("[Values] Sample and direction") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=*sine - sample=kick.wav - sample=kick.wav direction=reverse - )"); - synth.dispatchMessage(client, 0, "/region0/sample", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sample", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/direction", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/direction", "", nullptr); - std::vector expected { - "/region0/sample,s : { *sine }", - "/region1/sample,s : { kick.wav }", - "/region1/direction,s : { forward }", - "/region2/direction,s : { reverse }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Offset") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Sample and direction") + { + d.load(R"( + sample=*sine + sample=kick.wav + sample=kick.wav direction=reverse + )"); + REQUIRE(d.read("/region0/sample") == "*sine"); + REQUIRE(d.read("/region1/sample") == "kick.wav"); + REQUIRE(d.read("/region1/direction") == "forward"); + REQUIRE(d.read("/region2/direction") == "reverse"); + } - SECTION("Basic") + SECTION("Offset basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav offset=12 sample=kick.wav offset=-1 sample=kick.wav offset=12 offset=-1 )"); - synth.dispatchMessage(client, 0, "/region0/offset", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/offset", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/offset", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/offset", "", nullptr); - std::vector expected { - "/region0/offset,h : { 0 }", - "/region1/offset,h : { 12 }", - "/region2/offset,h : { -1 }", - "/region3/offset,h : { -1 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/offset") == 0); + REQUIRE(d.read("/region1/offset") == 12); + REQUIRE(d.read("/region2/offset") == -1); + REQUIRE(d.read("/region3/offset") == -1); } - SECTION("Random") + SECTION("Offset random") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav offset_random=1 sample=kick.wav offset_random=-1 sample=kick.wav offset_random=1 offset_random=-1 )"); - synth.dispatchMessage(client, 0, "/region0/offset_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/offset_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/offset_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/offset_random", "", nullptr); - std::vector expected { - "/region0/offset_random,h : { 0 }", - "/region1/offset_random,h : { 1 }", - "/region2/offset_random,h : { -1 }", - "/region3/offset_random,h : { -1 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/offset_random") == 0); + REQUIRE(d.read("/region1/offset_random") == 1); + REQUIRE(d.read("/region2/offset_random") == -1); + REQUIRE(d.read("/region3/offset_random") == -1); } - SECTION("CC") + SECTION("Offset CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav offset_cc12=12 sample=kick.wav offset_cc12=-12 sample=kick.wav offset_cc14=14 offset_cc12=12 offset_cc12=-12 )"); - synth.dispatchMessage(client, 0, "/region0/offset_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/offset_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/offset_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/offset_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/offset_cc12", "", nullptr); - std::vector expected { - "/region0/offset_cc12,h : { 0 }", - "/region1/offset_cc12,h : { 12 }", - "/region2/offset_cc12,h : { -12 }", - "/region3/offset_cc14,h : { 14 }", - "/region3/offset_cc12,h : { -12 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/offset_cc12") == 0); + REQUIRE(d.read("/region1/offset_cc12") == 12); + REQUIRE(d.read("/region2/offset_cc12") == -12); + REQUIRE(d.read("/region3/offset_cc14") == 14); + REQUIRE(d.read("/region3/offset_cc12") == -12); } -} -TEST_CASE("[Values] End") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Basic") + SECTION("Sample end basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav end=194 sample=kick.wav end=-1 sample=kick.wav end=0 sample=kick.wav end=194 end=-1 sample=kick.wav end=0 end=194 )"); - synth.dispatchMessage(client, 0, "/region0/end", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/enabled", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/enabled", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/enabled", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/enabled", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/enabled", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/end", "", nullptr); - std::vector expected { - "/region0/end,h : { 194 }", - "/region0/enabled,T : { }", - "/region1/enabled,F : { }", - "/region2/enabled,F : { }", - "/region3/enabled,F : { }", - "/region4/enabled,T : { }", - "/region4/end,h : { 194 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/end") == 194); + REQUIRE(d.read("/region0/enabled") == OSC::True); + REQUIRE(d.read("/region1/enabled") == OSC::False); + REQUIRE(d.read("/region2/enabled") == OSC::False); + REQUIRE(d.read("/region3/enabled") == OSC::False); + REQUIRE(d.read("/region4/enabled") == OSC::True); + REQUIRE(d.read("/region4/end") == 194); } - SECTION("CC") + + SECTION("Sample end CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav end_cc12=12 sample=kick.wav end_oncc12=-12 sample=kick.wav end_cc14=14 end_cc12=12 end_oncc12=-12 )"); - synth.dispatchMessage(client, 0, "/region0/end_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/end_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/end_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/end_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/end_cc12", "", nullptr); - std::vector expected { - "/region0/end_cc12,h : { 0 }", - "/region1/end_cc12,h : { 12 }", - "/region2/end_cc12,h : { -12 }", - "/region3/end_cc14,h : { 14 }", - "/region3/end_cc12,h : { -12 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/end_cc12") == 0); + REQUIRE(d.read("/region1/end_cc12") == 12); + REQUIRE(d.read("/region2/end_cc12") == -12); + REQUIRE(d.read("/region3/end_cc14") == 14); + REQUIRE(d.read("/region3/end_cc12") == -12); + } + SECTION("Count") + { + d.load(R"( + sample=kick.wav + sample=kick.wav count=2 + sample=kick.wav count=-1 + )"); + REQUIRE(d.read("/region0/count") == OSC::None); + REQUIRE(d.read("/region1/count") == 2); + REQUIRE(d.read("/region2/count") == OSC::None); } -} - -TEST_CASE("[Values] Count") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav count=2 - sample=kick.wav count=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/count", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/count", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/count", "", nullptr); - std::vector expected { - "/region0/count,N : { }", - "/region1/count,i : { 2 }", - "/region2/count,N : { }", - }; - REQUIRE(messageList == expected); -} - -TEST_CASE("[Values] Loop mode") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav loop_mode=one_shot - sample=kick.wav loopmode=one_shot - sample=kick.wav loop_mode=loop_sustain - sample=kick.wav loop_mode=loop_continuous - sample=kick.wav loop_mode=loop_continuous loop_mode=no_loop - )"); - synth.dispatchMessage(client, 0, "/region0/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/loop_mode", "", nullptr); - std::vector expected { - "/region0/loop_mode,s : { no_loop }", - "/region1/loop_mode,s : { one_shot }", - "/region2/loop_mode,s : { one_shot }", - "/region3/loop_mode,s : { loop_sustain }", - "/region4/loop_mode,s : { loop_continuous }", - "/region5/loop_mode,s : { no_loop }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Loops") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav loop_mode=one_shot - sample=kick.wav loopmode=one_shot - sample=kick.wav loop_mode=loop_sustain - sample=kick.wav loop_mode=loop_continuous - sample=kick.wav loop_mode=loop_continuous loop_mode=no_loop - )"); - synth.dispatchMessage(client, 0, "/region0/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/loop_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/loop_mode", "", nullptr); - std::vector expected { - "/region0/loop_mode,s : { no_loop }", - "/region1/loop_mode,s : { one_shot }", - "/region2/loop_mode,s : { one_shot }", - "/region3/loop_mode,s : { loop_sustain }", - "/region4/loop_mode,s : { loop_continuous }", - "/region5/loop_mode,s : { no_loop }", - }; - REQUIRE(messageList == expected); -} + SECTION("Loop mode") + { + SynthDiscussion d; + d.load(R"( + sample=kick.wav + sample=kick.wav loop_mode=one_shot + sample=kick.wav loopmode=one_shot + sample=kick.wav loop_mode=loop_sustain + sample=kick.wav loop_mode=loop_continuous + sample=kick.wav loop_mode=loop_continuous loop_mode=no_loop + )"); + REQUIRE(d.read("/region0/loop_mode") == "no_loop"); + REQUIRE(d.read("/region1/loop_mode") == "one_shot"); + REQUIRE(d.read("/region2/loop_mode") == "one_shot"); + REQUIRE(d.read("/region3/loop_mode") == "loop_sustain"); + REQUIRE(d.read("/region4/loop_mode") == "loop_continuous"); + REQUIRE(d.read("/region5/loop_mode") == "no_loop"); + } -TEST_CASE("[Values] Loop range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Basic") + SECTION("Loop range basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav loop_start=10 loop_end=100 sample=kick.wav loopstart=10 loopend=100 sample=kick.wav loop_start=-1 loopend=-100 )"); - synth.dispatchMessage(client, 0, "/region0/loop_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/loop_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/loop_range", "", nullptr); - std::vector expected { - "/region0/loop_range,hh : { 0, 44011 }", // Default loop points in the file - "/region1/loop_range,hh : { 10, 100 }", - "/region2/loop_range,hh : { 10, 100 }", - "/region3/loop_range,hh : { 0, 44011 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT(d.readAll("/region0/loop_range"), Catch::Approx(std::vector{ 0, 44011 })); + REQUIRE_THAT(d.readAll("/region1/loop_range"), Catch::Approx(std::vector{ 10, 100 })); + REQUIRE_THAT(d.readAll("/region2/loop_range"), Catch::Approx(std::vector{ 10, 100 })); + REQUIRE_THAT(d.readAll("/region3/loop_range"), Catch::Approx(std::vector{ 0, 44011 })); } - SECTION("CC") + + SECTION("Loop range CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav loop_start_cc12=10 loop_end_cc14=-100 sample=kick.wav loop_start_oncc12=-10 loop_end_oncc14=100 @@ -360,464 +207,259 @@ TEST_CASE("[Values] Loop range") sample=kick.wav loop_length_oncc14=100 sample=kick.wav loop_length_cc14=100 )"); - synth.dispatchMessage(client, 0, "/region0/loop_start_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/loop_end_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_start_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_end_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/loop_start_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/loop_end_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/loop_start_cc12", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/loop_end_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/loop_end_cc14", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/loop_end_cc14", "", nullptr); - std::vector expected { - "/region0/loop_start_cc12,h : { 0 }", - "/region0/loop_end_cc14,h : { 0 }", - "/region1/loop_start_cc12,h : { 10 }", - "/region1/loop_end_cc14,h : { -100 }", - "/region2/loop_start_cc12,h : { -10 }", - "/region2/loop_end_cc14,h : { 100 }", - "/region3/loop_start_cc12,h : { -10 }", - "/region3/loop_end_cc14,h : { 100 }", - "/region4/loop_end_cc14,h : { 100 }", - "/region5/loop_end_cc14,h : { 100 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/loop_start_cc12") == 0); + REQUIRE(d.read("/region0/loop_end_cc14") == 0); + REQUIRE(d.read("/region1/loop_start_cc12") == 10); + REQUIRE(d.read("/region1/loop_end_cc14") == -100); + REQUIRE(d.read("/region2/loop_start_cc12") == -10); + REQUIRE(d.read("/region2/loop_end_cc14") == 100); + REQUIRE(d.read("/region3/loop_start_cc12") == -10); + REQUIRE(d.read("/region3/loop_end_cc14") == 100); + REQUIRE(d.read("/region4/loop_end_cc14") == 100); + REQUIRE(d.read("/region5/loop_end_cc14") == 100); } -} - -TEST_CASE("[Values] Loop crossfade") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav loop_crossfade=0.5 - sample=kick.wav loop_crossfade=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/loop_crossfade", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_crossfade", "", nullptr); - std::vector expected { - "/region0/loop_crossfade,f : { 0.5 }", - "/region1/loop_crossfade,f : { 0.001 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Loop count") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav loop_count=2 - sample=kick.wav loop_count=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/loop_count", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/loop_count", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/loop_count", "", nullptr); - std::vector expected { - "/region0/loop_count,N : { }", - "/region1/loop_count,i : { 2 }", - "/region2/loop_count,N : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Loop crossfade") + { + d.load(R"( + sample=kick.wav loop_crossfade=0.5 + sample=kick.wav loop_crossfade=-1 + )"); + REQUIRE(d.read("/region0/loop_crossfade") == 0.5f); + REQUIRE(d.read("/region1/loop_crossfade") == 0.001f); + } -TEST_CASE("[Values] Output") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Loop count") + { + d.load(R"( + sample=kick.wav + sample=kick.wav loop_count=2 + sample=kick.wav loop_count=-1 + )"); + REQUIRE(d.read("/region0/loop_count") == OSC::None); + REQUIRE(d.read("/region1/loop_count") == 2); + REQUIRE(d.read("/region2/loop_count") == OSC::None); + } SECTION("No special outputs") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/output", "", nullptr); - synth.dispatchMessage(client, 0, "/num_outputs", "", nullptr); - std::vector expected { - "/region0/output,i : { 0 }", - "/num_outputs,i : { 1 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/output") == 0); + REQUIRE(d.read("/num_outputs") == 1); } SECTION("1 output") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav output=1 sample=kick.wav output=-1 )"); - synth.dispatchMessage(client, 0, "/region0/output", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/output", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/output", "", nullptr); - synth.dispatchMessage(client, 0, "/num_outputs", "", nullptr); - std::vector expected { - "/region0/output,i : { 0 }", - "/region1/output,i : { 1 }", - "/region2/output,i : { 0 }", - "/num_outputs,i : { 2 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/output") == 0); + REQUIRE(d.read("/region1/output") == 1); + REQUIRE(d.read("/region2/output") == 0); + REQUIRE(d.read("/num_outputs") == 2); } SECTION("More than 1 output") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav output=1 sample=kick.wav output=3 )"); - synth.dispatchMessage(client, 0, "/region0/output", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/output", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/output", "", nullptr); - synth.dispatchMessage(client, 0, "/num_outputs", "", nullptr); - std::vector expected { - "/region0/output,i : { 0 }", - "/region1/output,i : { 1 }", - "/region2/output,i : { 3 }", - "/num_outputs,i : { 4 }", - }; - REQUIRE(messageList == expected); + REQUIRE(d.read("/region0/output") == 0); + REQUIRE(d.read("/region1/output") == 1); + REQUIRE(d.read("/region2/output") == 3); + REQUIRE(d.read("/num_outputs") == 4); } -} - -TEST_CASE("[Values] Group") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav group=5 - sample=kick.wav group=-2 - )"); - synth.dispatchMessage(client, 0, "/region0/group", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/group", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/group", "", nullptr); - std::vector expected { - "/region0/group,h : { 0 }", - "/region1/group,h : { 5 }", - "/region2/group,h : { -2 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Off by") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav off_by=5 - sample=kick.wav off_by=-2 - )"); - synth.dispatchMessage(client, 0, "/region0/off_by", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/off_by", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/off_by", "", nullptr); - std::vector expected { - "/region0/off_by,N : { }", - "/region1/off_by,h : { 5 }", - "/region2/off_by,h : { -2 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Group") + { + d.load(R"( + sample=kick.wav + sample=kick.wav group=5 + sample=kick.wav group=-2 + )"); + REQUIRE(d.read("/region0/group") == 0); + REQUIRE(d.read("/region1/group") == 5); + REQUIRE(d.read("/region2/group") == -2); + } -TEST_CASE("[Values] Off mode") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav off_mode=fast - sample=kick.wav off_mode=normal - sample=kick.wav off_mode=time - sample=kick.wav off_mode=time off_mode=normal - sample=kick.wav off_mode=nothing - )"); - synth.dispatchMessage(client, 0, "/region0/off_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/off_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/off_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/off_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/off_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/off_mode", "", nullptr); - std::vector expected { - "/region0/off_mode,s : { fast }", - "/region1/off_mode,s : { fast }", - "/region2/off_mode,s : { normal }", - "/region3/off_mode,s : { time }", - "/region4/off_mode,s : { normal }", - "/region5/off_mode,s : { fast }", - }; - REQUIRE(messageList == expected); -} + SECTION("Off by") + { + d.load(R"( + sample=kick.wav + sample=kick.wav off_by=5 + sample=kick.wav off_by=-2 + )"); + REQUIRE( d.read("/region0/off_by") == OSC::None ); + REQUIRE( d.read("/region1/off_by") == 5 ); + REQUIRE( d.read("/region2/off_by") == -2 ); + } -TEST_CASE("[Values] Off time") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav off_time=0.1 - sample=kick.wav off_time=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/off_time", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/off_time", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/off_time", "", nullptr); - std::vector expected { - "/region0/off_time,f : { 0.006 }", - "/region1/off_time,f : { 0.1 }", - "/region2/off_time,f : { -1 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Off mode") + { + d.load(R"( + sample=kick.wav + sample=kick.wav off_mode=fast + sample=kick.wav off_mode=normal + sample=kick.wav off_mode=time + sample=kick.wav off_mode=time off_mode=normal + sample=kick.wav off_mode=nothing + )"); + REQUIRE( d.read("/region0/off_mode") == "fast" ); + REQUIRE( d.read("/region1/off_mode") == "fast" ); + REQUIRE( d.read("/region2/off_mode") == "normal" ); + REQUIRE( d.read("/region3/off_mode") == "time" ); + REQUIRE( d.read("/region4/off_mode") == "normal" ); + REQUIRE( d.read("/region5/off_mode") == "fast" ); + } -TEST_CASE("[Values] Key range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lokey=34 hikey=60 - sample=kick.wav lokey=c4 hikey=b5 - sample=kick.wav lokey=-3 hikey=60 - sample=kick.wav hikey=-1 - sample=kick.wav pitch_keycenter=32 - sample=kick.wav pitch_keycenter=-1 - sample=kick.wav key=26 - )"); - synth.dispatchMessage(client, 0, "/region0/key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitch_keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/pitch_keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region6/pitch_keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region7/key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region7/pitch_keycenter", "", nullptr); - std::vector expected { - "/region0/key_range,ii : { 0, 127 }", - "/region1/key_range,ii : { 34, 60 }", - "/region2/key_range,ii : { 60, 83 }", - "/region3/key_range,ii : { 0, 60 }", - "/region4/key_range,ii : { 0, 127 }", - "/region0/pitch_keycenter,i : { 60 }", - "/region5/pitch_keycenter,i : { 32 }", - "/region6/pitch_keycenter,i : { 60 }", - "/region7/key_range,ii : { 26, 26 }", - "/region7/pitch_keycenter,i : { 26 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Off time") + { + d.load(R"( + sample=kick.wav + sample=kick.wav off_time=0.1 + sample=kick.wav off_time=-1 + )"); + REQUIRE( d.read("/region0/off_time") == 0.006f); + REQUIRE( d.read("/region1/off_time") == 0.1f); + REQUIRE( d.read("/region2/off_time") == -1.0f); + } -TEST_CASE("[Values] Triggers on note") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav hikey=-1 - sample=kick.wav key=-1 - sample=kick.wav hikey=-1 lokey=12 - sample=kick.wav hikey=-1 lokey=-1 - sample=kick.wav hikey=0 lokey=12 - )"); - synth.dispatchMessage(client, 0, "/region0/trigger_on_note", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/trigger_on_note", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/trigger_on_note", "", nullptr); - // TODO: Double check with Sforzando/rgc - synth.dispatchMessage(client, 0, "/region3/trigger_on_note", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/trigger_on_note", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/trigger_on_note", "", nullptr); - std::vector expected { - "/region0/trigger_on_note,T : { }", - "/region1/trigger_on_note,F : { }", - "/region2/trigger_on_note,F : { }", - "/region3/trigger_on_note,F : { }", - "/region4/trigger_on_note,F : { }", - "/region5/trigger_on_note,T : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Key range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lokey=34 hikey=60 + sample=kick.wav lokey=c4 hikey=b5 + sample=kick.wav lokey=-3 hikey=60 + sample=kick.wav hikey=-1 + sample=kick.wav pitch_keycenter=32 + sample=kick.wav pitch_keycenter=-1 + sample=kick.wav key=26 + )"); + REQUIRE_THAT( d.readAll("/region0/key_range"), Catch::Approx(std::vector{ 0, 127 })); + REQUIRE_THAT( d.readAll("/region1/key_range"), Catch::Approx(std::vector{ 34, 60 })); + REQUIRE_THAT( d.readAll("/region2/key_range"), Catch::Approx(std::vector{ 60, 83 })); + REQUIRE_THAT( d.readAll("/region3/key_range"), Catch::Approx(std::vector{ 0, 60 })); + REQUIRE_THAT( d.readAll("/region4/key_range"), Catch::Approx(std::vector{ 0, 127 })); + REQUIRE( d.read("/region0/pitch_keycenter") == 60); + REQUIRE( d.read("/region5/pitch_keycenter") == 32); + REQUIRE( d.read("/region6/pitch_keycenter") == 60); + REQUIRE_THAT( d.readAll("/region7/key_range"), Catch::Approx(std::vector{ 26, 26 })); + REQUIRE( d.read("/region7/pitch_keycenter") == 26); + } -TEST_CASE("[Values] Velocity range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lovel=34 hivel=60 - sample=kick.wav lovel=-3 hivel=60 - sample=kick.wav hivel=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/vel_range", "", nullptr); - std::vector expected { - "/region0/vel_range,ff : { 0, 1 }", - "/region1/vel_range,ff : { 0.267717, 0.480315 }", - "/region2/vel_range,ff : { -0.023622, 0.480315 }", - "/region3/vel_range,ff : { 0, -0.00787402 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Triggers on note") + { + d.load(R"( + sample=kick.wav + sample=kick.wav hikey=-1 + sample=kick.wav key=-1 + sample=kick.wav hikey=-1 lokey=12 + sample=kick.wav hikey=-1 lokey=-1 + sample=kick.wav hikey=0 lokey=12 + )"); + REQUIRE( d.read("/region0/trigger_on_note") == OSC::True); + REQUIRE( d.read("/region1/trigger_on_note") == OSC::False); + REQUIRE( d.read("/region2/trigger_on_note") == OSC::False); + // TODO: Double check with Sforzando/rgc + REQUIRE( d.read("/region3/trigger_on_note") == OSC::False); + REQUIRE( d.read("/region4/trigger_on_note") == OSC::False); + REQUIRE( d.read("/region5/trigger_on_note") == OSC::True); + } -TEST_CASE("[Values] Bend range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lobend=891 hibend=2000 - sample=kick.wav lobend=-891 hibend=891 - sample=kick.wav hibend=-10000 - )"); - synth.dispatchMessage(client, 0, "/region0/bend_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/bend_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/bend_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/bend_range", "", nullptr); - std::vector expected { - "/region0/bend_range,ff : { -1, 1 }", - "/region1/bend_range,ff : { 0.108778, 0.24417 }", - "/region2/bend_range,ff : { -0.108778, 0.108778 }", - "/region3/bend_range,ff : { -1, -1.22085 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Velocity range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lovel=34 hivel=60 + sample=kick.wav lovel=-3 hivel=60 + sample=kick.wav hivel=-1 + )"); + REQUIRE_THAT( d.readAll("/region0/vel_range"), Catch::Approx(std::vector{ 0.0f, 1.0f })); + REQUIRE_THAT( d.readAll("/region1/vel_range"), Catch::Approx(std::vector{ 34_norm, 61_norm })); + REQUIRE_THAT( d.readAll("/region2/vel_range"), Catch::Approx(std::vector{ -3_norm, 61_norm })); + REQUIRE_THAT( d.readAll("/region3/vel_range"), Catch::Approx(std::vector{ 0.0f, -1_norm })); + } -TEST_CASE("[Values] Program range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav loprog=1 hiprog=45 - sample=kick.wav loprog=-1 hiprog=555 - sample=kick.wav hiprog=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/program_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/program_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/program_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/program_range", "", nullptr); - std::vector expected { - "/region0/program_range,ii : { 0, 127 }", - "/region1/program_range,ii : { 1, 45 }", - "/region2/program_range,ii : { 0, 127 }", - "/region3/program_range,ii : { 0, 127 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Bend range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lobend=891 hibend=2000 + sample=kick.wav lobend=-891 hibend=891 + sample=kick.wav hibend=-10000 + )"); + REQUIRE_THAT( d.readAll("/region0/bend_range"), Catch::Approx(std::vector{ -1.0f, 1.0f })); + REQUIRE_THAT( d.readAll("/region1/bend_range"), Catch::Approx(std::vector{ 891.0_bend, 2000.0_bend })); + REQUIRE_THAT( d.readAll("/region2/bend_range"), Catch::Approx(std::vector{ -891.0_bend, 891.0_bend })); + REQUIRE_THAT( d.readAll("/region3/bend_range"), Catch::Approx(std::vector{ -1.0f, -10000.0_bend })); + } -TEST_CASE("[Values] CC condition range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Program range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav loprog=1 hiprog=45 + sample=kick.wav loprog=-1 hiprog=555 + sample=kick.wav hiprog=-1 + )"); + REQUIRE_THAT( d.readAll("/region0/program_range"), Catch::Approx(std::vector{ 0, 127 })); + REQUIRE_THAT( d.readAll("/region1/program_range"), Catch::Approx(std::vector{ 1, 45 })); + REQUIRE_THAT( d.readAll("/region2/program_range"), Catch::Approx(std::vector{ 0, 127 })); + REQUIRE_THAT( d.readAll("/region3/program_range"), Catch::Approx(std::vector{ 0, 127 })); + } - SECTION("Basic") + SECTION("CC condition basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav locc1=0 hicc1=54 sample=kick.wav locc1=0 hicc1=54 locc2=2 hicc2=10 sample=kick.wav locc1=10 hicc1=-1 )"); - synth.dispatchMessage(client, 0, "/region0/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/cc_range2", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/cc_range1", "", nullptr); - std::vector expected { - "/region0/cc_range1,ff : { 0, 1 }", - "/region1/cc_range1,ff : { 0, 0.433071 }", - "/region2/cc_range1,ff : { 0, 0.433071 }", - "/region2/cc_range2,ff : { 0.015748, 0.0866142 }", - "/region3/cc_range1,ff : { 0.0787402, -0.00787402 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/cc_range1"), Catch::Approx(std::vector{ 0.0f, 1.0f })); + REQUIRE_THAT( d.readAll("/region1/cc_range1"), Catch::Approx(std::vector{ 0.0f, 55_norm })); + REQUIRE_THAT( d.readAll("/region2/cc_range1"), Catch::Approx(std::vector{ 0.0f, 55_norm })); + REQUIRE_THAT( d.readAll("/region2/cc_range2"), Catch::Approx(std::vector{ 2_norm, 11_norm })); + REQUIRE_THAT( d.readAll("/region3/cc_range1"), Catch::Approx(std::vector{ 10_norm, -1_norm })); } - SECTION("hdcc") + SECTION("HDCC conditions") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav lohdcc1=0 hihdcc1=0.1 sample=kick.wav lohdcc1=0 hihdcc1=0.1 lohdcc2=0.1 hihdcc2=0.2 sample=kick.wav lohdcc1=0.1 hihdcc1=-0.1 )"); - synth.dispatchMessage(client, 0, "/region0/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/cc_range2", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/cc_range1", "", nullptr); - std::vector expected { - "/region0/cc_range1,ff : { 0, 1 }", - "/region1/cc_range1,ff : { 0, 0.1 }", - "/region2/cc_range1,ff : { 0, 0.1 }", - "/region2/cc_range2,ff : { 0.1, 0.2 }", - "/region3/cc_range1,ff : { 0.1, -0.1 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/cc_range1"), Catch::Approx(std::vector{ 0.0f, 1.0f })); + REQUIRE_THAT( d.readAll("/region1/cc_range1"), Catch::Approx(std::vector{ 0.0f, 0.1f })); + REQUIRE_THAT( d.readAll("/region2/cc_range1"), Catch::Approx(std::vector{ 0.0f, 0.1f })); + REQUIRE_THAT( d.readAll("/region2/cc_range2"), Catch::Approx(std::vector{ 0.1f, 0.2f })); + REQUIRE_THAT( d.readAll("/region3/cc_range1"), Catch::Approx(std::vector{ 0.1f, -0.1f })); } - SECTION("realcc") + SECTION("RealCC conditions ") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav lorealcc1=0 hirealcc1=0.1 sample=kick.wav lorealcc1=0 hirealcc1=0.1 lorealcc2=0.1 hirealcc2=0.2 sample=kick.wav lorealcc1=0.1 hirealcc1=-0.1 )"); - synth.dispatchMessage(client, 0, "/region0/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/cc_range2", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/cc_range1", "", nullptr); - std::vector expected { - "/region0/cc_range1,ff : { 0, 1 }", - "/region1/cc_range1,ff : { 0, 0.1 }", - "/region2/cc_range1,ff : { 0, 0.1 }", - "/region2/cc_range2,ff : { 0.1, 0.2 }", - "/region3/cc_range1,ff : { 0.1, -0.1 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/cc_range1"), Catch::Approx(std::vector{ 0.0f, 1.0f })); + REQUIRE_THAT( d.readAll("/region1/cc_range1"), Catch::Approx(std::vector{ 0.0f, 0.1f })); + REQUIRE_THAT( d.readAll("/region2/cc_range1"), Catch::Approx(std::vector{ 0.0f, 0.1f })); + REQUIRE_THAT( d.readAll("/region2/cc_range2"), Catch::Approx(std::vector{ 0.1f, 0.2f })); + REQUIRE_THAT( d.readAll("/region3/cc_range1"), Catch::Approx(std::vector{ 0.1f, -0.1f })); } -} - -TEST_CASE("[Values] Last keyswitch") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Basic") + SECTION("Last keyswitch basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav sw_last=12 sample=kick.wav sw_last=c4 @@ -825,493 +467,287 @@ TEST_CASE("[Values] Last keyswitch") sample=kick.wav sw_lolast=c4 sw_hilast=b5 sample=kick.wav sw_last=-1 )"); - synth.dispatchMessage(client, 0, "/region0/sw_last", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sw_last", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sw_last", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sw_last", "", nullptr); + REQUIRE( d.read("/region0/sw_last") == OSC::None); + REQUIRE( d.read("/region1/sw_last") == 12); + REQUIRE( d.read("/region2/sw_last") == 60); + REQUIRE_THAT( d.readAll("/region3/sw_last"), Catch::Approx(std::vector{14, 16} )); // TODO: activate for the new region parser ; can handle note names - // synth.dispatchMessage(client, 0, "/region4/sw_last", "", nullptr); + // REQUIRE( d.readAll("/region4/sw_last") == ); // TODO: activate for the new region parser ; ignore the second value - // synth.dispatchMessage(client, 0, "/region5/sw_last", "", nullptr); - std::vector expected { - "/region0/sw_last,N : { }", - "/region1/sw_last,i : { 12 }", - "/region2/sw_last,i : { 60 }", - "/region3/sw_last,ii : { 14, 16 }", - // "/region4/sw_last,ii : { 60, 83 }", - // "/region5/sw_last,ii : { 0, 0 }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.readAll("/region5/sw_last") == ); } SECTION("sw_lolast disables sw_last over the whole region") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sw_last=12 sw_lolast=14 sw_last=16 )"); - synth.dispatchMessage(client, 0, "/region0/sw_last", "", nullptr); + REQUIRE_THAT( d.readAll("/region0/sw_last"), Catch::Approx(std::vector{14, 14} )); std::vector expected { "/region0/sw_last,ii : { 14, 14 }", }; - REQUIRE(messageList == expected); } -} -TEST_CASE("[Values] Keyswitch label") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sw_label=hello - )"); - synth.dispatchMessage(client, 0, "/region0/sw_label", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sw_label", "", nullptr); - std::vector expected { - "/region0/sw_label,N : { }", - "/region1/sw_label,s : { hello }", - }; - REQUIRE(messageList == expected); -} + SECTION("Keyswitch label") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sw_label=hello + )"); + REQUIRE( d.read("/region0/sw_label") == OSC::None ); + REQUIRE( d.read("/region1/sw_label") == "hello" ); + } -TEST_CASE("[Values] Upswitch") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sw_up=16 - sample=kick.wav sw_up=-1 - sample=kick.wav sw_up=128 - sample=kick.wav sw_up=c4 - sample=kick.wav sw_up=64 sw_up=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sw_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sw_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sw_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sw_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/sw_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/sw_up", "", nullptr); - std::vector expected { - "/region0/sw_up,N : { }", - "/region1/sw_up,i : { 16 }", - "/region2/sw_up,N : { }", - "/region3/sw_up,N : { }", - "/region4/sw_up,i : { 60 }", - "/region5/sw_up,N : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Upswitch") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sw_up=16 + sample=kick.wav sw_up=-1 + sample=kick.wav sw_up=128 + sample=kick.wav sw_up=c4 + sample=kick.wav sw_up=64 sw_up=-1 + )"); + REQUIRE( d.read("/region0/sw_up") == OSC::None); + REQUIRE( d.read("/region1/sw_up") == 16); + REQUIRE( d.read("/region2/sw_up") == OSC::None); + REQUIRE( d.read("/region3/sw_up") == OSC::None); + REQUIRE( d.read("/region4/sw_up") == 60); + REQUIRE( d.read("/region5/sw_up") == OSC::None); + } -TEST_CASE("[Values] Downswitch") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sw_down=16 - sample=kick.wav sw_down=-1 - sample=kick.wav sw_down=128 - sample=kick.wav sw_down=c4 - sample=kick.wav sw_down=64 sw_down=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sw_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sw_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sw_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sw_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/sw_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/sw_down", "", nullptr); - std::vector expected { - "/region0/sw_down,N : { }", - "/region1/sw_down,i : { 16 }", - "/region2/sw_down,N : { }", - "/region3/sw_down,N : { }", - "/region4/sw_down,i : { 60 }", - "/region5/sw_down,N : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Downswitch") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sw_down=16 + sample=kick.wav sw_down=-1 + sample=kick.wav sw_down=128 + sample=kick.wav sw_down=c4 + sample=kick.wav sw_down=64 sw_down=-1 + )"); + REQUIRE( d.read("/region0/sw_down") == OSC::None); + REQUIRE( d.read("/region1/sw_down") == 16); + REQUIRE( d.read("/region2/sw_down") == OSC::None); + REQUIRE( d.read("/region3/sw_down") == OSC::None); + REQUIRE( d.read("/region4/sw_down") == 60); + REQUIRE( d.read("/region5/sw_down") == OSC::None); + } -TEST_CASE("[Values] Previous keyswitch") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sw_previous=16 - sample=kick.wav sw_previous=-1 - sample=kick.wav sw_previous=128 - sample=kick.wav sw_previous=c4 - sample=kick.wav sw_previous=64 sw_previous=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sw_previous", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sw_previous", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sw_previous", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sw_previous", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/sw_previous", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/sw_previous", "", nullptr); - std::vector expected { - "/region0/sw_previous,N : { }", - "/region1/sw_previous,i : { 16 }", - "/region2/sw_previous,N : { }", - "/region3/sw_previous,N : { }", - "/region4/sw_previous,i : { 60 }", - "/region5/sw_previous,N : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Previous keyswitch") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sw_previous=16 + sample=kick.wav sw_previous=-1 + sample=kick.wav sw_previous=128 + sample=kick.wav sw_previous=c4 + sample=kick.wav sw_previous=64 sw_previous=-1 + )"); + REQUIRE( d.read("/region0/sw_previous") == OSC::None); + REQUIRE( d.read("/region1/sw_previous") == 16); + REQUIRE( d.read("/region2/sw_previous") == OSC::None); + REQUIRE( d.read("/region3/sw_previous") == OSC::None); + REQUIRE( d.read("/region4/sw_previous") == 60); + REQUIRE( d.read("/region5/sw_previous") == OSC::None); + } -TEST_CASE("[Values] Velocity override") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sw_vel=current - sample=kick.wav sw_vel=previous - sample=kick.wav sw_vel=previous sw_vel=current - )"); - synth.dispatchMessage(client, 0, "/region0/sw_vel", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sw_vel", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sw_vel", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sw_vel", "", nullptr); - std::vector expected { - "/region0/sw_vel,s : { current }", - "/region1/sw_vel,s : { current }", - "/region2/sw_vel,s : { previous }", - "/region3/sw_vel,s : { current }", - }; - REQUIRE(messageList == expected); -} + SECTION("Velocity override") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sw_vel=current + sample=kick.wav sw_vel=previous + sample=kick.wav sw_vel=previous sw_vel=current + )"); + REQUIRE( d.read("/region0/sw_vel") == "current"); + REQUIRE( d.read("/region1/sw_vel") == "current"); + REQUIRE( d.read("/region2/sw_vel") == "previous"); + REQUIRE( d.read("/region3/sw_vel") == "current"); + } -TEST_CASE("[Values] Aftertouch range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lochanaft=34 hichanaft=60 - sample=kick.wav lochanaft=-3 hichanaft=60 - sample=kick.wav lochanaft=20 hichanaft=-1 - sample=kick.wav lochanaft=20 hichanaft=10 - )"); - synth.dispatchMessage(client, 0, "/region0/chanaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/chanaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/chanaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/chanaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/chanaft_range", "", nullptr); - std::vector expected { - "/region0/chanaft_range,ff : { 0, 1 }", - "/region1/chanaft_range,ff : { 0.267717, 0.480315 }", - "/region2/chanaft_range,ff : { -0.023622, 0.480315 }", - "/region3/chanaft_range,ff : { 0.15748, -0.00787402 }", - "/region4/chanaft_range,ff : { 0.15748, 0.0866142 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Aftertouch range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lochanaft=34 hichanaft=60 + sample=kick.wav lochanaft=-3 hichanaft=60 + sample=kick.wav lochanaft=20 hichanaft=-1 + sample=kick.wav lochanaft=20 hichanaft=10 + )"); + REQUIRE_THAT( d.readAll("/region0/chanaft_range"), Catch::Approx(std::vector{ 0, 1 })); + REQUIRE_THAT( d.readAll("/region1/chanaft_range"), Catch::Approx(std::vector{ 34_norm, 61_norm })); + REQUIRE_THAT( d.readAll("/region2/chanaft_range"), Catch::Approx(std::vector{ -3_norm, 61_norm })); + REQUIRE_THAT( d.readAll("/region3/chanaft_range"), Catch::Approx(std::vector{ 20_norm, -1_norm })); + REQUIRE_THAT( d.readAll("/region4/chanaft_range"), Catch::Approx(std::vector{ 20_norm, 11_norm })); + } -TEST_CASE("[Values] Polyaftertouch range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lopolyaft=34 hipolyaft=60 - sample=kick.wav lopolyaft=-3 hipolyaft=60 - sample=kick.wav lopolyaft=20 hipolyaft=-1 - sample=kick.wav lopolyaft=20 hipolyaft=10 - )"); - synth.dispatchMessage(client, 0, "/region0/polyaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/polyaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/polyaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/polyaft_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/polyaft_range", "", nullptr); - std::vector expected { - "/region0/polyaft_range,ff : { 0, 1 }", - "/region1/polyaft_range,ff : { 0.267717, 0.480315 }", - "/region2/polyaft_range,ff : { -0.023622, 0.480315 }", - "/region3/polyaft_range,ff : { 0.15748, -0.00787402 }", - "/region4/polyaft_range,ff : { 0.15748, 0.0866142 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Polyaftertouch range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lopolyaft=34 hipolyaft=60 + sample=kick.wav lopolyaft=-3 hipolyaft=60 + sample=kick.wav lopolyaft=20 hipolyaft=-1 + sample=kick.wav lopolyaft=20 hipolyaft=10 + )"); + REQUIRE_THAT( d.readAll("/region0/polyaft_range"), Catch::Approx(std::vector{ 0, 1 })); + REQUIRE_THAT( d.readAll("/region1/polyaft_range"), Catch::Approx(std::vector{ 34_norm, 61_norm })); + REQUIRE_THAT( d.readAll("/region2/polyaft_range"), Catch::Approx(std::vector{ -3_norm, 61_norm })); + REQUIRE_THAT( d.readAll("/region3/polyaft_range"), Catch::Approx(std::vector{ 20_norm, -1_norm })); + REQUIRE_THAT( d.readAll("/region4/polyaft_range"), Catch::Approx(std::vector{ 20_norm, 11_norm })); + } -TEST_CASE("[Values] BPM range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lobpm=34.1 hibpm=60.2 - sample=kick.wav lobpm=-3 hibpm=60 - sample=kick.wav lobpm=20 hibpm=-1 - sample=kick.wav lobpm=20 hibpm=10 - )"); - synth.dispatchMessage(client, 0, "/region0/bpm_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/bpm_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/bpm_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/bpm_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/bpm_range", "", nullptr); - std::vector expected { - "/region0/bpm_range,ff : { 0, 500 }", - "/region1/bpm_range,ff : { 34.1, 60.2 }", - "/region2/bpm_range,ff : { -3, 60 }", - "/region3/bpm_range,ff : { 20, -1 }", - "/region4/bpm_range,ff : { 20, 10 }", - }; - REQUIRE(messageList == expected); -} + SECTION("BPM range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lobpm=34.1 hibpm=60.2 + sample=kick.wav lobpm=-3 hibpm=60 + sample=kick.wav lobpm=20 hibpm=-1 + sample=kick.wav lobpm=20 hibpm=10 + )"); + REQUIRE_THAT( d.readAll("/region0/bpm_range"), Catch::Approx(std::vector{ 0, 500 })); + REQUIRE_THAT( d.readAll("/region1/bpm_range"), Catch::Approx(std::vector{ 34.1, 60.2 })); + REQUIRE_THAT( d.readAll("/region2/bpm_range"), Catch::Approx(std::vector{ -3, 60 })); + REQUIRE_THAT( d.readAll("/region3/bpm_range"), Catch::Approx(std::vector{ 20, -1 })); + REQUIRE_THAT( d.readAll("/region4/bpm_range"), Catch::Approx(std::vector{ 20, 10 })); + } -TEST_CASE("[Values] Rand range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lorand=0.2 hirand=0.4 - sample=kick.wav lorand=-0.1 hirand=0.4 - sample=kick.wav lorand=0.2 hirand=-0.1 - sample=kick.wav lorand=0.2 hirand=0.1 - )"); - synth.dispatchMessage(client, 0, "/region0/rand_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/rand_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/rand_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/rand_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/rand_range", "", nullptr); - std::vector expected { - "/region0/rand_range,ff : { 0, 1 }", - "/region1/rand_range,ff : { 0.2, 0.4 }", - "/region2/rand_range,ff : { -0.1, 0.4 }", - "/region3/rand_range,ff : { 0.2, -0.1 }", - "/region4/rand_range,ff : { 0.2, 0.1 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Rand range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lorand=0.2 hirand=0.4 + sample=kick.wav lorand=-0.1 hirand=0.4 + sample=kick.wav lorand=0.2 hirand=-0.1 + sample=kick.wav lorand=0.2 hirand=0.1 + )"); -TEST_CASE("[Values] Timer range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav lotimer=0.2 hitimer=0.4 - sample=kick.wav lotimer=-0.1 hitimer=0.4 - sample=kick.wav lotimer=0.2 hitimer=-0.1 - )"); - synth.dispatchMessage(client, 0, "/region0/timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/use_timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/use_timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/use_timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/timer_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/use_timer_range", "", nullptr); - std::vector expected { - "/region0/timer_range,ff : { 0, 3.40282e+38 }", - "/region0/use_timer_range,F : { }", - "/region1/timer_range,ff : { 0.2, 0.4 }", - "/region1/use_timer_range,T : { }", - "/region2/timer_range,ff : { 0, 0.4 }", - "/region2/use_timer_range,T : { }", - "/region3/timer_range,ff : { 0.2, 3.40282e+38 }", - "/region3/use_timer_range,T : { }", - }; - REQUIRE(messageList == expected); -} + REQUIRE_THAT( d.readAll("/region0/rand_range"), Catch::Approx(std::vector{ 0, 1 })); + REQUIRE_THAT( d.readAll("/region1/rand_range"), Catch::Approx(std::vector{ 0.2, 0.4 })); + REQUIRE_THAT( d.readAll("/region2/rand_range"), Catch::Approx(std::vector{ -0.1, 0.4 })); + REQUIRE_THAT( d.readAll("/region3/rand_range"), Catch::Approx(std::vector{ 0.2, -0.1 })); + REQUIRE_THAT( d.readAll("/region4/rand_range"), Catch::Approx(std::vector{ 0.2, 0.1 })); + } -TEST_CASE("[Values] Sequence length") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav seq_length=12 - sample=kick.wav seq_length=-1 - sample=kick.wav seq_length=12 seq_length=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/seq_length", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/seq_length", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/seq_length", "", nullptr); - // TODO: activate for the new region parser ; ignore the second value - // synth.dispatchMessage(client, 0, "/region3/seq_length", "", nullptr); - std::vector expected { - "/region0/seq_length,i : { 1 }", - "/region1/seq_length,i : { 12 }", - "/region2/seq_length,i : { 1 }", - // TODO: activate for the new region parser ; ignore the second value - // "/region3/seq_length,f : { 12 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Timer range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav lotimer=0.2 hitimer=0.4 + sample=kick.wav lotimer=-0.1 hitimer=0.4 + sample=kick.wav lotimer=0.2 hitimer=-0.1 + )"); + REQUIRE_THAT( d.readAll("/region0/timer_range"), Catch::Approx(std::vector{ 0, 3.40282e+38 })); + REQUIRE_THAT( d.readAll("/region1/timer_range"), Catch::Approx(std::vector{ 0.2, 0.4 })); + REQUIRE_THAT( d.readAll("/region2/timer_range"), Catch::Approx(std::vector{ 0, 0.4 })); + REQUIRE_THAT( d.readAll("/region3/timer_range"), Catch::Approx(std::vector{ 0.2, 3.40282e+38 })); + REQUIRE( d.read("/region0/use_timer_range") == OSC::False); + REQUIRE( d.read("/region1/use_timer_range") == OSC::True); + REQUIRE( d.read("/region2/use_timer_range") == OSC::True); + REQUIRE( d.read("/region3/use_timer_range") == OSC::True); + } -TEST_CASE("[Values] Sequence position") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav seq_position=12 - sample=kick.wav seq_position=-1 - sample=kick.wav seq_position=12 seq_position=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/seq_position", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/seq_position", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/seq_position", "", nullptr); - // TODO: activate for the new region parser ; ignore the second value - // synth.dispatchMessage(client, 0, "/region3/seq_position", "", nullptr); - std::vector expected { - "/region0/seq_position,i : { 1 }", - "/region1/seq_position,i : { 12 }", - "/region2/seq_position,i : { 1 }", + SECTION("Sequence length") + { + d.load(R"( + sample=kick.wav + sample=kick.wav seq_length=12 + sample=kick.wav seq_length=-1 + sample=kick.wav seq_length=12 seq_length=-1 + )"); + REQUIRE( d.read("/region0/seq_length") == 1); + REQUIRE( d.read("/region1/seq_length") == 12); + REQUIRE( d.read("/region2/seq_length") == 1); // TODO: activate for the new region parser ; ignore the second value - // "/region3/seq_position,f : { 12 }", - }; - REQUIRE(messageList == expected); -} + // REQUIRE( d.read("/region3/seq_length") == 12); + } -TEST_CASE("[Values] Trigger type") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav trigger=release - sample=kick.wav trigger=release_key - sample=kick.wav trigger=legato - sample=kick.wav trigger=first - sample=kick.wav trigger=nothing - sample=kick.wav trigger=release trigger=attack - )"); - synth.dispatchMessage(client, 0, "/region0/trigger", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/trigger", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/trigger", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/trigger", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/trigger", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/trigger", "", nullptr); - synth.dispatchMessage(client, 0, "/region6/trigger", "", nullptr); - std::vector expected { - "/region0/trigger,s : { attack }", - "/region1/trigger,s : { release }", - "/region2/trigger,s : { release_key }", - "/region3/trigger,s : { legato }", - "/region4/trigger,s : { first }", - "/region5/trigger,s : { attack }", - "/region6/trigger,s : { attack }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sequence position") + { + d.load(R"( + sample=kick.wav + sample=kick.wav seq_position=12 + sample=kick.wav seq_position=-1 + sample=kick.wav seq_position=12 seq_position=-1 + )"); + REQUIRE( d.read("/region0/seq_position") == 1); + REQUIRE( d.read("/region1/seq_position") == 12); + REQUIRE( d.read("/region2/seq_position") == 1); + // TODO: activate for the new region parser ; ignore the second value + // REQUIRE( d.read("/region3/seq_position") == ); + } -TEST_CASE("[Values] Start on cc range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav on_locc1=15 - sample=kick.wav on_hicc1=84 - sample=kick.wav on_locc1=15 on_hicc1=84 - sample=kick.wav on_lohdcc2=0.1 - sample=kick.wav on_hihdcc2=0.4 - sample=kick.wav on_lohdcc2=0.1 on_hihdcc2=0.4 - )"); - synth.dispatchMessage(client, 0, "/region0/start_cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/start_cc_range2", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/start_cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/start_cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/start_cc_range1", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/start_cc_range2", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/start_cc_range2", "", nullptr); - synth.dispatchMessage(client, 0, "/region6/start_cc_range2", "", nullptr); - std::vector expected { - "/region0/start_cc_range1,N : { }", - "/region0/start_cc_range2,N : { }", - "/region1/start_cc_range1,ff : { 0.11811, 1 }", - "/region2/start_cc_range1,ff : { 0, 0.669291 }", - "/region3/start_cc_range1,ff : { 0.11811, 0.669291 }", - "/region4/start_cc_range2,ff : { 0.1, 1 }", - "/region5/start_cc_range2,ff : { 0, 0.4 }", - "/region6/start_cc_range2,ff : { 0.1, 0.4 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Trigger type") + { + d.load(R"( + sample=kick.wav + sample=kick.wav trigger=release + sample=kick.wav trigger=release_key + sample=kick.wav trigger=legato + sample=kick.wav trigger=first + sample=kick.wav trigger=nothing + sample=kick.wav trigger=release trigger=attack + )"); + REQUIRE( d.read("/region0/trigger") == "attack"); + REQUIRE( d.read("/region1/trigger") == "release"); + REQUIRE( d.read("/region2/trigger") == "release_key"); + REQUIRE( d.read("/region3/trigger") == "legato"); + REQUIRE( d.read("/region4/trigger") == "first"); + REQUIRE( d.read("/region5/trigger") == "attack"); + REQUIRE( d.read("/region6/trigger") == "attack"); + } -TEST_CASE("[Values] Volume") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Start on cc range") + { + d.load(R"( + sample=kick.wav + sample=kick.wav on_locc1=15 + sample=kick.wav on_hicc1=84 + sample=kick.wav on_locc1=15 on_hicc1=84 + sample=kick.wav on_lohdcc2=0.1 + sample=kick.wav on_hihdcc2=0.4 + sample=kick.wav on_lohdcc2=0.1 on_hihdcc2=0.4 + )"); + REQUIRE( d.read("/region0/start_cc_range1") == OSC::None); + REQUIRE( d.read("/region0/start_cc_range2") == OSC::None); + REQUIRE_THAT( d.readAll("/region1/start_cc_range1"), Catch::Approx(std::vector{ 15_norm, 1 })); + REQUIRE_THAT( d.readAll("/region2/start_cc_range1"), Catch::Approx(std::vector{ 0, 85_norm })); + REQUIRE_THAT( d.readAll("/region3/start_cc_range1"), Catch::Approx(std::vector{ 15_norm, 85_norm })); + REQUIRE_THAT( d.readAll("/region4/start_cc_range2"), Catch::Approx(std::vector{ 0.1, 1 })); + REQUIRE_THAT( d.readAll("/region5/start_cc_range2"), Catch::Approx(std::vector{ 0, 0.4f })); + REQUIRE_THAT( d.readAll("/region6/start_cc_range2"), Catch::Approx(std::vector{ 0.1, 0.4f })); + } - SECTION("Basic") + SECTION("Volume Basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav volume=4.2 sample=kick.wav gain=-200 )"); - synth.dispatchMessage(client, 0, "/region0/volume", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/volume", "", nullptr); + REQUIRE( d.read("/region0/volume") == 0); + REQUIRE( d.read("/region1/volume") == 4.2f); // TODO: activate for the new region parser ; allow oob - // synth.dispatchMessage(client, 0, "/region2/volume", "", nullptr); - std::vector expected { - "/region0/volume,f : { 0 }", - "/region1/volume,f : { 4.2 }", - // "/region2/volume,f : { -200 }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read<>("/region2/volume") == ); } - SECTION("CC Depth") + SECTION("Volume CC Depth") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav volume_oncc42=4.2 sample=kick.wav gain_oncc2=-10 )"); - synth.dispatchMessage(client, 0, "/region0/volume_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/volume_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/volume_cc2", "", nullptr); - std::vector expected { - "/region0/volume_cc42,N : { }", - "/region1/volume_cc42,f : { 4.2 }", - "/region2/volume_cc2,f : { -10 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/volume_cc42") == OSC::None); + REQUIRE( d.read("/region1/volume_cc42") == 4.2f); + REQUIRE( d.read("/region2/volume_cc2") == -10.0f); } - SECTION("CC Params") + SECTION("Volume CC Params") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav volume_stepcc42=4.2 sample=kick.wav volume_smoothcc42=4 @@ -1320,33 +756,21 @@ TEST_CASE("[Values] Volume") sample=kick.wav volume_smoothcc42=-4 sample=kick.wav volume_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/volume_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/volume_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/volume_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/volume_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/volume_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/volume_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/volume_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/volume_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/volume_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/volume_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/volume_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/volume_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/volume_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/volume_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/volume_curvecc42", "", nullptr); - std::vector expected { - "/region0/volume_stepcc42,N : { }", - "/region0/volume_smoothcc42,N : { }", - "/region0/volume_curvecc42,N : { }", - "/region1/volume_stepcc42,f : { 4.2 }", - "/region2/volume_smoothcc42,i : { 4 }", - "/region3/volume_curvecc42,i : { 2 }", - // "/region4/volume_stepcc42,N : { }", - // "/region5/volume_smoothcc42,N : { }", - // "/region6/volume_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/volume_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/volume_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/volume_curvecc42") == OSC::None); } - SECTION("CC Params (with gain_)") + SECTION("Volume CC Params (with gain_)") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav gain_stepcc42=4.2 sample=kick.wav gain_smoothcc42=4 @@ -1355,79 +779,46 @@ TEST_CASE("[Values] Volume") sample=kick.wav gain_smoothcc42=-4 sample=kick.wav gain_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/volume_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/volume_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/volume_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/volume_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/volume_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/volume_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/volume_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/volume_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/volume_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/volume_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/volume_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/volume_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/volume_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/volume_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/volume_curvecc42", "", nullptr); - std::vector expected { - "/region0/volume_stepcc42,N : { }", - "/region0/volume_smoothcc42,N : { }", - "/region0/volume_curvecc42,N : { }", - "/region1/volume_stepcc42,f : { 4.2 }", - "/region2/volume_smoothcc42,i : { 4 }", - "/region3/volume_curvecc42,i : { 2 }", - // "/region4/volume_stepcc42,N : { }", - // "/region5/volume_smoothcc42,N : { }", - // "/region6/volume_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/volume_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/volume_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/volume_curvecc42") == OSC::None); } -} - -TEST_CASE("[Values] Pan") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Basic") + SECTION("Pan Basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pan=4.2 sample=kick.wav pan=-200 )"); - synth.dispatchMessage(client, 0, "/region0/pan", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pan", "", nullptr); - // TODO: activate for the new region parser ; accept oob - // synth.dispatchMessage(client, 0, "/region2/pan", "", nullptr); - std::vector expected { - "/region0/pan,f : { 0 }", - "/region1/pan,f : { 4.2 }", - // TODO: activate for the new region parser ; accept oob - // "/region2/pan,f : { -200 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pan") == 0); + REQUIRE( d.read("/region1/pan") == 4.2f); + // TODO: activate for the new region parser ; allow oob + // REQUIRE( d.read<>("/region2/pan") == ); } - SECTION("CC Depth") + SECTION("Pan CC Depth") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pan_oncc42=4.2 sample=kick.wav pan_oncc2=-10 )"); - synth.dispatchMessage(client, 0, "/region0/pan_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pan_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pan_cc2", "", nullptr); - std::vector expected { - "/region0/pan_cc42,N : { }", - "/region1/pan_cc42,f : { 4.2 }", - "/region2/pan_cc2,f : { -10 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pan_cc42") == OSC::None); + REQUIRE( d.read("/region1/pan_cc42") == 4.2f); + REQUIRE( d.read("/region2/pan_cc2") == -10.0f); } - SECTION("CC Params") + SECTION("Pan CC Params") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pan_stepcc42=4.2 sample=kick.wav pan_smoothcc42=4 @@ -1436,79 +827,46 @@ TEST_CASE("[Values] Pan") sample=kick.wav pan_smoothcc42=-4 sample=kick.wav pan_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/pan_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pan_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pan_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pan_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pan_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/pan_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/pan_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/pan_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/pan_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/pan_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/pan_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/pan_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/pan_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/pan_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/pan_curvecc42", "", nullptr); - std::vector expected { - "/region0/pan_stepcc42,N : { }", - "/region0/pan_smoothcc42,N : { }", - "/region0/pan_curvecc42,N : { }", - "/region1/pan_stepcc42,f : { 4.2 }", - "/region2/pan_smoothcc42,i : { 4 }", - "/region3/pan_curvecc42,i : { 2 }", - // "/region4/pan_stepcc42,N : { }", - // "/region5/pan_smoothcc42,N : { }", - // "/region6/pan_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/pan_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/pan_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/pan_curvecc42") == OSC::None); } -} - -TEST_CASE("[Values] Width") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Basic") + SECTION("Width Basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav width=4.2 sample=kick.wav width=-200 )"); - synth.dispatchMessage(client, 0, "/region0/width", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/width", "", nullptr); - // TODO: activate for the new region parser ; accept oob - // synth.dispatchMessage(client, 0, "/region2/width", "", nullptr); - std::vector expected { - "/region0/width,f : { 100 }", - "/region1/width,f : { 4.2 }", - // TODO: activate for the new region parser ; accept oob - // "/region2/width,f : { -200 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/width") == 100.0f); + REQUIRE( d.read("/region1/width") == 4.2f); + // TODO: activate for the new region parser ; allow oob + // REQUIRE( d.read<>("/region2/width") == -200.0f); } - SECTION("CC Depth") + SECTION("Width CC Depth") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav width_oncc42=4.2 sample=kick.wav width_oncc2=-10 )"); - synth.dispatchMessage(client, 0, "/region0/width_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/width_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/width_cc2", "", nullptr); - std::vector expected { - "/region0/width_cc42,N : { }", - "/region1/width_cc42,f : { 4.2 }", - "/region2/width_cc2,f : { -10 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/width_cc42") == OSC::None); + REQUIRE( d.read("/region1/width_cc42") == 4.2f); + REQUIRE( d.read("/region2/width_cc2") == -10.0f); } - SECTION("CC Params") + SECTION("Width CC Params") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav width_stepcc42=4.2 sample=kick.wav width_smoothcc42=4 @@ -1517,79 +875,46 @@ TEST_CASE("[Values] Width") sample=kick.wav width_smoothcc42=-4 sample=kick.wav width_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/width_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/width_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/width_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/width_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/width_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/width_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/width_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/width_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/width_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/width_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/width_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/width_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/width_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/width_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/width_curvecc42", "", nullptr); - std::vector expected { - "/region0/width_stepcc42,N : { }", - "/region0/width_smoothcc42,N : { }", - "/region0/width_curvecc42,N : { }", - "/region1/width_stepcc42,f : { 4.2 }", - "/region2/width_smoothcc42,i : { 4 }", - "/region3/width_curvecc42,i : { 2 }", - // "/region4/width_stepcc42,N : { }", - // "/region5/width_smoothcc42,N : { }", - // "/region6/width_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/width_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/width_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/width_curvecc42") == OSC::None); } -} - -TEST_CASE("[Values] Position") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Basic") + SECTION("Position Basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav position=4.2 sample=kick.wav position=-200 )"); - synth.dispatchMessage(client, 0, "/region0/position", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/position", "", nullptr); - // TODO: activate for the new region parser; accept oob - // synth.dispatchMessage(client, 0, "/region2/position", "", nullptr); - std::vector expected { - "/region0/position,f : { 0 }", - "/region1/position,f : { 4.2 }", - // TODO: activate for the new region parser; accept oob - // "/region2/position,f : { -200 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/position") == 0); + REQUIRE( d.read("/region1/position") == 4.2f); + // TODO: activate for the new region parser ; allow oob + // REQUIRE( d.read<>("/region2/position") == ); } - SECTION("CC Depth") + SECTION("Position CC Depth") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav position_oncc42=4.2 sample=kick.wav position_oncc2=-10 )"); - synth.dispatchMessage(client, 0, "/region0/position_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/position_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/position_cc2", "", nullptr); - std::vector expected { - "/region0/position_cc42,N : { }", - "/region1/position_cc42,f : { 4.2 }", - "/region2/position_cc2,f : { -10 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/position_cc42") == OSC::None); + REQUIRE( d.read("/region1/position_cc42") == 4.2f); + REQUIRE( d.read("/region2/position_cc2") == -10.0f); } - SECTION("CC Params") + SECTION("Position CC Params") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav position_stepcc42=4.2 sample=kick.wav position_smoothcc42=4 @@ -1598,79 +923,46 @@ TEST_CASE("[Values] Position") sample=kick.wav position_smoothcc42=-4 sample=kick.wav position_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/position_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/position_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/position_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/position_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/position_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/position_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/position_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/position_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/position_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/position_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/position_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/position_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/position_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/position_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/position_curvecc42", "", nullptr); - std::vector expected { - "/region0/position_stepcc42,N : { }", - "/region0/position_smoothcc42,N : { }", - "/region0/position_curvecc42,N : { }", - "/region1/position_stepcc42,f : { 4.2 }", - "/region2/position_smoothcc42,i : { 4 }", - "/region3/position_curvecc42,i : { 2 }", - // "/region4/position_stepcc42,N : { }", - // "/region5/position_smoothcc42,N : { }", - // "/region6/position_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/position_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/position_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/position_curvecc42") == OSC::None); } -} -TEST_CASE("[Values] Amplitude") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") + SECTION("Amplitude Basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav amplitude=4.2 sample=kick.wav amplitude=-200 )"); - synth.dispatchMessage(client, 0, "/region0/amplitude", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amplitude", "", nullptr); - // TODO: activate for the new region parser; ignore oob - // synth.dispatchMessage(client, 0, "/region2/amplitude", "", nullptr); - std::vector expected { - "/region0/amplitude,f : { 100 }", - "/region1/amplitude,f : { 4.2 }", - // "/region2/amplitude,f : { 100 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/amplitude") == 100.0f); + REQUIRE( d.read("/region1/amplitude") == 4.2f); + // TODO: activate for the new region parser ; allow oob + // REQUIRE( d.read<>("/region2/amplitude") == ); } - SECTION("CC Depth") + SECTION("Amplitude CC Depth") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav amplitude_oncc42=4.2 sample=kick.wav amplitude_oncc2=-10 )"); - synth.dispatchMessage(client, 0, "/region0/amplitude_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amplitude_cc42", "", nullptr); - // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region2/amplitude_cc2", "", nullptr); - std::vector expected { - "/region0/amplitude_cc42,N : { }", - "/region1/amplitude_cc42,f : { 4.2 }", - // "/region2/amplitude_cc2,N : { }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/amplitude_cc42") == OSC::None); + REQUIRE( d.read("/region1/amplitude_cc42") == 4.2f); + REQUIRE( d.read("/region2/amplitude_cc2") == -10.0f); } - SECTION("CC Params") + SECTION("Amplitude CC Params") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav amplitude_stepcc42=4.2 sample=kick.wav amplitude_smoothcc42=4 @@ -1679,615 +971,348 @@ TEST_CASE("[Values] Amplitude") sample=kick.wav amplitude_smoothcc42=-4 sample=kick.wav amplitude_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/amplitude_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/amplitude_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/amplitude_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amplitude_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/amplitude_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/amplitude_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/amplitude_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/amplitude_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/amplitude_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/amplitude_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/amplitude_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/amplitude_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/amplitude_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/amplitude_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/amplitude_curvecc42", "", nullptr); - std::vector expected { - "/region0/amplitude_stepcc42,N : { }", - "/region0/amplitude_smoothcc42,N : { }", - "/region0/amplitude_curvecc42,N : { }", - "/region1/amplitude_stepcc42,f : { 4.2 }", - "/region2/amplitude_smoothcc42,i : { 4 }", - "/region3/amplitude_curvecc42,i : { 2 }", - // "/region4/amplitude_stepcc42,N : { }", - // "/region5/amplitude_smoothcc42,N : { }", - // "/region6/amplitude_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/amplitude_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/amplitude_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/amplitude_curvecc42") == OSC::None); } -} -TEST_CASE("[Values] Amp Keycenter") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav amp_keycenter=40 - sample=kick.wav amp_keycenter=-1 - sample=kick.wav amp_keycenter=c3 - )"); - synth.dispatchMessage(client, 0, "/region0/amp_keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_keycenter", "", nullptr); - // TODO: activate for the new region parser ; ignore oob and parse note - // synth.dispatchMessage(client, 0, "/region2/amp_keycenter", "", nullptr); - // synth.dispatchMessage(client, 0, "/region3/amp_keycenter", "", nullptr); - std::vector expected { - "/region0/amp_keycenter,i : { 60 }", - "/region1/amp_keycenter,i : { 40 }", - // "/region2/amp_keycenter,i : { 60 }", - // "/region3/amp_keycenter,i : { 48 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Amp Keycenter") + { + d.load(R"( + sample=kick.wav + sample=kick.wav amp_keycenter=40 + sample=kick.wav amp_keycenter=-1 + sample=kick.wav amp_keycenter=c3 + )"); + REQUIRE( d.read("/region0/amp_keycenter") == 60 ); + REQUIRE( d.read("/region1/amp_keycenter") == 40 ); + REQUIRE( d.read("/region2/amp_keycenter") == 60 ); + REQUIRE( d.read("/region3/amp_keycenter") == 48 ); + } -TEST_CASE("[Values] Amp Keytrack") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav amp_keytrack=10.1 - sample=kick.wav amp_keytrack=40 - )"); - synth.dispatchMessage(client, 0, "/region0/amp_keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_keytrack", "", nullptr); - // TODO: activate for the new region parser ; accept oob - // synth.dispatchMessage(client, 0, "/region2/amp_keytrack", "", nullptr); - std::vector expected { - "/region0/amp_keytrack,f : { 0 }", - "/region1/amp_keytrack,f : { 10.1 }", - // "/region2/amp_keytrack,f : { 40 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Amp Keytrack") + { + d.load(R"( + sample=kick.wav + sample=kick.wav amp_keytrack=10.1 + sample=kick.wav amp_keytrack=40 + )"); + REQUIRE( d.read("/region0/amp_keytrack") == 0.0f); + REQUIRE( d.read("/region1/amp_keytrack") == 10.1f); + REQUIRE( d.read("/region2/amp_keytrack") == 40.0f); + std::vector expected { + "/region0/amp_keytrack,f : {}", + "/region1/amp_keytrack,f : {}", + // "/region2/amp_keytrack,f : {}", + }; + } -TEST_CASE("[Values] Amp Veltrack") +SECTION("Amp Veltrack") { - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") + SECTION("Amp veltrack basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav amp_veltrack=10.1 sample=kick.wav amp_veltrack=-132 )"); - synth.dispatchMessage(client, 0, "/region0/amp_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr); - std::vector expected { - "/region0/amp_veltrack,f : { 100 }", - "/region1/amp_veltrack,f : { 10.1 }", - "/region2/amp_veltrack,f : { -132 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/amp_veltrack") == 100.0f); + REQUIRE( d.read("/region1/amp_veltrack") == 10.1f); + REQUIRE( d.read("/region2/amp_veltrack") == -132.0f); } - SECTION("CC") + SECTION("Amp veltrack CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav amp_veltrack_cc1=10.1 amp_veltrack_curvecc1=3 sample=kick.wav amp_veltrack_oncc2=-40 amp_veltrack_curvecc3=4 )"); - synth.dispatchMessage(client, 0, "/region0/amp_veltrack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_veltrack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_veltrack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/amp_veltrack_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/amp_veltrack_curvecc3", "", nullptr); - // TODO: activate for the new region parser ; accept oob - // synth.dispatchMessage(client, 0, "/region2/amp_veltrack", "", nullptr); - std::vector expected { - "/region0/amp_veltrack_cc1,N : { }", - "/region1/amp_veltrack_cc1,f : { 10.1 }", - "/region1/amp_veltrack_curvecc1,i : { 3 }", - "/region2/amp_veltrack_cc2,f : { -40 }", - "/region2/amp_veltrack_curvecc3,i : { 4 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/amp_veltrack_cc1") == OSC::None); + REQUIRE( d.read("/region1/amp_veltrack_cc1") == 10.1f); + REQUIRE( d.read("/region1/amp_veltrack_curvecc1") == 3); + REQUIRE( d.read("/region2/amp_veltrack_cc2") == -40.0f); + REQUIRE( d.read("/region2/amp_veltrack_curvecc3") == 4); } -} - -TEST_CASE("[Values] Amp Random") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav amp_random=10.1 - sample=kick.wav amp_random=-4 - )"); - synth.dispatchMessage(client, 0, "/region0/amp_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/amp_random", "", nullptr); - // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region2/amp_random", "", nullptr); - std::vector expected { - "/region0/amp_random,f : { 0 }", - "/region1/amp_random,f : { 10.1 }", - // "/region2/amp_random,f : { 0 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Crossfade key range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Amp Random") + { + d.load(R"( + sample=kick.wav + sample=kick.wav amp_random=10.1 + sample=kick.wav amp_random=-4 + )"); + REQUIRE( d.read("/region0/amp_random") == 0.0f); + REQUIRE( d.read("/region1/amp_random") == 10.1f); + REQUIRE( d.read("/region2/amp_random") == -4.0f); + } - SECTION("Xfin") + SECTION("Key Xfin") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xfin_lokey=10 xfin_hikey=40 sample=kick.wav xfin_lokey=c4 xfin_hikey=b5 sample=kick.wav xfin_lokey=-10 xfin_hikey=40 sample=kick.wav xfin_lokey=10 xfin_hikey=140 )"); - synth.dispatchMessage(client, 0, "/region0/xfin_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xfin_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xfin_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xfin_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/xfin_key_range", "", nullptr); - std::vector expected { - "/region0/xfin_key_range,ii : { 0, 0 }", - "/region1/xfin_key_range,ii : { 10, 40 }", - "/region2/xfin_key_range,ii : { 60, 83 }", - "/region3/xfin_key_range,ii : { 0, 40 }", - "/region4/xfin_key_range,ii : { 10, 0 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/xfin_key_range"), Catch::Approx(std::vector{ 0, 0 })); + REQUIRE_THAT( d.readAll("/region1/xfin_key_range"), Catch::Approx(std::vector{ 10, 40 })); + REQUIRE_THAT( d.readAll("/region2/xfin_key_range"), Catch::Approx(std::vector{ 60, 83 })); + REQUIRE_THAT( d.readAll("/region3/xfin_key_range"), Catch::Approx(std::vector{ 0, 40 })); + REQUIRE_THAT( d.readAll("/region4/xfin_key_range"), Catch::Approx(std::vector{ 10, 0 })); } - SECTION("Xfout") + SECTION("Key Xfout") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xfout_lokey=10 xfout_hikey=40 sample=kick.wav xfout_lokey=c4 xfout_hikey=b5 sample=kick.wav xfout_lokey=-10 xfout_hikey=40 sample=kick.wav xfout_lokey=10 xfout_hikey=140 )"); - synth.dispatchMessage(client, 0, "/region0/xfout_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xfout_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xfout_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xfout_key_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/xfout_key_range", "", nullptr); - std::vector expected { - "/region0/xfout_key_range,ii : { 127, 127 }", - "/region1/xfout_key_range,ii : { 10, 40 }", - "/region2/xfout_key_range,ii : { 60, 83 }", - "/region3/xfout_key_range,ii : { 127, 40 }", - "/region4/xfout_key_range,ii : { 10, 127 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/xfout_key_range"), Catch::Approx(std::vector{ 127, 127 })); + REQUIRE_THAT( d.readAll("/region1/xfout_key_range"), Catch::Approx(std::vector{ 10, 40 })); + REQUIRE_THAT( d.readAll("/region2/xfout_key_range"), Catch::Approx(std::vector{ 60, 83 })); + REQUIRE_THAT( d.readAll("/region3/xfout_key_range"), Catch::Approx(std::vector{ 127, 40 })); + REQUIRE_THAT( d.readAll("/region4/xfout_key_range"), Catch::Approx(std::vector{ 10, 127 })); } } - - -TEST_CASE("[Values] Crossfade velocity range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Xfin") + SECTION("Velocity Xfin") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xfin_lovel=10 xfin_hivel=40 sample=kick.wav xfin_lovel=-10 xfin_hivel=40 sample=kick.wav xfin_lovel=10 xfin_hivel=140 )"); - synth.dispatchMessage(client, 0, "/region0/xfin_vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xfin_vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xfin_vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xfin_vel_range", "", nullptr); - std::vector expected { - "/region0/xfin_vel_range,ff : { 0, 0 }", - "/region1/xfin_vel_range,ff : { 0.0787402, 0.322835 }", - "/region2/xfin_vel_range,ff : { -0.0787402, 0.322835 }", - "/region3/xfin_vel_range,ff : { 0.0787402, 1.10236 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/xfin_vel_range"), Catch::Approx(std::vector{ 0, 0 })); + REQUIRE_THAT( d.readAll("/region1/xfin_vel_range"), Catch::Approx(std::vector{ 10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region2/xfin_vel_range"), Catch::Approx(std::vector{ -10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region3/xfin_vel_range"), Catch::Approx(std::vector{ 10_norm, 140_norm })); } - SECTION("Xfout") + SECTION("Velocity Xfout") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xfout_lovel=10 xfout_hivel=40 sample=kick.wav xfout_lovel=-10 xfout_hivel=40 sample=kick.wav xfout_lovel=10 xfout_hivel=140 )"); - synth.dispatchMessage(client, 0, "/region0/xfout_vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xfout_vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xfout_vel_range", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xfout_vel_range", "", nullptr); - std::vector expected { - "/region0/xfout_vel_range,ff : { 1, 1 }", - "/region1/xfout_vel_range,ff : { 0.0787402, 0.322835 }", - "/region2/xfout_vel_range,ff : { -0.0787402, 0.322835 }", - "/region3/xfout_vel_range,ff : { 0.0787402, 1.10236 }", - }; - REQUIRE(messageList == expected); + REQUIRE_THAT( d.readAll("/region0/xfout_vel_range"), Catch::Approx(std::vector{ 1, 1 })); + REQUIRE_THAT( d.readAll("/region1/xfout_vel_range"), Catch::Approx(std::vector{ 10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region2/xfout_vel_range"), Catch::Approx(std::vector{ -10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region3/xfout_vel_range"), Catch::Approx(std::vector{ 10_norm, 140_norm })); } -} - -TEST_CASE("[Values] Crossfade curves") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Key") + SECTION("Crossfade key curve") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xf_keycurve=gain sample=kick.wav xf_keycurve=something sample=kick.wav xf_keycurve=gain xf_keycurve=power )"); - synth.dispatchMessage(client, 0, "/region0/xf_keycurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xf_keycurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xf_keycurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xf_keycurve", "", nullptr); - std::vector expected { - "/region0/xf_keycurve,s : { power }", - "/region1/xf_keycurve,s : { gain }", - "/region2/xf_keycurve,s : { power }", - "/region3/xf_keycurve,s : { power }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/xf_keycurve") == "power"); + REQUIRE( d.read("/region1/xf_keycurve") == "gain"); + REQUIRE( d.read("/region2/xf_keycurve") == "power"); + REQUIRE( d.read("/region3/xf_keycurve") == "power"); } SECTION("Velocity") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xf_velcurve=gain sample=kick.wav xf_velcurve=something sample=kick.wav xf_velcurve=gain xf_velcurve=power )"); - synth.dispatchMessage(client, 0, "/region0/xf_velcurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xf_velcurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xf_velcurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xf_velcurve", "", nullptr); - std::vector expected { - "/region0/xf_velcurve,s : { power }", - "/region1/xf_velcurve,s : { gain }", - "/region2/xf_velcurve,s : { power }", - "/region3/xf_velcurve,s : { power }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/xf_velcurve") == "power"); + REQUIRE( d.read("/region1/xf_velcurve") == "gain"); + REQUIRE( d.read("/region2/xf_velcurve") == "power"); + REQUIRE( d.read("/region3/xf_velcurve") == "power"); } SECTION("CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xf_cccurve=gain sample=kick.wav xf_cccurve=something sample=kick.wav xf_cccurve=gain xf_cccurve=power )"); - synth.dispatchMessage(client, 0, "/region0/xf_cccurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xf_cccurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xf_cccurve", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xf_cccurve", "", nullptr); - std::vector expected { - "/region0/xf_cccurve,s : { power }", - "/region1/xf_cccurve,s : { gain }", - "/region2/xf_cccurve,s : { power }", - "/region3/xf_cccurve,s : { power }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/xf_cccurve") == "power"); + REQUIRE( d.read("/region1/xf_cccurve") == "gain"); + REQUIRE( d.read("/region2/xf_cccurve") == "power"); + REQUIRE( d.read("/region3/xf_cccurve") == "power"); } -} - -TEST_CASE("[Values] Crossfade CC range") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - SECTION("Xfin") + SECTION("CC Xfin") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xfin_locc4=10 xfin_hicc4=40 sample=kick.wav xfin_locc4=-10 xfin_hicc4=40 sample=kick.wav xfin_locc4=10 xfin_hicc4=140 )"); - synth.dispatchMessage(client, 0, "/region0/xfin_cc_range4", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xfin_cc_range4", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xfin_cc_range4", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xfin_cc_range4", "", nullptr); - std::vector expected { - "/region0/xfin_cc_range4,N : { }", - "/region1/xfin_cc_range4,ff : { 0.0787402, 0.322835 }", - "/region2/xfin_cc_range4,ff : { -0.0787402, 0.322835 }", - "/region3/xfin_cc_range4,ff : { 0.0787402, 1.10236 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/xfin_cc_range4") == OSC::None ); + REQUIRE_THAT( d.readAll("/region1/xfin_cc_range4"), Catch::Approx(std::vector{ 10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region2/xfin_cc_range4"), Catch::Approx(std::vector{ -10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region3/xfin_cc_range4"), Catch::Approx(std::vector{ 10_norm, 140_norm })); } - SECTION("Xfout") + SECTION("CC Xfout") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav xfout_locc4=10 xfout_hicc4=40 sample=kick.wav xfout_locc4=-10 xfout_hicc4=40 sample=kick.wav xfout_locc4=10 xfout_hicc4=140 )"); - synth.dispatchMessage(client, 0, "/region0/xfout_cc_range4", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/xfout_cc_range4", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/xfout_cc_range4", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/xfout_cc_range4", "", nullptr); - std::vector expected { - "/region0/xfout_cc_range4,N : { }", - "/region1/xfout_cc_range4,ff : { 0.0787402, 0.322835 }", - "/region2/xfout_cc_range4,ff : { -0.0787402, 0.322835 }", - "/region3/xfout_cc_range4,ff : { 0.0787402, 1.10236 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/xfout_cc_range4") == OSC::None ); + REQUIRE_THAT( d.readAll("/region1/xfout_cc_range4"), Catch::Approx(std::vector{ 10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region2/xfout_cc_range4"), Catch::Approx(std::vector{ -10_norm, 41_norm })); + REQUIRE_THAT( d.readAll("/region3/xfout_cc_range4"), Catch::Approx(std::vector{ 10_norm, 140_norm })); } -} -TEST_CASE("[Values] Global volumes and amplitudes") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Volumes") + SECTION("Global Volume") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav global_volume=4.4 master_volume=5.5 group_volume=6.6 sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/global_volume", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/master_volume", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/group_volume", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/global_volume", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/master_volume", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/group_volume", "", nullptr); - std::vector expected { - "/region0/global_volume,f : { 0 }", - "/region0/master_volume,f : { 0 }", - "/region0/group_volume,f : { 0 }", - "/region1/global_volume,f : { 4.4 }", - "/region1/master_volume,f : { 5.5 }", - "/region1/group_volume,f : { 6.6 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/global_volume") == 0.0f); + REQUIRE( d.read("/region0/master_volume") == 0.0f); + REQUIRE( d.read("/region0/group_volume") == 0.0f); + REQUIRE( d.read("/region1/global_volume") == 4.4f); + REQUIRE( d.read("/region1/master_volume") == 5.5f); + REQUIRE( d.read("/region1/group_volume") == 6.6f); } SECTION("Amplitudes") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav global_amplitude=4.4 master_amplitude=5.5 group_amplitude=6.6 sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/global_amplitude", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/master_amplitude", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/group_amplitude", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/global_amplitude", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/master_amplitude", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/group_amplitude", "", nullptr); - std::vector expected { - "/region0/global_amplitude,f : { 100 }", - "/region0/master_amplitude,f : { 100 }", - "/region0/group_amplitude,f : { 100 }", - "/region1/global_amplitude,f : { 4.4 }", - "/region1/master_amplitude,f : { 5.5 }", - "/region1/group_amplitude,f : { 6.6 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/global_amplitude") == 100.0f); + REQUIRE( d.read("/region0/master_amplitude") == 100.0f); + REQUIRE( d.read("/region0/group_amplitude") == 100.0f); + REQUIRE( d.read("/region1/global_amplitude") == 4.4f); + REQUIRE( d.read("/region1/master_amplitude") == 5.5f); + REQUIRE( d.read("/region1/group_amplitude") == 6.6f); } -} - -TEST_CASE("[Values] Pitch Keytrack") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav pitch_keytrack=1000 - sample=kick.wav pitch_keytrack=-100 - )"); - synth.dispatchMessage(client, 0, "/region0/pitch_keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_keytrack", "", nullptr); - std::vector expected { - "/region0/pitch_keytrack,f : { 100 }", - "/region1/pitch_keytrack,f : { 1000 }", - "/region2/pitch_keytrack,f : { -100 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Pitch Veltrack") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Pitch Keytrack") + { + d.load(R"( + sample=kick.wav + sample=kick.wav pitch_keytrack=1000 + sample=kick.wav pitch_keytrack=-100 + )"); + REQUIRE( d.read("/region0/pitch_keytrack") == 100.0f); + REQUIRE( d.read("/region1/pitch_keytrack") == 1000.0f); + REQUIRE( d.read("/region2/pitch_keytrack") == -100.0f); + } - SECTION("Basic") + SECTION("Pitch veltrack basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pitch_veltrack=10 sample=kick.wav pitch_veltrack=-132 )"); - synth.dispatchMessage(client, 0, "/region0/pitch_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_veltrack", "", nullptr); - std::vector expected { - "/region0/pitch_veltrack,f : { 0 }", - "/region1/pitch_veltrack,f : { 10 }", - "/region2/pitch_veltrack,f : { -132 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pitch_veltrack") == 0.0f); + REQUIRE( d.read("/region1/pitch_veltrack") == 10.0f); + REQUIRE( d.read("/region2/pitch_veltrack") == -132.0f); } - SECTION("CC") + SECTION("Pitch veltrack CC") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pitch_veltrack_cc1=10.1 pitch_veltrack_curvecc1=3 sample=kick.wav pitch_veltrack_oncc2=-40 pitch_veltrack_curvecc3=4 )"); - synth.dispatchMessage(client, 0, "/region0/pitch_veltrack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_veltrack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_veltrack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_veltrack_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_veltrack_curvecc3", "", nullptr); + REQUIRE( d.read("/region0/pitch_veltrack_cc1") == OSC::None); + REQUIRE( d.read("/region1/pitch_veltrack_cc1") == 10.1f); + REQUIRE( d.read("/region1/pitch_veltrack_curvecc1") == 3); + REQUIRE( d.read("/region2/pitch_veltrack_cc2") == -40.0f); + REQUIRE( d.read("/region2/pitch_veltrack_curvecc3") == 4); // TODO: activate for the new region parser ; accept oob - // synth.dispatchMessage(client, 0, "/region2/pitch_veltrack", "", nullptr); - std::vector expected { - "/region0/pitch_veltrack_cc1,N : { }", - "/region1/pitch_veltrack_cc1,f : { 10.1 }", - "/region1/pitch_veltrack_curvecc1,i : { 3 }", - "/region2/pitch_veltrack_cc2,f : { -40 }", - "/region2/pitch_veltrack_curvecc3,i : { 4 }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read<>("/region2/pitch_veltrack") == ); } -} - -TEST_CASE("[Values] Pitch Random") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav pitch_random=10 - sample=kick.wav pitch_random=-4 - )"); - synth.dispatchMessage(client, 0, "/region0/pitch_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_random", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_random", "", nullptr); - std::vector expected { - "/region0/pitch_random,f : { 0 }", - "/region1/pitch_random,f : { 10 }", - "/region2/pitch_random,f : { -4 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Transpose") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav transpose=10 - sample=kick.wav transpose=-4 - sample=kick.wav transpose=-400 - sample=kick.wav transpose=400 - )"); - synth.dispatchMessage(client, 0, "/region0/transpose", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/transpose", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/transpose", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/transpose", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/transpose", "", nullptr); - std::vector expected { - "/region0/transpose,f : { 0 }", - "/region1/transpose,f : { 10 }", - "/region2/transpose,f : { -4 }", - "/region3/transpose,f : { -400 }", - "/region4/transpose,f : { 400 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Pitch Random") + { + d.load(R"( + sample=kick.wav + sample=kick.wav pitch_random=10 + sample=kick.wav pitch_random=-4 + )"); + REQUIRE( d.read("/region0/pitch_random") == 0.0f); + REQUIRE( d.read("/region1/pitch_random") == 10.0f); + REQUIRE( d.read("/region2/pitch_random") == -4.0f); + } -TEST_CASE("[Values] Pitch/Tune") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Transpose") + { + d.load(R"( + sample=kick.wav + sample=kick.wav transpose=10 + sample=kick.wav transpose=-4 + sample=kick.wav transpose=-400 + sample=kick.wav transpose=400 + )"); + REQUIRE( d.read("/region0/transpose") == 0.0f); + REQUIRE( d.read("/region1/transpose") == 10.0f); + REQUIRE( d.read("/region2/transpose") == -4.0f); + REQUIRE( d.read("/region3/transpose") == -400.0f); + REQUIRE( d.read("/region4/transpose") == 400.0f); + } - SECTION("Basic") + SECTION("Pitch/tune basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pitch=4.2 sample=kick.wav tune=-200 )"); - synth.dispatchMessage(client, 0, "/region0/pitch", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch", "", nullptr); - std::vector expected { - "/region0/pitch,f : { 0 }", - "/region1/pitch,f : { 4.2 }", - "/region2/pitch,f : { -200 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pitch") == 0.0f); + REQUIRE( d.read("/region1/pitch") == 4.2f); + REQUIRE( d.read("/region2/pitch") == -200.0f); } - SECTION("CC Depth") + SECTION("Pitch/tune CC Depth") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pitch_oncc42=4.2 sample=kick.wav pitch_oncc2=-10 )"); - synth.dispatchMessage(client, 0, "/region0/pitch_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_cc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_cc2", "", nullptr); - std::vector expected { - "/region0/pitch_cc42,N : { }", - "/region1/pitch_cc42,f : { 4.2 }", - "/region2/pitch_cc2,f : { -10 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pitch_cc42") == OSC::None); + REQUIRE( d.read("/region1/pitch_cc42") == 4.2f); + REQUIRE( d.read("/region2/pitch_cc2") == -10.0f); } - SECTION("CC Params") + SECTION("Pitch/tune CC Params") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pitch_stepcc42=4.2 sample=kick.wav pitch_smoothcc42=4 @@ -2296,33 +1321,21 @@ TEST_CASE("[Values] Pitch/Tune") sample=kick.wav pitch_smoothcc42=-4 sample=kick.wav pitch_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/pitch_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitch_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitch_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/pitch_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/pitch_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/pitch_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/pitch_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/pitch_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/pitch_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/pitch_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/pitch_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/pitch_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/pitch_curvecc42", "", nullptr); - std::vector expected { - "/region0/pitch_stepcc42,N : { }", - "/region0/pitch_smoothcc42,N : { }", - "/region0/pitch_curvecc42,N : { }", - "/region1/pitch_stepcc42,f : { 4.2 }", - "/region2/pitch_smoothcc42,i : { 4 }", - "/region3/pitch_curvecc42,i : { 2 }", - // "/region4/pitch_stepcc42,N : { }", - // "/region5/pitch_smoothcc42,N : { }", - // "/region6/pitch_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/pitch_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/pitch_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/pitch_curvecc42") == OSC::None); } - SECTION("CC Params (with pitch_)") + SECTION("Pitch/tune CC Params (with pitch_)") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav pitch_stepcc42=4.2 sample=kick.wav pitch_smoothcc42=4 @@ -2331,82 +1344,42 @@ TEST_CASE("[Values] Pitch/Tune") sample=kick.wav pitch_smoothcc42=-4 sample=kick.wav pitch_curvecc42=300 )"); - synth.dispatchMessage(client, 0, "/region0/pitch_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitch_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitch_curvecc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_stepcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/pitch_smoothcc42", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/pitch_curvecc42", "", nullptr); + REQUIRE( d.read("/region0/pitch_stepcc42") == OSC::None); + REQUIRE( d.read("/region0/pitch_smoothcc42") == OSC::None); + REQUIRE( d.read("/region0/pitch_curvecc42") == OSC::None); + REQUIRE( d.read("/region1/pitch_stepcc42") == 4.2f); + REQUIRE( d.read("/region2/pitch_smoothcc42") == 4 ); + REQUIRE( d.read("/region3/pitch_curvecc42") == 2); // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region4/pitch_stepcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region5/pitch_smoothcc42", "", nullptr); - // synth.dispatchMessage(client, 0, "/region6/pitch_curvecc42", "", nullptr); - std::vector expected { - "/region0/pitch_stepcc42,N : { }", - "/region0/pitch_smoothcc42,N : { }", - "/region0/pitch_curvecc42,N : { }", - "/region1/pitch_stepcc42,f : { 4.2 }", - "/region2/pitch_smoothcc42,i : { 4 }", - "/region3/pitch_curvecc42,i : { 2 }", - // "/region4/pitch_stepcc42,N : { }", - // "/region5/pitch_smoothcc42,N : { }", - // "/region6/pitch_curvecc42,N : { }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region4/pitch_stepcc42") == OSC::None); + // REQUIRE( d.read("/region5/pitch_smoothcc42") == OSC::None); + // REQUIRE( d.read("/region6/pitch_curvecc42") == OSC::None); } -} - -TEST_CASE("[Values] Bend behavior") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav bend_up=100 bend_down=-400 bend_step=10 bend_smooth=10 - sample=kick.wav bend_up=-100 bend_down=400 bend_step=-10 bend_smooth=-10 - )"); - synth.dispatchMessage(client, 0, "/region0/bend_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/bend_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/bend_step", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/bend_smooth", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/bend_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/bend_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/bend_step", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/bend_smooth", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/bend_up", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/bend_down", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/bend_step", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/bend_smooth", "", nullptr); - std::vector expected { - "/region0/bend_up,f : { 200 }", - "/region0/bend_down,f : { -200 }", - "/region0/bend_step,f : { 1 }", - "/region0/bend_smooth,i : { 0 }", - "/region1/bend_up,f : { 100 }", - "/region1/bend_down,f : { -400 }", - "/region1/bend_step,f : { 10 }", - "/region1/bend_smooth,i : { 10 }", - "/region2/bend_up,f : { -100 }", - "/region2/bend_down,f : { 400 }", - "/region2/bend_step,f : { 1 }", - "/region2/bend_smooth,i : { 0 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] ampeg") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Bend behavior") + { + d.load(R"( + sample=kick.wav + sample=kick.wav bend_up=100 bend_down=-400 bend_step=10 bend_smooth=10 + sample=kick.wav bend_up=-100 bend_down=400 bend_step=-10 bend_smooth=-10 + )"); + REQUIRE( d.read("/region0/bend_up") == 200.0f); + REQUIRE( d.read("/region0/bend_down") == -200.0f); + REQUIRE( d.read("/region0/bend_step") == 1.0f); + REQUIRE( d.read("/region0/bend_smooth") == 0); + REQUIRE( d.read("/region1/bend_up") == 100.0f); + REQUIRE( d.read("/region1/bend_down") == -400.0f); + REQUIRE( d.read("/region1/bend_step") == 10.0f); + REQUIRE( d.read("/region1/bend_smooth") == 10); + REQUIRE( d.read("/region2/bend_up") == -100.0f); + REQUIRE( d.read("/region2/bend_down") == 400.0f); + REQUIRE( d.read("/region2/bend_step") == 1.0f); + REQUIRE( d.read("/region2/bend_smooth") == 0); + } - SECTION("Basic") + SECTION("Ampeg basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav ampeg_attack=1 ampeg_delay=2 ampeg_decay=3 @@ -2417,1499 +1390,804 @@ TEST_CASE("[Values] ampeg") ampeg_hold=-4 ampeg_release=-5 ampeg_start=-6 ampeg_sustain=-7 ampeg_depth=-8 )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_attack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_decay", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_hold", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_release", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_start", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_sustain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_depth", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_attack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_decay", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_hold", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_release", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_start", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_sustain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_depth", "", nullptr); + REQUIRE( d.read("/region0/ampeg_attack") == 0.0f); + REQUIRE( d.read("/region0/ampeg_delay") == 0.0f); + REQUIRE( d.read("/region0/ampeg_decay") == 0.0f); + REQUIRE( d.read("/region0/ampeg_hold") == 0.0f); + REQUIRE( d.read("/region0/ampeg_release") == 0.001f); + REQUIRE( d.read("/region0/ampeg_start") == 0.0f); + REQUIRE( d.read("/region0/ampeg_sustain") == 100.0f); + REQUIRE( d.read("/region0/ampeg_depth") == 0.0f); + REQUIRE( d.read("/region1/ampeg_attack") == 1.0f); + REQUIRE( d.read("/region1/ampeg_delay") == 2.0f); + REQUIRE( d.read("/region1/ampeg_decay") == 3.0f); + REQUIRE( d.read("/region1/ampeg_hold") == 4.0f); + REQUIRE( d.read("/region1/ampeg_release") == 5.0f); + REQUIRE( d.read("/region1/ampeg_start") == 6.0f); + REQUIRE( d.read("/region1/ampeg_sustain") == 7.0f); + REQUIRE( d.read("/region1/ampeg_depth") == 0.0f); // TODO after new parser : ignore oob - // synth.dispatchMessage(client, 0, "/region2/ampeg_attack", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_delay", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_decay", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_hold", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_release", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_start", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_sustain", "", nullptr); - // synth.dispatchMessage(client, 0, "/region2/ampeg_depth", "", nullptr); - std::vector expected { - "/region0/ampeg_attack,f : { 0 }", - "/region0/ampeg_delay,f : { 0 }", - "/region0/ampeg_decay,f : { 0 }", - "/region0/ampeg_hold,f : { 0 }", - "/region0/ampeg_release,f : { 0.001 }", - "/region0/ampeg_start,f : { 0 }", - "/region0/ampeg_sustain,f : { 100 }", - "/region0/ampeg_depth,f : { 0 }", - "/region1/ampeg_attack,f : { 1 }", - "/region1/ampeg_delay,f : { 2 }", - "/region1/ampeg_decay,f : { 3 }", - "/region1/ampeg_hold,f : { 4 }", - "/region1/ampeg_release,f : { 5 }", - "/region1/ampeg_start,f : { 6 }", - "/region1/ampeg_sustain,f : { 7 }", - "/region1/ampeg_depth,f : { 0 }", - // "/region2/ampeg_attack,f : { 0 }", - // "/region2/ampeg_delay,f : { 0 }", - // "/region2/ampeg_decay,f : { 0 }", - // "/region2/ampeg_hold,f : { 0 }", - // "/region2/ampeg_release,f : { 0.001 }", - // "/region2/ampeg_start,f : { 0 }", - // "/region2/ampeg_sustain,f : { 100 }", - // "/region2/ampeg_depth,f : { 0 }", - }; - REQUIRE(messageList == expected); + // REQUIRE( d.read("/region2/ampeg_attack") == 0.0f); + // REQUIRE( d.read("/region2/ampeg_delay") == 0.0f); + // REQUIRE( d.read("/region2/ampeg_decay") == 0.0f); + // REQUIRE( d.read("/region2/ampeg_hold") == 0.0f); + // REQUIRE( d.read("/region2/ampeg_release") == 0.001f); + // REQUIRE( d.read("/region2/ampeg_start") == 0.0f); + // REQUIRE( d.read("/region2/ampeg_sustain") == 100.0f); + // REQUIRE( d.read("/region2/ampeg_depth") == 0.0f); } - SECTION("Velocity") + SECTION("Ampeg velocity") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav sample=kick.wav ampeg_vel2attack=1 ampeg_vel2delay=2 ampeg_vel2decay=3 ampeg_vel2hold=4 ampeg_vel2release=5 ampeg_vel2sustain=7 ampeg_vel2depth=8 )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2attack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2decay", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2hold", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2release", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2sustain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_vel2depth", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2attack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2delay", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2decay", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2hold", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2release", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2sustain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_vel2depth", "", nullptr); - std::vector expected { - "/region0/ampeg_vel2attack,f : { 0 }", - "/region0/ampeg_vel2delay,f : { 0 }", - "/region0/ampeg_vel2decay,f : { 0 }", - "/region0/ampeg_vel2hold,f : { 0 }", - "/region0/ampeg_vel2release,f : { 0 }", - "/region0/ampeg_vel2sustain,f : { 0 }", - "/region0/ampeg_vel2depth,f : { 0 }", - "/region1/ampeg_vel2attack,f : { 1 }", - "/region1/ampeg_vel2delay,f : { 2 }", - "/region1/ampeg_vel2decay,f : { 3 }", - "/region1/ampeg_vel2hold,f : { 4 }", - "/region1/ampeg_vel2release,f : { 5 }", - "/region1/ampeg_vel2sustain,f : { 7 }", - "/region1/ampeg_vel2depth,f : { 0 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/ampeg_vel2attack") == 0.0f ); + REQUIRE( d.read("/region0/ampeg_vel2delay") == 0.0f ); + REQUIRE( d.read("/region0/ampeg_vel2decay") == 0.0f ); + REQUIRE( d.read("/region0/ampeg_vel2hold") == 0.0f ); + REQUIRE( d.read("/region0/ampeg_vel2release") == 0.0f ); + REQUIRE( d.read("/region0/ampeg_vel2sustain") == 0.0f ); + REQUIRE( d.read("/region0/ampeg_vel2depth") == 0.0f ); + REQUIRE( d.read("/region1/ampeg_vel2attack") == 1.0f ); + REQUIRE( d.read("/region1/ampeg_vel2delay") == 2.0f ); + REQUIRE( d.read("/region1/ampeg_vel2decay") == 3.0f ); + REQUIRE( d.read("/region1/ampeg_vel2hold") == 4.0f ); + REQUIRE( d.read("/region1/ampeg_vel2release") == 5.0f ); + REQUIRE( d.read("/region1/ampeg_vel2sustain") == 7.0f ); + REQUIRE( d.read("/region1/ampeg_vel2depth") == 0.0f ); } -} - -TEST_CASE("[Values] Note polyphony") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav note_polyphony=10 - sample=kick.wav note_polyphony=-4 - sample=kick.wav note_polyphony=10 note_polyphony=-4 - )"); - synth.dispatchMessage(client, 0, "/region0/note_polyphony", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/note_polyphony", "", nullptr); - // TODO: activate for the new region parser ; ignore oob - // synth.dispatchMessage(client, 0, "/region2/note_polyphony", "", nullptr); - // synth.dispatchMessage(client, 0, "/region3/note_polyphony", "", nullptr); - std::vector expected { - "/region0/note_polyphony,N : { }", - "/region1/note_polyphony,i : { 10 }", - // "/region2/note_polyphony,N : { }", - // "/region3/note_polyphony,i : { 10 }", - }; - REQUIRE(messageList == expected); -} -TEST_CASE("[Values] Self-mask") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav note_selfmask=off - sample=kick.wav note_selfmask=off note_selfmask=on - sample=kick.wav note_selfmask=off note_selfmask=garbage - )"); - synth.dispatchMessage(client, 0, "/region0/note_selfmask", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/note_selfmask", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/note_selfmask", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/note_selfmask", "", nullptr); - std::vector expected { - "/region0/note_selfmask,T : { }", - "/region1/note_selfmask,F : { }", - "/region2/note_selfmask,T : { }", - "/region3/note_selfmask,T : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Note polyphony") + { + d.load(R"( + sample=kick.wav + sample=kick.wav note_polyphony=10 + sample=kick.wav note_polyphony=-4 + sample=kick.wav note_polyphony=10 note_polyphony=-4 + )"); + REQUIRE( d.read("/region0/note_polyphony") == OSC::None ); + REQUIRE( d.read("/region1/note_polyphony") == 10); + // TODO: activate for the new region parser ; ignore oob + // REQUIRE( d.read("/region2/note_polyphony") == OSC::None ); + // REQUIRE( d.read("/region3/note_polyphony") == 10); + } -TEST_CASE("[Values] RT dead") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav rt_dead=on - sample=kick.wav rt_dead=on rt_dead=off - sample=kick.wav rt_dead=on rt_dead=garbage - )"); - synth.dispatchMessage(client, 0, "/region0/rt_dead", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/rt_dead", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/rt_dead", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/rt_dead", "", nullptr); - std::vector expected { - "/region0/rt_dead,F : { }", - "/region1/rt_dead,T : { }", - "/region2/rt_dead,F : { }", - "/region3/rt_dead,F : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Self-mask") + { + d.load(R"( + sample=kick.wav + sample=kick.wav note_selfmask=off + sample=kick.wav note_selfmask=off note_selfmask=on + sample=kick.wav note_selfmask=off note_selfmask=garbage + )"); + REQUIRE( d.read("/region0/note_selfmask") == OSC::True); + REQUIRE( d.read("/region1/note_selfmask") == OSC::False); + REQUIRE( d.read("/region2/note_selfmask") == OSC::True); + REQUIRE( d.read("/region3/note_selfmask") == OSC::True); + } -TEST_CASE("[Values] Sustain switch") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sustain_sw=off - sample=kick.wav sustain_sw=off sustain_sw=on - sample=kick.wav sustain_sw=off sustain_sw=garbage - )"); - synth.dispatchMessage(client, 0, "/region0/sustain_sw", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sustain_sw", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sustain_sw", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sustain_sw", "", nullptr); - std::vector expected { - "/region0/sustain_sw,T : { }", - "/region1/sustain_sw,F : { }", - "/region2/sustain_sw,T : { }", - "/region3/sustain_sw,T : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("RT dead") + { + d.load(R"( + sample=kick.wav + sample=kick.wav rt_dead=on + sample=kick.wav rt_dead=on rt_dead=off + sample=kick.wav rt_dead=on rt_dead=garbage + )"); + REQUIRE( d.read("/region0/rt_dead") == OSC::False); + REQUIRE( d.read("/region1/rt_dead") == OSC::True); + REQUIRE( d.read("/region2/rt_dead") == OSC::False); + REQUIRE( d.read("/region3/rt_dead") == OSC::False); + } -TEST_CASE("[Values] Sostenuto switch") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sostenuto_sw=off - sample=kick.wav sostenuto_sw=off sostenuto_sw=on - sample=kick.wav sostenuto_sw=off sostenuto_sw=garbage - )"); - synth.dispatchMessage(client, 0, "/region0/sostenuto_sw", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sostenuto_sw", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sostenuto_sw", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/sostenuto_sw", "", nullptr); - std::vector expected { - "/region0/sostenuto_sw,T : { }", - "/region1/sostenuto_sw,F : { }", - "/region2/sostenuto_sw,T : { }", - "/region3/sostenuto_sw,T : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sustain switch") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sustain_sw=off + sample=kick.wav sustain_sw=off sustain_sw=on + sample=kick.wav sustain_sw=off sustain_sw=garbage + )"); + REQUIRE( d.read("/region0/sustain_sw") == OSC::True); + REQUIRE( d.read("/region1/sustain_sw") == OSC::False); + REQUIRE( d.read("/region2/sustain_sw") == OSC::True); + REQUIRE( d.read("/region3/sustain_sw") == OSC::True); + } -TEST_CASE("[Values] Sustain CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sustain_cc=10 - sample=kick.wav sustain_cc=20 sustain_cc=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sustain_cc", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sustain_cc", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sustain_cc", "", nullptr); - std::vector expected { - "/region0/sustain_cc,i : { 64 }", - "/region1/sustain_cc,i : { 10 }", - "/region2/sustain_cc,i : { 64 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sostenuto switch") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sostenuto_sw=off + sample=kick.wav sostenuto_sw=off sostenuto_sw=on + sample=kick.wav sostenuto_sw=off sostenuto_sw=garbage + )"); + REQUIRE( d.read("/region0/sostenuto_sw") == OSC::True); + REQUIRE( d.read("/region1/sostenuto_sw") == OSC::False); + REQUIRE( d.read("/region2/sostenuto_sw") == OSC::True); + REQUIRE( d.read("/region3/sostenuto_sw") == OSC::True); + } -TEST_CASE("[Values] Sustain low") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sustain_lo=10 - sample=kick.wav sustain_lo=10 sustain_lo=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sustain_lo", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sustain_lo", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sustain_lo", "", nullptr); - std::vector expected { - "/region0/sustain_lo,f : { 0.00787402 }", - "/region1/sustain_lo,f : { 0.0787402 }", - "/region2/sustain_lo,f : { -0.00787402 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sustain CC") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sustain_cc=10 + sample=kick.wav sustain_cc=20 sustain_cc=-1 + )"); + REQUIRE( d.read("/region0/sustain_cc") == 64); + REQUIRE( d.read("/region1/sustain_cc") == 10); + REQUIRE( d.read("/region2/sustain_cc") == 64); + } -TEST_CASE("[Values] Sostenuto CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sostenuto_cc=10 - sample=kick.wav sostenuto_cc=20 sostenuto_cc=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sostenuto_cc", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sostenuto_cc", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sostenuto_cc", "", nullptr); - std::vector expected { - "/region0/sostenuto_cc,i : { 66 }", - "/region1/sostenuto_cc,i : { 10 }", - "/region2/sostenuto_cc,i : { 66 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sustain low") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sustain_lo=10 + sample=kick.wav sustain_lo=10 sustain_lo=-1 + )"); + REQUIRE( d.read("/region0/sustain_lo") == 1_norm); + REQUIRE( d.read("/region1/sustain_lo") == 10_norm); + REQUIRE( d.read("/region2/sustain_lo") == -1_norm); + } -TEST_CASE("[Values] Sostenuto low") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav sostenuto_lo=10 - sample=kick.wav sostenuto_lo=10 sostenuto_lo=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/sostenuto_lo", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/sostenuto_lo", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/sostenuto_lo", "", nullptr); - std::vector expected { - "/region0/sostenuto_lo,f : { 0.00787402 }", - "/region1/sostenuto_lo,f : { 0.0787402 }", - "/region2/sostenuto_lo,f : { -0.00787402 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sostenuto CC") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sostenuto_cc=10 + sample=kick.wav sostenuto_cc=20 sostenuto_cc=-1 + )"); + REQUIRE( d.read("/region0/sostenuto_cc") == 66); + REQUIRE( d.read("/region1/sostenuto_cc") == 10); + REQUIRE( d.read("/region2/sostenuto_cc") == 66); + } -TEST_CASE("[Values] Oscillator phase") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav oscillator_phase=0.1 - sample=kick.wav oscillator_phase=1.1 - sample=kick.wav oscillator_phase=-1.2 - )"); - synth.dispatchMessage(client, 0, "/region0/oscillator_phase", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/oscillator_phase", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/oscillator_phase", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/oscillator_phase", "", nullptr); - std::vector expected { - "/region0/oscillator_phase,f : { 0 }", - "/region1/oscillator_phase,f : { 0.1 }", - "/region2/oscillator_phase,f : { 0.1 }", - "/region3/oscillator_phase,f : { -1 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Sostenuto low") + { + d.load(R"( + sample=kick.wav + sample=kick.wav sostenuto_lo=10 + sample=kick.wav sostenuto_lo=10 sostenuto_lo=-1 + )"); + REQUIRE( d.read("/region0/sostenuto_lo") == 1_norm); + REQUIRE( d.read("/region1/sostenuto_lo") == 10_norm); + REQUIRE( d.read("/region2/sostenuto_lo") == -1_norm); + } -TEST_CASE("[Values] Oscillator quality") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav oscillator_quality=2 - sample=kick.wav oscillator_quality=0 oscillator_quality=-2 - )"); - synth.dispatchMessage(client, 0, "/region0/oscillator_quality", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/oscillator_quality", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/oscillator_quality", "", nullptr); - std::vector expected { - "/region0/oscillator_quality,N : { }", - "/region1/oscillator_quality,i : { 2 }", - "/region2/oscillator_quality,N : { }", - }; - REQUIRE(messageList == expected); -} + SECTION("Oscillator phase") + { + d.load(R"( + sample=kick.wav + sample=kick.wav oscillator_phase=0.1 + sample=kick.wav oscillator_phase=1.1 + sample=kick.wav oscillator_phase=-1.2 + )"); + REQUIRE( d.read("/region0/oscillator_phase") == 0.0f); + REQUIRE_THAT( d.read("/region1/oscillator_phase"), Catch::WithinRel(0.1f)); + REQUIRE_THAT( d.read("/region2/oscillator_phase"), Catch::WithinRel(0.1f)); + REQUIRE( d.read("/region3/oscillator_phase") == -1.0f); + } -TEST_CASE("[Values] Oscillator mode/multi") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav oscillator_mode=2 - sample=kick.wav oscillator_mode=1 oscillator_mode=-2 - sample=kick.wav oscillator_multi=9 - sample=kick.wav oscillator_multi=-2 - )"); - synth.dispatchMessage(client, 0, "/region0/oscillator_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/oscillator_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/oscillator_mode", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/oscillator_multi", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/oscillator_multi", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/oscillator_multi", "", nullptr); - std::vector expected { - "/region0/oscillator_mode,i : { 0 }", - "/region1/oscillator_mode,i : { 2 }", - "/region2/oscillator_mode,i : { 0 }", - "/region0/oscillator_multi,i : { 1 }", - "/region3/oscillator_multi,i : { 9 }", - "/region4/oscillator_multi,i : { 1 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Oscillator quality") + { + d.load(R"( + sample=kick.wav + sample=kick.wav oscillator_quality=2 + sample=kick.wav oscillator_quality=0 oscillator_quality=-2 + )"); + REQUIRE( d.read("/region0/oscillator_quality") == OSC::None); + REQUIRE( d.read("/region1/oscillator_quality") == 2); + REQUIRE( d.read("/region2/oscillator_quality") == OSC::None); + } -TEST_CASE("[Values] Oscillator detune/mod depth") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav oscillator_detune=9.2 - sample=kick.wav oscillator_detune=-1200.2 - sample=kick.wav oscillator_mod_depth=1564.75 - sample=kick.wav oscillator_mod_depth=-2.2 - )"); - synth.dispatchMessage(client, 0, "/region0/oscillator_detune", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/oscillator_detune", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/oscillator_detune", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/oscillator_mod_depth", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/oscillator_mod_depth", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/oscillator_mod_depth", "", nullptr); - std::vector expected { - "/region0/oscillator_detune,f : { 0 }", - "/region1/oscillator_detune,f : { 9.2 }", - "/region2/oscillator_detune,f : { -1200.2 }", - "/region0/oscillator_mod_depth,f : { 0 }", - "/region3/oscillator_mod_depth,f : { 1564.75 }", - "/region4/oscillator_mod_depth,f : { -2.2 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Oscillator mode/multi") + { + d.load(R"( + sample=kick.wav + sample=kick.wav oscillator_mode=2 + sample=kick.wav oscillator_mode=1 oscillator_mode=-2 + sample=kick.wav oscillator_multi=9 + sample=kick.wav oscillator_multi=-2 + )"); + REQUIRE( d.read("/region0/oscillator_mode") == 0); + REQUIRE( d.read("/region1/oscillator_mode") == 2); + REQUIRE( d.read("/region2/oscillator_mode") == 0); + REQUIRE( d.read("/region0/oscillator_multi") == 1); + REQUIRE( d.read("/region3/oscillator_multi") == 9); + REQUIRE( d.read("/region4/oscillator_multi") == 1); + } -TEST_CASE("[Values] Effect sends") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav effect1=10 - sample=kick.wav effect2=50.4 - sample=kick.wav effect1=-1 - )"); - synth.dispatchMessage(client, 0, "/region0/effect1", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/effect1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/effect1", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/effect2", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/effect1", "", nullptr); - std::vector expected { - // No reply to the first question - "/region1/effect1,f : { 10 }", - "/region2/effect1,f : { 0 }", - "/region2/effect2,f : { 50.4 }", - // No reply to the last question - }; - REQUIRE(messageList == expected); -} + SECTION("Oscillator detune/mod depth") + { + d.load(R"( + sample=kick.wav + sample=kick.wav oscillator_detune=9.2 + sample=kick.wav oscillator_detune=-1200.2 + sample=kick.wav oscillator_mod_depth=1564.75 + sample=kick.wav oscillator_mod_depth=-2.2 + )"); + REQUIRE( d.read("/region0/oscillator_detune") == 0.0f); + REQUIRE( d.read("/region1/oscillator_detune") == 9.2f); + REQUIRE( d.read("/region2/oscillator_detune") == -1200.2f); + REQUIRE( d.read("/region0/oscillator_mod_depth") == 0.0f); + REQUIRE( d.read("/region3/oscillator_mod_depth") == 1564.75f); + REQUIRE( d.read("/region4/oscillator_mod_depth") == -2.2f); + } -TEST_CASE("[Values] Support floating point for int values") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav offset=1042.5 - sample=kick.wav pitch_keytrack=-2.1 - )"); - synth.dispatchMessage(client, 0, "/region0/offset", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitch_keytrack", "", nullptr); - std::vector expected { - "/region0/offset,h : { 1042 }", - "/region1/pitch_keytrack,f : { -2.1 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Effect sends") + { + d.load(R"( + sample=kick.wav + sample=kick.wav effect1=10 + sample=kick.wav effect2=50.4 + sample=kick.wav effect1=-1 + )"); + REQUIRE( !d.replied("/region0/effect1") ); + REQUIRE( d.read("/region1/effect1") == 10.0f); + REQUIRE( d.read("/region2/effect1") == 0.0f); + REQUIRE( d.read("/region2/effect2") == 50.4f); + REQUIRE( !d.replied("/region4/effect1") ); + } -TEST_CASE("[Values] ampeg CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("Support floating point for int values") + { + d.load(R"( + sample=kick.wav offset=1042.5 + sample=kick.wav pitch_keytrack=-2.1 + )"); + REQUIRE( d.read("/region0/offset") == 1042); + REQUIRE( d.read("/region1/pitch_keytrack") == -2.1f); + } - SECTION("Basic") + SECTION("ampeg CC Basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/ampeg_attack_cc1,f : { 0 }", - "/region0/ampeg_delay_cc2,f : { 0 }", - "/region0/ampeg_decay_cc3,f : { 0 }", - "/region0/ampeg_hold_cc4,f : { 0 }", - "/region0/ampeg_release_cc5,f : { 0 }", - "/region0/ampeg_start_cc6,f : { 0 }", - "/region0/ampeg_sustain_cc7,f : { 0 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/ampeg_attack_cc1") == 0.0f); + REQUIRE( d.read("/region0/ampeg_delay_cc2") == 0.0f); + REQUIRE( d.read("/region0/ampeg_decay_cc3") == 0.0f); + REQUIRE( d.read("/region0/ampeg_hold_cc4") == 0.0f); + REQUIRE( d.read("/region0/ampeg_release_cc5") == 0.0f); + REQUIRE( d.read("/region0/ampeg_start_cc6") == 0.0f); + REQUIRE( d.read("/region0/ampeg_sustain_cc7") == 0.0f); } - SECTION("Positive values") + SECTION("ampeg CC Positive values") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav ampeg_attack_oncc1=1 ampeg_delay_oncc2=2 ampeg_decay_oncc3=3 ampeg_hold_oncc4=4 ampeg_release_oncc5=5 ampeg_start_oncc6=6 ampeg_sustain_oncc7=7 )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/ampeg_attack_cc1,f : { 1 }", - "/region0/ampeg_delay_cc2,f : { 2 }", - "/region0/ampeg_decay_cc3,f : { 3 }", - "/region0/ampeg_hold_cc4,f : { 4 }", - "/region0/ampeg_release_cc5,f : { 5 }", - "/region0/ampeg_start_cc6,f : { 6 }", - "/region0/ampeg_sustain_cc7,f : { 7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/ampeg_attack_cc1") == 1.0f); + REQUIRE( d.read("/region0/ampeg_delay_cc2") == 2.0f); + REQUIRE( d.read("/region0/ampeg_decay_cc3") == 3.0f); + REQUIRE( d.read("/region0/ampeg_hold_cc4") == 4.0f); + REQUIRE( d.read("/region0/ampeg_release_cc5") == 5.0f); + REQUIRE( d.read("/region0/ampeg_start_cc6") == 6.0f); + REQUIRE( d.read("/region0/ampeg_sustain_cc7") == 7.0f); } - SECTION("Negative values") + SECTION("ampeg CC Negative values") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav ampeg_attack_cc1=-1 ampeg_delay_cc2=-2 ampeg_decay_cc3=-3 ampeg_hold_cc4=-4 ampeg_release_cc5=-5 ampeg_start_cc6=-6 ampeg_sustain_cc7=-7 )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/ampeg_attack_cc1,f : { -1 }", - "/region0/ampeg_delay_cc2,f : { -2 }", - "/region0/ampeg_decay_cc3,f : { -3 }", - "/region0/ampeg_hold_cc4,f : { -4 }", - "/region0/ampeg_release_cc5,f : { -5 }", - "/region0/ampeg_start_cc6,f : { -6 }", - "/region0/ampeg_sustain_cc7,f : { -7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/ampeg_attack_cc1") == -1.0f); + REQUIRE( d.read("/region0/ampeg_delay_cc2") == -2.0f); + REQUIRE( d.read("/region0/ampeg_decay_cc3") == -3.0f); + REQUIRE( d.read("/region0/ampeg_hold_cc4") == -4.0f); + REQUIRE( d.read("/region0/ampeg_release_cc5") == -5.0f); + REQUIRE( d.read("/region0/ampeg_start_cc6") == -6.0f); + REQUIRE( d.read("/region0/ampeg_sustain_cc7") == -7.0f); } -} -TEST_CASE("[Values] fileg CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") + SECTION("fileg basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/fileg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/fileg_attack_cc1,N : { }", - "/region0/fileg_delay_cc2,N : { }", - "/region0/fileg_decay_cc3,N : { }", - "/region0/fileg_hold_cc4,N : { }", - "/region0/fileg_release_cc5,N : { }", - "/region0/fileg_start_cc6,N : { }", - "/region0/fileg_sustain_cc7,N : { }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/fileg_attack_cc1") == OSC::None); + REQUIRE( d.read("/region0/fileg_delay_cc2") == OSC::None); + REQUIRE( d.read("/region0/fileg_decay_cc3") == OSC::None); + REQUIRE( d.read("/region0/fileg_hold_cc4") == OSC::None); + REQUIRE( d.read("/region0/fileg_release_cc5") == OSC::None); + REQUIRE( d.read("/region0/fileg_start_cc6") == OSC::None); + REQUIRE( d.read("/region0/fileg_sustain_cc7") == OSC::None); } - SECTION("Positive values") + SECTION("fileg positive values") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav fileg_attack_oncc1=1 fileg_delay_oncc2=2 fileg_decay_oncc3=3 fileg_hold_oncc4=4 fileg_release_oncc5=5 fileg_start_oncc6=6 fileg_sustain_oncc7=7 )"); - synth.dispatchMessage(client, 0, "/region0/fileg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/fileg_attack_cc1,f : { 1 }", - "/region0/fileg_delay_cc2,f : { 2 }", - "/region0/fileg_decay_cc3,f : { 3 }", - "/region0/fileg_hold_cc4,f : { 4 }", - "/region0/fileg_release_cc5,f : { 5 }", - "/region0/fileg_start_cc6,f : { 6 }", - "/region0/fileg_sustain_cc7,f : { 7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/fileg_attack_cc1") == 1.0f); + REQUIRE( d.read("/region0/fileg_delay_cc2") == 2.0f); + REQUIRE( d.read("/region0/fileg_decay_cc3") == 3.0f); + REQUIRE( d.read("/region0/fileg_hold_cc4") == 4.0f); + REQUIRE( d.read("/region0/fileg_release_cc5") == 5.0f); + REQUIRE( d.read("/region0/fileg_start_cc6") == 6.0f); + REQUIRE( d.read("/region0/fileg_sustain_cc7") == 7.0f); } - SECTION("Negative values") + SECTION("fileg negative values") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav fileg_attack_cc1=-1 fileg_delay_cc2=-2 fileg_decay_cc3=-3 fileg_hold_cc4=-4 fileg_release_cc5=-5 fileg_start_cc6=-6 fileg_sustain_cc7=-7 )"); - synth.dispatchMessage(client, 0, "/region0/fileg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/fileg_attack_cc1,f : { -1 }", - "/region0/fileg_delay_cc2,f : { -2 }", - "/region0/fileg_decay_cc3,f : { -3 }", - "/region0/fileg_hold_cc4,f : { -4 }", - "/region0/fileg_release_cc5,f : { -5 }", - "/region0/fileg_start_cc6,f : { -6 }", - "/region0/fileg_sustain_cc7,f : { -7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/fileg_attack_cc1") == -1.0f); + REQUIRE( d.read("/region0/fileg_delay_cc2") == -2.0f); + REQUIRE( d.read("/region0/fileg_decay_cc3") == -3.0f); + REQUIRE( d.read("/region0/fileg_hold_cc4") == -4.0f); + REQUIRE( d.read("/region0/fileg_release_cc5") == -5.0f); + REQUIRE( d.read("/region0/fileg_start_cc6") == -6.0f); + REQUIRE( d.read("/region0/fileg_sustain_cc7") == -7.0f); } -} -TEST_CASE("[Values] pitcheg CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") + SECTION("pitcheg basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/pitcheg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/pitcheg_attack_cc1,N : { }", - "/region0/pitcheg_delay_cc2,N : { }", - "/region0/pitcheg_decay_cc3,N : { }", - "/region0/pitcheg_hold_cc4,N : { }", - "/region0/pitcheg_release_cc5,N : { }", - "/region0/pitcheg_start_cc6,N : { }", - "/region0/pitcheg_sustain_cc7,N : { }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pitcheg_attack_cc1") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_delay_cc2") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_decay_cc3") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_hold_cc4") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_release_cc5") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_start_cc6") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_sustain_cc7") == OSC::None); } - SECTION("Positive values") + SECTION("pitcheg positive values") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav pitcheg_attack_oncc1=1 pitcheg_delay_oncc2=2 pitcheg_decay_oncc3=3 pitcheg_hold_oncc4=4 pitcheg_release_oncc5=5 pitcheg_start_oncc6=6 pitcheg_sustain_oncc7=7 )"); - synth.dispatchMessage(client, 0, "/region0/pitcheg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/pitcheg_attack_cc1,f : { 1 }", - "/region0/pitcheg_delay_cc2,f : { 2 }", - "/region0/pitcheg_decay_cc3,f : { 3 }", - "/region0/pitcheg_hold_cc4,f : { 4 }", - "/region0/pitcheg_release_cc5,f : { 5 }", - "/region0/pitcheg_start_cc6,f : { 6 }", - "/region0/pitcheg_sustain_cc7,f : { 7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pitcheg_attack_cc1") == 1.0f); + REQUIRE( d.read("/region0/pitcheg_delay_cc2") == 2.0f); + REQUIRE( d.read("/region0/pitcheg_decay_cc3") == 3.0f); + REQUIRE( d.read("/region0/pitcheg_hold_cc4") == 4.0f); + REQUIRE( d.read("/region0/pitcheg_release_cc5") == 5.0f); + REQUIRE( d.read("/region0/pitcheg_start_cc6") == 6.0f); + REQUIRE( d.read("/region0/pitcheg_sustain_cc7") == 7.0f); } - SECTION("Negative values") + SECTION("pitcheg negative values") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav pitcheg_attack_cc1=-1 pitcheg_delay_cc2=-2 pitcheg_decay_cc3=-3 pitcheg_hold_cc4=-4 pitcheg_release_cc5=-5 pitcheg_start_cc6=-6 pitcheg_sustain_cc7=-7 )"); - synth.dispatchMessage(client, 0, "/region0/pitcheg_attack_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_delay_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_decay_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_hold_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_release_cc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_start_cc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_sustain_cc7", "", nullptr); - std::vector expected { - "/region0/pitcheg_attack_cc1,f : { -1 }", - "/region0/pitcheg_delay_cc2,f : { -2 }", - "/region0/pitcheg_decay_cc3,f : { -3 }", - "/region0/pitcheg_hold_cc4,f : { -4 }", - "/region0/pitcheg_release_cc5,f : { -5 }", - "/region0/pitcheg_start_cc6,f : { -6 }", - "/region0/pitcheg_sustain_cc7,f : { -7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/pitcheg_attack_cc1") == -1.0f); + REQUIRE( d.read("/region0/pitcheg_delay_cc2") == -2.0f); + REQUIRE( d.read("/region0/pitcheg_decay_cc3") == -3.0f); + REQUIRE( d.read("/region0/pitcheg_hold_cc4") == -4.0f); + REQUIRE( d.read("/region0/pitcheg_release_cc5") == -5.0f); + REQUIRE( d.read("/region0/pitcheg_start_cc6") == -6.0f); + REQUIRE( d.read("/region0/pitcheg_sustain_cc7") == -7.0f); } -} -TEST_CASE("[Values] ampeg curve CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") + SECTION("ampeg curve CC basic") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_attack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_delay_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_decay_curvecc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_hold_curvecc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_release_curvecc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_start_curvecc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_sustain_curvecc7", "", nullptr); - std::vector expected { - "/region0/ampeg_attack_curvecc1,i : { 0 }", - "/region0/ampeg_delay_curvecc2,i : { 0 }", - "/region0/ampeg_decay_curvecc3,i : { 0 }", - "/region0/ampeg_hold_curvecc4,i : { 0 }", - "/region0/ampeg_release_curvecc5,i : { 0 }", - "/region0/ampeg_start_curvecc6,i : { 0 }", - "/region0/ampeg_sustain_curvecc7,i : { 0 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/ampeg_attack_curvecc1") == 0); + REQUIRE( d.read("/region0/ampeg_delay_curvecc2") == 0); + REQUIRE( d.read("/region0/ampeg_decay_curvecc3") == 0); + REQUIRE( d.read("/region0/ampeg_hold_curvecc4") == 0); + REQUIRE( d.read("/region0/ampeg_release_curvecc5") == 0); + REQUIRE( d.read("/region0/ampeg_start_curvecc6") == 0); + REQUIRE( d.read("/region0/ampeg_sustain_curvecc7") == 0); } - SECTION("Change curves") + SECTION("ampeg curve CC change curves") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav ampeg_attack_curvecc1=1 ampeg_delay_curvecc2=2 ampeg_decay_curvecc3=3 ampeg_hold_curvecc4=4 ampeg_release_curvecc5=5 ampeg_start_curvecc6=6 ampeg_sustain_curvecc7=7 )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_attack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_delay_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_decay_curvecc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_hold_curvecc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_release_curvecc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_start_curvecc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/ampeg_sustain_curvecc7", "", nullptr); - std::vector expected { - "/region0/ampeg_attack_curvecc1,i : { 1 }", - "/region0/ampeg_delay_curvecc2,i : { 2 }", - "/region0/ampeg_decay_curvecc3,i : { 3 }", - "/region0/ampeg_hold_curvecc4,i : { 4 }", - "/region0/ampeg_release_curvecc5,i : { 5 }", - "/region0/ampeg_start_curvecc6,i : { 6 }", - "/region0/ampeg_sustain_curvecc7,i : { 7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/ampeg_attack_curvecc1") == 1); + REQUIRE( d.read("/region0/ampeg_delay_curvecc2") == 2); + REQUIRE( d.read("/region0/ampeg_decay_curvecc3") == 3); + REQUIRE( d.read("/region0/ampeg_hold_curvecc4") == 4); + REQUIRE( d.read("/region0/ampeg_release_curvecc5") == 5); + REQUIRE( d.read("/region0/ampeg_start_curvecc6") == 6); + REQUIRE( d.read("/region0/ampeg_sustain_curvecc7") == 7); } -} - -TEST_CASE("[Values] fileg curve CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") - { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - )"); - synth.dispatchMessage(client, 0, "/region0/fileg_attack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_delay_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_decay_curvecc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_hold_curvecc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_release_curvecc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_start_curvecc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_sustain_curvecc7", "", nullptr); - std::vector expected { - "/region0/fileg_attack_curvecc1,N : { }", - "/region0/fileg_delay_curvecc2,N : { }", - "/region0/fileg_decay_curvecc3,N : { }", - "/region0/fileg_hold_curvecc4,N : { }", - "/region0/fileg_release_curvecc5,N : { }", - "/region0/fileg_start_curvecc6,N : { }", - "/region0/fileg_sustain_curvecc7,N : { }", - }; - REQUIRE(messageList == expected); + SECTION("fileg curve CC basic") + { + d.load(R"( + sample=kick.wav + )"); + REQUIRE( d.read("/region0/fileg_attack_curvecc1") == OSC::None); + REQUIRE( d.read("/region0/fileg_delay_curvecc2") == OSC::None); + REQUIRE( d.read("/region0/fileg_decay_curvecc3") == OSC::None); + REQUIRE( d.read("/region0/fileg_hold_curvecc4") == OSC::None); + REQUIRE( d.read("/region0/fileg_release_curvecc5") == OSC::None); + REQUIRE( d.read("/region0/fileg_start_curvecc6") == OSC::None); + REQUIRE( d.read("/region0/fileg_sustain_curvecc7") == OSC::None); } - SECTION("Change curves") + SECTION("fileg curve CC change curves") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav fileg_attack_curvecc1=1 fileg_delay_curvecc2=2 fileg_decay_curvecc3=3 fileg_hold_curvecc4=4 fileg_release_curvecc5=5 fileg_start_curvecc6=6 fileg_sustain_curvecc7=7 )"); - synth.dispatchMessage(client, 0, "/region0/fileg_attack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_delay_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_decay_curvecc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_hold_curvecc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_release_curvecc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_start_curvecc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_sustain_curvecc7", "", nullptr); - std::vector expected { - "/region0/fileg_attack_curvecc1,i : { 1 }", - "/region0/fileg_delay_curvecc2,i : { 2 }", - "/region0/fileg_decay_curvecc3,i : { 3 }", - "/region0/fileg_hold_curvecc4,i : { 4 }", - "/region0/fileg_release_curvecc5,i : { 5 }", - "/region0/fileg_start_curvecc6,i : { 6 }", - "/region0/fileg_sustain_curvecc7,i : { 7 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/fileg_attack_curvecc1") == 1); + REQUIRE( d.read("/region0/fileg_delay_curvecc2") == 2); + REQUIRE( d.read("/region0/fileg_decay_curvecc3") == 3); + REQUIRE( d.read("/region0/fileg_hold_curvecc4") == 4); + REQUIRE( d.read("/region0/fileg_release_curvecc5") == 5); + REQUIRE( d.read("/region0/fileg_start_curvecc6") == 6); + REQUIRE( d.read("/region0/fileg_sustain_curvecc7") == 7); } -} - -TEST_CASE("[Values] pitcheg curve CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Basic") - { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - )"); - synth.dispatchMessage(client, 0, "/region0/pitcheg_attack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_delay_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_decay_curvecc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_hold_curvecc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_release_curvecc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_start_curvecc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_sustain_curvecc7", "", nullptr); - std::vector expected { - "/region0/pitcheg_attack_curvecc1,N : { }", - "/region0/pitcheg_delay_curvecc2,N : { }", - "/region0/pitcheg_decay_curvecc3,N : { }", - "/region0/pitcheg_hold_curvecc4,N : { }", - "/region0/pitcheg_release_curvecc5,N : { }", - "/region0/pitcheg_start_curvecc6,N : { }", - "/region0/pitcheg_sustain_curvecc7,N : { }", - }; - REQUIRE(messageList == expected); + SECTION("pitcheg curve CC basic") + { + d.load(R"( + sample=kick.wav + )"); + REQUIRE( d.read("/region0/pitcheg_attack_curvecc1") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_delay_curvecc2") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_decay_curvecc3") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_hold_curvecc4") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_release_curvecc5") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_start_curvecc6") == OSC::None); + REQUIRE( d.read("/region0/pitcheg_sustain_curvecc7") == OSC::None); } - SECTION("Change curves") + SECTION("pitcheg curve CC change curves") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav pitcheg_attack_curvecc1=1 pitcheg_delay_curvecc2=2 pitcheg_decay_curvecc3=3 pitcheg_hold_curvecc4=4 pitcheg_release_curvecc5=5 pitcheg_start_curvecc6=6 pitcheg_sustain_curvecc7=7 )"); - synth.dispatchMessage(client, 0, "/region0/pitcheg_attack_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_delay_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_decay_curvecc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_hold_curvecc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_release_curvecc5", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_start_curvecc6", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_sustain_curvecc7", "", nullptr); - std::vector expected { - "/region0/pitcheg_attack_curvecc1,i : { 1 }", - "/region0/pitcheg_delay_curvecc2,i : { 2 }", - "/region0/pitcheg_decay_curvecc3,i : { 3 }", - "/region0/pitcheg_hold_curvecc4,i : { 4 }", - "/region0/pitcheg_release_curvecc5,i : { 5 }", - "/region0/pitcheg_start_curvecc6,i : { 6 }", - "/region0/pitcheg_sustain_curvecc7,i : { 7 }", - }; - REQUIRE(messageList == expected); - } -} - -TEST_CASE("[Values] Filter stacking and cutoffs") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav cutoff=50 - sample=kick.wav cutoff2=500 - )"); - - SECTION("Test first region") - { - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/type", "", nullptr); - std::vector expected { - // No filters - }; - REQUIRE(messageList == expected); - } - - SECTION("Test second region") - { - synth.dispatchMessage(client, 0, "/region1/filter0/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/type", "", nullptr); - std::vector expected { - "/region1/filter0/cutoff,f : { 50 }", - "/region1/filter0/gain,f : { 0 }", - "/region1/filter0/resonance,f : { 0 }", - "/region1/filter0/keycenter,i : { 60 }", - "/region1/filter0/keytrack,f : { 0 }", - "/region1/filter0/veltrack,f : { 0 }", - "/region1/filter0/type,s : { lpf_2p }", - // No second filter - }; - REQUIRE(messageList == expected); - } - - SECTION("Test third region") - { - synth.dispatchMessage(client, 0, "/region2/filter0/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter1/type", "", nullptr); - std::vector expected { - // The first filter is default-filled - "/region2/filter0/cutoff,f : { 0 }", - "/region2/filter0/gain,f : { 0 }", - "/region2/filter0/resonance,f : { 0 }", - "/region2/filter0/keycenter,i : { 60 }", - "/region2/filter0/keytrack,f : { 0 }", - "/region2/filter0/veltrack,f : { 0 }", - "/region2/filter0/type,s : { lpf_2p }", - "/region2/filter1/cutoff,f : { 500 }", - "/region2/filter1/gain,f : { 0 }", - "/region2/filter1/resonance,f : { 0 }", - "/region2/filter1/keycenter,i : { 60 }", - "/region2/filter1/keytrack,f : { 0 }", - "/region2/filter1/veltrack,f : { 0 }", - "/region2/filter1/type,s : { lpf_2p }", - // No second filter - }; - REQUIRE(messageList == expected); - } -} - -TEST_CASE("[Values] Cutoff modifiers") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav cutoff_cc2=1000 cutoff_stepcc2=10 cutoff_smoothcc2=2 cutoff_curvecc2=4 - sample=kick.wav cutoff2_cc3=100 cutoff2_stepcc3=1 cutoff2_smoothcc3=20 cutoff2_curvecc3=3 - )"); - - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_cc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_stepcc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_smoothcc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_curvecc1", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_stepcc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_smoothcc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff_curvecc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/cutoff_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/cutoff_stepcc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/cutoff_smoothcc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter1/cutoff_curvecc3", "", nullptr); - std::vector expected { - "/region0/filter0/cutoff_cc1,N : { }", - "/region0/filter0/cutoff_stepcc1,N : { }", - "/region0/filter0/cutoff_smoothcc1,N : { }", - "/region0/filter0/cutoff_curvecc1,N : { }", - "/region0/filter0/cutoff_cc2,f : { 1000 }", - "/region0/filter0/cutoff_stepcc2,f : { 10 }", - "/region0/filter0/cutoff_smoothcc2,i : { 2 }", - "/region0/filter0/cutoff_curvecc2,i : { 4 }", - "/region1/filter1/cutoff_cc3,f : { 100 }", - "/region1/filter1/cutoff_stepcc3,f : { 1 }", - "/region1/filter1/cutoff_smoothcc3,i : { 20 }", - "/region1/filter1/cutoff_curvecc3,i : { 3 }", - }; - REQUIRE(messageList == expected); -} - -TEST_CASE("[Values] Filter types") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav fil_type=lpf_1p - sample=kick.wav fil_type=hpf_1p - sample=kick.wav fil_type=lpf_2p - sample=kick.wav fil_type=hpf_2p - sample=kick.wav fil_type=bpf_2p - sample=kick.wav fil_type=brf_2p - sample=kick.wav fil_type=bpf_1p - sample=kick.wav fil_type=brf_1p - sample=kick.wav fil_type=apf_1p - sample=kick.wav fil_type=lpf_2p_sv - sample=kick.wav fil_type=hpf_2p_sv - sample=kick.wav fil_type=bpf_2p_sv - sample=kick.wav fil_type=brf_2p_sv - sample=kick.wav fil_type=lpf_4p - sample=kick.wav fil_type=hpf_4p - sample=kick.wav fil_type=lpf_6p - sample=kick.wav fil_type=hpf_6p - sample=kick.wav fil_type=pink - sample=kick.wav fil_type=lsh - sample=kick.wav fil_type=hsh - sample=kick.wav fil_type=peq - sample=kick.wav fil2_type=peq - sample=kick.wav fil2_type=something - )"); - - synth.dispatchMessage(client, 0, "/region0/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region4/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region5/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region6/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region7/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region8/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region9/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region10/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region11/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region12/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region13/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region14/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region15/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region16/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region17/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region18/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region19/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region20/filter0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region21/filter1/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region22/filter1/type", "", nullptr); - std::vector expected { - "/region0/filter0/type,s : { lpf_1p }", - "/region1/filter0/type,s : { hpf_1p }", - "/region2/filter0/type,s : { lpf_2p }", - "/region3/filter0/type,s : { hpf_2p }", - "/region4/filter0/type,s : { bpf_2p }", - "/region5/filter0/type,s : { brf_2p }", - "/region6/filter0/type,s : { bpf_1p }", - "/region7/filter0/type,s : { brf_2p }", // If we have a 1-pole brf at one point, change it back - "/region8/filter0/type,s : { none }", // If the apf 1-pole works, change it back - "/region9/filter0/type,s : { lpf_2p_sv }", - "/region10/filter0/type,s : { hpf_2p_sv }", - "/region11/filter0/type,s : { bpf_2p_sv }", - "/region12/filter0/type,s : { brf_2p_sv }", - "/region13/filter0/type,s : { lpf_4p }", - "/region14/filter0/type,s : { hpf_4p }", - "/region15/filter0/type,s : { lpf_6p }", - "/region16/filter0/type,s : { hpf_6p }", - "/region17/filter0/type,s : { pink }", - "/region18/filter0/type,s : { lsh }", - "/region19/filter0/type,s : { hsh }", - "/region20/filter0/type,s : { peq }", - "/region21/filter1/type,s : { peq }", - "/region22/filter1/type,s : { none }", - }; - REQUIRE(messageList == expected); -} - -TEST_CASE("[Values] Filter dispatching") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - cutoff3=50 resonance2=3 fil2_gain=-5 fil3_keytrack=100 - fil_gain=5 fil1_gain=-5 fil2_veltrack=-100 - fil4_veltrack_cc7=-100 fil5_veltrack_curvecc2=2 - )"); - - synth.dispatchMessage(client, 0, "/region0/filter2/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/resonance", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter2/keytrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter1/veltrack", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter3/veltrack_cc7", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/filter4/veltrack_curvecc2", "", nullptr); - std::vector expected { - "/region0/filter2/cutoff,f : { 50 }", - "/region0/filter1/resonance,f : { 3 }", - "/region0/filter1/gain,f : { -5 }", - "/region0/filter2/keytrack,f : { 100 }", - "/region0/filter0/gain,f : { -5 }", - "/region0/filter1/veltrack,f : { -100 }", - "/region0/filter3/veltrack_cc7,f : { -100 }", - "/region0/filter4/veltrack_curvecc2,i : { 2 }", - }; - REQUIRE(messageList == expected); -} - -TEST_CASE("[Values] Filter value bounds") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - SECTION("Cutoff") - { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + REQUIRE( d.read("/region0/pitcheg_attack_curvecc1") == 1); + REQUIRE( d.read("/region0/pitcheg_delay_curvecc2") == 2); + REQUIRE( d.read("/region0/pitcheg_decay_curvecc3") == 3); + REQUIRE( d.read("/region0/pitcheg_hold_curvecc4") == 4); + REQUIRE( d.read("/region0/pitcheg_release_curvecc5") == 5); + REQUIRE( d.read("/region0/pitcheg_start_curvecc6") == 6); + REQUIRE( d.read("/region0/pitcheg_sustain_curvecc7") == 7); + } + + SECTION("Filter stacking and cutoffs") + { + d.load(R"( + sample=kick.wav + sample=kick.wav cutoff=50 + sample=kick.wav cutoff2=500 + )"); + REQUIRE( !d.replied("/region0/filter0/cutoff") ); + REQUIRE( !d.replied("/region0/filter0/gain") ); + REQUIRE( !d.replied("/region0/filter0/resonance") ); + REQUIRE( !d.replied("/region0/filter0/keycenter") ); + REQUIRE( !d.replied("/region0/filter0/keytrack") ); + REQUIRE( !d.replied("/region0/filter0/veltrack") ); + REQUIRE( !d.replied("/region0/filter0/type") ); + REQUIRE( !d.replied("/region0/filter1/cutoff") ); + REQUIRE( !d.replied("/region0/filter1/gain") ); + REQUIRE( !d.replied("/region0/filter1/resonance") ); + REQUIRE( !d.replied("/region0/filter1/keycenter") ); + REQUIRE( !d.replied("/region0/filter1/keytrack") ); + REQUIRE( !d.replied("/region0/filter1/veltrack") ); + REQUIRE( !d.replied("/region0/filter1/type") );\ + + // Second region + REQUIRE( d.read("/region1/filter0/cutoff") == 50.0f); + REQUIRE( d.read("/region1/filter0/gain") == 0.0f); + REQUIRE( d.read("/region1/filter0/resonance") == 0.0f); + REQUIRE( d.read("/region1/filter0/keycenter") == 60); + REQUIRE( d.read("/region1/filter0/keytrack") == 0.0f); + REQUIRE( d.read("/region1/filter0/veltrack") == 0.0f); + REQUIRE( d.read("/region1/filter0/type") == "lpf_2p"); + // No second filter on the second region + REQUIRE( !d.replied("/region1/filter1/cutoff")); + REQUIRE( !d.replied("/region1/filter1/gain")); + REQUIRE( !d.replied("/region1/filter1/resonance")); + REQUIRE( !d.replied("/region1/filter1/keycenter")); + REQUIRE( !d.replied("/region1/filter1/keytrack")); + REQUIRE( !d.replied("/region1/filter1/veltrack")); + REQUIRE( !d.replied("/region1/filter1/type")); + + // Third region + REQUIRE( d.read("/region2/filter0/cutoff") == 0.0f); + REQUIRE( d.read("/region2/filter0/gain") == 0.0f); + REQUIRE( d.read("/region2/filter0/resonance") == 0.0f); + REQUIRE( d.read("/region2/filter0/keycenter") == 60); + REQUIRE( d.read("/region2/filter0/keytrack") == 0.0f); + REQUIRE( d.read("/region2/filter0/veltrack") == 0.0f); + REQUIRE( d.read("/region2/filter0/type") == "lpf_2p"); + REQUIRE( d.read("/region2/filter1/cutoff") == 500.0f); + REQUIRE( d.read("/region2/filter1/gain") == 0.0f); + REQUIRE( d.read("/region2/filter1/resonance") == 0.0f); + REQUIRE( d.read("/region2/filter1/keycenter") == 60); + REQUIRE( d.read("/region2/filter1/keytrack") == 0.0f); + REQUIRE( d.read("/region2/filter1/veltrack") == 0.0f); + REQUIRE( d.read("/region2/filter1/type") == "lpf_2p"); + } + + SECTION("Cutoff modifiers") + { + d.load(R"( + sample=kick.wav cutoff_cc2=1000 cutoff_stepcc2=10 cutoff_smoothcc2=2 cutoff_curvecc2=4 + sample=kick.wav cutoff2_cc3=100 cutoff2_stepcc3=1 cutoff2_smoothcc3=20 cutoff2_curvecc3=3 + )"); + + REQUIRE( d.read("/region0/filter0/cutoff_cc1") == OSC::None); + REQUIRE( d.read("/region0/filter0/cutoff_stepcc1") == OSC::None); + REQUIRE( d.read("/region0/filter0/cutoff_smoothcc1") == OSC::None); + REQUIRE( d.read("/region0/filter0/cutoff_curvecc1") == OSC::None); + REQUIRE( d.read("/region0/filter0/cutoff_cc2") == 1000.0f); + REQUIRE( d.read("/region0/filter0/cutoff_stepcc2") == 10.0f); + REQUIRE( d.read("/region0/filter0/cutoff_smoothcc2") == 2); + REQUIRE( d.read("/region0/filter0/cutoff_curvecc2") == 4); + REQUIRE( d.read("/region1/filter1/cutoff_cc3") == 100.0f); + REQUIRE( d.read("/region1/filter1/cutoff_stepcc3") == 1.0f); + REQUIRE( d.read("/region1/filter1/cutoff_smoothcc3") == 20); + REQUIRE( d.read("/region1/filter1/cutoff_curvecc3") == 3); + } + + SECTION("Filter types") + { + d.load(R"( + sample=kick.wav fil_type=lpf_1p + sample=kick.wav fil_type=hpf_1p + sample=kick.wav fil_type=lpf_2p + sample=kick.wav fil_type=hpf_2p + sample=kick.wav fil_type=bpf_2p + sample=kick.wav fil_type=brf_2p + sample=kick.wav fil_type=bpf_1p + sample=kick.wav fil_type=brf_1p + sample=kick.wav fil_type=apf_1p + sample=kick.wav fil_type=lpf_2p_sv + sample=kick.wav fil_type=hpf_2p_sv + sample=kick.wav fil_type=bpf_2p_sv + sample=kick.wav fil_type=brf_2p_sv + sample=kick.wav fil_type=lpf_4p + sample=kick.wav fil_type=hpf_4p + sample=kick.wav fil_type=lpf_6p + sample=kick.wav fil_type=hpf_6p + sample=kick.wav fil_type=pink + sample=kick.wav fil_type=lsh + sample=kick.wav fil_type=hsh + sample=kick.wav fil_type=peq + sample=kick.wav fil2_type=peq + sample=kick.wav fil2_type=something + )"); + + REQUIRE( d.read("/region0/filter0/type") == "lpf_1p" ); + REQUIRE( d.read("/region1/filter0/type") == "hpf_1p" ); + REQUIRE( d.read("/region2/filter0/type") == "lpf_2p" ); + REQUIRE( d.read("/region3/filter0/type") == "hpf_2p" ); + REQUIRE( d.read("/region4/filter0/type") == "bpf_2p" ); + REQUIRE( d.read("/region5/filter0/type") == "brf_2p" ); + REQUIRE( d.read("/region6/filter0/type") == "bpf_1p" ); + REQUIRE( d.read("/region7/filter0/type") == "brf_2p" ); // If we have a 1-pole brf at one point, change it back + REQUIRE( d.read("/region8/filter0/type") == "none" ); // If the apf 1-pole works, change it back + REQUIRE( d.read("/region9/filter0/type") == "lpf_2p_sv" ); + REQUIRE( d.read("/region10/filter0/type") == "hpf_2p_sv" ); + REQUIRE( d.read("/region11/filter0/type") == "bpf_2p_sv" ); + REQUIRE( d.read("/region12/filter0/type") == "brf_2p_sv" ); + REQUIRE( d.read("/region13/filter0/type") == "lpf_4p" ); + REQUIRE( d.read("/region14/filter0/type") == "hpf_4p" ); + REQUIRE( d.read("/region15/filter0/type") == "lpf_6p" ); + REQUIRE( d.read("/region16/filter0/type") == "hpf_6p" ); + REQUIRE( d.read("/region17/filter0/type") == "pink" ); + REQUIRE( d.read("/region18/filter0/type") == "lsh" ); + REQUIRE( d.read("/region19/filter0/type") == "hsh" ); + REQUIRE( d.read("/region20/filter0/type") == "peq" ); + REQUIRE( d.read("/region21/filter1/type") == "peq" ); + REQUIRE( d.read("/region22/filter1/type") == "none" ); + } + + SECTION("Filter dispatching") + { + d.load(R"( + sample=kick.wav + cutoff3=50 resonance2=3 fil2_gain=-5 fil3_keytrack=100 + fil_gain=5 fil1_gain=-5 fil2_veltrack=-100 + fil4_veltrack_cc7=-100 fil5_veltrack_curvecc2=2 + )"); + + REQUIRE( d.read("/region0/filter2/cutoff") == 50.0f); + REQUIRE( d.read("/region0/filter1/resonance") == 3.0f); + REQUIRE( d.read("/region0/filter1/gain") == -5.0f); + REQUIRE( d.read("/region0/filter2/keytrack") == 100.0f); + REQUIRE( d.read("/region0/filter0/gain") == -5.0f); + REQUIRE( d.read("/region0/filter1/veltrack") == -100.0f); + REQUIRE( d.read("/region0/filter3/veltrack_cc7") == -100.0f); + REQUIRE( d.read("/region0/filter4/veltrack_curvecc2") == 2); + } + + SECTION("Filter value bounds") + { + d.load(R"( sample=kick.wav cutoff=100000 sample=kick.wav cutoff=50 cutoff=-100 )"); - synth.dispatchMessage(client, 0, "/region0/filter0/cutoff", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/cutoff", "", nullptr); - std::vector expected { - "/region0/filter0/cutoff,f : { 100000 }", - "/region1/filter0/cutoff,f : { -100 }", - }; - REQUIRE(messageList == expected); - } - - SECTION("Cutoff") - { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + REQUIRE( d.read("/region0/filter0/cutoff") == 100000.0f); + REQUIRE( d.read("/region1/filter0/cutoff") == -100.0f); + + d.load(R"( sample=kick.wav resonance=5 resonance=-5 )"); - synth.dispatchMessage(client, 0, "/region0/filter0/resonance", "", nullptr); - std::vector expected { - "/region0/filter0/resonance,f : { -5 }", - }; - REQUIRE(messageList == expected); - } - - SECTION("Keycenter") - { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + REQUIRE( d.read("/region0/filter0/resonance") == -5.0f); + + d.load(R"( sample=kick.wav fil_keycenter=40 sample=kick.wav fil_keycenter=40 fil_keycenter=1000 sample=kick.wav fil_keycenter=c3 )"); - synth.dispatchMessage(client, 0, "/region0/filter0/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/filter0/keycenter", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/filter0/keycenter", "", nullptr); - std::vector expected { - "/region0/filter0/keycenter,i : { 40 }", - "/region1/filter0/keycenter,i : { 60 }", - "/region2/filter0/keycenter,i : { 48 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/filter0/keycenter") == 40); + REQUIRE( d.read("/region1/filter0/keycenter") == 60); + REQUIRE( d.read("/region2/filter0/keycenter") == 48); } -} -TEST_CASE("[Values] EQ stacking and gains") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav eq1_gain=3 - sample=kick.wav eq4_gain=6 - )"); - - SECTION("Test first region") - { - synth.dispatchMessage(client, 0, "/region0/eq0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/vel2freq", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/vel2freq", "", nullptr); - std::vector expected { - // No eqs - }; - REQUIRE(messageList == expected); - } - - SECTION("Test second region") - { - synth.dispatchMessage(client, 0, "/region1/eq0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/vel2freq", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq1/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq1/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq1/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq1/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq1/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq1/vel2freq", "", nullptr); - std::vector expected { - "/region1/eq0/gain,f : { 3 }", - "/region1/eq0/type,s : { peak }", - "/region1/eq0/bandwidth,f : { 1 }", - "/region1/eq0/frequency,f : { 50 }", - "/region1/eq0/vel2gain,f : { 0 }", - "/region1/eq0/vel2freq,f : { 0 }", - // No second eq - }; - REQUIRE(messageList == expected); - } - - SECTION("Test third region") - { - synth.dispatchMessage(client, 0, "/region2/eq0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq0/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq0/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq0/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq0/vel2freq", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq3/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq3/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq3/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq3/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq3/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq3/vel2freq", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq1/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq2/frequency", "", nullptr); - std::vector expected { - // The first eq is default-filled - "/region2/eq0/gain,f : { 0 }", - "/region2/eq0/type,s : { peak }", - "/region2/eq0/bandwidth,f : { 1 }", - "/region2/eq0/frequency,f : { 50 }", - "/region2/eq0/vel2gain,f : { 0 }", - "/region2/eq0/vel2freq,f : { 0 }", - "/region2/eq3/gain,f : { 6 }", - "/region2/eq3/type,s : { peak }", - "/region2/eq3/bandwidth,f : { 1 }", - "/region2/eq3/frequency,f : { 0 }", - "/region2/eq3/vel2gain,f : { 0 }", - "/region2/eq3/vel2freq,f : { 0 }", - "/region2/eq1/frequency,f : { 500 }", - "/region2/eq2/frequency,f : { 5000 }", - }; - REQUIRE(messageList == expected); + SECTION("EQ stacking and gains") + { + d.load(R"( + sample=kick.wav + sample=kick.wav eq1_gain=3 + sample=kick.wav eq4_gain=6 + )"); + + REQUIRE( !d.replied("/region0/eq0/gain") ); + REQUIRE( !d.replied("/region0/eq0/type") ); + REQUIRE( !d.replied("/region0/eq0/bandwidth") ); + REQUIRE( !d.replied("/region0/eq0/frequency") ); + REQUIRE( !d.replied("/region0/eq0/vel2gain") ); + REQUIRE( !d.replied("/region0/eq0/vel2freq") ); + REQUIRE( !d.replied("/region0/eq1/gain") ); + REQUIRE( !d.replied("/region0/eq1/type") ); + REQUIRE( !d.replied("/region0/eq1/bandwidth") ); + REQUIRE( !d.replied("/region0/eq1/frequency") ); + REQUIRE( !d.replied("/region0/eq1/vel2gain") ); + REQUIRE( !d.replied("/region0/eq1/vel2freq") ); + + REQUIRE( d.read("/region1/eq0/gain") == 3.0f); + REQUIRE( d.read("/region1/eq0/type") == "peak"); + REQUIRE( d.read("/region1/eq0/bandwidth") == 1.0f); + REQUIRE( d.read("/region1/eq0/frequency") == 50.0f); + REQUIRE( d.read("/region1/eq0/vel2gain") == 0.0f); + REQUIRE( d.read("/region1/eq0/vel2freq") == 0.0f); + REQUIRE( !d.replied("/region1/eq1/gain") ); + REQUIRE( !d.replied("/region1/eq1/type") ); + REQUIRE( !d.replied("/region1/eq1/bandwidth") ); + REQUIRE( !d.replied("/region1/eq1/frequency") ); + REQUIRE( !d.replied("/region1/eq1/vel2gain") ); + REQUIRE( !d.replied("/region1/eq1/vel2freq") ); + + // The first eq is default-filled + REQUIRE( d.read("/region2/eq0/gain") == 0.0f); + REQUIRE( d.read("/region2/eq0/type") == "peak"); + REQUIRE( d.read("/region2/eq0/bandwidth") == 1.0f); + REQUIRE( d.read("/region2/eq0/frequency") == 50.0f); + REQUIRE( d.read("/region2/eq0/vel2gain") == 0.0f); + REQUIRE( d.read("/region2/eq0/vel2freq") == 0.0f); + REQUIRE( d.read("/region2/eq3/gain") == 6.0f); + REQUIRE( d.read("/region2/eq3/type") == "peak"); + REQUIRE( d.read("/region2/eq3/bandwidth") == 1.0f); + REQUIRE( d.read("/region2/eq3/frequency") == 0.0f); + REQUIRE( d.read("/region2/eq3/vel2gain") == 0.0f); + REQUIRE( d.read("/region2/eq3/vel2freq") == 0.0f); + REQUIRE( d.read("/region2/eq1/frequency") == 500.0f); + REQUIRE( d.read("/region2/eq2/frequency") == 5000.0f); } -} -TEST_CASE("[Values] EQ types") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav eq1_type=hshelf - sample=kick.wav eq1_type=lshelf - sample=kick.wav eq1_type=hshelf eq1_type=peak - sample=kick.wav eq1_type=something - )"); - - synth.dispatchMessage(client, 0, "/region0/eq0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region2/eq0/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region3/eq0/type", "", nullptr); - std::vector expected { - "/region0/eq0/type,s : { hshelf }", - "/region1/eq0/type,s : { lshelf }", - "/region2/eq0/type,s : { peak }", - "/region3/eq0/type,s : { none }", - }; - REQUIRE(messageList == expected); -} + SECTION("EQ types") + { + d.load(R"( + sample=kick.wav eq1_type=hshelf + sample=kick.wav eq1_type=lshelf + sample=kick.wav eq1_type=hshelf eq1_type=peak + sample=kick.wav eq1_type=something + )"); -TEST_CASE("[Values] EQ dispatching") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - eq3_bw=2 eq1_gain=-25 eq2_freq=300 eq3_type=lshelf - eq3_vel2gain=10 eq1_vel2freq=100 - )"); - - synth.dispatchMessage(client, 0, "/region0/eq2/bandwidth", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq1/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq2/type", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq2/vel2gain", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eq0/vel2freq", "", nullptr); - std::vector expected { - "/region0/eq2/bandwidth,f : { 2 }", - "/region0/eq0/gain,f : { -25 }", - "/region0/eq1/frequency,f : { 300 }", - "/region0/eq2/type,s : { lshelf }", - "/region0/eq2/vel2gain,f : { 10 }", - "/region0/eq0/vel2freq,f : { 100 }", - }; - REQUIRE(messageList == expected); -} + REQUIRE( d.read("/region0/eq0/type") == "hshelf"); + REQUIRE( d.read("/region1/eq0/type") == "lshelf"); + REQUIRE( d.read("/region2/eq0/type") == "peak"); + REQUIRE( d.read("/region3/eq0/type") == "none"); + } -TEST_CASE("[Values] EQ value bounds") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); + SECTION("EQ dispatching") + { + d.load(R"( + sample=kick.wav + eq3_bw=2 eq1_gain=-25 eq2_freq=300 eq3_type=lshelf + eq3_vel2gain=10 eq1_vel2freq=100 + )"); - SECTION("Frequency") + REQUIRE( d.read("/region0/eq2/bandwidth") == 2.0f); + REQUIRE( d.read("/region0/eq0/gain") == -25.0f); + REQUIRE( d.read("/region0/eq1/frequency") == 300.0f); + REQUIRE( d.read("/region0/eq2/type") == "lshelf"); + REQUIRE( d.read("/region0/eq2/vel2gain") == 10.0f); + REQUIRE( d.read("/region0/eq0/vel2freq") == 100.0f); + } + + SECTION("EQ value bounds") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( + d.load(R"( sample=kick.wav eq1_freq=100000 sample=kick.wav eq1_freq=50 eq1_freq=-100 )"); - synth.dispatchMessage(client, 0, "/region0/eq0/frequency", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/eq0/frequency", "", nullptr); - std::vector expected { - "/region0/eq0/frequency,f : { 100000 }", - "/region1/eq0/frequency,f : { -100 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/eq0/frequency") == 100000.0f); + REQUIRE( d.read("/region1/eq0/frequency") == -100.0f); + + d.load(R"( + sample=kick.wav eq1_bw=5 eq1_bw=-5 + )"); + REQUIRE( d.read("/region0/eq0/bandwidth") == -5.0f); } - SECTION("Bandwidth") + SECTION("Flex EGs") { - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav eq1_bw=5 eq1_bw=-5 + d.load(R"( + sample=kick.wav eg1_time1=0.1 eg1_level1=0.5 eg1_time2=0.4 eg1_level2=2 eg2_time1=4 eg2_level1=0.1 )"); - synth.dispatchMessage(client, 0, "/region0/eq0/bandwidth", "", nullptr); - std::vector expected { - "/region0/eq0/bandwidth,f : { -5 }", - }; - REQUIRE(messageList == expected); + REQUIRE( d.read("/region0/eg0/point0/time") == 0.1f); + REQUIRE( d.read("/region0/eg0/point0/level") == 0.5f); + REQUIRE( d.read("/region0/eg0/point1/time") == 0.4f); + REQUIRE( d.read("/region0/eg0/point1/level") == 1.0f); // Level values in EGs are clamped in Sforzando + REQUIRE( d.read("/region0/eg1/point0/time") == 4.0f); + REQUIRE( d.read("/region0/eg1/point0/level") == 0.1f); } -} -TEST_CASE("[Values] Flex EGs") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav eg1_time1=0.1 eg1_level1=0.5 eg1_time2=0.4 eg1_level2=2 eg2_time1=4 eg2_level1=0.1 - )"); - synth.dispatchMessage(client, 0, "/region0/eg0/point0/time", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg0/point0/level", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg0/point1/time", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg0/point1/level", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg1/point0/time", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg1/point0/level", "", nullptr); - std::vector expected { - "/region0/eg0/point0/time,f : { 0.1 }", - "/region0/eg0/point0/level,f : { 0.5 }", - "/region0/eg0/point1/time,f : { 0.4 }", - "/region0/eg0/point1/level,f : { 1 }", // Level values in EGs are clamped in Sforzando - "/region0/eg1/point0/time,f : { 4 }", - "/region0/eg1/point0/level,f : { 0.1 }", - }; - REQUIRE(messageList == expected); -} - -TEST_CASE("[Values] Flex EGs CC") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav eg1_time1_cc2=0.1 eg1_level1_oncc3=0.5 - )"); - synth.dispatchMessage(client, 0, "/region0/eg0/point0/time_cc2", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg0/point0/time_cc4", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg0/point0/level_cc3", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/eg0/point0/level_cc12", "", nullptr); - std::vector expected { - "/region0/eg0/point0/time_cc2,f : { 0.1 }", - "/region0/eg0/point0/time_cc4,f : { 0 }", - "/region0/eg0/point0/level_cc3,f : { 0.5 }", - "/region0/eg0/point0/level_cc12,f : { 0 }", - }; - REQUIRE(messageList == expected); -} + SECTION("Flex EGs CC") + { + d.load(R"( + sample=kick.wav eg1_time1_cc2=0.1 eg1_level1_oncc3=0.5 + )"); + REQUIRE( d.read("/region0/eg0/point0/time_cc2") == 0.1f); + REQUIRE( d.read("/region0/eg0/point0/time_cc4") == 0.0f); + REQUIRE( d.read("/region0/eg0/point0/level_cc3") == 0.5f); + REQUIRE( d.read("/region0/eg0/point0/level_cc12") == 0.0f); + } -TEST_CASE("[Values] Dynamic EGs") -{ - Synth synth; - std::vector messageList; - Client client(&messageList); - client.setReceiveCallback(&simpleMessageReceiver); - - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"( - sample=kick.wav - sample=kick.wav ampeg_dynamic=1 pitcheg_dynamic=1 fileg_dynamic=1 - )"); - synth.dispatchMessage(client, 0, "/region0/ampeg_dynamic", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/pitcheg_dynamic", "", nullptr); - synth.dispatchMessage(client, 0, "/region0/fileg_dynamic", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/ampeg_dynamic", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/pitcheg_dynamic", "", nullptr); - synth.dispatchMessage(client, 0, "/region1/fileg_dynamic", "", nullptr); - std::vector expected { - "/region0/ampeg_dynamic,F : { }", - "/region0/pitcheg_dynamic,N : { }", - "/region0/fileg_dynamic,N : { }", - "/region1/ampeg_dynamic,T : { }", - "/region1/pitcheg_dynamic,T : { }", - "/region1/fileg_dynamic,T : { }", - }; - REQUIRE(messageList == expected); + SECTION("Dynamic EGs") + { + d.load(R"( + sample=kick.wav + sample=kick.wav ampeg_dynamic=1 pitcheg_dynamic=1 fileg_dynamic=1 + )"); + REQUIRE( d.read("/region0/ampeg_dynamic") == OSC::False); + REQUIRE( d.read("/region0/pitcheg_dynamic") == OSC::None); + REQUIRE( d.read("/region0/fileg_dynamic") == OSC::None); + REQUIRE( d.read("/region1/ampeg_dynamic") == OSC::True); + REQUIRE( d.read("/region1/pitcheg_dynamic") == OSC::True); + REQUIRE( d.read("/region1/fileg_dynamic") == OSC::True); + } } diff --git a/tests/SynthDiscussion.h b/tests/SynthDiscussion.h new file mode 100644 index 000000000..0381a0089 --- /dev/null +++ b/tests/SynthDiscussion.h @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz +#pragma once +#include +#include +#include +#include +#include "absl/strings/string_view.h" +#include "sfizz/Synth.h" +#include "sfizz_message.h" +#include "catch2/catch.hpp" +#include "utility/Size.h" + +using namespace sfz; +constexpr int maxArgs = 8; + +enum class OSCValueLess { True, False, None }; +using OSCVariant = absl::variant; + +// template +// struct vecToTuple +// { +// template +// static void rewrap(std::tuple &tup, const std::vector& variants) +// { +// std::get(tup) = std::get(tup))>(variants[I-1]); +// rewrap(tup, variants); +// } +// template <> +// static void rewrap<0>(std::tuple &, const std::vector&) { } +// }; + +// template +struct SynthDiscussion +{ + SynthDiscussion() + { + client.setReceiveCallback(&SynthDiscussion::receiver); + } + + static void receiver(void* data, int, const char* path, const char* sig, const sfizz_arg_t* args) + { + SynthDiscussion* self = (SynthDiscussion*)data; + self->recv_path = path; + self->recv_sig = sig; + self->recv_args.clear(); + for (int i = 0, n = self->recv_sig.size(); i < n; ++i) { + switch (self->recv_sig[i]) { + case 'i': self->recv_args.emplace_back(args[i].i); break; + case 'f': self->recv_args.emplace_back(args[i].f); break; + case 'd': self->recv_args.emplace_back(args[i].d); break; + case 'h': self->recv_args.emplace_back(args[i].h); break; + case 's': self->recv_args.emplace_back(args[i].s); break; + case 'T': self->recv_args.emplace_back(OSCValueLess::True); break; + case 'F': self->recv_args.emplace_back(OSCValueLess::False); break; + case 'N': self->recv_args.emplace_back(OSCValueLess::None); break; + default: + ASSERTFALSE; + } + } + } + + void load(absl::string_view sfz) + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/discussion.sfz", sfz); + } + + void send(absl::string_view path, int32_t value) + { + sent_args[0].i = value; + synth.dispatchMessage(client, 0, path.data(), "i", sent_args); + } + + void send(absl::string_view path, int64_t value) + { + sent_args[0].h = value; + synth.dispatchMessage(client, 0, path.data(), "h", sent_args); + } + + void send(absl::string_view path, float value) + { + sent_args[0].f = value; + synth.dispatchMessage(client, 0, path.data(), "f", sent_args); + } + + void send(absl::string_view path, const std::string& value) + { + sent_args[0].s = value.c_str(); + synth.dispatchMessage(client, 0, path.data(), "s", sent_args); + } + + void send(absl::string_view path, std::nullptr_t) + { + synth.dispatchMessage(client, 0, path.data(), "N", nullptr); + } + + void send(absl::string_view path, bool value) + { + synth.dispatchMessage(client, 0, path.data(), value ? "T" : "F", nullptr); + } + + template T read(absl::string_view path) + { + synth.dispatchMessage(client, 0, path.data(), "", nullptr); + return absl::get(recv_args[0]); + } + + bool replied(absl::string_view path) + { + recv_path = ""; + synth.dispatchMessage(client, 0, path.data(), "", nullptr); + return !recv_path.empty(); + } + + template std::vector readAll(absl::string_view path) + { + synth.dispatchMessage(client, 0, path.data(), "", nullptr); + std::vector returned; + for (const auto& arg: recv_args) + returned.push_back(absl::get(arg)); + + return returned; + } + + void sendAll(absl::string_view path, const std::vector& value) + { + std::string signature(value.size(), 'f'); + for (int i = 0; i < ssize(value) && i < maxArgs; ++i) + sent_args[i].f = value[i]; + synth.dispatchMessage(client, 0, path.data(), signature.c_str(), sent_args); + } + + void sendAll(absl::string_view path, const std::vector& value) + { + std::string signature(value.size(), 'i'); + for (int i = 0; i < ssize(value) && i < maxArgs; ++i) + sent_args[i].i = value[i]; + synth.dispatchMessage(client, 0, path.data(), signature.c_str(), sent_args); + } + + void sendAll(absl::string_view path, const std::vector& value) + { + std::string signature(value.size(), 'h'); + for (int i = 0; i < ssize(value) && i < maxArgs; ++i) + sent_args[i].h = value[i]; + synth.dispatchMessage(client, 0, path.data(), signature.c_str(), sent_args); + } + + template + T sendAndRead(absl::string_view path, T value) + { + send(path, value); + return read(path); + } + + template + std::vector sendAndReadAll(absl::string_view path, const std::vector& value) + { + sendAll(path, value); + return readAll(path); + } + + std::string formatLast() const + { + std::string newMessage = absl::StrCat(recv_path, ",", recv_sig, " : { "); + for (unsigned i = 0, n = recv_sig.size(); i < n; ++i) { + absl::visit([&](auto&& arg){ absl::StrAppend(&newMessage, arg); }, recv_args[i]); + if (i == (n - 1)) + absl::StrAppend(&newMessage, " }"); + else + absl::StrAppend(&newMessage, ", "); + } + + return newMessage; + } + + Synth synth; + sfizz_arg_t sent_args[maxArgs]; + std::vector recv_args; + std::string recv_path; + std::string recv_sig; + AudioBuffer buffer { 2, 256 }; + Client client { this }; +};