From 5527282159b7e001f4b021d7002e163c7b777f28 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Tue, 29 Jun 2021 10:48:53 +0200 Subject: [PATCH 1/7] Parse the relevant inputs --- src/sfizz/Defaults.cpp | 3 + src/sfizz/Defaults.h | 3 + src/sfizz/FilterDescription.h | 1 + src/sfizz/Region.cpp | 50 +++++++++++++++ src/sfizz/Region.h | 2 + src/sfizz/SfzHelpers.h | 7 +++ src/sfizz/SynthMessaging.cpp | 62 ++++++++++++++++++ tests/RegionValuesT.cpp | 115 +++++++++++++++++++++++++--------- 8 files changed, 215 insertions(+), 28 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 40ce37c7..a70f3602 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -90,6 +90,7 @@ FloatSpec width { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds FloatSpec widthMod { 0.0f, {-200.0f, 200.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec ampKeytrack { 0.0f, {-96.0f, 12.0f}, kPermissiveBounds }; FloatSpec ampVeltrack { 100.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec ampVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec ampVelcurve { 0.0f, {0.0f, 1.0f}, kPermissiveBounds }; FloatSpec ampRandom { 0.0f, {-24.0f, 24.0f}, kPermissiveBounds }; BoolSpec rtDead { false, {false, true}, kEnforceBounds }; @@ -103,6 +104,7 @@ FloatSpec filterGainMod { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec filterRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec filterKeytrack { 0, {0, 1200}, kPermissiveBounds }; FloatSpec filterVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; +FloatSpec filterVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec eqBandwidth { 1.0f, {0.001f, 4.0f}, kPermissiveBounds }; FloatSpec eqBandwidthMod { 0.0f, {-4.0f, 4.0f}, kPermissiveBounds }; FloatSpec eqFrequency { 0.0f, {0.0f, 20000.0f}, kPermissiveBounds }; @@ -114,6 +116,7 @@ FloatSpec eqVel2Gain { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec pitchKeytrack { 100, {-1200, 1200}, kPermissiveBounds }; FloatSpec pitchRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec pitchVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; +FloatSpec pitchVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; FloatSpec transpose { 0, {-127, 127}, kPermissiveBounds }; FloatSpec pitch { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; FloatSpec pitchMod { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 7d6c54fa..2a271920 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -202,6 +202,7 @@ namespace Default extern const OpcodeSpec widthMod; extern const OpcodeSpec ampKeytrack; extern const OpcodeSpec ampVeltrack; + extern const OpcodeSpec ampVeltrackMod; extern const OpcodeSpec ampVelcurve; extern const OpcodeSpec ampRandom; extern const OpcodeSpec rtDead; @@ -215,6 +216,7 @@ namespace Default extern const OpcodeSpec filterRandom; extern const OpcodeSpec filterKeytrack; extern const OpcodeSpec filterVeltrack; + extern const OpcodeSpec filterVeltrackMod; extern const OpcodeSpec eqBandwidth; extern const OpcodeSpec eqBandwidthMod; extern const OpcodeSpec eqFrequency; @@ -226,6 +228,7 @@ namespace Default extern const OpcodeSpec pitchKeytrack; extern const OpcodeSpec pitchRandom; extern const OpcodeSpec pitchVeltrack; + extern const OpcodeSpec pitchVeltrackMod; extern const OpcodeSpec transpose; extern const OpcodeSpec pitch; extern const OpcodeSpec pitchMod; diff --git a/src/sfizz/FilterDescription.h b/src/sfizz/FilterDescription.h index 4674477c..59eadab9 100644 --- a/src/sfizz/FilterDescription.h +++ b/src/sfizz/FilterDescription.h @@ -20,6 +20,7 @@ struct FilterDescription float keytrack { Default::filterKeytrack }; uint8_t keycenter { Default::key }; float veltrack { Default::filterVeltrack }; + CCMap> veltrackCC { ModifierCurvePair{ Default::filterVeltrackMod, Default::curveCC } }; float random { Default::filterRandom }; FilterType type { FilterType::kFilterLpf2p }; }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 344d7c6a..0dc7fc3d 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -449,6 +449,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) case hash("amp_veltrack"): ampVeltrack = opcode.read(Default::ampVeltrack); break; + case hash("amp_veltrack_oncc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + ampVeltrackCC[opcode.parameters.back()].modifier = opcode.read(Default::ampVeltrackMod); + break; + case hash("amp_veltrack_curvecc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + ampVeltrackCC[opcode.parameters.back()].curve = opcode.read(Default::curveCC); + break; case hash("amp_random"): ampRandom = opcode.read(Default::ampRandom); break; @@ -625,6 +637,32 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) filters[filterIndex].veltrack = opcode.read(Default::filterVeltrack); } break; + case hash("fil&_veltrack_oncc&"): + { + const auto filterIndex = opcode.parameters.front() - 1; + if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) + return false; + + const auto cc = opcode.parameters.back(); + if (cc >= config::numCCs) + return false; + + filters[filterIndex].veltrackCC[cc].modifier = opcode.read(Default::ampVeltrackMod); + } + break; + case hash("fil&_veltrack_curvecc&"): + { + const auto filterIndex = opcode.parameters.front() - 1; + if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) + return false; + + const auto cc = opcode.parameters.back(); + if (cc >= config::numCCs) + return false; + + filters[filterIndex].veltrackCC[cc].curve = opcode.read(Default::curveCC); + } + break; case hash("fil&_random"): // also fil_random, cutoff_random, cutoff&_random { const auto filterIndex = opcode.parameters.front() - 1; @@ -755,6 +793,18 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) case hash("pitch_veltrack"): pitchVeltrack = opcode.read(Default::pitchVeltrack); break; + case hash("pitch_veltrack_oncc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + pitchVeltrackCC[opcode.parameters.back()].modifier = opcode.read(Default::pitchVeltrackMod); + break; + case hash("pitch_veltrack_curvecc&"): + if (opcode.parameters.back() >= config::numCCs) + return false; + + pitchVeltrackCC[opcode.parameters.back()].curve = opcode.read(Default::curveCC); + break; case hash("pitch_random"): pitchRandom = opcode.read(Default::pitchRandom); break; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fe407065..11a5757b 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -384,6 +384,7 @@ struct Region { uint8_t ampKeycenter { Default::key }; // amp_keycenter float ampKeytrack { Default::ampKeytrack }; // amp_keytrack float ampVeltrack { Default::ampVeltrack }; // amp_veltrack + CCMap> ampVeltrackCC { ModifierCurvePair{ Default::ampVeltrackMod, Default::curveCC } }; std::vector> velocityPoints; // amp_velcurve_N absl::optional velCurve {}; float ampRandom { Default::ampRandom }; // amp_random @@ -415,6 +416,7 @@ struct Region { float pitchKeytrack { Default::pitchKeytrack }; // pitch_keytrack float pitchRandom { Default::pitchRandom }; // pitch_random float pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack + CCMap> pitchVeltrackCC { ModifierCurvePair{ Default::pitchVeltrackMod, Default::curveCC } }; float transpose { Default::transpose }; // transpose float pitch { Default::pitch }; // tune float bendUp { Default::bendUp }; diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 46a7e166..8fce77fa 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -22,6 +22,13 @@ namespace sfz { using CCNamePair = std::pair; using NoteNamePair = std::pair; +template +struct ModifierCurvePair +{ + T modifier {}; + uint8_t curve {}; +}; + template using MidiNoteArray = std::array; template diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 3e6a87f2..ce0c62b3 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -782,6 +782,26 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'f'>(delay, path, region.ampVeltrack * 100.0f); } break; + MATCH("/region&/amp_veltrack_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.ampVeltrackCC.contains(indices[1])) { + const auto& cc = region.ampVeltrackCC.getWithDefault(indices[1]); + client.receive<'f'>(delay, path, cc.modifier * 100.0f); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + + MATCH("/region&/amp_veltrack_curvecc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.ampVeltrackCC.contains(indices[1])) { + const auto& cc = region.ampVeltrackCC.getWithDefault(indices[1]); + client.receive<'i'>(delay, path, cc.curve ); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + MATCH("/region&/amp_random", "") { GET_REGION_OR_BREAK(indices[0]) client.receive<'f'>(delay, path, region.ampRandom); @@ -921,6 +941,26 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'i'>(delay, path, region.pitchVeltrack); } break; + MATCH("/region&/pitch_veltrack_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.pitchVeltrackCC.contains(indices[1])) { + const auto& cc = region.pitchVeltrackCC.getWithDefault(indices[1]); + client.receive<'f'>(delay, path, cc.modifier * 100.0f); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + + MATCH("/region&/pitch_veltrack_curvecc&", "") { + GET_REGION_OR_BREAK(indices[0]) + if (region.pitchVeltrackCC.contains(indices[1])) { + const auto& cc = region.pitchVeltrackCC.getWithDefault(indices[1]); + client.receive<'i'>(delay, path, cc.curve ); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + MATCH("/region&/pitch_random", "") { GET_REGION_OR_BREAK(indices[0]) client.receive<'f'>(delay, path, region.pitchRandom); @@ -1277,6 +1317,28 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co client.receive<'i'>(delay, path, filter.veltrack); } break; + MATCH("/region&/filter&/veltrack_cc&", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_FILTER_OR_BREAK(indices[1]) + if (filter.veltrackCC.contains(indices[2])) { + const auto& cc = filter.veltrackCC.getWithDefault(indices[2]); + client.receive<'f'>(delay, path, cc.modifier * 100.0f); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + + MATCH("/region&/filter&/veltrack_curvecc&", "") { + GET_REGION_OR_BREAK(indices[0]) + GET_FILTER_OR_BREAK(indices[1]) + if (filter.veltrackCC.contains(indices[2])) { + const auto& cc = filter.veltrackCC.getWithDefault(indices[2]); + client.receive<'i'>(delay, path, cc.curve ); + } else { + client.receive<'N'>(delay, path, {}); + } + } break; + MATCH("/region&/filter&/type", "") { GET_REGION_OR_BREAK(indices[0]) GET_FILTER_OR_BREAK(indices[1]) diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 970a259e..dfbb3f51 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -1629,20 +1629,47 @@ TEST_CASE("[Values] Amp Veltrack") Client client(&messageList); client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", 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); + SECTION("Basic") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", 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); + } + + SECTION("CC") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", 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); + } } TEST_CASE("[Values] Amp Random") @@ -1987,20 +2014,47 @@ TEST_CASE("[Values] Pitch Veltrack") Client client(&messageList); client.setReceiveCallback(&simpleMessageReceiver); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", 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,i : { 0 }", - "/region1/pitch_veltrack,i : { 10 }", - "/region2/pitch_veltrack,i : { -132 }", - }; - REQUIRE(messageList == expected); + SECTION("Basic") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", 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,i : { 0 }", + "/region1/pitch_veltrack,i : { 10 }", + "/region2/pitch_veltrack,i : { -132 }", + }; + REQUIRE(messageList == expected); + } + + SECTION("CC") + { + synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", 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); + // 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); + } } TEST_CASE("[Values] Pitch Random") @@ -2992,6 +3046,7 @@ TEST_CASE("[Values] Filter dispatching") 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); @@ -3000,6 +3055,8 @@ TEST_CASE("[Values] Filter dispatching") 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 }", @@ -3007,6 +3064,8 @@ TEST_CASE("[Values] Filter dispatching") "/region0/filter2/keytrack,i : { 100 }", "/region0/filter0/gain,f : { -5 }", "/region0/filter1/veltrack,i : { -100 }", + "/region0/filter3/veltrack_cc7,f : { -100 }", + "/region0/filter4/veltrack_curvecc2,i : { 2 }", }; REQUIRE(messageList == expected); } From 66ec8c4423492a9ec0cf217dece5709fab02d399 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 4 Jul 2021 19:32:17 +0200 Subject: [PATCH 2/7] Move the stateful region value computations in a separate file --- common.mk | 1 + scripts/run_clang_tidy.sh | 1 + src/CMakeLists.txt | 2 + src/sfizz/Region.cpp | 123 ----------- src/sfizz/Region.h | 56 ----- src/sfizz/RegionStateful.cpp | 134 ++++++++++++ src/sfizz/RegionStateful.h | 97 +++++++++ src/sfizz/Voice.cpp | 19 +- tests/RegionValueComputationsT.cpp | 330 ++++++++++++++++++++--------- tests/SynthT.cpp | 92 -------- 10 files changed, 476 insertions(+), 379 deletions(-) create mode 100644 src/sfizz/RegionStateful.cpp create mode 100644 src/sfizz/RegionStateful.h diff --git a/common.mk b/common.mk index 37e82c1e..24a261a8 100644 --- a/common.mk +++ b/common.mk @@ -107,6 +107,7 @@ SFIZZ_SOURCES = \ src/sfizz/PowerFollower.cpp \ src/sfizz/Region.cpp \ src/sfizz/RegionSet.cpp \ + src/sfizz/RegionStateful.cpp \ src/sfizz/Resources.cpp \ src/sfizz/RTSemaphore.cpp \ src/sfizz/ScopedFTZ.cpp \ diff --git a/scripts/run_clang_tidy.sh b/scripts/run_clang_tidy.sh index e970a75f..1072c538 100755 --- a/scripts/run_clang_tidy.sh +++ b/scripts/run_clang_tidy.sh @@ -14,6 +14,7 @@ clang-tidy \ src/sfizz/Panning.cpp \ src/sfizz/sfizz.cpp \ src/sfizz/Region.cpp \ + src/sfizz/RegionStateful.cpp \ src/sfizz/SIMDHelpers.cpp \ src/sfizz/simd/HelpersSSE.cpp \ src/sfizz/simd/HelpersAVX.cpp \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a1a2533b..f78bf315 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -96,6 +96,7 @@ set(SFIZZ_HEADERS sfizz/railsback/4-1.h sfizz/railsback/4-2.h sfizz/Region.h + sfizz/RegionStateful.h sfizz/RegionSet.h sfizz/Resources.h sfizz/RTSemaphore.h @@ -131,6 +132,7 @@ set(SFIZZ_SOURCES sfizz/AudioReader.cpp sfizz/FilterPool.cpp sfizz/EQPool.cpp + sfizz/RegionStateful.cpp sfizz/Region.cpp sfizz/Voice.cpp sfizz/ScopedFTZ.cpp diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 0dc7fc3d..40357c71 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -6,13 +6,11 @@ #include "Region.h" #include "Opcode.h" -#include "MidiState.h" #include "MathHelpers.h" #include "utility/SwapAndPop.h" #include "utility/StringViewHelpers.h" #include "utility/Macros.h" #include "utility/Debug.h" -#include "ModifierHelpers.h" #include "modulations/ModId.h" #include "absl/strings/str_replace.h" #include "absl/strings/str_cat.h" @@ -1739,18 +1737,6 @@ float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const return centsFactor(pitchVariationInCents); } -float sfz::Region::getBaseVolumedB(const MidiState& midiState, int noteNumber) const noexcept -{ - fast_real_distribution volumeDistribution { 0.0f, ampRandom }; - auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator); - baseVolumedB += globalVolume; - baseVolumedB += masterVolume; - baseVolumedB += groupVolume; - if (trigger == Trigger::release || trigger == Trigger::release_key) - baseVolumedB -= rtDecay * midiState.getNoteDuration(noteNumber); - return baseVolumedB; -} - float sfz::Region::getBaseGain() const noexcept { float baseGain = amplitude; @@ -1774,115 +1760,6 @@ float sfz::Region::getPhase() const noexcept return phase; } -uint64_t sfz::Region::getOffset(const MidiState& midiState) const noexcept -{ - std::uniform_int_distribution offsetDistribution { 0, offsetRandom }; - uint64_t finalOffset = offset + offsetDistribution(Random::randomGenerator); - for (const auto& mod: offsetCC) - finalOffset += static_cast(mod.data * midiState.getCCValue(mod.cc)); - return Default::offset.bounds.clamp(finalOffset); -} - -float sfz::Region::getDelay(const MidiState& midiState) const noexcept -{ - fast_real_distribution delayDistribution { 0, delayRandom }; - float finalDelay { delay }; - finalDelay += delayDistribution(Random::randomGenerator); - for (const auto& mod: delayCC) - finalDelay += mod.data * midiState.getCCValue(mod.cc); - - return Default::delay.bounds.clamp(finalDelay); -} - -uint32_t sfz::Region::getSampleEnd(MidiState& midiState) const noexcept -{ - int64_t end = sampleEnd; - for (const auto& mod: endCC) - end += static_cast(mod.data * midiState.getCCValue(mod.cc)); - - end = clamp(end, int64_t { 0 }, sampleEnd); - return static_cast(end); -} - -uint32_t sfz::Region::loopStart(MidiState& midiState) const noexcept -{ - auto start = loopRange.getStart(); - for (const auto& mod: loopStartCC) - start += static_cast(mod.data * midiState.getCCValue(mod.cc)); - - start = clamp(start, int64_t { 0 }, sampleEnd); - return static_cast(start); -} - -uint32_t sfz::Region::loopEnd(MidiState& midiState) const noexcept -{ - auto end = loopRange.getEnd(); - for (const auto& mod: loopEndCC) - end += static_cast(mod.data * midiState.getCCValue(mod.cc)); - - end = clamp(end, int64_t { 0 }, sampleEnd); - return static_cast(end); -} - -float sfz::Region::getNoteGain(int noteNumber, float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - float baseGain { 1.0f }; - - // Amplitude key tracking - baseGain *= db2mag(ampKeytrack * static_cast(noteNumber - ampKeycenter)); - - // Crossfades related to the note number - baseGain *= crossfadeIn(crossfadeKeyInRange, noteNumber, crossfadeKeyCurve); - baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve); - - // Amplitude velocity tracking - baseGain *= velocityCurve(velocity); - - // Crossfades related to velocity - baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve); - baseGain *= crossfadeOut(crossfadeVelOutRange, velocity, crossfadeVelCurve); - - return baseGain; -} - -float sfz::Region::getCrossfadeGain(const MidiState& midiState) const noexcept -{ - float gain { 1.0f }; - - // Crossfades due to CC states - for (const auto& ccData : crossfadeCCInRange) { - const auto ccValue = midiState.getCCValue(ccData.cc); - const auto crossfadeRange = ccData.data; - gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); - } - - for (const auto& ccData : crossfadeCCOutRange) { - const auto ccValue = midiState.getCCValue(ccData.cc); - const auto crossfadeRange = ccData.data; - gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); - } - - return gain; -} - -float sfz::Region::velocityCurve(float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - float gain; - if (velCurve) - gain = velCurve->evalNormalized(velocity); - else - gain = velocity * velocity; - - gain = std::fabs(ampVeltrack) * (1.0f - gain); - gain = (ampVeltrack < 0) ? gain : (1.0f - gain); - - return gain; -} - void sfz::Region::offsetAllKeys(int offset) noexcept { // Offset key range diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 11a5757b..ab1e76d8 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -108,32 +108,6 @@ struct Region { * @return float */ float getBasePitchVariation(float noteNumber, float velocity) const noexcept; - /** - * @brief Get the note-related gain of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getNoteGain(int noteNumber, float velocity) const noexcept; - /** - * @brief Get the additional crossfade gain of the region depending on the - * CC values - * - * @param midiState - * @return float - */ - float getCrossfadeGain(const MidiState& midiState) const noexcept; - /** - * @brief Get the base volume of the region depending on which note has been - * pressed to trigger the region. - * - * @param midiState - * @param noteNumber - * @return float - */ - float getBaseVolumedB(const MidiState& midiState, int noteNumber) const noexcept; /** * @brief Get the base gain of the region. * @@ -146,12 +120,6 @@ struct Region { * @return float */ float getPhase() const noexcept; - /** - * @brief Computes the gain value related to the velocity of the note - * - * @return float - */ - float velocityCurve(float velocity) const noexcept; /** * @brief Get the detuning in cents for a given bend value between -1 and 1 * @@ -160,27 +128,6 @@ struct Region { */ float getBendInCents(float bend) const noexcept; - /** - * @brief Get the region offset in samples - * - * @param midiState - * @return uint32_t - */ - uint64_t getOffset(const MidiState& midiState) const noexcept; - /** - * @brief Get the region delay in seconds - * - * @param midiState - * @return float - */ - float getDelay(const MidiState& midiState) const noexcept; - /** - * @brief Get the index of the sample end, either natural end or forced - * loop. - * - * @return uint32_t - */ - uint32_t getSampleEnd(MidiState& midiState) const noexcept; /** * @brief Parse a new opcode into the region to fill in the proper parameters. * This must be called multiple times for each opcode applying to this region. @@ -260,9 +207,6 @@ struct Region { void offsetAllKeys(int offset) noexcept; - uint32_t loopStart(MidiState& midiState) const noexcept; - uint32_t loopEnd(MidiState& midiState) const noexcept; - /** * @brief Get the gain this region contributes into the input of the Nth * effect bus diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp new file mode 100644 index 00000000..b04a52ae --- /dev/null +++ b/src/sfizz/RegionStateful.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "RegionStateful.h" +#include "ModifierHelpers.h" + +namespace sfz { + +float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept +{ + fast_real_distribution volumeDistribution { 0.0f, region.ampRandom }; + auto baseVolumedB = region.volume + volumeDistribution(Random::randomGenerator); + baseVolumedB += region.globalVolume; + baseVolumedB += region.masterVolume; + baseVolumedB += region.groupVolume; + if (region.trigger == Trigger::release || region.trigger == Trigger::release_key) + baseVolumedB -= region.rtDecay * midiState.getNoteDuration(noteNumber); + return baseVolumedB; +} + + +uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept +{ + std::uniform_int_distribution offsetDistribution { 0, region.offsetRandom }; + uint64_t finalOffset = region.offset + offsetDistribution(Random::randomGenerator); + for (const auto& mod: region.offsetCC) + finalOffset += static_cast(mod.data * midiState.getCCValue(mod.cc)); + return Default::offset.bounds.clamp(finalOffset); +} + +float getDelay(const Region& region, const MidiState& midiState) noexcept +{ + fast_real_distribution delayDistribution { 0, region.delayRandom }; + float finalDelay { region.delay }; + finalDelay += delayDistribution(Random::randomGenerator); + for (const auto& mod: region.delayCC) + finalDelay += mod.data * midiState.getCCValue(mod.cc); + + return Default::delay.bounds.clamp(finalDelay); +} + +uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept +{ + int64_t end = region.sampleEnd; + for (const auto& mod: region.endCC) + end += static_cast(mod.data * midiState.getCCValue(mod.cc)); + + end = clamp(end, int64_t { 0 }, region.sampleEnd); + return static_cast(end); +} + +uint32_t loopStart(const Region& region, MidiState& midiState) noexcept +{ + auto start = region.loopRange.getStart(); + for (const auto& mod: region.loopStartCC) + start += static_cast(mod.data * midiState.getCCValue(mod.cc)); + + start = clamp(start, int64_t { 0 }, region.sampleEnd); + return static_cast(start); +} + +uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept +{ + auto end = region.loopRange.getEnd(); + for (const auto& mod: region.loopEndCC) + end += static_cast(mod.data * midiState.getCCValue(mod.cc)); + + end = clamp(end, int64_t { 0 }, region.sampleEnd); + return static_cast(end); +} + +float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + float baseGain { 1.0f }; + + // Amplitude key tracking + baseGain *= db2mag(region.ampKeytrack * static_cast(noteNumber - region.ampKeycenter)); + + // Crossfades related to the note number + baseGain *= crossfadeIn(region.crossfadeKeyInRange, noteNumber, region.crossfadeKeyCurve); + baseGain *= crossfadeOut(region.crossfadeKeyOutRange, noteNumber, region.crossfadeKeyCurve); + + // Amplitude velocity tracking + baseGain *= velocityCurve(region, velocity, midiState, curveSet); + + // Crossfades related to velocity + baseGain *= crossfadeIn(region.crossfadeVelInRange, velocity, region.crossfadeVelCurve); + baseGain *= crossfadeOut(region.crossfadeVelOutRange, velocity, region.crossfadeVelCurve); + + return baseGain; +} + +float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept +{ + float gain { 1.0f }; + + // Crossfades due to CC states + for (const auto& ccData : region.crossfadeCCInRange) { + const auto ccValue = midiState.getCCValue(ccData.cc); + const auto crossfadeRange = ccData.data; + gain *= crossfadeIn(crossfadeRange, ccValue, region.crossfadeCCCurve); + } + + for (const auto& ccData : region.crossfadeCCOutRange) { + const auto ccValue = midiState.getCCValue(ccData.cc); + const auto crossfadeRange = ccData.data; + gain *= crossfadeOut(crossfadeRange, ccValue, region.crossfadeCCCurve); + } + + return gain; +} + +float velocityCurve(const Region& region, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + float gain; + if (region.velCurve) + gain = region.velCurve->evalNormalized(velocity); + else + gain = velocity * velocity; + + gain = std::fabs(region.ampVeltrack) * (1.0f - gain); + gain = (region.ampVeltrack < 0) ? gain : (1.0f - gain); + + return gain; +} + +} diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h new file mode 100644 index 00000000..9c2574b3 --- /dev/null +++ b/src/sfizz/RegionStateful.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include "Region.h" +#include "MidiState.h" +#include "Curve.h" + +namespace sfz { +/** + * @brief Get the note-related gain of the region depending on which note has been + * pressed and at which velocity. + * + * @param region + * @param noteNumber + * @param velocity + * @param midiState + * @param curveSet + * @return float + */ +float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + +/** + * @brief Get the additional crossfade gain of the region depending on the + * CC values + * + * @param region + * @param midiState + * @return float + */ +float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept; + +/** + * @brief Get the base volume of the region depending on which note has been + * pressed to trigger the region. + * + * @param region + * @param midiState + * @param noteNumber + * @return float + */ +float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept; + +/** + * @brief Get the region offset in samples + * + * @param region + * @param midiState + * @return uint32_t + */ +uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept; +/** + * @brief Get the region delay in seconds + * + * @param region + * @param midiState + * @return float + */ +float getDelay(const Region& region, const MidiState& midiState) noexcept; +/** + * @brief Get the index of the sample end, either natural end or forced + * loop. + * + * @param region + * @param midiState + * @return uint32_t + */ +uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept; + +/** + * @brief Computes the gain value related to the velocity of the note + * + * @return float + */ +float velocityCurve(const Region& region, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + +/** + * @brief Returns the start of the loop for a given region + * + * @param region + * @param midiState + * @return uint32_t + */ +uint32_t loopStart(const Region& region, MidiState& midiState) noexcept; + +/** + * @brief Returns the end of the loop for a given region + * + * @param region + * @param midiState + * @return uint32_t + */ +uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept; + +} diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 14f14881..098b16a5 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -16,6 +16,7 @@ #include "LFO.h" #include "MathHelpers.h" #include "ModifierHelpers.h" +#include "RegionStateful.h" #include "TriggerEvent.h" #include "modulations/ModId.h" #include "modulations/ModKey.h" @@ -407,6 +408,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc Resources& resources = impl.resources_; MidiState& midiState = resources.getMidiState(); + CurveSet& curveSet = resources.getCurves(); const Region& region = layer->getRegion(); impl.region_ = ®ion; @@ -474,7 +476,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.updateLoopInformation(); impl.speedRatio_ = static_cast(impl.currentPromise_->information.sampleRate / impl.sampleRate_); - impl.sourcePosition_ = region.getOffset(midiState); + impl.sourcePosition_ = getOffset(region, midiState); } // do Scala retuning and reconvert the frequency into a 12TET key number @@ -488,10 +490,10 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.pitchRatio_ *= stretch->getRatioForFractionalKey(numberRetuned); impl.pitchKeycenter_ = region.pitchKeycenter; - impl.baseVolumedB_ = region.getBaseVolumedB(midiState, impl.triggerEvent_.number); + impl.baseVolumedB_ = getBaseVolumedB(region, midiState, impl.triggerEvent_.number); impl.baseGain_ = region.getBaseGain(); if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous) - impl.baseGain_ *= region.getNoteGain(impl.triggerEvent_.number, impl.triggerEvent_.value); + impl.baseGain_ *= getNoteGain(region, impl.triggerEvent_.number, impl.triggerEvent_.value, midiState, curveSet); impl.gainSmoother_.reset(); impl.resetCrossfades(); @@ -505,9 +507,9 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.triggerDelay_ = delay; - impl.initialDelay_ = delay + static_cast(region.getDelay(midiState) * impl.sampleRate_); + impl.initialDelay_ = delay + static_cast(getDelay(region, midiState) * impl.sampleRate_); impl.baseFrequency_ = tuning.getFrequencyOfKey(impl.triggerEvent_.number); - impl.sampleEnd_ = int(region.getSampleEnd(midiState)); + impl.sampleEnd_ = int(getSampleEnd(region, midiState)); impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); impl.bendSmoother_.reset(region.getBendInCents(midiState.getPitchBend())); @@ -1724,14 +1726,15 @@ void Voice::Impl::updateLoopInformation() noexcept if (!region_->shouldLoop()) return; + const Region& region = *region_; MidiState& midiState = resources_.getMidiState(); const FileInformation& info = currentPromise_->information; const double rate = info.sampleRate; - loop_.start = static_cast(region_->loopStart(midiState)); - loop_.end = max(static_cast(region_->loopEnd(midiState)), loop_.start); + loop_.start = static_cast(loopStart(region, midiState)); + loop_.end = max(static_cast(loopEnd(region, midiState)), loop_.start); loop_.size = loop_.end + 1 - loop_.start; - loop_.xfSize = static_cast(lroundPositive(region_->loopCrossfade * rate)); + loop_.xfSize = static_cast(lroundPositive(region.loopCrossfade * rate)); // Clamp the crossfade to the part available before the loop starts loop_.xfSize = min(loop_.start, loop_.xfSize); loop_.xfOutStart = loop_.end + 1 - loop_.xfSize; diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index c7516830..8d833ccc 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -6,6 +6,7 @@ #include "sfizz/Defaults.h" #include "sfizz/Region.h" +#include "sfizz/RegionStateful.h" #include "sfizz/MidiState.h" #include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" @@ -19,137 +20,155 @@ constexpr int numRandomTests { 64 }; TEST_CASE("[Region] Crossfade in on key") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE(region.getNoteGain(2, 127_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(3, 127_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - 2") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(3, 127_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(4, 127_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(5, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(6, 127_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 6, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(region.getNoteGain(1, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 127_norm) == 0.25_a); - REQUIRE(region.getNoteGain(3, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(4, 127_norm) == 0.75_a); - REQUIRE(region.getNoteGain(5, 127_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on key") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE(region.getNoteGain(50, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(51, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(52, 127_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(53, 127_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(54, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(55, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(56, 127_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on key - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(region.getNoteGain(50, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(51, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(52, 127_norm) == 0.75_a); - REQUIRE(region.getNoteGain(53, 127_norm) == 0.5_a); - REQUIRE(region.getNoteGain(54, 127_norm) == 0.25_a); - REQUIRE(region.getNoteGain(55, 127_norm) == 0.0_a); - REQUIRE(region.getNoteGain(56, 127_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on velocity") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(1, 19_norm) == 0.0_a); - REQUIRE(region.getNoteGain(1, 20_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 21_norm) == 0.5_a); - REQUIRE(region.getNoteGain(3, 22_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(4, 23_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(5, 24_norm) == 1.0_a); - REQUIRE(region.getNoteGain(6, 25_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 6, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on vel - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(1, 19_norm) == 0.0_a); - REQUIRE(region.getNoteGain(1, 20_norm) == 0.0_a); - REQUIRE(region.getNoteGain(2, 21_norm) == 0.25_a); - REQUIRE(region.getNoteGain(3, 22_norm) == 0.5_a); - REQUIRE(region.getNoteGain(4, 23_norm) == 0.75_a); - REQUIRE(region.getNoteGain(5, 24_norm) == 1.0_a); - REQUIRE(region.getNoteGain(5, 25_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 5, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on vel") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(5, 50_norm) == 1.0_a); - REQUIRE(region.getNoteGain(5, 51_norm) == 1.0_a); - REQUIRE(region.getNoteGain(5, 52_norm) == 0.86603_a); - REQUIRE(region.getNoteGain(5, 53_norm) == 0.70711_a); - REQUIRE(region.getNoteGain(5, 54_norm) == 0.5_a); - REQUIRE(region.getNoteGain(5, 55_norm) == 0.0_a); - REQUIRE(region.getNoteGain(5, 56_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 5, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 5, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 5, 52_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(getNoteGain(region, 5, 53_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(getNoteGain(region, 5, 54_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 5, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 5, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on vel - gain") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(56, 50_norm) == 1.0_a); - REQUIRE(region.getNoteGain(56, 51_norm) == 1.0_a); - REQUIRE(region.getNoteGain(56, 52_norm) == 0.75_a); - REQUIRE(region.getNoteGain(56, 53_norm) == 0.5_a); - REQUIRE(region.getNoteGain(56, 54_norm) == 0.25_a); - REQUIRE(region.getNoteGain(56, 55_norm) == 0.0_a); - REQUIRE(region.getNoteGain(56, 56_norm) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 56, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 56, 52_norm, midiState, curveSet) == 0.75_a); + REQUIRE(getNoteGain(region, 56, 53_norm, midiState, curveSet) == 0.5_a); + REQUIRE(getNoteGain(region, 56, 54_norm, midiState, curveSet) == 0.25_a); + REQUIRE(getNoteGain(region, 56, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(getNoteGain(region, 56, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on CC") @@ -161,19 +180,19 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.70711_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.86603_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -186,19 +205,19 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.25_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.75_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade out on CC") { @@ -209,19 +228,19 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.86603_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.70711_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -234,47 +253,53 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 1.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.75_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.5_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.25_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(region.getCrossfadeGain(midiState) == 0.0_a); + REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(64, 0_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a); - REQUIRE(region.getNoteGain(64, 0_norm) == Approx(0.0).margin(0.0001)); + REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE(region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001)); - REQUIRE(region.getNoteGain(64, 0_norm) == 1.0_a); + REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); + REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] rt_decay") @@ -287,15 +312,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "rt_decay", "10" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( region.getBaseVolumedB(midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); + REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); region.parseOpcode({ "rt_decay", "20" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( region.getBaseVolumedB(midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); + REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); region.parseOpcode({ "trigger", "attack" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( region.getBaseVolumedB(midiState, 64) == Approx(Default::volume).margin(0.1) ); + REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume).margin(0.1) ); } TEST_CASE("[Region] Base delay") @@ -304,12 +329,12 @@ TEST_CASE("[Region] Base delay") Region region { 0 }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "delay", "10" }); - REQUIRE( region.getDelay(midiState) == 10.0f ); + REQUIRE( getDelay(region, midiState) == 10.0f ); region.parseOpcode({ "delay_random", "10" }); Random::randomGenerator.seed(42); for (int i = 0; i < numRandomTests; ++i) { - auto delay = region.getDelay(midiState); + auto delay = getDelay(region, midiState); REQUIRE( (delay >= 10.0 && delay <= 20.0) ); } } @@ -321,15 +346,15 @@ TEST_CASE("[Region] Offsets with CCs") region.parseOpcode({ "offset_cc4", "255" }); region.parseOpcode({ "offset", "10" }); - REQUIRE( region.getOffset(midiState) == 10 ); + REQUIRE( getOffset(region, midiState) == 10 ); midiState.ccEvent(0, 4, 127_norm); - REQUIRE( region.getOffset(midiState) == 265 ); + REQUIRE( getOffset(region, midiState) == 265 ); midiState.ccEvent(0, 4, 100_norm); - REQUIRE( region.getOffset(midiState) == 210 ); + REQUIRE( getOffset(region, midiState) == 210 ); midiState.ccEvent(0, 4, 10_norm); - REQUIRE( region.getOffset(midiState) == 30 ); + REQUIRE( getOffset(region, midiState) == 30 ); midiState.ccEvent(0, 4, 0); - REQUIRE( region.getOffset(midiState) == 10 ); + REQUIRE( getOffset(region, midiState) == 10 ); } TEST_CASE("[Region] Pitch variation with veltrack") @@ -344,3 +369,108 @@ TEST_CASE("[Region] Pitch variation with veltrack") REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == Approx(centsFactor(600.0)).margin(0.01f)); REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == Approx(centsFactor(1200.0)).margin(0.01f)); } + +TEST_CASE("[Synth] velcurve") +{ + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; + + struct VelocityData { float velocity, gain; bool exact; }; + static const VelocityData veldata[] = { + { 0_norm, 0.0, true }, + { 32_norm, 0.5f, false }, + { 64_norm, 1.0, true }, + { 96_norm, 1.0, true }, + { 127_norm, 1.0, true }, + }; + + SECTION("Default veltrack") + { + sfz::Region region { 0 }; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "amp_velcurve_064", "1" }); + region.velCurve = Curve::buildFromVelcurvePoints( + region.velocityPoints, Curve::Interpolator::Linear); + for (const VelocityData& vd : veldata) { + if (vd.exact) { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == vd.gain); + } else { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == Approx(vd.gain).margin(1e-2)); + } + } + } + + SECTION("Inverted veltrack") + { + sfz::Region region { 0 }; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "amp_velcurve_064", "1" }); + region.parseOpcode({ "amp_veltrack", "-100" }); + region.velCurve = Curve::buildFromVelcurvePoints( + region.velocityPoints, Curve::Interpolator::Linear); + for (const VelocityData& vd : veldata) { + if (vd.exact) { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == 1.0f - vd.gain); + } else { + REQUIRE(velocityCurve(region, vd.velocity, midiState, curveSet) == Approx( 1.0f - vd.gain).margin(1e-2)); + } + } + } +} + +TEST_CASE("[Synth] veltrack") +{ + struct VelocityData { float velocity, dBGain; }; + struct VeltrackData { float veltrack; absl::Span veldata; }; + + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; + + // measured on ARIA + const VelocityData veldata25[] = { + { 127_norm, 0.0 }, + { 96_norm, -1 }, + { 64_norm, -1.8 }, + { 32_norm, -2.3 }, + { 1_norm, -2.5 }, + }; + const VelocityData veldata50[] = { + { 127_norm, 0.0 }, + { 96_norm, -2.1 }, + { 64_norm, -4.1 }, + { 32_norm, -5.5 }, + { 1_norm, -6.0 }, + }; + const VelocityData veldata75[] = { + { 127_norm, 0.0 }, + { 96_norm, -3.4 }, + { 64_norm, -7.2 }, + { 32_norm, -10.5 }, + { 1_norm, -12.0 }, + }; + const VelocityData veldata100[] = { + { 127_norm, 0.0 }, + { 96_norm, -4.9 }, + { 64_norm, -12.0 }, + { 32_norm, -24.0 }, + { 1_norm, -84.1 }, + }; + + const VeltrackData veltrackdata[] = { + { 25, absl::MakeConstSpan(veldata25) }, + { 50, absl::MakeConstSpan(veldata50) }, + { 75, absl::MakeConstSpan(veldata75) }, + { 100, absl::MakeConstSpan(veldata100) }, + }; + + for (const VeltrackData& vt : veltrackdata) { + sfz::Region region { 0 }; + region.parseOpcode({ "sample", "*sine" }); + region.parseOpcode({ "amp_veltrack", std::to_string(vt.veltrack) }); + + for (const VelocityData& vd : vt.veldata) { + float dBGain = 20.0f * std::log10(velocityCurve(region, vd.velocity, midiState, curveSet)); + REQUIRE(dBGain == Approx(vd.dBGain).margin(0.1)); + } + } +} diff --git a/tests/SynthT.cpp b/tests/SynthT.cpp index 639acefa..8c037cb1 100644 --- a/tests/SynthT.cpp +++ b/tests/SynthT.cpp @@ -431,98 +431,6 @@ TEST_CASE("[Synth] Velocity points") REQUIRE( synth.getRegionView(1)->velocityPoints[0].second == 1.0_a ); } -TEST_CASE("[Synth] velcurve") -{ - sfz::Synth synth; - synth.loadSfzString(fs::current_path() / "tests/TestFiles/velocity_endpoints.sfz", R"( - amp_velcurve_064=1 sample=*sine - amp_velcurve_064=1 amp_veltrack=-100 sample=*sine - )"); - - struct VelocityData { float velocity, gain; bool exact; }; - - static const VelocityData veldata[] = { - { 0_norm, 0.0, true }, - { 32_norm, 0.5f, false }, - { 64_norm, 1.0, true }, - { 96_norm, 1.0, true }, - { 127_norm, 1.0, true }, - }; - - REQUIRE(synth.getNumRegions() == 2); - const sfz::Region* r1 = synth.getRegionView(0); - const sfz::Region* r2 = synth.getRegionView(1); - - for (const VelocityData& vd : veldata) { - if (vd.exact) { - REQUIRE(r1->velocityCurve(vd.velocity) == vd.gain); - REQUIRE(r2->velocityCurve(vd.velocity) == 1.0f - vd.gain); - } - else { - REQUIRE(r1->velocityCurve(vd.velocity) == Approx(vd.gain).margin(1e-2)); - REQUIRE(r2->velocityCurve(vd.velocity) == Approx(1.0f - vd.gain).margin(1e-2)); - } - } -} - -TEST_CASE("[Synth] veltrack") -{ - struct VelocityData { float velocity, dBGain; }; - struct VeltrackData { float veltrack; absl::Span veldata; }; - - // measured on ARIA - const VelocityData veldata25[] = { - { 127_norm, 0.0 }, - { 96_norm, -1 }, - { 64_norm, -1.8 }, - { 32_norm, -2.3 }, - { 1_norm, -2.5 }, - }; - const VelocityData veldata50[] = { - { 127_norm, 0.0 }, - { 96_norm, -2.1 }, - { 64_norm, -4.1 }, - { 32_norm, -5.5 }, - { 1_norm, -6.0 }, - }; - const VelocityData veldata75[] = { - { 127_norm, 0.0 }, - { 96_norm, -3.4 }, - { 64_norm, -7.2 }, - { 32_norm, -10.5 }, - { 1_norm, -12.0 }, - }; - const VelocityData veldata100[] = { - { 127_norm, 0.0 }, - { 96_norm, -4.9 }, - { 64_norm, -12.0 }, - { 32_norm, -24.0 }, - { 1_norm, -84.1 }, - }; - - const VeltrackData veltrackdata[] = { - { 25, absl::MakeConstSpan(veldata25) }, - { 50, absl::MakeConstSpan(veldata50) }, - { 75, absl::MakeConstSpan(veldata75) }, - { 100, absl::MakeConstSpan(veldata100) }, - }; - - for (const VeltrackData& vt : veltrackdata) { - sfz::Synth synth; - const std::string sfzCode = "sample=*sine amp_veltrack=" + - std::to_string(vt.veltrack); - synth.loadSfzString(fs::current_path() / "tests/TestFiles/veltrack.sfz", sfzCode); - - REQUIRE(synth.getNumRegions() == 1); - const sfz::Region* r = synth.getRegionView(0); - - for (const VelocityData& vd : vt.veldata) { - float dBGain = 20.0f * std::log10(r->velocityCurve(vd.velocity)); - REQUIRE(dBGain == Approx(vd.dBGain).margin(0.1)); - } - } -} - TEST_CASE("[Synth] Region by identifier") { sfz::Synth synth; From f7a56d67bf903e5abd369ba338d4cc0b09c151cc Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sun, 4 Jul 2021 19:36:14 +0200 Subject: [PATCH 3/7] MOD build --- src/sfizz/SfzHelpers.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 8fce77fa..079835f3 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -25,6 +25,8 @@ using NoteNamePair = std::pair; template struct ModifierCurvePair { + ModifierCurvePair(const T& modifier, uint8_t curve) + : modifier(modifier), curve(curve) {} T modifier {}; uint8_t curve {}; }; From f84cacebf6570ac8bf710e182f3abfe4bd7b5aa9 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 09:12:35 +0200 Subject: [PATCH 4/7] Rename the stateful free functions --- src/sfizz/RegionStateful.cpp | 12 +- src/sfizz/RegionStateful.h | 12 +- src/sfizz/Voice.cpp | 10 +- tests/RegionValueComputationsT.cpp | 200 ++++++++++++++--------------- 4 files changed, 117 insertions(+), 117 deletions(-) diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp index b04a52ae..90963d7d 100644 --- a/src/sfizz/RegionStateful.cpp +++ b/src/sfizz/RegionStateful.cpp @@ -9,7 +9,7 @@ namespace sfz { -float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept +float baseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept { fast_real_distribution volumeDistribution { 0.0f, region.ampRandom }; auto baseVolumedB = region.volume + volumeDistribution(Random::randomGenerator); @@ -22,7 +22,7 @@ float getBaseVolumedB(const Region& region, const MidiState& midiState, int note } -uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept +uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept { std::uniform_int_distribution offsetDistribution { 0, region.offsetRandom }; uint64_t finalOffset = region.offset + offsetDistribution(Random::randomGenerator); @@ -31,7 +31,7 @@ uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept return Default::offset.bounds.clamp(finalOffset); } -float getDelay(const Region& region, const MidiState& midiState) noexcept +float regionDelay(const Region& region, const MidiState& midiState) noexcept { fast_real_distribution delayDistribution { 0, region.delayRandom }; float finalDelay { region.delay }; @@ -42,7 +42,7 @@ float getDelay(const Region& region, const MidiState& midiState) noexcept return Default::delay.bounds.clamp(finalDelay); } -uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept +uint32_t sampleEnd(const Region& region, MidiState& midiState) noexcept { int64_t end = region.sampleEnd; for (const auto& mod: region.endCC) @@ -72,7 +72,7 @@ uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept return static_cast(end); } -float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +float noteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept { ASSERT(velocity >= 0.0f && velocity <= 1.0f); @@ -95,7 +95,7 @@ float getNoteGain(const Region& region, int noteNumber, float velocity, const Mi return baseGain; } -float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept +float crossfadeGain(const Region& region, const MidiState& midiState) noexcept { float gain { 1.0f }; diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h index 9c2574b3..aa33b932 100644 --- a/src/sfizz/RegionStateful.h +++ b/src/sfizz/RegionStateful.h @@ -20,7 +20,7 @@ namespace sfz { * @param curveSet * @return float */ -float getNoteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; +float noteGain(const Region& region, int noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; /** * @brief Get the additional crossfade gain of the region depending on the @@ -30,7 +30,7 @@ float getNoteGain(const Region& region, int noteNumber, float velocity, const Mi * @param midiState * @return float */ -float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcept; +float crossfadeGain(const Region& region, const MidiState& midiState) noexcept; /** * @brief Get the base volume of the region depending on which note has been @@ -41,7 +41,7 @@ float getCrossfadeGain(const Region& region, const MidiState& midiState) noexcep * @param noteNumber * @return float */ -float getBaseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept; +float baseVolumedB(const Region& region, const MidiState& midiState, int noteNumber) noexcept; /** * @brief Get the region offset in samples @@ -50,7 +50,7 @@ float getBaseVolumedB(const Region& region, const MidiState& midiState, int note * @param midiState * @return uint32_t */ -uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept; +uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept; /** * @brief Get the region delay in seconds * @@ -58,7 +58,7 @@ uint64_t getOffset(const Region& region, const MidiState& midiState) noexcept; * @param midiState * @return float */ -float getDelay(const Region& region, const MidiState& midiState) noexcept; +float regionDelay(const Region& region, const MidiState& midiState) noexcept; /** * @brief Get the index of the sample end, either natural end or forced * loop. @@ -67,7 +67,7 @@ float getDelay(const Region& region, const MidiState& midiState) noexcept; * @param midiState * @return uint32_t */ -uint32_t getSampleEnd(const Region& region, MidiState& midiState) noexcept; +uint32_t sampleEnd(const Region& region, MidiState& midiState) noexcept; /** * @brief Computes the gain value related to the velocity of the note diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 098b16a5..fd7c80a2 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -476,7 +476,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.updateLoopInformation(); impl.speedRatio_ = static_cast(impl.currentPromise_->information.sampleRate / impl.sampleRate_); - impl.sourcePosition_ = getOffset(region, midiState); + impl.sourcePosition_ = sampleOffset(region, midiState); } // do Scala retuning and reconvert the frequency into a 12TET key number @@ -490,10 +490,10 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc impl.pitchRatio_ *= stretch->getRatioForFractionalKey(numberRetuned); impl.pitchKeycenter_ = region.pitchKeycenter; - impl.baseVolumedB_ = getBaseVolumedB(region, midiState, impl.triggerEvent_.number); + impl.baseVolumedB_ = baseVolumedB(region, midiState, impl.triggerEvent_.number); impl.baseGain_ = region.getBaseGain(); if (impl.triggerEvent_.type != TriggerEventType::CC || region.velocityOverride == VelocityOverride::previous) - impl.baseGain_ *= getNoteGain(region, impl.triggerEvent_.number, impl.triggerEvent_.value, midiState, curveSet); + impl.baseGain_ *= noteGain(region, impl.triggerEvent_.number, impl.triggerEvent_.value, midiState, curveSet); impl.gainSmoother_.reset(); impl.resetCrossfades(); @@ -507,9 +507,9 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc } impl.triggerDelay_ = delay; - impl.initialDelay_ = delay + static_cast(getDelay(region, midiState) * impl.sampleRate_); + impl.initialDelay_ = delay + static_cast(regionDelay(region, midiState) * impl.sampleRate_); impl.baseFrequency_ = tuning.getFrequencyOfKey(impl.triggerEvent_.number); - impl.sampleEnd_ = int(getSampleEnd(region, midiState)); + impl.sampleEnd_ = int(sampleEnd(region, midiState)); impl.sampleSize_ = impl.sampleEnd_- impl.sourcePosition_ - 1; impl.bendSmoother_.setSmoothing(region.bendSmooth, impl.sampleRate_); impl.bendSmoother_.reset(region.getBendInCents(midiState.getPitchBend())); diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index 8d833ccc..d468b8c8 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -25,9 +25,9 @@ TEST_CASE("[Region] Crossfade in on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "3" }); - REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 2, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 3, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - 2") @@ -38,12 +38,12 @@ TEST_CASE("[Region] Crossfade in on key - 2") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); - REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 6, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 3, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 4, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 6, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on key - gain") @@ -55,11 +55,11 @@ TEST_CASE("[Region] Crossfade in on key - gain") region.parseOpcode({ "xfin_lokey", "1" }); region.parseOpcode({ "xfin_hikey", "5" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(getNoteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 127_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 3, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 4, 127_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 3, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 4, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 5, 127_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on key") @@ -70,13 +70,13 @@ TEST_CASE("[Region] Crossfade out on key") region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); - REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 52, 127_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 53, 127_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 54, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on key - gain") @@ -88,13 +88,13 @@ TEST_CASE("[Region] Crossfade out on key - gain") region.parseOpcode({ "xfout_lokey", "51" }); region.parseOpcode({ "xfout_hikey", "55" }); region.parseOpcode({ "xf_keycurve", "gain" }); - REQUIRE(getNoteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 52, 127_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 53, 127_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 54, 127_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 50, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 51, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 52, 127_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 53, 127_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 54, 127_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 55, 127_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 127_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on velocity") @@ -106,13 +106,13 @@ TEST_CASE("[Region] Crossfade in on velocity") region.parseOpcode({ "xfin_lovel", "20" }); region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 6, 25_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 21_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 3, 22_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 4, 23_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 6, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade in on vel - gain") @@ -125,13 +125,13 @@ TEST_CASE("[Region] Crossfade in on vel - gain") region.parseOpcode({ "xfin_hivel", "24" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 2, 21_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 3, 22_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 4, 23_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 5, 25_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 1, 19_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 1, 20_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 2, 21_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 3, 22_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 4, 23_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 5, 24_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 5, 25_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] Crossfade out on vel") @@ -143,13 +143,13 @@ TEST_CASE("[Region] Crossfade out on vel") region.parseOpcode({ "xfout_lovel", "51" }); region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 5, 50_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 5, 51_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 5, 52_norm, midiState, curveSet) == 0.86603_a); - REQUIRE(getNoteGain(region, 5, 53_norm, midiState, curveSet) == 0.70711_a); - REQUIRE(getNoteGain(region, 5, 54_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 5, 55_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 5, 56_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 5, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 5, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 5, 52_norm, midiState, curveSet) == 0.86603_a); + REQUIRE(noteGain(region, 5, 53_norm, midiState, curveSet) == 0.70711_a); + REQUIRE(noteGain(region, 5, 54_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 5, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 5, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade out on vel - gain") @@ -162,13 +162,13 @@ TEST_CASE("[Region] Crossfade out on vel - gain") region.parseOpcode({ "xfout_hivel", "55" }); region.parseOpcode({ "xf_velcurve", "gain" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 56, 50_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 56, 51_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 56, 52_norm, midiState, curveSet) == 0.75_a); - REQUIRE(getNoteGain(region, 56, 53_norm, midiState, curveSet) == 0.5_a); - REQUIRE(getNoteGain(region, 56, 54_norm, midiState, curveSet) == 0.25_a); - REQUIRE(getNoteGain(region, 56, 55_norm, midiState, curveSet) == 0.0_a); - REQUIRE(getNoteGain(region, 56, 56_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 50_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 56, 51_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 56, 52_norm, midiState, curveSet) == 0.75_a); + REQUIRE(noteGain(region, 56, 53_norm, midiState, curveSet) == 0.5_a); + REQUIRE(noteGain(region, 56, 54_norm, midiState, curveSet) == 0.25_a); + REQUIRE(noteGain(region, 56, 55_norm, midiState, curveSet) == 0.0_a); + REQUIRE(noteGain(region, 56, 56_norm, midiState, curveSet) == 0.0_a); } TEST_CASE("[Region] Crossfade in on CC") @@ -180,19 +180,19 @@ TEST_CASE("[Region] Crossfade in on CC") region.parseOpcode({ "xfin_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); + REQUIRE(crossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); + REQUIRE(crossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade in on CC - gain") @@ -205,19 +205,19 @@ TEST_CASE("[Region] Crossfade in on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); + REQUIRE(crossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); + REQUIRE(crossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); } TEST_CASE("[Region] Crossfade out on CC") { @@ -228,19 +228,19 @@ TEST_CASE("[Region] Crossfade out on CC") region.parseOpcode({ "xfout_hicc24", "24" }); region.parseOpcode({ "amp_veltrack", "0" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.86603_a); + REQUIRE(crossfadeGain(region, midiState) == 0.86603_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.70711_a); + REQUIRE(crossfadeGain(region, midiState) == 0.70711_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Crossfade out on CC - gain") @@ -253,19 +253,19 @@ TEST_CASE("[Region] Crossfade out on CC - gain") region.parseOpcode({ "amp_veltrack", "0" }); region.parseOpcode({ "xf_cccurve", "gain" }); midiState.ccEvent(0, 24, 19_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 20_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 1.0_a); + REQUIRE(crossfadeGain(region, midiState) == 1.0_a); midiState.ccEvent(0, 24, 21_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.75_a); + REQUIRE(crossfadeGain(region, midiState) == 0.75_a); midiState.ccEvent(0, 24, 22_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.5_a); + REQUIRE(crossfadeGain(region, midiState) == 0.5_a); midiState.ccEvent(0, 24, 23_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.25_a); + REQUIRE(crossfadeGain(region, midiState) == 0.25_a); midiState.ccEvent(0, 24, 24_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); midiState.ccEvent(0, 24, 25_norm); - REQUIRE(getCrossfadeGain(region, midiState) == 0.0_a); + REQUIRE(crossfadeGain(region, midiState) == 0.0_a); } TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") @@ -275,8 +275,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0") CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "0" }); - REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } @@ -287,8 +287,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack") CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "100" }); - REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); - REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); + REQUIRE(noteGain(region, 64, 127_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 0_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); } TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") @@ -298,8 +298,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack") CurveSet curveSet { CurveSet::createPredefined() }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "amp_veltrack", "-100" }); - REQUIRE(getNoteGain(region, 64, 127_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); - REQUIRE(getNoteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); + REQUIRE(noteGain(region, 64, 127_norm, midiState, curveSet) == Approx(0.0).margin(0.0001)); + REQUIRE(noteGain(region, 64, 0_norm, midiState, curveSet) == 1.0_a); } TEST_CASE("[Region] rt_decay") @@ -312,15 +312,15 @@ TEST_CASE("[Region] rt_decay") region.parseOpcode({ "rt_decay", "10" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); + REQUIRE( baseVolumedB(region, midiState, 64) == Approx(Default::volume - 1.0f).margin(0.1) ); region.parseOpcode({ "rt_decay", "20" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); + REQUIRE( baseVolumedB(region, midiState, 64) == Approx(Default::volume - 2.0f).margin(0.1) ); region.parseOpcode({ "trigger", "attack" }); midiState.noteOnEvent(0, 64, 64_norm); midiState.advanceTime(100); - REQUIRE( getBaseVolumedB(region, midiState, 64) == Approx(Default::volume).margin(0.1) ); + REQUIRE( baseVolumedB(region, midiState, 64) == Approx(Default::volume).margin(0.1) ); } TEST_CASE("[Region] Base delay") @@ -329,12 +329,12 @@ TEST_CASE("[Region] Base delay") Region region { 0 }; region.parseOpcode({ "sample", "*sine" }); region.parseOpcode({ "delay", "10" }); - REQUIRE( getDelay(region, midiState) == 10.0f ); + REQUIRE( regionDelay(region, midiState) == 10.0f ); region.parseOpcode({ "delay_random", "10" }); Random::randomGenerator.seed(42); for (int i = 0; i < numRandomTests; ++i) { - auto delay = getDelay(region, midiState); + auto delay = regionDelay(region, midiState); REQUIRE( (delay >= 10.0 && delay <= 20.0) ); } } @@ -346,15 +346,15 @@ TEST_CASE("[Region] Offsets with CCs") region.parseOpcode({ "offset_cc4", "255" }); region.parseOpcode({ "offset", "10" }); - REQUIRE( getOffset(region, midiState) == 10 ); + REQUIRE( sampleOffset(region, midiState) == 10 ); midiState.ccEvent(0, 4, 127_norm); - REQUIRE( getOffset(region, midiState) == 265 ); + REQUIRE( sampleOffset(region, midiState) == 265 ); midiState.ccEvent(0, 4, 100_norm); - REQUIRE( getOffset(region, midiState) == 210 ); + REQUIRE( sampleOffset(region, midiState) == 210 ); midiState.ccEvent(0, 4, 10_norm); - REQUIRE( getOffset(region, midiState) == 30 ); + REQUIRE( sampleOffset(region, midiState) == 30 ); midiState.ccEvent(0, 4, 0); - REQUIRE( getOffset(region, midiState) == 10 ); + REQUIRE( sampleOffset(region, midiState) == 10 ); } TEST_CASE("[Region] Pitch variation with veltrack") From 3658de4497f5f0f1717faf94c5d38fd65394c6d8 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 09:22:07 +0200 Subject: [PATCH 5/7] Add amp and pitch veltrack oncc --- src/sfizz/Region.cpp | 13 ------------ src/sfizz/Region.h | 9 -------- src/sfizz/RegionStateful.cpp | 34 ++++++++++++++++++++++++++++-- src/sfizz/RegionStateful.h | 13 ++++++++++++ src/sfizz/Voice.cpp | 2 +- tests/RegionValueComputationsT.cpp | 14 ++++++------ 6 files changed, 54 insertions(+), 31 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 40357c71..07b327fa 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1724,19 +1724,6 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec spec, return true; } -float sfz::Region::getBasePitchVariation(float noteNumber, float velocity) const noexcept -{ - ASSERT(velocity >= 0.0f && velocity <= 1.0f); - - fast_real_distribution pitchDistribution { 0.0f, pitchRandom }; - float pitchVariationInCents = pitchKeytrack * (noteNumber - float(pitchKeycenter)); // note difference with pitch center - pitchVariationInCents += pitch; // sample tuning - pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose - pitchVariationInCents += velocity * pitchVeltrack; // track velocity - pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes - return centsFactor(pitchVariationInCents); -} - float sfz::Region::getBaseGain() const noexcept { float baseGain = amplitude; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index ab1e76d8..d0abf16d 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -99,15 +99,6 @@ struct Region { */ bool shouldLoop() const noexcept { return (loopMode == LoopMode::loop_continuous || loopMode == LoopMode::loop_sustain); } - /** - * @brief Get the base pitch of the region depending on which note has been - * pressed and at which velocity. - * - * @param noteNumber - * @param velocity - * @return float - */ - float getBasePitchVariation(float noteNumber, float velocity) const noexcept; /** * @brief Get the base gain of the region. * diff --git a/src/sfizz/RegionStateful.cpp b/src/sfizz/RegionStateful.cpp index 90963d7d..e0f7bf86 100644 --- a/src/sfizz/RegionStateful.cpp +++ b/src/sfizz/RegionStateful.cpp @@ -125,10 +125,40 @@ float velocityCurve(const Region& region, float velocity, const MidiState& midiS else gain = velocity * velocity; - gain = std::fabs(region.ampVeltrack) * (1.0f - gain); - gain = (region.ampVeltrack < 0) ? gain : (1.0f - gain); + float veltrack = region.ampVeltrack; + + for (const auto& mod : region.ampVeltrackCC) { + const auto& curve = curveSet.getCurve(mod.data.curve); + const float value = midiState.getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + gain = std::fabs(veltrack) * (1.0f - gain); + gain = (veltrack < 0) ? gain : (1.0f - gain); return gain; } +float basePitchVariation(const Region& region, float noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept +{ + ASSERT(velocity >= 0.0f && velocity <= 1.0f); + + fast_real_distribution pitchDistribution { 0.0f, region.pitchRandom }; + float pitchVariationInCents = region.pitchKeytrack * (noteNumber - float(region.pitchKeycenter)); // note difference with pitch center + pitchVariationInCents += region.pitch; // sample tuning + pitchVariationInCents += config::centPerSemitone * region.transpose; // sample transpose + + float veltrack = region.pitchVeltrack; + + for (const auto& mod : region.pitchVeltrackCC) { + const auto& curve = curveSet.getCurve(mod.data.curve); + const float value = midiState.getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + pitchVariationInCents += velocity * veltrack; // track velocity + pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes + return centsFactor(pitchVariationInCents); +} + } diff --git a/src/sfizz/RegionStateful.h b/src/sfizz/RegionStateful.h index aa33b932..83031265 100644 --- a/src/sfizz/RegionStateful.h +++ b/src/sfizz/RegionStateful.h @@ -94,4 +94,17 @@ uint32_t loopStart(const Region& region, MidiState& midiState) noexcept; */ uint32_t loopEnd(const Region& region, MidiState& midiState) noexcept; +/** + * @brief Get the base pitch of the region depending on which note has been + * pressed and at which velocity. + * + * @param region + * @param noteNumber + * @param velocity + * @param midiState + * @param curveSet + * @return float + */ +float basePitchVariation(const Region& region, float noteNumber, float velocity, const MidiState& midiState, const CurveSet& curveSet) noexcept; + } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index fd7c80a2..7841cacd 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -483,7 +483,7 @@ bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexc Tuning& tuning = resources.getTuning(); const float numberRetuned = tuning.getKeyFractional12TET(impl.triggerEvent_.number); - impl.pitchRatio_ = region.getBasePitchVariation(numberRetuned, impl.triggerEvent_.value); + impl.pitchRatio_ = basePitchVariation(region, numberRetuned, impl.triggerEvent_.value, midiState, curveSet); // apply stretch tuning if set if (absl::optional& stretch = resources.getStretch()) diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index d468b8c8..30eef25b 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -360,14 +360,16 @@ TEST_CASE("[Region] Offsets with CCs") TEST_CASE("[Region] Pitch variation with veltrack") { Region region { 0 }; + MidiState midiState; + CurveSet curveSet { CurveSet::createPredefined() }; - REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 0_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 64_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 127_norm, midiState, curveSet) == 1.0); region.parseOpcode({ "pitch_veltrack", "1200" }); - REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0); - REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == Approx(centsFactor(600.0)).margin(0.01f)); - REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == Approx(centsFactor(1200.0)).margin(0.01f)); + REQUIRE(basePitchVariation(region, 60.0, 0_norm, midiState, curveSet) == 1.0); + REQUIRE(basePitchVariation(region, 60.0, 64_norm, midiState, curveSet) == Approx(centsFactor(600.0)).margin(0.01f)); + REQUIRE(basePitchVariation(region, 60.0, 127_norm, midiState, curveSet) == Approx(centsFactor(1200.0)).margin(0.01f)); } TEST_CASE("[Synth] velcurve") From 5dbeb58eb084ad1b88dcf67591a745b7c90824b5 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Mon, 5 Jul 2021 09:24:39 +0200 Subject: [PATCH 6/7] Add filter veltrack oncc --- src/sfizz/FilterPool.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp index 88b1802f..8fbd356d 100644 --- a/src/sfizz/FilterPool.cpp +++ b/src/sfizz/FilterPool.cpp @@ -38,8 +38,15 @@ void sfz::FilterHolder::setup(const Region& region, unsigned filterId, int noteN } const auto keytrack = description->keytrack * float(noteNumber - description->keycenter); baseCutoff *= centsFactor(keytrack); - const auto veltrack = description->veltrack * velocity; - baseCutoff *= centsFactor(veltrack); + auto veltrack = description->veltrack; + + for (const auto& mod : description->veltrackCC) { + const auto& curve = resources.getCurves().getCurve(mod.data.curve); + const float value = resources.getMidiState().getCCValue(mod.cc); + veltrack += curve.evalNormalized(value) * mod.data.modifier; + } + + baseCutoff *= centsFactor(veltrack * velocity); baseCutoff = Default::filterCutoff.bounds.clamp(baseCutoff); baseGain = description->gain; From 5e1b06d810140e47eba652cbe02b778a0f340e36 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 10 Jul 2021 20:58:04 +0200 Subject: [PATCH 7/7] Don't normalize pitch and filter --- src/sfizz/Defaults.cpp | 4 ++-- src/sfizz/Region.cpp | 2 +- src/sfizz/SynthMessaging.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index a70f3602..be65df60 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -104,7 +104,7 @@ FloatSpec filterGainMod { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec filterRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec filterKeytrack { 0, {0, 1200}, kPermissiveBounds }; FloatSpec filterVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; -FloatSpec filterVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec filterVeltrackMod { 0.0f, {-12000, 12000}, kPermissiveBounds }; FloatSpec eqBandwidth { 1.0f, {0.001f, 4.0f}, kPermissiveBounds }; FloatSpec eqBandwidthMod { 0.0f, {-4.0f, 4.0f}, kPermissiveBounds }; FloatSpec eqFrequency { 0.0f, {0.0f, 20000.0f}, kPermissiveBounds }; @@ -116,7 +116,7 @@ FloatSpec eqVel2Gain { 0.0f, {-96.0f, 96.0f}, kPermissiveBounds }; FloatSpec pitchKeytrack { 100, {-1200, 1200}, kPermissiveBounds }; FloatSpec pitchRandom { 0.0f, {-12000.0f, 12000.0f}, kPermissiveBounds }; FloatSpec pitchVeltrack { 0, {-12000, 12000}, kPermissiveBounds }; -FloatSpec pitchVeltrackMod { 0.0f, {-100.0f, 100.0f}, kNormalizePercent|kPermissiveBounds }; +FloatSpec pitchVeltrackMod { 0.0f, {-12000, 12000}, kPermissiveBounds }; FloatSpec transpose { 0, {-127, 127}, kPermissiveBounds }; FloatSpec pitch { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; FloatSpec pitchMod { 0.0f, {-2400.0f, 2400.0f}, kPermissiveBounds }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 07b327fa..5832e07f 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -645,7 +645,7 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode, bool cleanOpcode) if (cc >= config::numCCs) return false; - filters[filterIndex].veltrackCC[cc].modifier = opcode.read(Default::ampVeltrackMod); + filters[filterIndex].veltrackCC[cc].modifier = opcode.read(Default::filterVeltrackMod); } break; case hash("fil&_veltrack_curvecc&"): diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index ce0c62b3..36f4bc03 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -945,7 +945,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_REGION_OR_BREAK(indices[0]) if (region.pitchVeltrackCC.contains(indices[1])) { const auto& cc = region.pitchVeltrackCC.getWithDefault(indices[1]); - client.receive<'f'>(delay, path, cc.modifier * 100.0f); + client.receive<'f'>(delay, path, cc.modifier); } else { client.receive<'N'>(delay, path, {}); } @@ -1322,7 +1322,7 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co GET_FILTER_OR_BREAK(indices[1]) if (filter.veltrackCC.contains(indices[2])) { const auto& cc = filter.veltrackCC.getWithDefault(indices[2]); - client.receive<'f'>(delay, path, cc.modifier * 100.0f); + client.receive<'f'>(delay, path, cc.modifier); } else { client.receive<'N'>(delay, path, {}); }