Let the flex eg free-run when set as the ampeg

This commit is contained in:
Paul Ferrand 2020-10-28 09:17:10 +01:00
parent b3eb8e47ca
commit 84019a00e4
4 changed files with 89 additions and 8 deletions

View file

@ -45,6 +45,7 @@ struct FlexEnvelope::Impl {
float currentTime_ { 0.0 };
absl::optional<size_t> currentFramesUntilRelease_ { absl::nullopt };
bool isReleased_ { false };
bool freeRunning_ { false };
//
void process(absl::Span<float> out);
@ -70,6 +71,15 @@ void FlexEnvelope::configure(const FlexEGDescription* desc)
{
Impl& impl = *impl_;
impl.desc_ = desc;
//
impl.freeRunning_ = false;
impl.isReleased_ = false;
//
impl.currentStageNumber_ = 0;
impl.currentLevel_ = 0.0;
impl.currentTime_ = 0.0;
}
void FlexEnvelope::start(unsigned triggerDelay)
@ -90,12 +100,12 @@ void FlexEnvelope::start(unsigned triggerDelay)
impl.stageSustained_ = desc.sustain == 0;
impl.stageCurve_ = &point.curve();
impl.currentFramesUntilRelease_ = absl::nullopt;
impl.isReleased_ = false;
}
//
impl.currentStageNumber_ = 0;
impl.currentLevel_ = 0.0;
impl.currentTime_ = 0.0;
void FlexEnvelope::setFreeRunning(bool freeRunning)
{
Impl& impl = *impl_;
impl.freeRunning_ = freeRunning;
}
void FlexEnvelope::release(unsigned releaseDelay)
@ -134,7 +144,6 @@ void FlexEnvelope::Impl::process(absl::Span<float> out)
const FlexEGDescription& desc = *desc_;
size_t numFrames = out.size();
const float samplePeriod = samplePeriod_;
// Skip the initial delay, for frame-accurate trigger
size_t skipFrames = std::min(numFrames, delayFramesLeft_);
if (skipFrames > 0) {
@ -171,9 +180,9 @@ void FlexEnvelope::Impl::process(absl::Span<float> out)
}
}
}
while (!stageSustained_ && currentTime_ >= stageTime_) {
while ((!stageSustained_ || freeRunning_) && currentTime_ >= stageTime_) {
// advance through completed timed stages
ASSERT(isReleased_ || !stageSustained_);
ASSERT(isReleased_ || !stageSustained_ || freeRunning_);
if (stageTime_ == 0) {
// if stage is of zero duration, immediate transition to level
currentLevel_ = stageTargetLevel_;

View file

@ -30,6 +30,13 @@ public:
*/
void configure(const FlexEGDescription* desc);
/**
* @brief Set the EG to be freeRunning or not
*
* @param freeRunning
*/
void setFreeRunning(bool freeRunning);
/**
Start processing an EG as a region is triggered.
*/

View file

@ -38,6 +38,12 @@ void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId,
FlexEnvelope* eg = voice->getFlexEG(egIndex);
eg->configure(&region->flexEGs[egIndex]);
bool freeRunning = (
(region->loopMode == SfzLoopMode::one_shot && region->isOscillator())
);
if (freeRunning && region->flexAmpEG && egIndex == *region->flexAmpEG)
eg->setFreeRunning(true);
eg->start(delay);
}

View file

@ -6,6 +6,7 @@
#include "sfizz/Synth.h"
#include "sfizz/AudioBuffer.h"
#include "sfizz/FlexEnvelope.h"
#include "catch2/catch.hpp"
#include "TestHelpers.h"
@ -357,3 +358,61 @@ TEST_CASE("[FlexEG] Early release")
}
}
}
TEST_CASE("[FlexEG] Free-running flex AmpEG (no sustain)")
{
sfz::Synth synth;
synth.loadSfzString(fs::current_path(), R"(
<region> sample=*noise
key=60
loop_mode=one_shot
eg1_ampeg=1
eg1_time1=0 eg1_level1=1
eg1_time2=0.03 eg1_level2=0.6
eg1_time3=0.06 eg1_level3=0.3
eg1_time4=0.12 eg1_level4=0.1
eg1_time5=0.3 eg1_level5=0
<region> sample=*noise
key=62
loop_mode=one_shot
eg1_ampeg=1
eg1_time1=0 eg1_level1=1
eg1_time2=0.03 eg1_level2=0.6
eg1_time3=0.06 eg1_level3=0.3
eg1_time4=0.12 eg1_level4=0.1
eg1_time5=0.3 eg1_level5=0 eg1_sustain=5
<region> sample=*noise
key=64
eg1_ampeg=1
eg1_time1=0 eg1_level1=1
eg1_time2=0.03 eg1_level2=0.6
eg1_time3=0.06 eg1_level3=0.3
eg1_time4=0.12 eg1_level4=0.1
eg1_time5=0.3 eg1_level5=0 eg1_sustain=5
)");
synth.noteOn(0, 60, 0);
sfz::AudioBuffer<float> buffer { 2, 256 };
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 1 );
for (unsigned i = 0; i < 100; ++i)
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 0 );
synth.noteOn(0, 62, 0);
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 1 );
for (unsigned i = 0; i < 100; ++i)
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 0 );
synth.noteOn(0, 64, 0);
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 1 );
for (unsigned i = 0; i < 100; ++i)
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 1 );
synth.noteOff(0, 64, 0); // the release stage is 0 duration
synth.renderBlock(buffer);
REQUIRE( synth.getNumActiveVoices(true) == 0 );
}