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:
parent
bb621282c9
commit
863a08c421
5 changed files with 33 additions and 27 deletions
|
|
@ -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
|
void sfz::Synth::noteOnDispatch(int delay, int noteNumber, float velocity) noexcept
|
||||||
{
|
{
|
||||||
const auto randValue = randNoteDistribution(Random::randomGenerator);
|
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]) {
|
for (auto& region : noteActivationLists[noteNumber]) {
|
||||||
if (region->registerNoteOn(noteNumber, velocity, randValue)) {
|
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);
|
startVoice(region, delay, triggerEvent, ring);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -848,14 +848,6 @@ private:
|
||||||
*/
|
*/
|
||||||
void startVoice(Region* region, int delay, const TriggerEvent& triggerEvent, SisterVoiceRingBuilder& ring) noexcept;
|
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
|
* @brief Start all delayed release voices of the region if necessary
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -708,12 +708,14 @@ void sfz::Voice::fillWithGenerator(AudioSpan<float> buffer) noexcept
|
||||||
#endif
|
#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;
|
return false;
|
||||||
|
|
||||||
if (triggerEvent.type == TriggerEventType::NoteOn && region->offBy == group) {
|
if (triggerEvent.type == TriggerEventType::NoteOn
|
||||||
|
&& region->offBy == other->group
|
||||||
|
&& noteNumber != triggerEvent.number) {
|
||||||
off(delay);
|
off(delay);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,11 +163,12 @@ public:
|
||||||
* This will trigger the release if true.
|
* This will trigger the release if true.
|
||||||
*
|
*
|
||||||
* @param delay
|
* @param delay
|
||||||
|
* @param noteNumber
|
||||||
* @param group
|
* @param group
|
||||||
* @return true
|
* @return true
|
||||||
* @return false
|
* @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
|
* @brief Render a block of data for this voice into the span
|
||||||
|
|
|
||||||
|
|
@ -1256,8 +1256,24 @@ TEST_CASE("[Synth] Off by same group")
|
||||||
REQUIRE( playingVoices.front()->getRegion()->keyRange.containsWithEnd(60) );
|
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::Synth synth;
|
||||||
sfz::AudioBuffer<float> buffer { 2, 256 };
|
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
|
<region> group=1 off_by=1 sample=*triangle key=60
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 60, 85);
|
synth.noteOn(0, 60, 85);
|
||||||
REQUIRE( numPlayingVoices(synth) == 1 );
|
REQUIRE( numPlayingVoices(synth) == 2 );
|
||||||
auto playingVoices = getPlayingVoices(synth);
|
synth.noteOn(0, 60, 85);
|
||||||
REQUIRE( playingVoices.front()->getRegion()->sampleId.filename() == "*triangle" );
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue