diff --git a/src/sfizz/Region.cpp b/src/sfizz/Region.cpp index 71eb7ca6..601f2a48 100644 --- a/src/sfizz/Region.cpp +++ b/src/sfizz/Region.cpp @@ -292,11 +292,8 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode) if (auto value = readOpcode(opcode.value, Default::normalizedRange)) ccConditions[opcode.parameters.back()].setEnd(*value); break; - case hash("sw_lokey"): - setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange); - break; + case hash("sw_lokey"): // fallthrough case hash("sw_hikey"): - setRangeEndFromOpcode(opcode, keyswitchRange, Default::keyRange); break; case hash("sw_last"): setValueFromOpcode(opcode, keyswitch, Default::keyRange); @@ -1591,16 +1588,11 @@ bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue { ASSERT(velocity >= 0.0f && velocity <= 1.0f); - if (keyswitchRange.containsWithEnd(noteNumber)) { - if (keyswitch) - keySwitched = (*keyswitch == noteNumber); + if (keyswitchDown && *keyswitchDown == noteNumber) + keySwitched = true; - if (keyswitchDown && *keyswitchDown == noteNumber) - keySwitched = true; - - if (keyswitchUp && *keyswitchUp == noteNumber) - keySwitched = false; - } + if (keyswitchUp && *keyswitchUp == noteNumber) + keySwitched = false; const bool keyOk = keyRange.containsWithEnd(noteNumber); if (keyOk) { @@ -1634,13 +1626,11 @@ bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValu { ASSERT(velocity >= 0.0f && velocity <= 1.0f); - if (keyswitchRange.containsWithEnd(noteNumber)) { - if (keyswitchDown && *keyswitchDown == noteNumber) - keySwitched = false; + if (keyswitchDown && *keyswitchDown == noteNumber) + keySwitched = false; - if (keyswitchUp && *keyswitchUp == noteNumber) - keySwitched = true; - } + if (keyswitchUp && *keyswitchUp == noteNumber) + keySwitched = true; if (!isSwitchedOn()) return false; @@ -1860,57 +1850,40 @@ float sfz::Region::velocityCurve(float velocity) const noexcept return gain; } -uint8_t offsetAndClamp(uint8_t key, int offset, sfz::Range range) -{ - const int offsetKey { key + offset }; - if (offsetKey > std::numeric_limits::max()) - return range.getEnd(); - if (offsetKey < std::numeric_limits::min()) - return range.getStart(); - - return range.clamp(static_cast(offsetKey)); -} - void sfz::Region::offsetAllKeys(int offset) noexcept { // Offset key range if (keyRange != Default::keyRange) { const auto start = keyRange.getStart(); const auto end = keyRange.getEnd(); - keyRange.setStart(offsetAndClamp(start, offset, Default::keyRange)); - keyRange.setEnd(offsetAndClamp(end, offset, Default::keyRange)); + keyRange.setStart(offsetAndClampKey(start, offset, Default::keyRange)); + keyRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange)); } - pitchKeycenter = offsetAndClamp(pitchKeycenter, offset, Default::keyRange); + pitchKeycenter = offsetAndClampKey(pitchKeycenter, offset, Default::keyRange); // Offset key switches - if (keyswitchRange != Default::keyRange) { - const auto start = keyswitchRange.getStart(); - const auto end = keyswitchRange.getEnd(); - keyswitchRange.setStart(offsetAndClamp(start, offset, Default::keyRange)); - keyswitchRange.setEnd(offsetAndClamp(end, offset, Default::keyRange)); - } if (keyswitchUp) - keyswitchUp = offsetAndClamp(*keyswitchUp, offset, Default::keyRange); + keyswitchUp = offsetAndClampKey(*keyswitchUp, offset, Default::keyRange); if (keyswitch) - keyswitch = offsetAndClamp(*keyswitch, offset, Default::keyRange); + keyswitch = offsetAndClampKey(*keyswitch, offset, Default::keyRange); if (keyswitchDown) - keyswitchDown = offsetAndClamp(*keyswitchDown, offset, Default::keyRange); + keyswitchDown = offsetAndClampKey(*keyswitchDown, offset, Default::keyRange); if (previousNote) - previousNote = offsetAndClamp(*previousNote, offset, Default::keyRange); + previousNote = offsetAndClampKey(*previousNote, offset, Default::keyRange); // Offset crossfade ranges if (crossfadeKeyInRange != Default::crossfadeKeyInRange) { const auto start = crossfadeKeyInRange.getStart(); const auto end = crossfadeKeyInRange.getEnd(); - crossfadeKeyInRange.setStart(offsetAndClamp(start, offset, Default::keyRange)); - crossfadeKeyInRange.setEnd(offsetAndClamp(end, offset, Default::keyRange)); + crossfadeKeyInRange.setStart(offsetAndClampKey(start, offset, Default::keyRange)); + crossfadeKeyInRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange)); } if (crossfadeKeyOutRange != Default::crossfadeKeyOutRange) { const auto start = crossfadeKeyOutRange.getStart(); const auto end = crossfadeKeyOutRange.getEnd(); - crossfadeKeyOutRange.setStart(offsetAndClamp(start, offset, Default::keyRange)); - crossfadeKeyOutRange.setEnd(offsetAndClamp(end, offset, Default::keyRange)); + crossfadeKeyOutRange.setStart(offsetAndClampKey(start, offset, Default::keyRange)); + crossfadeKeyOutRange.setEnd(offsetAndClampKey(end, offset, Default::keyRange)); } } diff --git a/src/sfizz/Region.h b/src/sfizz/Region.h index 2f0f7f0b..a88ab965 100644 --- a/src/sfizz/Region.h +++ b/src/sfizz/Region.h @@ -292,8 +292,6 @@ struct Region { uint32_t loopStart(Oversampling factor = Oversampling::x1) const noexcept; uint32_t loopEnd(Oversampling factor = Oversampling::x1) const noexcept; - bool hasKeyswitches() const noexcept { return keyswitchDown || keyswitchUp || keyswitch || previousNote; } - /** * @brief Get the gain this region contributes into the input of the Nth * effect bus @@ -349,7 +347,6 @@ struct Region { // Region logic: MIDI conditions Range bendRange { Default::bendValueRange }; // hibend and lobend CCMap> ccConditions { Default::ccValueRange }; - Range keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey absl::optional keyswitch {}; // sw_last absl::optional keyswitchLabel {}; absl::optional keyswitchUp {}; // sw_up @@ -455,7 +452,7 @@ struct Region { // Started notes std::vector> delayedReleases; -private: + const MidiState& midiState; bool keySwitched { true }; bool previousKeySwitched { true }; diff --git a/src/sfizz/Resources.h b/src/sfizz/Resources.h index 577ff756..cf5d27b5 100644 --- a/src/sfizz/Resources.h +++ b/src/sfizz/Resources.h @@ -33,6 +33,8 @@ struct Resources absl::optional stretch; ModMatrix modMatrix; + std::vector keyswitches; + void setSampleRate(float samplerate) { midiState.setSampleRate(samplerate); @@ -54,6 +56,7 @@ struct Resources logger.clear(); midiState.reset(); modMatrix.clear(); + keyswitches.clear(); } }; } diff --git a/src/sfizz/SfzHelpers.h b/src/sfizz/SfzHelpers.h index 416b2304..a2d2439e 100644 --- a/src/sfizz/SfzHelpers.h +++ b/src/sfizz/SfzHelpers.h @@ -177,6 +177,25 @@ constexpr float normalizeBend(float bendValue) return clamp(bendValue, -8191.0f, 8191.0f) / 8191.0f; } +/** + * @brief Offset a key and clamp it to a reasonable range + * + * @param key + * @param offset + * @param range + * @return uint8_t + */ +inline CXX14_CONSTEXPR uint8_t offsetAndClampKey(uint8_t key, int offset, sfz::Range range) +{ + const int offsetKey { key + offset }; + if (offsetKey > std::numeric_limits::max()) + return range.getEnd(); + if (offsetKey < std::numeric_limits::min()) + return range.getStart(); + + return range.clamp(static_cast(offsetKey)); +} + namespace literals { inline float operator""_norm(unsigned long long int value) { diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index f947331f..b52d538d 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -175,6 +175,12 @@ void sfz::Synth::buildRegion(const std::vector& regionOpcodes) if (octaveOffset != 0 || noteOffset != 0) lastRegion->offsetAllKeys(octaveOffset * 12 + noteOffset); + if (lastRegion->keyswitch) { + auto it = absl::c_find(resources.keyswitches, *lastRegion->keyswitch); + if (it == resources.keyswitches.end()) + resources.keyswitches.push_back(*lastRegion->keyswitch); + } + // There was a combination of group= and polyphony= on a region, so set the group polyphony if (lastRegion->group != Default::group && lastRegion->polyphony != config::maxVoices) setGroupPolyphony(lastRegion->group, lastRegion->polyphony); @@ -529,6 +535,7 @@ void sfz::Synth::finalizeSfzLoad() bool haveFilterEG { false }; FlexEGs::clearUnusedCurves(); + absl::c_sort(resources.keyswitches); while (currentRegionIndex < currentRegionCount) { auto region = regions[currentRegionIndex].get(); @@ -595,8 +602,13 @@ void sfz::Synth::finalizeSfzLoad() } } - if (region->keyswitchLabel && region->keyswitch) - insertPairUniquely(keyswitchLabels, *region->keyswitch, *region->keyswitchLabel); + if (region->keyswitch) { + if (defaultSwitch) + region->keySwitched = (*defaultSwitch == *region->keyswitch); + + if (region->keyswitchLabel) + insertPairUniquely(keyswitchLabels, *region->keyswitch, *region->keyswitchLabel); + } // Some regions had group number but no "group-level" opcodes handled the polyphony while (polyphonyGroups.size() <= region->group) { @@ -605,7 +617,15 @@ void sfz::Synth::finalizeSfzLoad() } for (auto note = 0; note < 128; note++) { - if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note))) + bool noteIsKeyswitch = (absl::c_binary_search(resources.keyswitches, note)); + + if ( + region->keyRange.containsWithEnd(note) + || (region->keyswitch && noteIsKeyswitch) + || (region->keyswitchDown && *region->keyswitchDown == note) + || (region->keyswitchUp && *region->keyswitchUp == note) + || (region->previousNote && *region->previousNote == note) + ) noteActivationLists[note].push_back(region); } @@ -621,10 +641,6 @@ void sfz::Synth::finalizeSfzLoad() region->registerCC(cc, resources.midiState.getCCValue(cc)); } - if (defaultSwitch) { - region->registerNoteOn(*defaultSwitch, 1.0f, 1.0f); - region->registerNoteOff(*defaultSwitch, 0.0f, 1.0f); - } // Set the default frequencies on equalizers if needed if (region->equalizers.size() > 0 @@ -1126,7 +1142,12 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc SisterVoiceRingBuilder ring; const TriggerEvent triggerEvent { TriggerEventType::NoteOn, noteNumber, velocity }; + bool noteIsKeyswitch = absl::c_binary_search(resources.keyswitches, noteNumber); + for (auto& region : noteActivationLists[noteNumber]) { + if (noteIsKeyswitch && region->keyswitch) + region->keySwitched = (*region->keyswitch == noteNumber); + if (region->registerNoteOn(noteNumber, velocity, randValue)) { for (auto& voice : voices) { if (voice->checkOffGroup(region, delay, noteNumber)) { diff --git a/tests/FilesT.cpp b/tests/FilesT.cpp index 5f524b64..8917e591 100644 --- a/tests/FilesT.cpp +++ b/tests/FilesT.cpp @@ -356,46 +356,6 @@ TEST_CASE("[Files] Channels (channels_multi.sfz)") REQUIRE(region->oscillatorEnabled == Region::OscillatorEnabled::Auto); } -TEST_CASE("[Files] sw_default") -{ - Synth synth; - synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz"); - REQUIRE( synth.getNumRegions() == 4 ); - REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(1)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(3)->isSwitchedOn() ); -} - -TEST_CASE("[Files] sw_default and playing with switches") -{ - Synth synth; - synth.loadSfzFile(fs::current_path() / "tests/TestFiles/sw_default.sfz"); - REQUIRE( synth.getNumRegions() == 4 ); - REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(1)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(3)->isSwitchedOn() ); - synth.noteOn(0, 41, 64); - synth.noteOff(0, 41, 0); - REQUIRE( synth.getRegionView(0)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(1)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(2)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(3)->isSwitchedOn() ); - synth.noteOn(0, 42, 64); - synth.noteOff(0, 42, 0); - REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(1)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(3)->isSwitchedOn() ); - synth.noteOn(0, 40, 64); - synth.noteOff(0, 40, 64); - REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(1)->isSwitchedOn() ); - REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); - REQUIRE( synth.getRegionView(3)->isSwitchedOn() ); -} - TEST_CASE("[Files] wrong (overlapping) replacement for defines") { Synth synth; @@ -491,7 +451,6 @@ TEST_CASE("[Files] Note and octave offsets") REQUIRE(synth.getRegionView(0)->keyRange == Range(64, 64)); REQUIRE( synth.getRegionView(0)->pitchKeycenter == 64 ); - REQUIRE(synth.getRegionView(0)->keyswitchRange == Default::keyRange); REQUIRE(synth.getRegionView(0)->crossfadeKeyInRange == Default::crossfadeKeyInRange); REQUIRE(synth.getRegionView(0)->crossfadeKeyOutRange == Default::crossfadeKeyOutRange); @@ -504,7 +463,6 @@ TEST_CASE("[Files] Note and octave offsets") REQUIRE(synth.getRegionView(2)->crossfadeKeyOutRange == Range(45, 49)); REQUIRE(synth.getRegionView(3)->keyRange == Range(62, 62)); - REQUIRE(synth.getRegionView(3)->keyswitchRange == Range(23, 27)); REQUIRE( synth.getRegionView(3)->keyswitch ); REQUIRE( *synth.getRegionView(3)->keyswitch == 24 ); REQUIRE( synth.getRegionView(3)->keyswitchUp ); diff --git a/tests/RegionActivationT.cpp b/tests/RegionActivationT.cpp index e7957ffb..af5ee739 100644 --- a/tests/RegionActivationT.cpp +++ b/tests/RegionActivationT.cpp @@ -5,6 +5,7 @@ // If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz #include "sfizz/Region.h" +#include "sfizz/Synth.h" #include "sfizz/SfzHelpers.h" #include "catch2/catch.hpp" using namespace Catch::literals; @@ -113,83 +114,6 @@ TEST_CASE("Region activation", "Region tests") REQUIRE(!region.isSwitchedOn()); } - // TODO: add keyswitches - SECTION("Keyswitches: sw_last") - { - region.parseOpcode({ "sw_last", "40" }); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 64_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0_norm, 0.5f); - } - - SECTION("Keyswitches: sw_last with non-default keyswitch range") - { - region.parseOpcode({ "sw_lokey", "30" }); - region.parseOpcode({ "sw_hikey", "50" }); - region.parseOpcode({ "sw_last", "40" }); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(60, 64_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(60, 0_norm, 0.5f); - region.registerNoteOn(41, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0_norm, 0.5f); - } - - SECTION("Keyswitches: sw_down with non-default keyswitch range") - { - region.parseOpcode({ "sw_lokey", "30" }); - region.parseOpcode({ "sw_hikey", "50" }); - region.parseOpcode({ "sw_down", "40" }); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(40, 64_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOff(40, 0_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOn(60, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(60, 0_norm, 0.5f); - region.registerNoteOn(41, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(41, 0_norm, 0.5f); - } - - SECTION("Keyswitches: sw_up with non-default keyswitch range") - { - region.parseOpcode({ "sw_lokey", "30" }); - region.parseOpcode({ "sw_hikey", "50" }); - region.parseOpcode({ "sw_up", "40" }); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(41, 64_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - region.registerNoteOn(40, 64_norm, 0.5f); - REQUIRE(!region.isSwitchedOn()); - region.registerNoteOff(40, 0_norm, 0.5f); - region.registerNoteOff(41, 0_norm, 0.5f); - REQUIRE(region.isSwitchedOn()); - } - SECTION("Keyswitches: sw_previous") { region.parseOpcode({ "sw_previous", "40" }); @@ -273,3 +197,202 @@ TEST_CASE("Region activation", "Region tests") REQUIRE(!region.isSwitchedOn()); } } + +TEST_CASE("[Keyswitches] Normal keyswitch range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=40 sw_hikey=42 sw_default=40 + sw_last=40 key=60 sample=*sine + sw_last=41 key=62 sample=*saw + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 41, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); +} + +TEST_CASE("[Keyswitches] No keyswitch range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_last=40 key=60 sample=*sine + sw_last=41 key=62 sample=*saw + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 0); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 0); + synth.noteOn(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 41, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); +} + +TEST_CASE("[Keyswitches] Out of keyswitch range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=40 sw_hikey=42 sw_default=40 + sw_last=40 key=60 sample=*sine + sw_last=43 key=62 sample=*saw + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 43, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); +} + +TEST_CASE("[Keyswitches] Overlapping key and keyswitch range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=1 sw_hikey=127 sw_default=40 + sw_last=40 key=60 sample=*sine + sw_last=41 key=62 sample=*saw + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 41, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); + synth.noteOn(0, 43, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); + synth.noteOn(0, 62, 64); + REQUIRE(synth.getNumActiveVoices(true) == 3); +} + +TEST_CASE("[Keyswitches] sw_down, in range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=1 sw_hikey=127 sw_default=40 + sw_down=40 key=60 sample=*sine + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 0); + synth.noteOn(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOff(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); +} + +TEST_CASE("[Keyswitches] sw_down, out of range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=1 sw_hikey=10 sw_default=40 + sw_down=40 key=60 sample=*sine + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 0); + synth.noteOn(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOff(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); +} + +TEST_CASE("[Keyswitches] sw_up, in range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=1 sw_hikey=127 sw_default=40 + sw_up=40 key=60 sample=*sine + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOff(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); +} + +TEST_CASE("[Keyswitches] sw_up, out of range") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/keyswitches.sfz", R"( + sw_lokey=1 sw_hikey=127 sw_default=40 + sw_up=40 key=60 sample=*sine + )"); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOn(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 1); + synth.noteOff(0, 40, 64); + synth.noteOn(0, 60, 64); + REQUIRE(synth.getNumActiveVoices(true) == 2); +} + +TEST_CASE("[Keyswitches] sw_default") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_default.sfz", R"( + sw_lokey=30 sw_hikey=50 sw_default=40 + sw_last=41 key=51 sample=*sine + sw_last=40 key=52 sample=*sine + sw_last=41 key=53 sample=*sine + sw_last=40 key=54 sample=*sine + )"); + REQUIRE( synth.getNumRegions() == 4 ); + REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(1)->isSwitchedOn() ); + REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(3)->isSwitchedOn() ); +} + +TEST_CASE("[Keyswitches] sw_default and playing with switches") +{ + sfz::Synth synth; + synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_default.sfz", R"( + sw_lokey=30 sw_hikey=50 sw_default=40 + sw_last=41 key=51 sample=*sine + sw_last=40 key=52 sample=*sine + sw_last=41 key=53 sample=*sine + sw_last=40 key=54 sample=*sine + )"); + REQUIRE( synth.getNumRegions() == 4 ); + REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(1)->isSwitchedOn() ); + REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(3)->isSwitchedOn() ); + synth.noteOn(0, 41, 64); + synth.noteOff(0, 41, 0); + REQUIRE( synth.getRegionView(0)->isSwitchedOn() ); + REQUIRE( !synth.getRegionView(1)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(2)->isSwitchedOn() ); + REQUIRE( !synth.getRegionView(3)->isSwitchedOn() ); + synth.noteOn(0, 40, 64); + synth.noteOff(0, 40, 64); + REQUIRE( !synth.getRegionView(0)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(1)->isSwitchedOn() ); + REQUIRE( !synth.getRegionView(2)->isSwitchedOn() ); + REQUIRE( synth.getRegionView(3)->isSwitchedOn() ); +} diff --git a/tests/RegionT.cpp b/tests/RegionT.cpp index f15bd286..2b1cc780 100644 --- a/tests/RegionT.cpp +++ b/tests/RegionT.cpp @@ -339,23 +339,6 @@ TEST_CASE("[Region] Parsing opcodes") REQUIRE(region.ccConditions[125] == sfz::Range(0.0f, 1.0f)); } - SECTION("sw_lokey, sw_hikey") - { - REQUIRE(region.keyswitchRange == Range(0, 127)); - region.parseOpcode({ "sw_lokey", "4" }); - REQUIRE(region.keyswitchRange == Range(4, 127)); - region.parseOpcode({ "sw_lokey", "128" }); - REQUIRE(region.keyswitchRange == Range(127, 127)); - region.parseOpcode({ "sw_lokey", "0" }); - REQUIRE(region.keyswitchRange == Range(0, 127)); - region.parseOpcode({ "sw_hikey", "39" }); - REQUIRE(region.keyswitchRange == Range(0, 39)); - region.parseOpcode({ "sw_hikey", "135" }); - REQUIRE(region.keyswitchRange == Range(0, 127)); - region.parseOpcode({ "sw_hikey", "-1" }); - REQUIRE(region.keyswitchRange == Range(0, 0)); - } - SECTION("sw_label") { REQUIRE(!region.keyswitchLabel); diff --git a/tests/RegionValueComputationsT.cpp b/tests/RegionValueComputationsT.cpp index 50deb231..3b2d3e32 100644 --- a/tests/RegionValueComputationsT.cpp +++ b/tests/RegionValueComputationsT.cpp @@ -7,7 +7,6 @@ #include "sfizz/Defaults.h" #include "sfizz/Region.h" #include "sfizz/SfzHelpers.h" -#include "sfizz/MidiState.h" #include "catch2/catch.hpp" #include #include diff --git a/tests/TestFiles/sw_default.sfz b/tests/TestFiles/sw_default.sfz deleted file mode 100644 index 44d896c5..00000000 --- a/tests/TestFiles/sw_default.sfz +++ /dev/null @@ -1,5 +0,0 @@ - sw_lokey=30 sw_hikey=50 sw_default=40 - sw_last=41 key=51 sample=*silence - sw_last=40 key=52 sample=*silence - sw_last=41 key=53 sample=*silence - sw_last=40 key=54 sample=*silence \ No newline at end of file