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->peak = 1.0;
|
||||
this->sustain = normalizePercents(desc.getSustain(state, velocity));
|
||||
this->sustain = max(this->sustain, config::virtuallyZero);
|
||||
this->start = this->peak * normalizePercents(desc.getStart(state, velocity));
|
||||
|
||||
releaseDelay = 0;
|
||||
sustainThreshold = this->sustain + config::virtuallyZero;
|
||||
shouldRelease = false;
|
||||
freeRunning = ((region.trigger == SfzTrigger::release)
|
||||
freeRunning = (
|
||||
(region.trigger == SfzTrigger::release)
|
||||
|| (this->sustain == 0.0f)
|
||||
|| (region.trigger == SfzTrigger::release_key)
|
||||
|| (region.loopMode == SfzLoopMode::one_shot && (region.isGenerator() || region.oscillator)));
|
||||
currentValue = this->start;
|
||||
|
|
@ -89,7 +91,7 @@ Type ADSREnvelope<Type>::getNextValue() noexcept
|
|||
// fallthrough
|
||||
case State::Decay:
|
||||
currentValue *= decayRate;
|
||||
if (currentValue > sustain)
|
||||
if (currentValue > sustainThreshold )
|
||||
return currentValue;
|
||||
|
||||
currentState = State::Sustain;
|
||||
|
|
@ -159,7 +161,7 @@ void ADSREnvelope<Type>::getBlock(absl::Span<Type> output) noexcept
|
|||
case State::Decay:
|
||||
while (count < size && (currentValue *= decayRate) > sustain)
|
||||
output[count++] = currentValue;
|
||||
if (currentValue <= sustain) {
|
||||
if (currentValue <= sustainThreshold) {
|
||||
currentValue = sustain;
|
||||
currentState = State::Sustain;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ private:
|
|||
Type start { 0 };
|
||||
Type peak { 0 };
|
||||
Type sustain { 0 };
|
||||
Type sustainThreshold { config::virtuallyZero };
|
||||
int releaseDelay { 0 };
|
||||
bool shouldRelease { 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 );
|
||||
}
|
||||
|
||||
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")
|
||||
{
|
||||
sfz::Synth synth;
|
||||
|
|
@ -1222,4 +1271,3 @@ TEST_CASE("[Synth] Off by same note")
|
|||
auto playingVoices = getPlayingVoices(synth);
|
||||
REQUIRE( playingVoices.front()->getRegion()->sampleId.filename() == "*triangle" );
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue