Use Cakewalk/RGC behavior for self-choking notes

Basically if group=off_by, a note will not choke other voices with the same note number
This commit is contained in:
Paul Ferrand 2020-09-07 23:18:02 +02:00
parent bb621282c9
commit 863a08c421
5 changed files with 33 additions and 27 deletions

View file

@ -1040,16 +1040,6 @@ void sfz::Synth::checkSetPolyphony(const Region* region, int delay) noexcept
}
}
void sfz::Synth::checkOffGroups(Region* region, int delay) noexcept
{
for (auto& voice : voices) {
if (voice->checkOffGroup(delay, region->group)) {
const TriggerEvent& event = voice->getTriggerEvent();
noteOffDispatch(delay, event.number, event.value);
}
}
}
void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept
{
const auto randValue = randNoteDistribution(Random::randomGenerator);
@ -1058,7 +1048,13 @@ void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexc
for (auto& region : noteActivationLists[noteNumber]) {
if (region->registerNoteOn(noteNumber, velocity, randValue)) {
checkOffGroups(region, delay);
for (auto& voice : voices) {
if (voice->checkOffGroup(region, delay, noteNumber)) {
const TriggerEvent& event = voice->getTriggerEvent();
noteOffDispatch(delay, event.number, event.value);
}
}
startVoice(region, delay, triggerEvent, ring);
}
}

View file

@ -848,14 +848,6 @@ private:
*/
void startVoice(Region* region, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept;
/**
* @brief Check the off groups of all playing voices, releasing if necessary
*
* @param region
* @param delay
*/
void checkOffGroups(Region* region, int delay) noexcept;
/**
* @brief Start all delayed release voices of the region if necessary
*

View file

@ -708,12 +708,14 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
#endif
}
bool sfz::Voice::checkOffGroup(int delay, uint32_t group) noexcept
bool sfz::Voice::checkOffGroup(const Region* other, int delay, int noteNumber) noexcept
{
if (region == nullptr)
if (region == nullptr || other == nullptr)
return false;
if (triggerEvent.type == TriggerEventType::NoteOn && region->offBy == group) {
if (triggerEvent.type == TriggerEventType::NoteOn
&& region->offBy == other->group
&& noteNumber != triggerEvent.number) {
off(delay);
return true;
}

View file

@ -163,11 +163,12 @@ public:
* This will trigger the release if true.
*
* @param delay
* @param noteNumber
* @param group
* @return true
* @return false
*/
bool checkOffGroup(int delay, uint32_t group) noexcept;
bool checkOffGroup(const Region* other, int delay, int noteNumber) noexcept;
/**
* @brief Render a block of data for this voice into the span

View file

@ -1256,8 +1256,24 @@ TEST_CASE("[Synth] Off by same group")
REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(60) );
}
TEST_CASE("[Synth] Off by alone and repeated")
{
sfz::Synth synth;
sfz::AudioBuffer<float> buffer { 2, 256 };
TEST_CASE("[Synth] Off by same note")
synth.loadSfzString(fs::current_path(), R"(
<region> group=1 off_by=1 sample=*sine key=60
)");
synth.noteOn(0, 60, 85);
REQUIRE( numPlayingVoices(synth) == 1 );
synth.noteOn(0, 60, 85);
REQUIRE( numPlayingVoices(synth) == 2 );
synth.noteOn(0, 60, 85);
REQUIRE( numPlayingVoices(synth) == 3 );
}
TEST_CASE("[Synth] Off by same note and group")
{
sfz::Synth synth;
sfz::AudioBuffer<float> buffer { 2, 256 };
@ -1267,7 +1283,6 @@ TEST_CASE("[Synth] Off by same note")
<region> group=1 off_by=1 sample=*triangle key=60
)");
synth.noteOn(0, 60, 85);
REQUIRE( numPlayingVoices(synth) == 1 );
auto playingVoices = getPlayingVoices(synth);
REQUIRE( playingVoices.front()->getRegion()->sampleId.filename() == "*triangle" );
REQUIRE( numPlayingVoices(synth) == 2 );
synth.noteOn(0, 60, 85);
}