From 5ea4b876dfd207f2e2e9c57a0b4706610e46bfde Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Tue, 27 Oct 2020 14:50:31 +0100 Subject: [PATCH] Add sw_lolast/sw_hilast --- src/sfizz/Region.cpp | 28 ++++++++++++++-- src/sfizz/Region.h | 1 + src/sfizz/Synth.cpp | 6 ++++ tests/RegionActivationT.cpp | 64 ++++++++++++++++++++++++++++++++++++- tests/RegionT.cpp | 42 ++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 3 deletions(-) diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index d7826720..42be5f79 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -296,8 +296,32 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) case hash("sw_hikey"): break; case hash("sw_last"): - setValueFromOpcode(opcode, lastKeyswitch, Default::keyRange); - keySwitched = false; + if (!lastKeyswitchRange) { + setValueFromOpcode(opcode, lastKeyswitch, Default::keyRange); + keySwitched = false; + } + break; + case hash("sw_lolast"): + if (auto value = readOpcode(opcode.value, Default::keyRange)) { + if (!lastKeyswitchRange) + lastKeyswitchRange.emplace(*value, *value); + else + lastKeyswitchRange->setStart(*value); + + keySwitched = false; + lastKeyswitch = absl::nullopt; + } + break; + case hash("sw_hilast"): + if (auto value = readOpcode(opcode.value, Default::keyRange)) { + if (!lastKeyswitchRange) + lastKeyswitchRange.emplace(*value, *value); + else + lastKeyswitchRange->setEnd(*value); + + keySwitched = false; + lastKeyswitch = absl::nullopt; + } break; case hash("sw_label"): keyswitchLabel = opcode.value; diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 996eec4e..7fb0b695 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -348,6 +348,7 @@ struct Region { Range bendRange { Default::bendValueRange }; // hibend and lobend CCMap> ccConditions { Default::ccValueRange }; absl::optional lastKeyswitch {}; // sw_last + absl::optional> lastKeyswitchRange {}; // sw_last absl::optional keyswitchLabel {}; absl::optional upKeyswitch {}; // sw_up absl::optional downKeyswitch {}; // sw_down diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index 631a5b43..ba5c109d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -178,6 +178,12 @@ void sfz::Synth::buildRegion(const std::vector& regionOpcodes) if (lastRegion->lastKeyswitch) lastKeyswitchLists[*lastRegion->lastKeyswitch].push_back(lastRegion.get()); + if (lastRegion->lastKeyswitchRange) { + auto& range = *lastRegion->lastKeyswitchRange; + for (uint8_t note = range.getStart(), end = range.getEnd(); note <= end; note++) + lastKeyswitchLists[note].push_back(lastRegion.get()); + } + if (lastRegion->upKeyswitch) upKeyswitchLists[*lastRegion->upKeyswitch].push_back(lastRegion.get()); diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index 0763b56f..6c5a3ed9 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -402,7 +402,6 @@ TEST_CASE("[Keyswitches] sw_previous in range") REQUIRE(synth.getRegionView(0)->isSwitchedOn()); } - TEST_CASE("[Keyswitches] sw_previous out of range") { // The behavior is the same in this case, regardless of the keyrange @@ -425,3 +424,66 @@ TEST_CASE("[Keyswitches] sw_previous out of range") synth.noteOn(0, 61, 64); REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); } + +TEST_CASE("[Keyswitches] sw_lolast and sw_hilast") +{ + // The behavior is the same in this case, regardless of the keyrange + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_previous.sfz", R"( + sw_lolast=57 sw_hilast=59 key=70 sample=*saw + sw_lolast=60 sw_hilast=62 key=72 sample=*sine + )"); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 51, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 57, 64); + REQUIRE(synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 60, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 58, 64); + REQUIRE(synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 61, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 59, 64); + REQUIRE(synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 62, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(synth.getRegionView(1)->isSwitchedOn()); +} + +TEST_CASE("[Keyswitches] sw_lolast and sw_hilast with sw_last") +{ + // The behavior is the same in this case, regardless of the keyrange + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_previous.sfz", R"( + sw_last=40 sw_lolast=57 sw_hilast=59 key=70 sample=*saw + sw_lolast=60 sw_hilast=62 sw_last=41 key=72 sample=*sine + )"); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 40, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 41, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 57, 64); + REQUIRE(synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 41, 64); + REQUIRE(synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(!synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 60, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(synth.getRegionView(1)->isSwitchedOn()); + synth.noteOn(0, 40, 64); + REQUIRE(!synth.getRegionView(0)->isSwitchedOn()); + REQUIRE(synth.getRegionView(1)->isSwitchedOn()); +} diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index 7e20583c..312d17a0 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -362,6 +362,48 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(*region.lastKeyswitch == 0); } + SECTION("sw_lolast/hilast") + { + REQUIRE(!region.lastKeyswitchRange); + region.parseOpcode({ "sw_lolast", "4" }); + REQUIRE(region.lastKeyswitchRange); + REQUIRE(*region.lastKeyswitchRange == Range(4, 4)); + region.parseOpcode({ "sw_hilast", "128" }); + REQUIRE(*region.lastKeyswitchRange == Range(4, 127)); + region.parseOpcode({ "sw_hilast", "63" }); + REQUIRE(*region.lastKeyswitchRange == Range(4, 63)); + region.parseOpcode({ "sw_lolast", "64" }); + REQUIRE(*region.lastKeyswitchRange == Range(64, 64)); + region.parseOpcode({ "sw_lolast", "-1" }); + REQUIRE(*region.lastKeyswitchRange == Range(0, 64)); + } + + SECTION("sw_hilast disables sw_last") + { + REQUIRE(!region.lastKeyswitchRange); + REQUIRE(!region.lastKeyswitch); + region.parseOpcode({ "sw_last", "4" }); + REQUIRE(region.lastKeyswitch); + region.parseOpcode({ "sw_hilast", "63" }); + REQUIRE(region.lastKeyswitchRange); + REQUIRE(!region.lastKeyswitch); + region.parseOpcode({ "sw_last", "4" }); + REQUIRE(!region.lastKeyswitch); + } + + SECTION("sw_lolast disables sw_last") + { + REQUIRE(!region.lastKeyswitchRange); + REQUIRE(!region.lastKeyswitch); + region.parseOpcode({ "sw_last", "4" }); + REQUIRE(region.lastKeyswitch); + region.parseOpcode({ "sw_lolast", "63" }); + REQUIRE(region.lastKeyswitchRange); + REQUIRE(!region.lastKeyswitch); + region.parseOpcode({ "sw_last", "4" }); + REQUIRE(!region.lastKeyswitch); + } + SECTION("sw_up") { REQUIRE(!region.upKeyswitch);