#define SAMPLES 1024




class Snare {
  public:
    float frequency;
    float base_frequency;
    float frequency_range;
    EnvelopeSimpleLog volume_envelope;
    EnvelopeSimpleLog volume_envelope2;
    EnvelopeSimpleLog pitch_envelope;
    EnvelopeSigh pitch_envelope_sigh;
    LPF lpf;
    HPF hpf;
    float gain = 0.3;
    void setup();
    void update();
    void reset();
    void trigger();
    float render();
    void advanceOscillator();
    void setVolumeDecay(float speed);
    void setPitchDecay(float speed);
    void setPitchAmount(float amount);
    void setFrequency(float frequency);
    void setLpfFrequency(float frequency);
    void setLpfResonance(float resonance);

  private:
    int sinetable[SAMPLES];
    int noisetable[SAMPLES];
    float osc_progress = 0.0;
    float osc_progress2 = 0.0;
};

void Snare::setup() {
  // Fill the sinetable with a sine-wave
  for (int i = 0; i < SAMPLES; i++) {
    this->sinetable[i] = (sin(2 * M_PI * i  / SAMPLES)) * 511;
  }

  // Fill the noise-table with noise
  for (int i = 0; i < SAMPLES; i++) {
    this->noisetable[i] = random(0, 1023);
  }

  // Set some parameters
  this->volume_envelope.amount = 1.0;
  this->hpf.setFrequency(200.0);
  this->volume_envelope.setSnap(0.0005);
  this->volume_envelope2.setSnap(0.0005);
  this->pitch_envelope.setSnap(0.0005);
  // lpf.addFrequency(pitch_envelope.value*100.0 + pitch_envelope_sigh.value*50.0);

}

void Snare::update() {
  this->volume_envelope.update();
  this->pitch_envelope.update();
  this->pitch_envelope_sigh.update();
}

void Snare::reset() {
  this->volume_envelope.reset();
  this->pitch_envelope.reset();
  this->pitch_envelope_sigh.reset();
  this->osc_progress = 0;
}

void Snare::trigger() {
  this->reset();
  this->pitch_envelope_sigh.trigger();
}

float Snare::render() {
  float orig_sample = this->volume_envelope.value * (this->sinetable[(int)this->osc_progress] 
                      + this->pitch_envelope.value * noisetable[(int)this->osc_progress]);
  float filtered_sample = hpf.render(lpf.render(orig_sample));
  float sample = (orig_sample/2 + filtered_sample/2) * this->gain;
  return sample / 2 + 511;
}

void Snare::advanceOscillator() {
  this->osc_progress += this->frequency + this->pitch_envelope.value + this->pitch_envelope_sigh.value;
  if (this->osc_progress > SAMPLES) {
    this->osc_progress = 0;
  }

  this->osc_progress2 += this->frequency/2+ this->pitch_envelope.value*0.97 + this->pitch_envelope_sigh.value*1.2;
  if (this->osc_progress2 > SAMPLES) {
    this->osc_progress2 = 0;
  }
}

void Snare::setVolumeDecay(float speed) {
  // Describes the decay speed of the volume envelopes. Incoming speed
  // variable is assumed to be in the range 0.0 to 1.0
  speed = 0.0001 + speed / 100.0;
  this->volume_envelope.setSpeed(speed);
  this->volume_envelope2.setSpeed(speed*2);
}

void Snare::setPitchDecay(float speed) {
  // Describes the decay speed of the pitch envelope. Incoming speed
  // variable is assumed to be in the range 0.0 to 1.0
  speed = 0.005 + speed / 120.0;
  this->pitch_envelope.setSpeed(speed);
}

void Snare::setPitchAmount(float amount) {
  amount = 2.0 + amount * 8.5;
  this->pitch_envelope.setAmount(amount);
}

void Snare::setFrequency(float frequency) {
  this->frequency = this->base_frequency + frequency * this->frequency_range;
}

void Snare::setLpfFrequency(float frequency) {
  // Set the frequency of the Lowpass filter
  frequency = 150.0 + frequency * 5.0;
  this->lpf.setFrequency(frequency);
  this->gain = map(frequency, 150, 5115, 1.0, 0.05);
}

void Snare::setLpfResonance(float resonance) {
  this->lpf.setResonance(resonance);
}