diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index b8921d99..9e6f0d84 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -68,14 +68,13 @@ namespace Default // Region logic: key mapping constexpr Range keyRange { 0, 127 }; - constexpr Range velocityRange { 0.0f, 1.0f }; + constexpr auto velocityRange = normalizedRange; // Region logic: MIDI conditions constexpr Range channelRange { 1, 16 }; constexpr Range midiChannelRange { 0, 15 }; constexpr Range ccNumberRange { 0, config::numCCs }; - constexpr Range ccValueRange { 0, 127 }; - constexpr uint8_t cc { 0 }; + constexpr auto ccValueRange = normalizedRange; constexpr Range bendRange { -8192, 8192 }; constexpr int bend { 0 }; constexpr SfzVelocityOverride velocityOverride { SfzVelocityOverride::current }; @@ -92,7 +91,7 @@ namespace Default // Region logic: Triggers constexpr SfzTrigger trigger { SfzTrigger::attack }; - constexpr Range ccTriggerValueRange{ 0, 127 }; + constexpr Range ccTriggerValueRange = normalizedRange; // Performance parameters: amplifier constexpr float globalVolume { -7.35f }; @@ -123,8 +122,8 @@ namespace Default constexpr Range crossfadeKeyOutRange { 127, 127 }; constexpr Range crossfadeVelInRange { 0.0f, 0.0f }; constexpr Range crossfadeVelOutRange { 1.0f, 1.0f }; - constexpr Range crossfadeCCInRange { 0, 0 }; - constexpr Range crossfadeCCOutRange { 127, 127 }; + constexpr Range crossfadeCCInRange { 0.0f, 0.0f }; + constexpr Range crossfadeCCOutRange { 1.0f, 1.0f }; constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power }; constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power }; diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 95151b47..61082200 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -155,10 +155,16 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) setRangeEndFromOpcode(opcode, bendRange, Default::bendRange); break; case hash("locc&"): - setRangeStartFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccConditions[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("hicc&"): - setRangeEndFromOpcode(opcode, ccConditions[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccConditions[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("sw_lokey"): setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange); @@ -250,11 +256,17 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) break; case hash("on_locc&"): // fallthrough case hash("start_locc&"): - setRangeStartFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("on_hicc&"): // fallthrough case hash("start_hicc&"): - setRangeEndFromOpcode(opcode, ccTriggers[opcode.parameters.back()], Default::ccTriggerValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + ccTriggers[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; // Performance parameters: amplifier @@ -366,16 +378,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode) } break; case hash("xfin_locc&"): - setRangeStartFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCInRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfin_hicc&"): - setRangeEndFromOpcode(opcode, crossfadeCCInRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCInRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xfout_locc&"): - setRangeStartFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCOutRange[opcode.parameters.back()].setStart(normalizeCC(*value)); break; case hash("xfout_hicc&"): - setRangeEndFromOpcode(opcode, crossfadeCCOutRange[opcode.parameters.back()], Default::ccValueRange); + if (opcode.parameters.back() > config::numCCs) + return false; + if (auto value = readOpcode(opcode.value, Default::midi7Range)) + crossfadeCCOutRange[opcode.parameters.back()].setEnd(normalizeCC(*value)); break; case hash("xf_cccurve"): switch (hash(opcode.value)) { @@ -872,6 +896,11 @@ bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, floa } bool sfz::Region::registerCC(int ccNumber, uint8_t ccValue) noexcept +{ + return registerCCNormalized(ccNumber, normalizeCC(ccValue)); +} + +bool sfz::Region::registerCCNormalized(int ccNumber, float ccValue) noexcept { if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue)) ccSwitched.set(ccNumber, true); @@ -1063,13 +1092,13 @@ float sfz::Region::getCrossfadeGain() const noexcept // Crossfades due to CC states for (const auto& valuePair : crossfadeCCInRange) { - const auto ccValue = midiState.getCCValue(valuePair.cc); + const auto ccValue = midiState.getCCValueNormalized(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeIn(crossfadeRange, ccValue, crossfadeCCCurve); } for (const auto& valuePair : crossfadeCCOutRange) { - const auto ccValue = midiState.getCCValue(valuePair.cc); + const auto ccValue = midiState.getCCValueNormalized(valuePair.cc); const auto crossfadeRange = valuePair.value; gain *= crossfadeOut(crossfadeRange, ccValue, crossfadeCCCurve); } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 7466507c..b97e90c0 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -132,6 +132,16 @@ struct Region { * @return false */ bool registerCC(int ccNumber, uint8_t ccValue) noexcept; + /** + * @brief Register a new CC event. The region may be switched on or off using CCs so + * this function checks if it indeeds need to activate or not. + * + * @param ccNumber + * @param ccValue + * @return true if the region should trigger on this event + * @return false + */ + bool registerCCNormalized(int ccNumber, float ccValue) noexcept; /** * @brief Register a new pitch wheel event. * @@ -295,7 +305,7 @@ struct Region { // Region logic: MIDI conditions Range bendRange { Default::bendRange }; // hibend and lobend - CCMap> ccConditions { Default::ccValueRange }; + CCMap> ccConditions { Default::ccValueRange }; Range keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey absl::optional keyswitch {}; // sw_last absl::optional keyswitchUp {}; // sw_up @@ -314,7 +324,7 @@ struct Region { // Region logic: triggers SfzTrigger trigger { Default::trigger }; // trigger - CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN + CCMap> ccTriggers { Default::ccTriggerValueRange }; // on_loccN on_hiccN // Performance parameters: amplifier float volume { Default::volume }; // volume @@ -339,8 +349,8 @@ struct Region { SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve }; SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve }; SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve }; - CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN - CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN + CCMap> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN + CCMap> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN float rtDecay { Default::rtDecay }; // rt_decay // Filters and EQs diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index e6d4f294..7bec6cbf 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -181,7 +181,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector& members) case hash("Set_cc&"): // fallthrough case hash("set_cc&"): if (Default::ccNumberRange.containsWithEnd(member.parameters.back())) { - const auto ccValue = readOpcode(member.value, Default::ccValueRange).value_or(0); + const auto ccValue = readOpcode(member.value, Default::midi7Range).value_or(0); resources.midiState.ccEvent(0, member.parameters.back(), ccValue); } break; diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index c58976a5..7bc99da4 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -236,7 +236,7 @@ TEST_CASE("[Files] Pizz basic") REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range(12, 22)); REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range(97_norm, 127_norm)); REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21); - REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0, 13)); + REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range(0_norm, 13_norm)); } REQUIRE(synth.getRegionView(0)->randRange == sfz::Range(0, 0.25)); REQUIRE(synth.getRegionView(1)->randRange == sfz::Range(0.25, 0.5)); diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 35af3f8c..c5338b49 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -240,16 +240,16 @@ TEST_CASE("[Region] Parsing opcodes") SECTION("locc, hicc") { - REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0, 127)); - REQUIRE(region.ccConditions[127] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions.getWithDefault(0) == sfz::Range(0_norm, 127_norm)); + REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "locc6", "4" }); - REQUIRE(region.ccConditions[6] == sfz::Range(4, 127)); + REQUIRE(region.ccConditions[6] == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "locc12", "-128" }); - REQUIRE(region.ccConditions[12] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions[12] == sfz::Range(0_norm, 127_norm)); region.parseOpcode({ "hicc65", "39" }); - REQUIRE(region.ccConditions[65] == sfz::Range(0, 39)); + REQUIRE(region.ccConditions[65] == sfz::Range(0_norm, 39_norm)); region.parseOpcode({ "hicc127", "135" }); - REQUIRE(region.ccConditions[127] == sfz::Range(0, 127)); + REQUIRE(region.ccConditions[127] == sfz::Range(0_norm, 127_norm)); } SECTION("sw_lokey, sw_hikey") @@ -427,10 +427,10 @@ TEST_CASE("[Region] Parsing opcodes") } region.parseOpcode({ "on_locc45", "15" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[45] == sfz::Range(15, 127)); + REQUIRE(region.ccTriggers[45] == sfz::Range(15_norm, 127_norm)); region.parseOpcode({ "on_hicc4", "47" }); REQUIRE(region.ccTriggers.contains(45)); - REQUIRE(region.ccTriggers[4] == sfz::Range(0, 47)); + REQUIRE(region.ccTriggers[4] == sfz::Range(0_norm, 47_norm)); } SECTION("volume") @@ -650,38 +650,38 @@ TEST_CASE("[Region] Parsing opcodes") { REQUIRE(!region.crossfadeCCInRange.contains(4)); region.parseOpcode({ "xfin_locc4", "4" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4, 4)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(4_norm, 4_norm)); region.parseOpcode({ "xfin_locc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfin_locc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "59" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 59)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfin_hicc4", "128" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfin_hicc4", "0" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfin_hicc4", "-1" }); - REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCInRange[4] == sfz::Range(0_norm, 0_norm)); } SECTION("xfout_locc, xfout_hicc") { REQUIRE(!region.crossfadeCCOutRange.contains(4)); region.parseOpcode({ "xfout_locc4", "4" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(4, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(4_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(127_norm, 127_norm)); region.parseOpcode({ "xfout_locc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "59" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 59)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 59_norm)); region.parseOpcode({ "xfout_hicc4", "128" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59, 127)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(59_norm, 127_norm)); region.parseOpcode({ "xfout_hicc4", "0" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); region.parseOpcode({ "xfout_hicc4", "-1" }); - REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0, 0)); + REQUIRE(region.crossfadeCCOutRange[4] == sfz::Range(0_norm, 0_norm)); } SECTION("xf_keycurve")