// Copyright 2013 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.gillet@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// 
// See http://creativecommons.org/licenses/MIT/ for more information.
//
// -----------------------------------------------------------------------------
//
// Voice.

#include "yarns/voice.h"

#include <algorithm>
#include <cstdio>
#include <cstdlib>

#include "stmlib/midi/midi.h"
#include "stmlib/utils/dsp.h"
#include "stmlib/utils/random.h"

#include "yarns/resources.h"

namespace yarns {
  
using namespace stmlib;
using namespace stmlib_midi;

const int32_t kOctave = 12 << 7;
const int32_t kMaxNote = 120 << 7;

void Voice::Init(bool reset_calibration) {
  note_ = -1;
  note_source_ = note_target_ = note_portamento_ = 60 << 7;
  gate_ = false;
  
  mod_velocity_ = 0;
  ResetAllControllers();
  
  modulation_rate_ = 0;
  pitch_bend_range_ = 2;
  vibrato_range_ = 0;
  
  lfo_phase_ = portamento_phase_ = 0;
  portamento_phase_increment_ = 1U << 31;
  portamento_exponential_shape_ = false;
  
  trigger_duration_ = 2;
  
  if (reset_calibration) {
    for (uint8_t i = 0; i < kNumOctaves; ++i) {
      calibrated_dac_code_[i] = 54586 - 5133 * i;
    }
  }
  dirty_ = false;
  oscillator_.Init(
    calibrated_dac_code_[3] - calibrated_dac_code_[8],
    calibrated_dac_code_[3]);
}

void Voice::Calibrate(uint16_t* calibrated_dac_code) {
  std::copy(
      &calibrated_dac_code[0],
      &calibrated_dac_code[kNumOctaves],
      &calibrated_dac_code_[0]);
}

inline uint16_t Voice::NoteToDacCode(int32_t note) const {
  if (note <= 0) {
    note = 0;
  }
  if (note >= kMaxNote) {
    note = kMaxNote - 1;
  }
  uint8_t octave = 0;
  while (note >= kOctave) {
    note -= kOctave;
    ++octave;
  }
  
  // Note is now between 0 and kOctave
  // Octave indicates the octave. Look up in the DAC code table.
  int32_t a = calibrated_dac_code_[octave];
  int32_t b = calibrated_dac_code_[octave + 1];
  return a + ((b - a) * note / kOctave);
}

void Voice::ResetAllControllers() {
  mod_pitch_bend_ = 8192;
  mod_wheel_ = 0;
  std::fill(&mod_aux_[0], &mod_aux_[7], 0);
}

void Voice::Refresh() {
  // Compute base pitch with portamento.
  portamento_phase_ += portamento_phase_increment_;
  if (portamento_phase_ < portamento_phase_increment_) {
    portamento_phase_ = 0;
    portamento_phase_increment_ = 0;
    note_source_ = note_target_;
  }
  uint16_t portamento_level = portamento_exponential_shape_
      ? Interpolate824(lut_env_expo, portamento_phase_)
      : portamento_phase_ >> 16;
  int32_t note = note_source_ + \
      ((note_target_ - note_source_) * portamento_level >> 16);

  note_portamento_ = note;
  
  // Add pitch-bend.
  note += static_cast<int32_t>(mod_pitch_bend_ - 8192) * pitch_bend_range_ >> 6;
  
  // Add transposition/fine tuning.
  note += tuning_;
  
  // Add vibrato.
  if (modulation_rate_ < 100) {
    lfo_phase_ += lut_lfo_increments[modulation_rate_];
  } else {
    lfo_phase_ += lfo_pll_phase_increment_;
  }
  int32_t lfo = lfo_phase_ < 1UL << 31
      ?  -32768 + (lfo_phase_ >> 15)
      : 0x17fff - (lfo_phase_ >> 15);
  note += lfo * mod_wheel_ * vibrato_range_ >> 15;
  mod_aux_[0] = mod_velocity_ << 9;
  mod_aux_[1] = mod_wheel_ << 9;
  mod_aux_[5] = static_cast<uint16_t>(mod_pitch_bend_) << 2;
  mod_aux_[6] = (lfo * mod_wheel_ >> 7) + 32768;
  mod_aux_[7] = lfo + 32768;
  
  if (retrigger_delay_) {
    --retrigger_delay_;
  }
  
  if (trigger_pulse_) {
    --trigger_pulse_;
  }
  
  if (trigger_phase_increment_) {
    trigger_phase_ += trigger_phase_increment_;
    if (trigger_phase_ < trigger_phase_increment_) {
      trigger_phase_ = 0;
      trigger_phase_increment_ = 0;
    }
  }
  if (note != note_ || dirty_) {
    note_dac_code_ = NoteToDacCode(note);
    note_ = note;
    dirty_ = false;
  }
}

void Voice::NoteOn(
    int16_t note,
    uint8_t velocity,
    uint8_t portamento,
    bool trigger) {
  note_source_ = note_portamento_;  
  note_target_ = note;
  if (!portamento) {
    note_source_ = note_target_;
  }
  portamento_phase_ = 0;
  if (portamento <= 50) {
    portamento_phase_increment_ = lut_portamento_increments[portamento << 1];
    portamento_exponential_shape_ = true;
  } else {
    uint32_t base_increment = lut_portamento_increments[(portamento - 51) << 1];
    uint32_t delta = abs(note_target_ - note_source_) + 1;
    portamento_phase_increment_ = (1536 * (base_increment >> 11) / delta) << 11;
    CONSTRAIN(portamento_phase_increment_, 1, 2147483647);
    portamento_exponential_shape_ = false;
  }

  mod_velocity_ = velocity;

  if (gate_ && trigger) {
    retrigger_delay_ = 2;
  }
  if (trigger) {
    trigger_pulse_ = trigger_duration_ * 8;
    trigger_phase_ = 0;
    trigger_phase_increment_ = lut_portamento_increments[trigger_duration_];
  }
  gate_ = true;
}

void Voice::NoteOff() {
  gate_ = false;
}

void Voice::ControlChange(uint8_t controller, uint8_t value) {
  switch (controller) {
    case kCCModulationWheelMsb:
      mod_wheel_ = value;
      break;
    
    case kCCBreathController:
      mod_aux_[3] = value << 9;
      break;
      
    case kCCFootPedalMsb:
      mod_aux_[4] = value << 9;
      break;
  }
}

uint16_t Voice::trigger_dac_code() const {
  if (trigger_phase_ <= trigger_phase_increment_) {
    return calibrated_dac_code_[3]; // 0V.
  } else {
    int32_t velocity_coefficient = trigger_scale_ ? mod_velocity_ << 8 : 32768;
    int32_t value = 0;
    switch(trigger_shape_) {
      case TRIGGER_SHAPE_SQUARE:
        value = 32767;
        break;
      case TRIGGER_SHAPE_LINEAR:
        value = 32767 - (trigger_phase_ >> 17);
        break;
      default:
        {
          const int16_t* table = waveform_table[
              trigger_shape_ - TRIGGER_SHAPE_EXPONENTIAL];
          value = Interpolate824(table, trigger_phase_);
        }
        break;
    }
    value = value * velocity_coefficient >> 15;
    int32_t max = calibrated_dac_code_[8];
    int32_t min = calibrated_dac_code_[3];
    return min + ((max - min) * value >> 15);
  }
}

static const uint16_t kHighestNote = 128 * 128;
static const uint16_t kPitchTableStart = 116 * 128;


void Oscillator::Init(int32_t scale, int32_t offset) {
  audio_buffer_.Init();
  phase_ = 0;
  next_sample_ = 0;
  high_ = false;
  scale_ = scale;
  offset_ = offset;
  integrator_state_ = 0;
}

uint32_t Oscillator::ComputePhaseIncrement(int16_t midi_pitch) {
  if (midi_pitch >= kHighestNote) {
    midi_pitch = kHighestNote - 1;
  }

  int32_t ref_pitch = midi_pitch;
  ref_pitch -= kPitchTableStart;

  size_t num_shifts = 0;
  while (ref_pitch < 0) {
    ref_pitch += kOctave;
    ++num_shifts;
  }

  uint32_t a = lut_oscillator_increments[ref_pitch >> 4];
  uint32_t b = lut_oscillator_increments[(ref_pitch >> 4) + 1];
  uint32_t phase_increment = a + \
      (static_cast<int32_t>(b - a) * (ref_pitch & 0xf) >> 4);
  phase_increment >>= num_shifts;
  return phase_increment;
}

void Oscillator::RenderSilence() {
  size_t size = kAudioBlockSize;
  while (size--) {
    audio_buffer_.Overwrite(offset_);
  }
}

void Oscillator::RenderSine(uint32_t phase_increment) {
  size_t size = kAudioBlockSize;
  while (size--) {
    phase_ += phase_increment;
    int32_t sample = Interpolate1022(wav_sine, phase_);
    audio_buffer_.Overwrite(offset_ - (scale_ * sample >> 16));
  }
}

void Oscillator::RenderNoise() {
  size_t size = kAudioBlockSize;
  while (size--) {
    int16_t sample = Random::GetSample();
    audio_buffer_.Overwrite(offset_ - (scale_ * sample >> 16));
  }
}

void Oscillator::RenderSaw(uint32_t phase_increment) {
  uint32_t phase = phase_;
  int32_t next_sample = next_sample_;
  size_t size = kAudioBlockSize;

  while (size--) {
    int32_t this_sample = next_sample;
    next_sample = 0;
    phase += phase_increment;
    if (phase < phase_increment) {
      uint32_t t = phase / (phase_increment >> 16);
      this_sample -= ThisBlepSample(t);
      next_sample -= NextBlepSample(t);
    }
    next_sample += phase >> 17;
    this_sample = (this_sample - 16384) << 1;
    audio_buffer_.Overwrite(offset_ - (scale_ * this_sample >> 16));
  }
  next_sample_ = next_sample;
  phase_ = phase;
}

void Oscillator::RenderSquare(
    uint32_t phase_increment,
    uint32_t pw,
    bool integrate) {
  uint32_t phase = phase_;
  int32_t next_sample = next_sample_;
  int32_t integrator_state = integrator_state_;
  int16_t integrator_coefficient = phase_increment >> 18;
  size_t size = kAudioBlockSize;

  while (size--) {
    int32_t this_sample = next_sample;
    next_sample = 0;
    phase += phase_increment;

    if (!high_) {
      if (phase >= pw) {
        uint32_t t = (phase - pw) / (phase_increment >> 16);
        this_sample += ThisBlepSample(t);
        next_sample += NextBlepSample(t);
        high_ = true;
      }
    }
    if (high_ && (phase < phase_increment)) {
      uint32_t t = phase / (phase_increment >> 16);
      this_sample -= ThisBlepSample(t);
      next_sample -= NextBlepSample(t);
      high_ = false;
    }
    next_sample += phase < pw ? 0 : 32767;
    this_sample = (this_sample - 16384) << 1;
    if (integrate) {
      integrator_state += integrator_coefficient * (this_sample - integrator_state) >> 15;
      this_sample = integrator_state << 3;
    }
    audio_buffer_.Overwrite(offset_ - (scale_ * this_sample >> 16));
  }
  integrator_state_ = integrator_state;
  next_sample_ = next_sample;
  phase_ = phase;
}

void Oscillator::Render(uint8_t mode, int16_t note, bool gate) {
  if (mode == 0 || audio_buffer_.writable() < kAudioBlockSize) {
    return;
  }
  
  if ((mode & 0x80) && !gate) {
    RenderSilence();
    return;
  }
  
  uint32_t phase_increment = ComputePhaseIncrement(note);
  switch ((mode & 0x0f) - 1) {
    case 0:
      RenderSaw(phase_increment);
      break;
    case 1:
      RenderSquare(phase_increment, 0x40000000, false);
      break;
    case 2:
      RenderSquare(phase_increment, 0x80000000, false);
      break;
    case 3:
      RenderSquare(phase_increment, 0x80000000, true);
      break;
    case 4:
      RenderSine(phase_increment);
      break;
    default:
      RenderNoise();
      break;
  }
}

}  // namespace yarns