WIP on the region state
This commit is contained in:
parent
ed09e30fcf
commit
8539508e0a
7 changed files with 156 additions and 34 deletions
|
|
@ -183,6 +183,11 @@ namespace config {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
constexpr int playheadMovedFrames { 16 };
|
constexpr int playheadMovedFrames { 16 };
|
||||||
|
/**
|
||||||
|
* @brief Max number of voices to start on release pedal up
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constexpr unsigned delayedReleaseVoices { 16 };
|
||||||
} // namespace config
|
} // namespace config
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Macros.h"
|
#include "Macros.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "Opcode.h"
|
#include "Opcode.h"
|
||||||
|
#include "SwapAndPop.h"
|
||||||
#include "StringViewHelpers.h"
|
#include "StringViewHelpers.h"
|
||||||
#include "ModifierHelpers.h"
|
#include "ModifierHelpers.h"
|
||||||
#include "modulations/ModId.h"
|
#include "modulations/ModId.h"
|
||||||
|
|
@ -1503,6 +1504,29 @@ bool sfz::Region::isSwitchedOn() const noexcept
|
||||||
return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all();
|
return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && ccSwitched.all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sfz::Region::delaySustainRelease(int noteNumber, float velocity)
|
||||||
|
{
|
||||||
|
if (delayedSustainReleases.size() == delayedSustainReleases.capacity())
|
||||||
|
return;
|
||||||
|
|
||||||
|
delayedSustainReleases.emplace_back(noteNumber, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::Region::delaySostenutoRelease(int noteNumber, float velocity)
|
||||||
|
{
|
||||||
|
if (delayedSostenutoReleases.size() == delayedSostenutoReleases.capacity())
|
||||||
|
return;
|
||||||
|
|
||||||
|
delayedSostenutoReleases.emplace_back(noteNumber, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sfz::Region::removeFromSostenutoReleases(int noteNumber)
|
||||||
|
{
|
||||||
|
swapAndPopFirst(delayedSostenutoReleases, [=](const std::pair<int, float>& p) {
|
||||||
|
return p.first == noteNumber;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue) noexcept
|
bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue) noexcept
|
||||||
{
|
{
|
||||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||||
|
|
@ -1529,6 +1553,13 @@ bool sfz::Region::registerNoteOn(int noteNumber, float velocity, float randValue
|
||||||
const bool attackTrigger = (trigger == Trigger::attack);
|
const bool attackTrigger = (trigger == Trigger::attack);
|
||||||
const bool notFirstLegatoNote = (trigger == Trigger::legato && midiState.getActiveNotes() > 1);
|
const bool notFirstLegatoNote = (trigger == Trigger::legato && midiState.getActiveNotes() > 1);
|
||||||
|
|
||||||
|
if (trigger == Trigger::release &&
|
||||||
|
keyOk && velOk
|
||||||
|
&& checkSostenuto && midiState.getCCValue(sostenutoCC) < sostenutoThreshold) {
|
||||||
|
// This note on will possibly be "sostenutoed"
|
||||||
|
delaySostenutoRelease(noteNumber, velocity);
|
||||||
|
}
|
||||||
|
|
||||||
return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
|
return keyOk && velOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1557,13 +1588,22 @@ bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValu
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (trigger == Trigger::release) {
|
if (trigger == Trigger::release) {
|
||||||
if (midiState.getCCValue(sustainCC) < sustainThreshold)
|
if (checkSostenuto && midiState.getCCValue(sostenutoCC) < sostenutoThreshold)
|
||||||
|
removeFromSostenutoReleases(noteNumber);
|
||||||
|
|
||||||
|
const bool shouldSustain = checkSustain && midiState.getCCValue(sustainCC) >= sustainThreshold;
|
||||||
|
const bool shouldSostenuto =
|
||||||
|
checkSostenuto && midiState.getCCValue(sostenutoCC) >= sostenutoThreshold
|
||||||
|
&& absl::c_find_if(delayedSostenutoReleases, [=](const std::pair<int, float>& p) {
|
||||||
|
return p.first == noteNumber;
|
||||||
|
}) != delayedSostenutoReleases.end();
|
||||||
|
|
||||||
|
if (!shouldSustain && !shouldSostenuto)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If we reach this part, we're storing the notes to delay their release on CC up
|
// If we reach this part, we're storing the notes to delay their release on CC up
|
||||||
// This is handled by the Synth object
|
// This is handled by the Synth object
|
||||||
|
delaySustainRelease(noteNumber, midiState.getNoteVelocity(noteNumber));
|
||||||
delayedReleases.emplace_back(noteNumber, midiState.getNoteVelocity(noteNumber));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -508,7 +508,11 @@ struct Region {
|
||||||
RegionSet* parent { nullptr };
|
RegionSet* parent { nullptr };
|
||||||
|
|
||||||
// Started notes
|
// Started notes
|
||||||
std::vector<std::pair<int, float>> delayedReleases;
|
std::vector<std::pair<int, float>> delayedSustainReleases;
|
||||||
|
std::vector<std::pair<int, float>> delayedSostenutoReleases;
|
||||||
|
void delaySustainRelease(int noteNumber, float velocity);
|
||||||
|
void delaySostenutoRelease(int noteNumber, float velocity);
|
||||||
|
void removeFromSostenutoReleases(int noteNumber);
|
||||||
|
|
||||||
const MidiState& midiState;
|
const MidiState& midiState;
|
||||||
bool keySwitched { true };
|
bool keySwitched { true };
|
||||||
|
|
|
||||||
|
|
@ -205,7 +205,12 @@ void Synth::Impl::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapt the size of the delayed releases to avoid allocating later on
|
// Adapt the size of the delayed releases to avoid allocating later on
|
||||||
lastRegion->delayedReleases.reserve(lastRegion->keyRange.length());
|
if (lastRegion->trigger == Trigger::release) {
|
||||||
|
const auto keyLength = static_cast<unsigned>(lastRegion->keyRange.length());
|
||||||
|
const auto size = max(config::delayedReleaseVoices, keyLength);
|
||||||
|
lastRegion->delayedSustainReleases.reserve(size);
|
||||||
|
lastRegion->delayedSostenutoReleases.reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
regions_.push_back(std::move(lastRegion));
|
regions_.push_back(std::move(lastRegion));
|
||||||
}
|
}
|
||||||
|
|
@ -1147,20 +1152,33 @@ void Synth::Impl::noteOnDispatch(int delay, int noteNumber, float velocity) noex
|
||||||
region->previousKeySwitched = (*region->previousKeyswitch == noteNumber);
|
region->previousKeySwitched = (*region->previousKeyswitch == noteNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Synth::Impl::startDelayedReleaseVoices(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept
|
void Synth::Impl::startDelayedSustainReleases(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept
|
||||||
{
|
{
|
||||||
if (!region->rtDead && !voiceManager_.playingAttackVoice(region)) {
|
if (!region->rtDead && !voiceManager_.playingAttackVoice(region)) {
|
||||||
region->delayedReleases.clear();
|
region->delayedSustainReleases.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& note: region->delayedReleases) {
|
for (auto& note: region->delayedSustainReleases) {
|
||||||
const TriggerEvent noteOffEvent { TriggerEventType::NoteOff, note.first, note.second };
|
const TriggerEvent noteOffEvent { TriggerEventType::NoteOff, note.first, note.second };
|
||||||
startVoice(region, delay, noteOffEvent, ring);
|
startVoice(region, delay, noteOffEvent, ring);
|
||||||
}
|
}
|
||||||
region->delayedReleases.clear();
|
region->delayedSustainReleases.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Synth::Impl::startDelayedSostenutoReleases(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept
|
||||||
|
{
|
||||||
|
if (!region->rtDead && !voiceManager_.playingAttackVoice(region)) {
|
||||||
|
region->delayedSostenutoReleases.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& note: region->delayedSostenutoReleases) {
|
||||||
|
const TriggerEvent noteOffEvent { TriggerEventType::NoteOff, note.first, note.second };
|
||||||
|
startVoice(region, delay, noteOffEvent, ring);
|
||||||
|
}
|
||||||
|
region->delayedSostenutoReleases.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
|
void Synth::cc(int delay, int ccNumber, uint8_t ccValue) noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -1173,8 +1191,11 @@ void Synth::Impl::ccDispatch(int delay, int ccNumber, float value) noexcept
|
||||||
SisterVoiceRingBuilder ring;
|
SisterVoiceRingBuilder ring;
|
||||||
const TriggerEvent triggerEvent { TriggerEventType::CC, ccNumber, value };
|
const TriggerEvent triggerEvent { TriggerEventType::CC, ccNumber, value };
|
||||||
for (auto& region : ccActivationLists_[ccNumber]) {
|
for (auto& region : ccActivationLists_[ccNumber]) {
|
||||||
if (ccNumber == region->sustainCC)
|
if (ccNumber == region->sustainCC && value < region->sustainThreshold)
|
||||||
startDelayedReleaseVoices(region, delay, ring);
|
startDelayedSustainReleases(region, delay, ring);
|
||||||
|
|
||||||
|
if (ccNumber == region->sostenutoCC && value < region->sostenutoThreshold)
|
||||||
|
startDelayedSostenutoReleases(region, delay, ring);
|
||||||
|
|
||||||
if (region->registerCC(ccNumber, value))
|
if (region->registerCC(ccNumber, value))
|
||||||
startVoice(region, delay, triggerEvent, ring);
|
startVoice(region, delay, triggerEvent, ring);
|
||||||
|
|
|
||||||
|
|
@ -155,13 +155,22 @@ struct Synth::Impl final: public Parser::Listener {
|
||||||
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 Start all delayed release voices of the region if necessary
|
* @brief Start all delayed sustain release voices of the region if necessary
|
||||||
*
|
*
|
||||||
* @param region
|
* @param region
|
||||||
* @param delay
|
* @param delay
|
||||||
* @param ring
|
* @param ring
|
||||||
*/
|
*/
|
||||||
void startDelayedReleaseVoices(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept;
|
void startDelayedSustainReleases(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start all delayed sostenuto release voices of the region if necessary
|
||||||
|
*
|
||||||
|
* @param region
|
||||||
|
* @param delay
|
||||||
|
* @param ring
|
||||||
|
*/
|
||||||
|
void startDelayedSostenutoReleases(Region* region, int delay, SisterVoiceRingBuilder& ring) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Finalize SFZ loading, following a successful execution of the
|
* @brief Finalize SFZ loading, following a successful execution of the
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
||||||
region.parseOpcode({ "lokey", "63" });
|
region.parseOpcode({ "lokey", "63" });
|
||||||
region.parseOpcode({ "hikey", "65" });
|
region.parseOpcode({ "hikey", "65" });
|
||||||
region.parseOpcode({ "sample", "*sine" });
|
region.parseOpcode({ "sample", "*sine" });
|
||||||
|
region.delayedSustainReleases.reserve(config::delayedReleaseVoices);
|
||||||
SECTION("Release key without sustain")
|
SECTION("Release key without sustain")
|
||||||
{
|
{
|
||||||
region.parseOpcode({ "trigger", "release_key" });
|
region.parseOpcode({ "trigger", "release_key" });
|
||||||
|
|
@ -68,11 +69,11 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
||||||
midiState.noteOnEvent(0, 63, 0.5f);
|
midiState.noteOnEvent(0, 63, 0.5f);
|
||||||
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
|
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
|
||||||
REQUIRE( !region.registerNoteOff(63, 0.5f, 0.0f) );
|
REQUIRE( !region.registerNoteOff(63, 0.5f, 0.0f) );
|
||||||
REQUIRE( region.delayedReleases.size() == 1 );
|
REQUIRE( region.delayedSustainReleases.size() == 1 );
|
||||||
std::vector<std::pair<int, float>> expected = {
|
std::vector<std::pair<int, float>> expected = {
|
||||||
{ 63, 0.5f }
|
{ 63, 0.5f }
|
||||||
};
|
};
|
||||||
REQUIRE( region.delayedReleases == expected );
|
REQUIRE( region.delayedSustainReleases == expected );
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Release with sustain and 2 notes")
|
SECTION("Release with sustain and 2 notes")
|
||||||
|
|
@ -85,12 +86,12 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
||||||
REQUIRE( !region.registerNoteOn(64, 0.6f, 0.0f) );
|
REQUIRE( !region.registerNoteOn(64, 0.6f, 0.0f) );
|
||||||
REQUIRE( !region.registerNoteOff(63, 0.0f, 0.0f) );
|
REQUIRE( !region.registerNoteOff(63, 0.0f, 0.0f) );
|
||||||
REQUIRE( !region.registerNoteOff(64, 0.2f, 0.0f) );
|
REQUIRE( !region.registerNoteOff(64, 0.2f, 0.0f) );
|
||||||
REQUIRE( region.delayedReleases.size() == 2 );
|
REQUIRE( region.delayedSustainReleases.size() == 2 );
|
||||||
std::vector<std::pair<int, float>> expected = {
|
std::vector<std::pair<int, float>> expected = {
|
||||||
{ 63, 0.5f },
|
{ 63, 0.5f },
|
||||||
{ 64, 0.6f }
|
{ 64, 0.6f }
|
||||||
};
|
};
|
||||||
REQUIRE( region.delayedReleases == expected );
|
REQUIRE( region.delayedSustainReleases == expected );
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("Release with sustain and 2 notes but 1 outside")
|
SECTION("Release with sustain and 2 notes but 1 outside")
|
||||||
|
|
@ -103,10 +104,10 @@ TEST_CASE("[Direct Region Tests] Release and release key")
|
||||||
REQUIRE( !region.registerNoteOn(66, 0.6f, 0.0f) );
|
REQUIRE( !region.registerNoteOn(66, 0.6f, 0.0f) );
|
||||||
REQUIRE( !region.registerNoteOff(63, 0.0f, 0.0f) );
|
REQUIRE( !region.registerNoteOff(63, 0.0f, 0.0f) );
|
||||||
REQUIRE( !region.registerNoteOff(66, 0.2f, 0.0f) );
|
REQUIRE( !region.registerNoteOff(66, 0.2f, 0.0f) );
|
||||||
REQUIRE( region.delayedReleases.size() == 1 );
|
REQUIRE( region.delayedSustainReleases.size() == 1 );
|
||||||
std::vector<std::pair<int, float>> expected = {
|
std::vector<std::pair<int, float>> expected = {
|
||||||
{ 63, 0.5f }
|
{ 63, 0.5f }
|
||||||
};
|
};
|
||||||
REQUIRE( region.delayedReleases == expected );
|
REQUIRE( region.delayedSustainReleases == expected );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -771,8 +771,6 @@ TEST_CASE("[Synth] Release (pedal was already down)")
|
||||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("[Synth] Release samples don't play unless there is another playing region that matches")
|
TEST_CASE("[Synth] Release samples don't play unless there is another playing region that matches")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
@ -793,7 +791,7 @@ TEST_CASE("[Synth] Release key (Different sustain CC)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"(
|
||||||
<global>sustain_cc=54
|
<global> sustain_cc=54
|
||||||
<region> key=62 sample=*sine trigger=release_key
|
<region> key=62 sample=*sine trigger=release_key
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 62, 85);
|
synth.noteOn(0, 62, 85);
|
||||||
|
|
@ -806,7 +804,7 @@ TEST_CASE("[Synth] Release (Different sustain CC)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"(
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"(
|
||||||
<global>sustain_cc=54
|
<global> sustain_cc=54
|
||||||
<region> key=62 sample=*silence
|
<region> key=62 sample=*silence
|
||||||
<region> key=62 sample=*sine trigger=release
|
<region> key=62 sample=*sine trigger=release
|
||||||
)");
|
)");
|
||||||
|
|
@ -818,6 +816,20 @@ TEST_CASE("[Synth] Release (Different sustain CC)")
|
||||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Synth] Release (don't check sustain)")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"(
|
||||||
|
<global>sustain_cc=54 sustain_sw=off
|
||||||
|
<region> key=62 sample=*silence
|
||||||
|
<region> key=62 sample=*sine trigger=release
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 62, 85);
|
||||||
|
synth.cc(0, 54, 127);
|
||||||
|
synth.noteOff(0, 62, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Release key (Different sostenuto CC)")
|
TEST_CASE("[Synth] Release key (Different sostenuto CC)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
@ -831,6 +843,20 @@ TEST_CASE("[Synth] Release key (Different sostenuto CC)")
|
||||||
REQUIRE( synth.getNumActiveVoices() == 1 );
|
REQUIRE( synth.getNumActiveVoices() == 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Synth] Release (don't check sostenuto)")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/release.sfz", R"(
|
||||||
|
<global> sostenuto_cc=54 sostenuto_sw=off
|
||||||
|
<region> key=62 sample=*silence
|
||||||
|
<region> key=62 sample=*sine trigger=release
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 62, 85);
|
||||||
|
synth.cc(0, 54, 127);
|
||||||
|
synth.noteOff(0, 62, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Release (Different sostenuto CC)")
|
TEST_CASE("[Synth] Release (Different sostenuto CC)")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
@ -838,13 +864,29 @@ TEST_CASE("[Synth] Release (Different sostenuto CC)")
|
||||||
<global> sostenuto_cc=54
|
<global> sostenuto_cc=54
|
||||||
<region> key=62 sample=*silence
|
<region> key=62 sample=*silence
|
||||||
<region> key=62 sample=*sine trigger=release
|
<region> key=62 sample=*sine trigger=release
|
||||||
|
<region> key=64 sample=*silence
|
||||||
|
<region> key=64 sample=*sine trigger=release
|
||||||
)");
|
)");
|
||||||
synth.noteOn(0, 62, 85);
|
SECTION("One note with sostenuto")
|
||||||
synth.cc(1, 54, 127);
|
{
|
||||||
synth.noteOff(2, 62, 85);
|
synth.noteOn(0, 62, 85);
|
||||||
REQUIRE( synth.getNumActiveVoices() == 1 );
|
synth.cc(1, 54, 127);
|
||||||
synth.cc(3, 54, 0);
|
synth.noteOff(2, 62, 85);
|
||||||
REQUIRE( synth.getNumActiveVoices() == 2 );
|
REQUIRE( synth.getNumActiveVoices() == 1 );
|
||||||
|
synth.cc(3, 54, 0);
|
||||||
|
REQUIRE( synth.getNumActiveVoices() == 2 );
|
||||||
|
}
|
||||||
|
SECTION("Two notes, only one with sostenuto")
|
||||||
|
{
|
||||||
|
synth.noteOn(0, 62, 85);
|
||||||
|
synth.cc(1, 54, 127);
|
||||||
|
synth.noteOn(2, 64, 85);
|
||||||
|
synth.noteOff(3, 62, 85);
|
||||||
|
synth.noteOff(3, 64, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices() == 3 );
|
||||||
|
synth.cc(4, 54, 0);
|
||||||
|
REQUIRE( synth.getNumActiveVoices() == 4 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Sustain threshold default")
|
TEST_CASE("[Synth] Sustain threshold default")
|
||||||
|
|
@ -1034,7 +1076,7 @@ TEST_CASE("[Synth] Release (Multiple notes, release, cleared the delayed voices
|
||||||
sortAll(requiredVelocities, actualVelocities);
|
sortAll(requiredVelocities, actualVelocities);
|
||||||
REQUIRE( requiredVelocities == actualVelocities );
|
REQUIRE( requiredVelocities == actualVelocities );
|
||||||
|
|
||||||
REQUIRE( synth.getRegionView(1)->delayedReleases.empty() );
|
REQUIRE( synth.getRegionView(1)->delayedSustainReleases.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Release (Multiple notes after pedal is down, release, cleared the delayed voices after)")
|
TEST_CASE("[Synth] Release (Multiple notes after pedal is down, release, cleared the delayed voices after)")
|
||||||
|
|
@ -1064,7 +1106,7 @@ TEST_CASE("[Synth] Release (Multiple notes after pedal is down, release, cleared
|
||||||
sortAll(requiredVelocities, actualVelocities);
|
sortAll(requiredVelocities, actualVelocities);
|
||||||
REQUIRE( requiredVelocities == actualVelocities );
|
REQUIRE( requiredVelocities == actualVelocities );
|
||||||
|
|
||||||
REQUIRE( synth.getRegionView(1)->delayedReleases.empty() );
|
REQUIRE( synth.getRegionView(1)->delayedSustainReleases.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Release (Multiple note ons during pedal down)")
|
TEST_CASE("[Synth] Release (Multiple note ons during pedal down)")
|
||||||
|
|
@ -1091,7 +1133,7 @@ TEST_CASE("[Synth] Release (Multiple note ons during pedal down)")
|
||||||
}
|
}
|
||||||
sortAll(requiredVelocities, actualVelocities);
|
sortAll(requiredVelocities, actualVelocities);
|
||||||
REQUIRE( requiredVelocities == actualVelocities );
|
REQUIRE( requiredVelocities == actualVelocities );
|
||||||
REQUIRE( synth.getRegionView(1)->delayedReleases.empty() );
|
REQUIRE( synth.getRegionView(1)->delayedSustainReleases.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] No release sample after the main sample stopped sounding by default")
|
TEST_CASE("[Synth] No release sample after the main sample stopped sounding by default")
|
||||||
|
|
@ -1126,7 +1168,7 @@ TEST_CASE("[Synth] No release sample after the main sample stopped sounding by d
|
||||||
synth.cc(0, 64, 0);
|
synth.cc(0, 64, 0);
|
||||||
REQUIRE( synth.getNumActiveVoices() == 0 );
|
REQUIRE( synth.getNumActiveVoices() == 0 );
|
||||||
|
|
||||||
REQUIRE( synth.getRegionView(1)->delayedReleases.empty() );
|
REQUIRE( synth.getRegionView(1)->delayedSustainReleases.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] If rt_dead is active the release sample can sound after the attack sample died")
|
TEST_CASE("[Synth] If rt_dead is active the release sample can sound after the attack sample died")
|
||||||
|
|
@ -1161,7 +1203,7 @@ TEST_CASE("[Synth] If rt_dead is active the release sample can sound after the a
|
||||||
synth.cc(0, 64, 0);
|
synth.cc(0, 64, 0);
|
||||||
REQUIRE( synth.getNumActiveVoices() == 0 );
|
REQUIRE( synth.getNumActiveVoices() == 0 );
|
||||||
|
|
||||||
REQUIRE( synth.getRegionView(1)->delayedReleases.empty() );
|
REQUIRE( synth.getRegionView(1)->delayedSustainReleases.empty() );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] sw_default works at a global level")
|
TEST_CASE("[Synth] sw_default works at a global level")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue