Honor lorand/hirand in CC triggered regions
This commit is contained in:
parent
ba8716da21
commit
6310aa1abc
6 changed files with 118 additions and 57 deletions
|
|
@ -129,7 +129,7 @@ bool Layer::registerNoteOff(int noteNumber, float velocity, float randValue) noe
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept
|
||||
void Layer::updateCCState(int ccNumber, float ccValue) noexcept
|
||||
{
|
||||
const Region& region = region_;
|
||||
|
||||
|
|
@ -151,8 +151,21 @@ bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept
|
|||
ccSwitched_.set(ccNumber, true);
|
||||
else
|
||||
ccSwitched_.set(ccNumber, false);
|
||||
}
|
||||
|
||||
if (dontTrigger || !region.triggerOnCC)
|
||||
bool Layer::registerCC(int ccNumber, float ccValue, float randValue) noexcept
|
||||
{
|
||||
const Region& region = region_;
|
||||
|
||||
updateCCState(ccNumber, ccValue);
|
||||
|
||||
if (!region.triggerOnCC)
|
||||
return false;
|
||||
|
||||
const bool randOk = region.randRange.contains(randValue)
|
||||
|| (randValue >= 1.0f && region.randRange.isValid() && region.randRange.getEnd() >= 1.0f);
|
||||
|
||||
if (!randOk)
|
||||
return false;
|
||||
|
||||
if (auto triggerRange = region.ccTriggers.get(ccNumber)) {
|
||||
|
|
|
|||
|
|
@ -80,15 +80,26 @@ public:
|
|||
*/
|
||||
bool registerNoteOff(int noteNumber, float velocity, float randValue) 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.
|
||||
* @brief Update the internal state of the layer with respect to CC events (sustain, CC
|
||||
* switch, etc).
|
||||
*
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
* @return true if the region should trigger on this event
|
||||
* @return false
|
||||
*/
|
||||
bool registerCC(int ccNumber, float ccValue, bool dontTrigger = false) noexcept;
|
||||
void updateCCState(int ccNumber, float ccValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new CC event,. This method updates the internal CC state with respect
|
||||
* to CC events (sustain, CC switch, etc) and checks if the region should trigger on this
|
||||
* event.
|
||||
*
|
||||
* @param ccNumber
|
||||
* @param ccValue
|
||||
* @param randValue
|
||||
* @return true if the region should trigger on this event
|
||||
* @return false otherwise
|
||||
*/
|
||||
bool registerCC(int ccNumber, float ccValue, float randValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new pitch wheel event.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -727,7 +727,7 @@ void Synth::Impl::finalizeSfzLoad()
|
|||
// Defaults
|
||||
MidiState& midiState = resources_.getMidiState();
|
||||
for (int cc = 0; cc < config::numCCs; cc++) {
|
||||
layer.registerCC(cc, midiState.getCCValue(cc), true);
|
||||
layer.updateCCState(cc, midiState.getCCValue(cc));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1273,6 +1273,7 @@ void Synth::Impl::ccDispatch(int delay, int ccNumber, float value) noexcept
|
|||
{
|
||||
SisterVoiceRingBuilder ring;
|
||||
TriggerEvent triggerEvent { TriggerEventType::CC, ccNumber, value };
|
||||
const auto randValue = randNoteDistribution_(Random::randomGenerator);
|
||||
for (Layer* layer : ccActivationLists_[ccNumber]) {
|
||||
const Region& region = layer->getRegion();
|
||||
|
||||
|
|
@ -1290,7 +1291,7 @@ void Synth::Impl::ccDispatch(int delay, int ccNumber, float value) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
if (layer->registerCC(ccNumber, value)) {
|
||||
if (layer->registerCC(ccNumber, value, randValue)) {
|
||||
checkOffGroups(®ion, delay, ccNumber);
|
||||
startVoice(layer, delay, triggerEvent, ring);
|
||||
}
|
||||
|
|
@ -1952,7 +1953,7 @@ void Synth::Impl::resetAllControllers(int delay) noexcept
|
|||
for (const LayerPtr& layerPtr : layers_) {
|
||||
Layer& layer = *layerPtr;
|
||||
for (int cc = 0; cc < config::numCCs; ++cc)
|
||||
layer.registerCC(cc, defaultCCValues_[cc], true);
|
||||
layer.updateCCState(cc, defaultCCValues_[cc]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
|||
Layer layer { region, midiState };
|
||||
layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices);
|
||||
midiState.ccEvent(0, 64, 0.0f);
|
||||
layer.registerCC(64, 0.0f);
|
||||
layer.updateCCState(64, 0.0f);
|
||||
REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) );
|
||||
REQUIRE( layer.registerNoteOff(63, 0.5f, 0.0f) );
|
||||
}
|
||||
|
|
@ -53,8 +53,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
|||
Layer layer { region, midiState };
|
||||
layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices);
|
||||
midiState.ccEvent(0, 64, 1.0f);
|
||||
layer.registerCC(64, 1.0f);
|
||||
REQUIRE( !layer.registerCC(64, 1.0f) );
|
||||
layer.updateCCState(64, 1.0f);
|
||||
REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) );
|
||||
REQUIRE( layer.registerNoteOff(63, 0.5f, 0.0f) );
|
||||
}
|
||||
|
|
@ -65,7 +64,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
|||
Layer layer { region, midiState };
|
||||
layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices);
|
||||
midiState.ccEvent(0, 64, 0.0f);
|
||||
layer.registerCC(64, 0.0f);
|
||||
layer.updateCCState(64, 0.0f);
|
||||
REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) );
|
||||
REQUIRE( layer.registerNoteOff(63, 0.5f, 0.0f) );
|
||||
}
|
||||
|
|
@ -76,7 +75,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
|||
Layer layer { region, midiState };
|
||||
layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices);
|
||||
midiState.ccEvent(0, 64, 1.0f);
|
||||
layer.registerCC(64, 1.0f);
|
||||
layer.updateCCState(64, 1.0f);
|
||||
midiState.noteOnEvent(0, 63, 0.5f);
|
||||
REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) );
|
||||
REQUIRE( !layer.registerNoteOff(63, 0.5f, 0.0f) );
|
||||
|
|
@ -93,7 +92,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
|||
Layer layer { region, midiState };
|
||||
layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices);
|
||||
midiState.ccEvent(0, 64, 1.0f);
|
||||
layer.registerCC(64, 1.0f);
|
||||
layer.updateCCState(64, 1.0f);
|
||||
midiState.noteOnEvent(0, 63, 0.5f);
|
||||
REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) );
|
||||
midiState.noteOnEvent(0, 64, 0.6f);
|
||||
|
|
@ -114,7 +113,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
|||
Layer layer { region, midiState };
|
||||
layer.delayedSustainReleases_.reserve(config::delayedReleaseVoices);
|
||||
midiState.ccEvent(0, 64, 1.0f);
|
||||
layer.registerCC(64, 1.0f);
|
||||
layer.updateCCState(64, 1.0f);
|
||||
midiState.noteOnEvent(0, 63, 0.5f);
|
||||
REQUIRE( !layer.registerNoteOn(63, 0.5f, 0.0f) );
|
||||
midiState.noteOnEvent(0, 66, 0.6f);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ TEST_CASE("Region activation", "Region tests")
|
|||
SECTION("Basic state")
|
||||
{
|
||||
sfz::Layer layer { region, midiState };
|
||||
layer.registerCC(4, 0_norm);
|
||||
layer.updateCCState(4, 0_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -30,19 +30,19 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "locc4", "56" });
|
||||
region.parseOpcode({ "hicc4", "59" });
|
||||
sfz::Layer layer { region, midiState };
|
||||
layer.registerCC(4, 0_norm);
|
||||
layer.updateCCState(4, 0_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(4, 57_norm);
|
||||
layer.updateCCState(4, 57_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(4, 56_norm);
|
||||
layer.updateCCState(4, 56_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(4, 59_norm);
|
||||
layer.updateCCState(4, 59_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(4, 43_norm);
|
||||
layer.updateCCState(4, 43_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(4, 65_norm);
|
||||
layer.updateCCState(4, 65_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(6, 57_norm);
|
||||
layer.updateCCState(6, 57_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
@ -53,26 +53,26 @@ TEST_CASE("Region activation", "Region tests")
|
|||
region.parseOpcode({ "locc54", "18" });
|
||||
region.parseOpcode({ "hicc54", "27" });
|
||||
sfz::Layer layer { region, midiState };
|
||||
layer.registerCC(4, 0_norm);
|
||||
layer.registerCC(54, 0_norm);
|
||||
layer.updateCCState(4, 0_norm);
|
||||
layer.updateCCState(54, 0_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(4, 57_norm);
|
||||
layer.updateCCState(4, 57_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(54, 19_norm);
|
||||
layer.updateCCState(54, 19_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(54, 17_norm);
|
||||
layer.updateCCState(54, 17_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(54, 27_norm);
|
||||
layer.updateCCState(54, 27_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(4, 56_norm);
|
||||
layer.updateCCState(4, 56_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(4, 59_norm);
|
||||
layer.updateCCState(4, 59_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(54, 2_norm);
|
||||
layer.updateCCState(54, 2_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
layer.registerCC(54, 26_norm);
|
||||
layer.updateCCState(54, 26_norm);
|
||||
REQUIRE(layer.isSwitchedOn());
|
||||
layer.registerCC(4, 65_norm);
|
||||
layer.updateCCState(4, 65_norm);
|
||||
REQUIRE(!layer.isSwitchedOn());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
REQUIRE(layer.registerNoteOn(40, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm, 0.0f));
|
||||
}
|
||||
SECTION("lokey and hikey")
|
||||
{
|
||||
|
|
@ -42,7 +42,7 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
REQUIRE(!layer.registerNoteOn(43, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOff(42, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOff(42, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm, 0.0f));
|
||||
}
|
||||
SECTION("key and release trigger")
|
||||
{
|
||||
|
|
@ -53,7 +53,7 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
REQUIRE(layer.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOff(41, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm, 0.0f));
|
||||
}
|
||||
SECTION("key and release_key trigger")
|
||||
{
|
||||
|
|
@ -64,7 +64,7 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
REQUIRE(layer.registerNoteOff(40, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOn(41, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerNoteOff(41, 64_norm, 0.5f));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm));
|
||||
REQUIRE(!layer.registerCC(63, 64_norm, 0.0f));
|
||||
}
|
||||
// TODO: first and legato triggers
|
||||
SECTION("lovel and hivel")
|
||||
|
|
@ -130,18 +130,36 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "on_locc47", "64" });
|
||||
region.parseOpcode({ "on_hicc47", "68" });
|
||||
Layer layer1 { region, midiState };
|
||||
REQUIRE(!layer1.registerCC(47, 63_norm));
|
||||
REQUIRE(layer1.registerCC(47, 64_norm));
|
||||
REQUIRE(layer1.registerCC(47, 65_norm));
|
||||
REQUIRE(!layer1.registerCC(47, 63_norm, 0.0f));
|
||||
REQUIRE(layer1.registerCC(47, 64_norm, 0.0f));
|
||||
REQUIRE(layer1.registerCC(47, 65_norm, 0.0f));
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
Layer layer2 { region, midiState };
|
||||
REQUIRE(layer2.registerCC(47, 64_norm));
|
||||
REQUIRE(layer2.registerCC(47, 65_norm));
|
||||
REQUIRE(layer2.registerCC(47, 66_norm));
|
||||
REQUIRE(layer2.registerCC(47, 67_norm));
|
||||
REQUIRE(layer2.registerCC(47, 68_norm));
|
||||
REQUIRE(!layer2.registerCC(47, 69_norm));
|
||||
REQUIRE(!layer2.registerCC(40, 64_norm));
|
||||
REQUIRE(layer2.registerCC(47, 64_norm, 0.0f));
|
||||
REQUIRE(layer2.registerCC(47, 65_norm, 0.0f));
|
||||
REQUIRE(layer2.registerCC(47, 66_norm, 0.0f));
|
||||
REQUIRE(layer2.registerCC(47, 67_norm, 0.0f));
|
||||
REQUIRE(layer2.registerCC(47, 68_norm, 0.0f));
|
||||
REQUIRE(!layer2.registerCC(47, 69_norm, 0.0f));
|
||||
REQUIRE(!layer2.registerCC(40, 64_norm, 0.0f));
|
||||
}
|
||||
|
||||
SECTION("lorand and hirand with CC triggers")
|
||||
{
|
||||
region.parseOpcode({ "on_locc47", "64" });
|
||||
region.parseOpcode({ "on_hicc47", "68" });
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
region.parseOpcode({ "lorand", "0.35" });
|
||||
region.parseOpcode({ "hirand", "0.40" });
|
||||
Layer layer { region, midiState };
|
||||
REQUIRE(!layer.registerCC(47, 64_norm, 0.0f));
|
||||
REQUIRE(!layer.registerCC(47, 64_norm, 0.34f));
|
||||
REQUIRE(layer.registerCC(47, 64_norm, 0.35f));
|
||||
REQUIRE(layer.registerCC(47, 64_norm, 0.36f));
|
||||
REQUIRE(layer.registerCC(47, 64_norm, 0.37f));
|
||||
REQUIRE(layer.registerCC(47, 64_norm, 0.38f));
|
||||
REQUIRE(layer.registerCC(47, 64_norm, 0.39f));
|
||||
REQUIRE(!layer.registerCC(47, 64_norm, 0.40f));
|
||||
}
|
||||
|
||||
SECTION("on_loccN does not disable key triggering")
|
||||
|
|
@ -150,9 +168,9 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "on_locc1", "127" });
|
||||
region.parseOpcode({ "on_hicc1", "127" });
|
||||
Layer layer { region, midiState };
|
||||
REQUIRE(!layer.registerCC(1, 126_norm));
|
||||
REQUIRE(!layer.registerCC(2, 127_norm));
|
||||
REQUIRE(layer.registerCC(1, 127_norm));
|
||||
REQUIRE(!layer.registerCC(1, 126_norm, 0.0f));
|
||||
REQUIRE(!layer.registerCC(2, 127_norm, 0.0f));
|
||||
REQUIRE(layer.registerCC(1, 127_norm, 0.0f));
|
||||
REQUIRE(layer.registerNoteOn(64, 127_norm, 0.5f));
|
||||
}
|
||||
|
||||
|
|
@ -163,8 +181,8 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "on_hicc1", "127" });
|
||||
region.parseOpcode({ "key", "-1" });
|
||||
Layer layer { region, midiState };
|
||||
REQUIRE(!layer.registerCC(1, 126_norm));
|
||||
REQUIRE(layer.registerCC(1, 127_norm));
|
||||
REQUIRE(!layer.registerCC(1, 126_norm, 0.0f));
|
||||
REQUIRE(layer.registerCC(1, 127_norm, 0.0f));
|
||||
REQUIRE(!layer.registerNoteOn(64, 127_norm, 0.5f));
|
||||
}
|
||||
|
||||
|
|
@ -175,9 +193,9 @@ TEST_CASE("Basic triggers", "Region triggers")
|
|||
region.parseOpcode({ "on_hicc1", "127" });
|
||||
region.parseOpcode({ "hikey", "-1" });
|
||||
Layer layer { region, midiState };
|
||||
REQUIRE(!layer.registerCC(1, 126_norm));
|
||||
REQUIRE(!layer.registerCC(2, 127_norm));
|
||||
REQUIRE(layer.registerCC(1, 127_norm));
|
||||
REQUIRE(!layer.registerCC(1, 126_norm, 0.0f));
|
||||
REQUIRE(!layer.registerCC(2, 127_norm, 0.0f));
|
||||
REQUIRE(layer.registerCC(1, 127_norm, 0.0f));
|
||||
REQUIRE(!layer.registerNoteOn(64, 127_norm, 0.5f));
|
||||
}
|
||||
}
|
||||
|
|
@ -371,3 +389,22 @@ TEST_CASE("[Triggers] sw_vel, consider the previous velocity for triggers")
|
|||
REQUIRE(messageList == expected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("[Triggers] Honor lorand/hirand on CC triggers")
|
||||
{
|
||||
Synth synth;
|
||||
sfz::AudioBuffer<float> buffer { 2, static_cast<unsigned>(synth.getSamplesPerBlock()) };
|
||||
std::vector<std::string> messageList;
|
||||
Client client(&messageList);
|
||||
client.setReceiveCallback(&simpleMessageReceiver);
|
||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/sw_vel.sfz", R"(
|
||||
<region> sample=*sine hikey=-1 start_locc64=63 start_hicc64=127 lorand=0 hirand=0.5
|
||||
<region> sample=*saw hikey=-1 start_locc64=63 start_hicc64=127 lorand=0.5 hirand=1
|
||||
)");
|
||||
|
||||
synth.hdcc(0, 64, 10_norm);
|
||||
REQUIRE(numPlayingVoices(synth) == 0);
|
||||
synth.hdcc(0, 64, 100_norm);
|
||||
REQUIRE(numPlayingVoices(synth) == 1);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue