From d4bbe806be938d5fe42c2537139a4335106032c1 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 19 Mar 2021 14:10:11 +0100 Subject: [PATCH 1/5] Add enum kEnforceBounds --- src/sfizz/Defaults.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index bf33f5b0..6b65e4b7 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -50,6 +50,7 @@ enum OpcodeFlags : int { kCanBeNote = 1, kEnforceLowerBound = 1 << 1, kEnforceUpperBound = 1 << 2, + kEnforceBounds = kEnforceLowerBound|kEnforceUpperBound, kNormalizePercent = 1 << 3, kNormalizeMidi = 1 << 4, kNormalizeBend = 1 << 5, From 8672a5b5f7b2be275ac707f13d1bc33684f66299 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 19 Mar 2021 14:16:49 +0100 Subject: [PATCH 2/5] Add opcode flag: permissive bounds --- src/sfizz/Defaults.h | 11 ++++++----- src/sfizz/Opcode.cpp | 26 +++++++++++++++++--------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 6b65e4b7..a4692622 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -51,11 +51,12 @@ enum OpcodeFlags : int { kEnforceLowerBound = 1 << 1, kEnforceUpperBound = 1 << 2, kEnforceBounds = kEnforceLowerBound|kEnforceUpperBound, - kNormalizePercent = 1 << 3, - kNormalizeMidi = 1 << 4, - kNormalizeBend = 1 << 5, - kWrapPhase = 1 << 6, - kDb2Mag = 1 << 7, + kPermissiveBounds = 1 << 3, + kNormalizePercent = 1 << 4, + kNormalizeMidi = 1 << 5, + kNormalizeBend = 1 << 6, + kWrapPhase = 1 << 7, + kDb2Mag = 1 << 8, }; template diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index 79ac2eac..935498e8 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -11,6 +11,7 @@ #include "absl/strings/ascii.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" +#include #include #include #include @@ -144,6 +145,8 @@ OpcodeCategory Opcode::identifyCategory(absl::string_view name) template absl::optional readInt_(OpcodeSpec spec, absl::string_view v) { + using Limits = std::numeric_limits; + size_t numberEnd = 0; if (numberEnd < v.size() && (v[numberEnd] == '+' || v[numberEnd] == '-')) @@ -164,15 +167,18 @@ absl::optional readInt_(OpcodeSpec spec, absl::string_view v) if (returnedValue > static_cast(spec.bounds.getEnd())) { if (spec.flags & kEnforceUpperBound) return spec.bounds.getEnd(); - - return absl::nullopt; + else if (!(spec.flags & kPermissiveBounds)) + return absl::nullopt; } else if (returnedValue < static_cast(spec.bounds.getStart())) { if (spec.flags & kEnforceLowerBound) return spec.bounds.getStart(); - - return absl::nullopt; + else if (!(spec.flags & kPermissiveBounds)) + return absl::nullopt; } + returnedValue = std::max(returnedValue, Limits::min()); + returnedValue = std::min(returnedValue, Limits::max()); + return static_cast(returnedValue); } @@ -219,16 +225,18 @@ absl::optional readFloat_(OpcodeSpec spec, absl::string_view v) else if (returnedValue > static_cast(spec.bounds.getEnd())) { if (spec.flags & kEnforceUpperBound) return spec.bounds.getEnd(); - - return absl::nullopt; + else if (!(spec.flags & kPermissiveBounds)) + return absl::nullopt; } else if (returnedValue < static_cast(spec.bounds.getStart())) { if (spec.flags & kEnforceLowerBound) return spec.bounds.getStart(); - - return absl::nullopt; + else if (!(spec.flags & kPermissiveBounds)) + return absl::nullopt; } - return spec.normalizeInput(returnedValue); + returnedValue = spec.normalizeInput(returnedValue); + + return returnedValue; } #define INSTANTIATE_FOR_FLOATING_POINT(T) \ From 4dc429a572df1ab95461aba6b7a4c75414c49075 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 19 Mar 2021 14:26:47 +0100 Subject: [PATCH 3/5] Make the aftertouch opcode normalized --- src/sfizz/Defaults.cpp | 4 ++-- src/sfizz/Defaults.h | 4 ++-- src/sfizz/Region.cpp | 2 +- src/sfizz/Region.h | 4 ++-- src/sfizz/Synth.cpp | 15 ++++++++++----- src/sfizz/Synth.h | 8 ++++++++ src/sfizz/SynthMessaging.cpp | 6 +++--- tests/RegionActivationT.cpp | 8 ++++---- tests/RegionValuesT.cpp | 10 +++++----- 9 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index b20b7db7..6921be22 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -49,8 +49,8 @@ FloatSpec loCC { 0, {0.0f, 127.0f}, kNormalizeMidi }; FloatSpec hiCC { 127, {0.0f, 127.0f}, kNormalizeMidi }; FloatSpec loVel { 0, {0.0f, 127.0f}, kNormalizeMidi }; FloatSpec hiVel { 127, {0.0f, 127.0f}, kNormalizeMidi }; -UInt8Spec loChannelAftertouch { 0, {0, 127}, 0 }; -UInt8Spec hiChannelAftertouch { 127, {0, 127}, 0 }; +FloatSpec loChannelAftertouch { 0, {0, 127}, kNormalizeMidi }; +FloatSpec hiChannelAftertouch { 127, {0, 127}, kNormalizeMidi }; FloatSpec loBend { -8192, {-8192.0f, 8192.0f}, kNormalizeBend }; FloatSpec hiBend { 8192, {-8192.0f, 8192.0f}, kNormalizeBend }; FloatSpec loNormalized { 0.0f, {0.0f, 1.0f}, 0 }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index a4692622..6f8d31f1 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -153,8 +153,8 @@ namespace Default extern const OpcodeSpec hiNormalized; extern const OpcodeSpec loBipolar; extern const OpcodeSpec hiBipolar; - extern const OpcodeSpec loChannelAftertouch; - extern const OpcodeSpec hiChannelAftertouch; + extern const OpcodeSpec loChannelAftertouch; + extern const OpcodeSpec hiChannelAftertouch; extern const OpcodeSpec ccNumber; extern const OpcodeSpec curveCC; extern const OpcodeSpec smoothCC; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 40e80fed..02ed7293 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -1565,7 +1565,7 @@ void sfz::Region::registerPitchWheel(float pitch) noexcept pitchSwitched = false; } -void sfz::Region::registerAftertouch(uint8_t aftertouch) noexcept +void sfz::Region::registerAftertouch(float aftertouch) noexcept { if (aftertouchRange.containsWithEnd(aftertouch)) aftertouchSwitched = true; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index fd983db3..6244424f 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -150,7 +150,7 @@ struct Region { * * @param aftertouch */ - void registerAftertouch(uint8_t aftertouch) noexcept; + void registerAftertouch(float aftertouch) noexcept; /** * @brief Register tempo * @@ -409,7 +409,7 @@ struct Region { float sustainThreshold { Default::sustainThreshold }; // sustain_cc // Region logic: internal conditions - Range aftertouchRange { Default::loChannelAftertouch, Default::hiChannelAftertouch }; // hichanaft and lochanaft + Range aftertouchRange { Default::loChannelAftertouch, Default::hiChannelAftertouch }; // hichanaft and lochanaft Range bpmRange { Default::loBPM, Default::hiBPM }; // hibpm and lobpm Range randRange { Default::loNormalized, Default::hiNormalized }; // hirand and lorand uint8_t sequenceLength { Default::sequence }; // seq_length diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d1808b3d..abdefb14 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -1257,22 +1257,27 @@ void Synth::pitchWheel(int delay, int pitch) noexcept } void Synth::aftertouch(int delay, uint8_t aftertouch) noexcept +{ + const float normalizedAftertouch = normalize7Bits(aftertouch); + hdAftertouch(delay, normalizedAftertouch); +} + +void Synth::hdAftertouch(int delay, float normAftertouch) noexcept { Impl& impl = *impl_; ScopedTiming logger { impl.dispatchDuration_, ScopedTiming::Operation::addToDuration }; - const auto normalizedAftertouch = normalize7Bits(aftertouch); - impl.resources_.midiState.channelAftertouchEvent(delay, normalizedAftertouch); + impl.resources_.midiState.channelAftertouchEvent(delay, normAftertouch); for (auto& region : impl.regions_) { - region->registerAftertouch(aftertouch); + region->registerAftertouch(normAftertouch); } for (auto& voice : impl.voiceManager_) { - voice.registerAftertouch(delay, aftertouch); + voice.registerAftertouch(delay, normAftertouch); } - impl.performHdcc(delay, ExtendedCCs::channelAftertouch, normalizedAftertouch, false); + impl.performHdcc(delay, ExtendedCCs::channelAftertouch, normAftertouch, false); } void Synth::tempo(int delay, float secondsPerBeat) noexcept diff --git a/src/sfizz/Synth.h b/src/sfizz/Synth.h index 168900f9..a213cf2d 100644 --- a/src/sfizz/Synth.h +++ b/src/sfizz/Synth.h @@ -389,6 +389,14 @@ public: * @param aftertouch the aftertouch value */ void aftertouch(int delay, uint8_t aftertouch) noexcept; + /** + * @brief Send a high precision aftertouch event to the synth + * + * @param delay the delay at which the event occurs; this should be lower than the size of + * the block in the next call to renderBlock(). + * @param normAftertouch the normalized aftertouch value, in domain 0 to 1 + */ + void hdAftertouch(int delay, float normAftertouch) noexcept; /** * @brief Send a tempo event to the synth * diff --git a/src/sfizz/SynthMessaging.cpp b/src/sfizz/SynthMessaging.cpp index 3b4b18bf..9b2bcd54 100644 --- a/src/sfizz/SynthMessaging.cpp +++ b/src/sfizz/SynthMessaging.cpp @@ -374,9 +374,9 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co MATCH("/region&/chanaft_range", "") { GET_REGION_OR_BREAK(indices[0]) sfizz_arg_t args[2]; - args[0].i = region.aftertouchRange.getStart(); - args[1].i = region.aftertouchRange.getEnd(); - client.receive(delay, path, "ii", args); + args[0].f = region.aftertouchRange.getStart(); + args[1].f = region.aftertouchRange.getEnd(); + client.receive(delay, path, "ff", args); } break; MATCH("/region&/bpm_range", "") { diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 89a4254a..13f6b660 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -90,13 +90,13 @@ TEST_CASE("Region activation", "Region tests") { region.parseOpcode({ "lochanaft", "56" }); region.parseOpcode({ "hichanaft", "68" }); - region.registerAftertouch(0); + region.registerAftertouch(sfz::normalize7Bits(0)); REQUIRE(!region.isSwitchedOn()); - region.registerAftertouch(56); + region.registerAftertouch(sfz::normalize7Bits(56)); REQUIRE(region.isSwitchedOn()); - region.registerAftertouch(68); + region.registerAftertouch(sfz::normalize7Bits(68)); REQUIRE(region.isSwitchedOn()); - region.registerAftertouch(98); + region.registerAftertouch(sfz::normalize7Bits(98)); REQUIRE(!region.isSwitchedOn()); } diff --git a/tests/RegionValuesT.cpp b/tests/RegionValuesT.cpp index 5c0ae4b3..c1944e55 100644 --- a/tests/RegionValuesT.cpp +++ b/tests/RegionValuesT.cpp @@ -869,11 +869,11 @@ TEST_CASE("[Values] Aftertouch range") synth.dispatchMessage(client, 0, "/region3/chanaft_range", "", nullptr); synth.dispatchMessage(client, 0, "/region4/chanaft_range", "", nullptr); std::vector expected { - "/region0/chanaft_range,ii : { 0, 127 }", - "/region1/chanaft_range,ii : { 34, 60 }", - "/region2/chanaft_range,ii : { 0, 60 }", - "/region3/chanaft_range,ii : { 20, 127 }", - "/region4/chanaft_range,ii : { 10, 10 }", + "/region0/chanaft_range,ff : { 0, 1 }", + "/region1/chanaft_range,ff : { 0.267717, 0.472441 }", + "/region2/chanaft_range,ff : { 0, 0.472441 }", + "/region3/chanaft_range,ff : { 0.15748, 1 }", + "/region4/chanaft_range,ff : { 0.0787402, 0.0787402 }", }; REQUIRE(messageList == expected); } From 73608d20913c913a404c627d6bb0724c4eb6969c Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 19 Mar 2021 14:40:01 +0100 Subject: [PATCH 4/5] Add distinct bound permissivity for low and high --- src/sfizz/Defaults.h | 14 ++++++++------ src/sfizz/Opcode.cpp | 8 ++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 6f8d31f1..f9850390 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -51,12 +51,14 @@ enum OpcodeFlags : int { kEnforceLowerBound = 1 << 1, kEnforceUpperBound = 1 << 2, kEnforceBounds = kEnforceLowerBound|kEnforceUpperBound, - kPermissiveBounds = 1 << 3, - kNormalizePercent = 1 << 4, - kNormalizeMidi = 1 << 5, - kNormalizeBend = 1 << 6, - kWrapPhase = 1 << 7, - kDb2Mag = 1 << 8, + kPermissiveLowerBound = 1 << 3, + kPermissiveUpperBound = 1 << 4, + kPermissiveBounds = kPermissiveLowerBound|kPermissiveUpperBound, + kNormalizePercent = 1 << 5, + kNormalizeMidi = 1 << 6, + kNormalizeBend = 1 << 7, + kWrapPhase = 1 << 8, + kDb2Mag = 1 << 9, }; template diff --git a/src/sfizz/Opcode.cpp b/src/sfizz/Opcode.cpp index 935498e8..a09301da 100644 --- a/src/sfizz/Opcode.cpp +++ b/src/sfizz/Opcode.cpp @@ -167,12 +167,12 @@ absl::optional readInt_(OpcodeSpec spec, absl::string_view v) if (returnedValue > static_cast(spec.bounds.getEnd())) { if (spec.flags & kEnforceUpperBound) return spec.bounds.getEnd(); - else if (!(spec.flags & kPermissiveBounds)) + else if (!(spec.flags & kPermissiveUpperBound)) return absl::nullopt; } else if (returnedValue < static_cast(spec.bounds.getStart())) { if (spec.flags & kEnforceLowerBound) return spec.bounds.getStart(); - else if (!(spec.flags & kPermissiveBounds)) + else if (!(spec.flags & kPermissiveLowerBound)) return absl::nullopt; } @@ -225,12 +225,12 @@ absl::optional readFloat_(OpcodeSpec spec, absl::string_view v) else if (returnedValue > static_cast(spec.bounds.getEnd())) { if (spec.flags & kEnforceUpperBound) return spec.bounds.getEnd(); - else if (!(spec.flags & kPermissiveBounds)) + else if (!(spec.flags & kPermissiveUpperBound)) return absl::nullopt; } else if (returnedValue < static_cast(spec.bounds.getStart())) { if (spec.flags & kEnforceLowerBound) return spec.bounds.getStart(); - else if (!(spec.flags & kPermissiveBounds)) + else if (!(spec.flags & kPermissiveLowerBound)) return absl::nullopt; } From 171411ac1886c7b6c1edef412b87b47c8690a8de Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Fri, 19 Mar 2021 15:12:51 +0100 Subject: [PATCH 5/5] Support long smoothcc times up to 64k ms --- src/sfizz/Config.h | 4 ++-- src/sfizz/Defaults.cpp | 2 +- src/sfizz/Defaults.h | 2 +- src/sfizz/Region.h | 2 +- src/sfizz/Smoothers.cpp | 4 ++-- src/sfizz/Smoothers.h | 4 ++-- src/sfizz/modulations/ModKey.cpp | 2 +- src/sfizz/modulations/ModKey.h | 4 ++-- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index ad3e82ee..97061613 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -56,8 +56,8 @@ namespace config { constexpr int numVoices { 64 }; constexpr unsigned maxVoices { 256 }; constexpr unsigned smoothingSteps { 512 }; - constexpr uint8_t xfadeSmoothing { 5 }; - constexpr uint8_t gainSmoothing { 0 }; + constexpr uint16_t xfadeSmoothing { 5 }; + constexpr uint16_t gainSmoothing { 0 }; constexpr unsigned powerTableSizeExponent { 11 }; constexpr int maxFilePromises { maxVoices }; constexpr int allSoundOffCC { 120 }; diff --git a/src/sfizz/Defaults.cpp b/src/sfizz/Defaults.cpp index 6921be22..b01a8f77 100644 --- a/src/sfizz/Defaults.cpp +++ b/src/sfizz/Defaults.cpp @@ -58,7 +58,7 @@ FloatSpec hiNormalized { 1.0f, {0.0f, 1.0f}, 0 }; FloatSpec loBipolar { -1.0f, {-1.0f, 1.0f}, 0 }; FloatSpec hiBipolar { 1.0f, {-1.0f, 1.0f}, 0 }; UInt16Spec ccNumber { 0, {0, config::numCCs}, 0 }; -UInt8Spec smoothCC { 0, {0, 100}, 0 }; +UInt16Spec smoothCC { 0, {0, 100}, kPermissiveUpperBound }; UInt8Spec curveCC { 0, {0, 255}, 0 }; UInt8Spec sustainCC { 64, {0, 127}, 0 }; FloatSpec sustainThreshold { 1.0f, {0.0f, 127.0f}, kNormalizeMidi }; diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index f9850390..8e0dec68 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -159,7 +159,7 @@ namespace Default extern const OpcodeSpec hiChannelAftertouch; extern const OpcodeSpec ccNumber; extern const OpcodeSpec curveCC; - extern const OpcodeSpec smoothCC; + extern const OpcodeSpec smoothCC; extern const OpcodeSpec sustainCC; extern const OpcodeSpec checkSustain; extern const OpcodeSpec checkSostenuto; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 6244424f..89e720ac 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -464,7 +464,7 @@ struct Region { float bendUp { Default::bendUp }; float bendDown { Default::bendDown }; float bendStep { Default::bendStep }; - uint8_t bendSmooth { Default::smoothCC }; + uint16_t bendSmooth { Default::smoothCC }; // Envelopes EGDescription amplitudeEG; diff --git a/src/sfizz/Smoothers.cpp b/src/sfizz/Smoothers.cpp index df935bc6..df17280e 100644 --- a/src/sfizz/Smoothers.cpp +++ b/src/sfizz/Smoothers.cpp @@ -20,7 +20,7 @@ OnePoleSmoother::OnePoleSmoother() { } -void OnePoleSmoother::setSmoothing(uint8_t smoothValue, float sampleRate) +void OnePoleSmoother::setSmoothing(unsigned smoothValue, float sampleRate) { smoothing = (smoothValue > 0); if (smoothing) { @@ -62,7 +62,7 @@ LinearSmoother::LinearSmoother() { } -void LinearSmoother::setSmoothing(uint8_t smoothValue, float sampleRate) +void LinearSmoother::setSmoothing(unsigned smoothValue, float sampleRate) { const float smoothTime = 1e-3f * smoothValue; smoothFrames_ = static_cast(smoothTime * sampleRate); diff --git a/src/sfizz/Smoothers.h b/src/sfizz/Smoothers.h index e62190eb..0527e96d 100644 --- a/src/sfizz/Smoothers.h +++ b/src/sfizz/Smoothers.h @@ -24,7 +24,7 @@ public: * @param smoothValue * @param sampleRate */ - void setSmoothing(uint8_t smoothValue, float sampleRate); + void setSmoothing(unsigned smoothValue, float sampleRate); /** * @brief Reset the filter state to a given value * @@ -63,7 +63,7 @@ public: * @param smoothValue * @param sampleRate */ - void setSmoothing(uint8_t smoothValue, float sampleRate); + void setSmoothing(unsigned smoothValue, float sampleRate); /** * @brief Reset the filter state to a given value * diff --git a/src/sfizz/modulations/ModKey.cpp b/src/sfizz/modulations/ModKey.cpp index 55d9cee5..5dbdc057 100644 --- a/src/sfizz/modulations/ModKey.cpp +++ b/src/sfizz/modulations/ModKey.cpp @@ -56,7 +56,7 @@ ModKey::Parameters& ModKey::Parameters::operator=(Parameters&& other) noexcept return *this; } -ModKey ModKey::createCC(uint16_t cc, uint8_t curve, uint8_t smooth, float step) +ModKey ModKey::createCC(uint16_t cc, uint8_t curve, uint16_t smooth, float step) { ModKey::Parameters p; p.cc = cc; diff --git a/src/sfizz/modulations/ModKey.h b/src/sfizz/modulations/ModKey.h index 9498ee39..69dc50b3 100644 --- a/src/sfizz/modulations/ModKey.h +++ b/src/sfizz/modulations/ModKey.h @@ -28,7 +28,7 @@ public: explicit ModKey(ModId id, NumericId region = {}, Parameters params = {}) : id_(id), region_(region), params_(params), flags_(ModIds::flags(id_)) {} - static ModKey createCC(uint16_t cc, uint8_t curve, uint8_t smooth, float step); + static ModKey createCC(uint16_t cc, uint8_t curve, uint16_t smooth, float step); static ModKey createNXYZ(ModId id, NumericId region = {}, uint8_t N = 0, uint8_t X = 0, uint8_t Y = 0, uint8_t Z = 0); explicit operator bool() const noexcept { return id_ != ModId(); } @@ -45,7 +45,7 @@ public: struct RawParameters { union { //! Parameters if this key identifies a CC source - struct { uint16_t cc; uint8_t curve, smooth; float step; }; + struct { uint16_t cc; uint8_t curve; uint16_t smooth; float step; }; //! Parameters otherwise, based on the related opcode // eg. `N` in `lfoN`, `N, X` in `lfoN_eqX` struct { uint8_t N, X, Y, Z; };