Skip to content

Commit

Permalink
Merge pull request #997 from paulfd/cc-trigger-rand
Browse files Browse the repository at this point in the history
Honor lorand/hirand in CC triggered regions
  • Loading branch information
paulfd authored Oct 20, 2021
2 parents ba8716d + 6310aa1 commit 213647d
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 57 deletions.
17 changes: 15 additions & 2 deletions src/sfizz/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_;

Expand All @@ -151,8 +151,21 @@ bool Layer::registerCC(int ccNumber, float ccValue, bool dontTrigger) noexcept
ccSwitched_.set(ccNumber, true);
else
ccSwitched_.set(ccNumber, false);
}

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 (dontTrigger || !region.triggerOnCC)
if (!randOk)
return false;

if (auto triggerRange = region.ccTriggers.get(ccNumber)) {
Expand Down
19 changes: 15 additions & 4 deletions src/sfizz/Layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,26 @@ struct Layer {
*/
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.
*
Expand Down
7 changes: 4 additions & 3 deletions src/sfizz/Synth.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}


Expand Down Expand Up @@ -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();

Expand All @@ -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(&region, delay, ccNumber);
startVoice(layer, delay, triggerEvent, ring);
}
Expand Down Expand Up @@ -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]);
}
}

Expand Down
13 changes: 6 additions & 7 deletions tests/DirectRegionT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) );
}
Expand All @@ -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) );
}
Expand All @@ -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) );
}
Expand All @@ -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) );
Expand All @@ -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);
Expand All @@ -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);
Expand Down
38 changes: 19 additions & 19 deletions tests/RegionActivationT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand All @@ -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());
}

Expand All @@ -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());
}

Expand Down
81 changes: 59 additions & 22 deletions tests/RegionTriggersT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")
{
Expand All @@ -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")
{
Expand All @@ -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")
{
Expand All @@ -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")
Expand Down Expand Up @@ -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")
Expand All @@ -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));
}

Expand All @@ -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));
}

Expand All @@ -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));
}
}
Expand Down Expand Up @@ -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);
}

0 comments on commit 213647d

Please sign in to comment.