/************************************************************************************* * Original code copyright (C) 2012 Steve Folta * Converted to Juce module (C) 2016 Leo Olivers * Forked from https://github.com/stevefolta/SFZero * For license info please see the LICENSE file distributed with this source code *************************************************************************************/ #include "SFZEG.h" namespace sfzero { static const float fastReleaseTime = 0.01f; EG::EG() : segment_(), sampleRate_(0), exponentialDecay_(false), level_(0), slope_(0), samplesUntilNextSegment_(0), segmentIsExponential_(false) { } void EG::setExponentialDecay(bool newExponentialDecay) { exponentialDecay_ = newExponentialDecay; } void EG::startNote(const EGParameters *newParameters, float floatVelocity, double newSampleRate, const EGParameters *velMod) { parameters_ = *newParameters; if (velMod) { parameters_.delay += floatVelocity * velMod->delay; parameters_.attack += floatVelocity * velMod->attack; parameters_.hold += floatVelocity * velMod->hold; parameters_.decay += floatVelocity * velMod->decay; parameters_.sustain += floatVelocity * velMod->sustain; if (parameters_.sustain < 0.0) { parameters_.sustain = 0.0; } else if (parameters_.sustain > 100.0) { parameters_.sustain = 100.0; } parameters_.release += floatVelocity * velMod->release; } sampleRate_ = newSampleRate; startDelay(); } void EG::nextSegment() { switch (segment_) { case Delay: startAttack(); break; case Attack: startHold(); break; case Hold: startDecay(); break; case Decay: startSustain(); break; case Sustain: // Shouldn't be called. break; case Release: default: segment_ = Done; break; } } void EG::noteOff() { startRelease(); } void EG::fastRelease() { segment_ = Release; samplesUntilNextSegment_ = static_cast(fastReleaseTime * sampleRate_); slope_ = -level_ / samplesUntilNextSegment_; segmentIsExponential_ = false; } void EG::startDelay() { if (parameters_.delay <= 0) { startAttack(); } else { segment_ = Delay; level_ = 0.0; slope_ = 0.0; samplesUntilNextSegment_ = static_cast(parameters_.delay * sampleRate_); segmentIsExponential_ = false; } } void EG::startAttack() { if (parameters_.attack <= 0) { startHold(); } else { segment_ = Attack; level_ = parameters_.start / 100.0f; samplesUntilNextSegment_ = static_cast(parameters_.attack * sampleRate_); slope_ = 1.0f / samplesUntilNextSegment_; segmentIsExponential_ = false; } } void EG::startHold() { if (parameters_.hold <= 0) { level_ = 1.0; startDecay(); } else { segment_ = Hold; samplesUntilNextSegment_ = static_cast(parameters_.hold * sampleRate_); level_ = 1.0; slope_ = 0.0; segmentIsExponential_ = false; } } void EG::startDecay() { if (parameters_.decay <= 0) { startSustain(); } else { segment_ = Decay; samplesUntilNextSegment_ = static_cast(parameters_.decay * sampleRate_); level_ = 1.0; if (exponentialDecay_) { // I don't truly understand this; just following what LinuxSampler does. float mysterySlope = -9.226f / samplesUntilNextSegment_; slope_ = exp(mysterySlope); segmentIsExponential_ = true; if (parameters_.sustain > 0.0) { // Again, this is following LinuxSampler's example, which is similar to // SF2-style decay, where "decay" specifies the time it would take to // get to zero, not to the sustain level. The SFZ spec is not that // specific about what "decay" means, so perhaps it's really supposed // to specify the time to reach the sustain level. samplesUntilNextSegment_ = static_cast(log((parameters_.sustain / 100.0) / level_) / mysterySlope); if (samplesUntilNextSegment_ <= 0) { startSustain(); } } } else { slope_ = (parameters_.sustain / 100.0f - 1.0f) / samplesUntilNextSegment_; segmentIsExponential_ = false; } } } void EG::startSustain() { if (parameters_.sustain <= 0) { startRelease(); } else { segment_ = Sustain; level_ = parameters_.sustain / 100.0f; slope_ = 0.0; samplesUntilNextSegment_ = 0x7FFFFFFF; segmentIsExponential_ = false; } } void EG::startRelease() { float release = parameters_.release; if (release <= 0) { // Enforce a short release, to prevent clicks. release = fastReleaseTime; } segment_ = Release; samplesUntilNextSegment_ = static_cast(release * sampleRate_); if (exponentialDecay_) { // I don't truly understand this; just following what LinuxSampler does. float mysterySlope = -9.226f / samplesUntilNextSegment_; slope_ = exp(mysterySlope); segmentIsExponential_ = true; } else { slope_ = -level_ / samplesUntilNextSegment_; segmentIsExponential_ = false; } } const float EG::BottomLevel = 0.001f; }