Sustain tweaks:
- Separate the sustain value from the sustain threshold after which the envelope goes from decay to sustain - If the sustain value is set to 0, the envelope is freerunning
This commit is contained in:
parent
f6e0bb8b3f
commit
f6f1e68f18
3 changed files with 56 additions and 5 deletions
|
|
@ -48,12 +48,14 @@ void ADSREnvelope<Type>::reset(const EGDescription& desc, const Region& region,
|
||||||
this->hold = secondsToSamples(desc.getHold(state, velocity));
|
this->hold = secondsToSamples(desc.getHold(state, velocity));
|
||||||
this->peak = 1.0;
|
this->peak = 1.0;
|
||||||
this->sustain = normalizePercents(desc.getSustain(state, velocity));
|
this->sustain = normalizePercents(desc.getSustain(state, velocity));
|
||||||
this->sustain = max(this->sustain, config::virtuallyZero);
|
|
||||||
this->start = this->peak * normalizePercents(desc.getStart(state, velocity));
|
this->start = this->peak * normalizePercents(desc.getStart(state, velocity));
|
||||||
|
|
||||||
releaseDelay = 0;
|
releaseDelay = 0;
|
||||||
|
sustainThreshold = this->sustain + config::virtuallyZero;
|
||||||
shouldRelease = false;
|
shouldRelease = false;
|
||||||
freeRunning = ((region.trigger == SfzTrigger::release)
|
freeRunning = (
|
||||||
|
(region.trigger == SfzTrigger::release)
|
||||||
|
|| (this->sustain == 0.0f)
|
||||||
|| (region.trigger == SfzTrigger::release_key)
|
|| (region.trigger == SfzTrigger::release_key)
|
||||||
|| (region.loopMode == SfzLoopMode::one_shot && (region.isGenerator() || region.oscillator)));
|
|| (region.loopMode == SfzLoopMode::one_shot && (region.isGenerator() || region.oscillator)));
|
||||||
currentValue = this->start;
|
currentValue = this->start;
|
||||||
|
|
@ -89,7 +91,7 @@ Type ADSREnvelope<Type>::getNextValue() noexcept
|
||||||
// fallthrough
|
// fallthrough
|
||||||
case State::Decay:
|
case State::Decay:
|
||||||
currentValue *= decayRate;
|
currentValue *= decayRate;
|
||||||
if (currentValue > sustain)
|
if (currentValue > sustainThreshold )
|
||||||
return currentValue;
|
return currentValue;
|
||||||
|
|
||||||
currentState = State::Sustain;
|
currentState = State::Sustain;
|
||||||
|
|
@ -159,7 +161,7 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
|
||||||
case State::Decay:
|
case State::Decay:
|
||||||
while (count < size && (currentValue *= decayRate) > sustain)
|
while (count < size && (currentValue *= decayRate) > sustain)
|
||||||
output[count++] = currentValue;
|
output[count++] = currentValue;
|
||||||
if (currentValue <= sustain) {
|
if (currentValue <= sustainThreshold) {
|
||||||
currentValue = sustain;
|
currentValue = sustain;
|
||||||
currentState = State::Sustain;
|
currentState = State::Sustain;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ private:
|
||||||
Type start { 0 };
|
Type start { 0 };
|
||||||
Type peak { 0 };
|
Type peak { 0 };
|
||||||
Type sustain { 0 };
|
Type sustain { 0 };
|
||||||
|
Type sustainThreshold { config::virtuallyZero };
|
||||||
int releaseDelay { 0 };
|
int releaseDelay { 0 };
|
||||||
bool shouldRelease { false };
|
bool shouldRelease { false };
|
||||||
bool freeRunning { false };
|
bool freeRunning { false };
|
||||||
|
|
|
||||||
|
|
@ -1167,6 +1167,55 @@ TEST_CASE("[Synth] end=-1 voices are immediately killed after triggering but the
|
||||||
REQUIRE( numPlayingVoices(synth) == 0 );
|
REQUIRE( numPlayingVoices(synth) == 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Synth] end=0 voices are immediately killed after triggering but they kill other voices")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, 256 };
|
||||||
|
|
||||||
|
synth.loadSfzString(fs::current_path(), R"(
|
||||||
|
<region> key=60 end=0 sample=*sine
|
||||||
|
<region> key=61 end=0 sample=*silence
|
||||||
|
<region> key=62 sample=*sine off_by=2
|
||||||
|
<region> key=63 end=0 sample=*saw group=2
|
||||||
|
)");
|
||||||
|
synth.noteOn(0, 60, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 0 );
|
||||||
|
synth.noteOn(0, 61, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 0 );
|
||||||
|
synth.noteOn(0, 62, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 1 );
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 1 );
|
||||||
|
synth.noteOn(1, 63, 85);
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( numPlayingVoices(synth) == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Synth] ampeg_sustain = 0 puts the ampeg envelope in free-running mode, which kills the voice almost instantly in most cases")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
sfz::AudioBuffer<float> buffer { 2, 256 };
|
||||||
|
|
||||||
|
synth.loadSfzString(fs::current_path(), R"(
|
||||||
|
<region> key=60 sample=*sine ampeg_sustain=0
|
||||||
|
<region> key=61 sample=*sine ampeg_sustain=0 ampeg_attack=0.1 ampeg_decay=0.1
|
||||||
|
)");
|
||||||
|
|
||||||
|
synth.noteOn(0, 60, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 1 );
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 0 );
|
||||||
|
synth.noteOn(0, 61, 85);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 1 );
|
||||||
|
// Render a bit; this does not kill the voice
|
||||||
|
for (unsigned i = 0; i < 5; ++i)
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 1 );
|
||||||
|
// Render about half a second
|
||||||
|
for (unsigned i = 0; i < 100; ++i)
|
||||||
|
synth.renderBlock(buffer);
|
||||||
|
REQUIRE( synth.getNumActiveVoices(true) == 0 );
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("[Synth] Off by standard")
|
TEST_CASE("[Synth] Off by standard")
|
||||||
{
|
{
|
||||||
sfz::Synth synth;
|
sfz::Synth synth;
|
||||||
|
|
@ -1222,4 +1271,3 @@ TEST_CASE("[Synth] Off by same note")
|
||||||
auto playingVoices = getPlayingVoices(synth);
|
auto playingVoices = getPlayingVoices(synth);
|
||||||
REQUIRE( playingVoices.front()->getRegion()->sampleId.filename() == "*triangle" );
|
REQUIRE( playingVoices.front()->getRegion()->sampleId.filename() == "*triangle" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue