Support for sustain_lo

This commit is contained in:
Paul Ferrand 2020-07-19 23:09:36 +02:00
parent 0160af3bb7
commit 2d7e81edef
7 changed files with 58 additions and 6 deletions

View file

@ -50,7 +50,6 @@ namespace config {
constexpr int allNotesOffCC { 123 }; constexpr int allNotesOffCC { 123 };
constexpr int omniOffCC { 124 }; constexpr int omniOffCC { 124 };
constexpr int omniOnCC { 125 }; constexpr int omniOnCC { 125 };
constexpr float halfCCThreshold { 0.5f };
constexpr int centPerSemitone { 100 }; constexpr int centPerSemitone { 100 };
constexpr float virtuallyZero { 0.001f }; constexpr float virtuallyZero { 0.001f };
constexpr float fastReleaseDuration { 0.01f }; constexpr float fastReleaseDuration { 0.01f };

View file

@ -57,6 +57,7 @@ namespace Default
// common defaults // common defaults
constexpr Range<uint8_t> midi7Range { 0, 127 }; constexpr Range<uint8_t> midi7Range { 0, 127 };
constexpr Range<float> float7Range { 0.0f, 127.0f };
constexpr Range<float> normalizedRange { 0.0f, 1.0f }; constexpr Range<float> normalizedRange { 0.0f, 1.0f };
constexpr Range<float> symmetricNormalizedRange { -1.0, 1.0 }; constexpr Range<float> symmetricNormalizedRange { -1.0, 1.0 };
@ -216,6 +217,7 @@ namespace Default
constexpr float start { 0.0 }; constexpr float start { 0.0 };
constexpr float sustain { 100.0 }; constexpr float sustain { 100.0 };
constexpr uint16_t sustainCC { 64 }; constexpr uint16_t sustainCC { 64 };
constexpr float sustainThreshold { 0.0039f }; // sforzando default (0.5f/127.0f)
constexpr float vel2sustain { 0.0 }; constexpr float vel2sustain { 0.0 };
constexpr int depth { 0 }; constexpr int depth { 0 };
constexpr Range<float> egTimeRange { 0.0, 100.0 }; constexpr Range<float> egTimeRange { 0.0, 100.0 };

View file

@ -281,6 +281,11 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
case hash("sustain_cc"): case hash("sustain_cc"):
setValueFromOpcode(opcode, sustainCC, Default::ccNumberRange); setValueFromOpcode(opcode, sustainCC, Default::ccNumberRange);
break; break;
case hash("sustain_lo"):
if (auto value = readOpcode(opcode.value, Default::float7Range)) {
sustainThreshold = normalizeCC(*value);
}
break;
case hash("sustain_sw"): case hash("sustain_sw"):
checkSustain = readBooleanFromOpcode(opcode).value_or(Default::checkSustain); checkSustain = readBooleanFromOpcode(opcode).value_or(Default::checkSustain);
break; break;
@ -1038,7 +1043,7 @@ bool sfz::Region::registerNoteOff(int noteNumber, float velocity, float randValu
const bool randOk = randRange.contains(randValue); const bool randOk = randRange.contains(randValue);
bool releaseTrigger = (trigger == SfzTrigger::release_key); bool releaseTrigger = (trigger == SfzTrigger::release_key);
if (trigger == SfzTrigger::release) { if (trigger == SfzTrigger::release) {
if (midiState.getCCValue(sustainCC) < config::halfCCThreshold) if (midiState.getCCValue(sustainCC) < sustainThreshold)
releaseTrigger = true; releaseTrigger = true;
else else
noteIsOff = true; noteIsOff = true;
@ -1057,7 +1062,7 @@ bool sfz::Region::registerCC(int ccNumber, float ccValue) noexcept
if (!isSwitchedOn()) if (!isSwitchedOn())
return false; return false;
if (sustainCC == ccNumber && ccValue < config::halfCCThreshold && noteIsOff) { if (sustainCC == ccNumber && ccValue < sustainThreshold && noteIsOff) {
noteIsOff = false; noteIsOff = false;
return true; return true;
} }

View file

@ -306,6 +306,7 @@ struct Region {
bool checkSustain { Default::checkSustain }; // sustain_sw bool checkSustain { Default::checkSustain }; // sustain_sw
bool checkSostenuto { Default::checkSostenuto }; // sostenuto_sw bool checkSostenuto { Default::checkSostenuto }; // sostenuto_sw
uint16_t sustainCC { Default::sustainCC }; // sustain_cc uint16_t sustainCC { Default::sustainCC }; // sustain_cc
float sustainThreshold { Default::sustainThreshold }; // sustain_cc
// Region logic: internal conditions // Region logic: internal conditions
Range<uint8_t> aftertouchRange { Default::aftertouchRange }; // hichanaft and lochanaft Range<uint8_t> aftertouchRange { Default::aftertouchRange }; // hichanaft and lochanaft
@ -372,7 +373,7 @@ struct Region {
bool triggerOnCC { false }; // whether the region triggers on CC events or note events bool triggerOnCC { false }; // whether the region triggers on CC events or note events
bool triggerOnNote { true }; bool triggerOnNote { true };
// Parent // Parent
RegionSet* parent { nullptr }; RegionSet* parent { nullptr };
private: private:

View file

@ -206,7 +206,7 @@ void sfz::Voice::registerNoteOff(int delay, int noteNumber, float velocity) noex
if (region->loopMode == SfzLoopMode::one_shot) if (region->loopMode == SfzLoopMode::one_shot)
return; return;
if (!region->checkSustain || resources.midiState.getCCValue(region->sustainCC) < config::halfCCThreshold) if (!region->checkSustain || resources.midiState.getCCValue(region->sustainCC) < region->sustainThreshold)
release(delay); release(delay);
} }
} }
@ -220,7 +220,7 @@ void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept
if (state != State::playing) if (state != State::playing)
return; return;
if (region->checkSustain && noteIsOff && ccNumber == region->sustainCC && ccValue < config::halfCCThreshold) if (region->checkSustain && noteIsOff && ccNumber == region->sustainCC && ccValue < region->sustainThreshold)
release(delay); release(delay);
} }

