text: /*************************************************************************************
   * 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<int>(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<int>(parameters_.delay * sampleRate_);
      segmentIsExponential_ = false;
    }
  }
  
  void EG::startAttack()
  {
    if (parameters_.attack <= 0)
    {
      startHold();
    }
    else
    {
      segment_ = Attack;
      level_ = parameters_.start / 100.0f;
      samplesUntilNextSegment_ = static_cast<int>(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<int>(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<int>(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<int>(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<int>(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;
  
  }
  