View file

@ -1216,6 +1216,19 @@ TEST_CASE("[Region] Parsing opcodes")
REQUIRE(region.sustainCC == 0); REQUIRE(region.sustainCC == 0);
} }
SECTION("sustain_lo")
{
REQUIRE(region.sustainThreshold == Approx(0.5_norm).margin(1e-3));
region.parseOpcode({ "sustain_lo", "-1" });
REQUIRE(region.sustainThreshold == 0_norm);
region.parseOpcode({ "sustain_lo", "1" });
REQUIRE(region.sustainThreshold == 1_norm);
region.parseOpcode({ "sustain_lo", "63" });
REQUIRE(region.sustainThreshold == 63_norm);
region.parseOpcode({ "sustain_lo", "128" });
REQUIRE(region.sustainThreshold == 127_norm);
}
SECTION("Filter stacking and cutoffs") SECTION("Filter stacking and cutoffs")
{ {
REQUIRE(region.filters.empty()); REQUIRE(region.filters.empty());

View file

@ -663,3 +663,35 @@ TEST_CASE("[Synth] Release (Different sustain CC)")
synth.cc(0, 54, 0); synth.cc(0, 54, 0);
REQUIRE( synth.getNumActiveVoices() == 1 ); REQUIRE( synth.getNumActiveVoices() == 1 );
} }
TEST_CASE("[Synth] Sustain threshold default")
{
sfz::Synth synth;
synth.loadSfzString(fs::current_path(), R"(
<region> key=62 sample=*sine trigger=release
)");
synth.noteOn(0, 62, 85);
synth.cc(0, 64, 1);
synth.noteOff(0, 62, 85);
REQUIRE( synth.getNumActiveVoices() == 0 );
}
TEST_CASE("[Synth] Sustain threshold")
{
sfz::Synth synth;
synth.loadSfzString(fs::current_path(), R"(
<global> sustain_lo=63
<region> key=62 sample=*sine trigger=release
)");
synth.noteOn(0, 62, 85);
synth.cc(0, 64, 1);
synth.noteOff(0, 62, 85);
REQUIRE( synth.getNumActiveVoices() == 1 );
synth.noteOn(0, 62, 85);
synth.noteOff(0, 62, 85);
REQUIRE( synth.getNumActiveVoices() == 2 );
synth.noteOn(0, 62, 85);
synth.cc(0, 64, 64);
synth.noteOff(0, 62, 85);
REQUIRE( synth.getNumActiveVoices() == 2 );
}