// 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.
//
// -----------------------------------------------------------------------------
//
// Number station.

#include "peaks/number_station/number_station.h"

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

#include "peaks/resources.h"

namespace peaks {

const uint8_t kDownsample = 4;

using namespace stmlib;

void NumberStation::Init() {
  tone_amplitude_ = 0;
  phase_ = 0;
  lp_.Init();
  lp_.set_frequency(120 << 7);
  lp_.set_resonance(16000);

  hp_.Init();
  hp_.set_frequency(70 << 7);
  hp_.set_resonance(8000);

  previous_inner_sample_ = 0;
  previous_outer_sample_ = 0;
}

size_t voice_digits[] = {
  0, 4913, 7830, 11306, 14601, 18651, 22308, 26438, 30495, 33296, 36801
};

void NumberStation::Process(
    const GateFlags* gate_flags, int16_t* out, size_t size) {
  uint32_t phase_increment;

  if (voice_) {
    phase_increment = pitch_shift_;
  } else {
    uint16_t frequency = tone_;
    int32_t a = lut_lfo_increments[frequency >> 8];
    int32_t b = lut_lfo_increments[(frequency >> 8) + 1];
    phase_increment = a + (((b - a) >> 1) * (frequency & 0xff) >> 7);
    phase_increment <<= 6;
    phase_increment *= digit_ + 1;
  }

  int32_t drift_target = Random::GetSample();
  if (drift_target > drift_) {
    drift_ += (drift_target - drift_) >> 13;
  } else {
    drift_ -= (drift_ - drift_target) >> 13;
  }
  int32_t slow_noise = drift_ << 5;
  CLIP(slow_noise);

  size /= kDownsample;
  while (size--) {
    for (uint8_t i = 0; i < kDownsample; ++i) {
      GateFlags gate_flag = *gate_flags++;
      if (gate_flag & GATE_FLAG_RISING) {
        uint16_t random = Random::GetSample();
        if (random < transition_probability_) {
          digit_ = random >> 2;
          digit_ = voice_ ? digit_ % 10 : digit_ & 3;
        }
        if (voice_) {
          phase_ = voice_digits[digit_] << 16;
        }
      }
      if (gate_flag & GATE_FLAG_HIGH) {
        tone_amplitude_ += (32767 - tone_amplitude_) >> 6;
      } else {
        tone_amplitude_ -= tone_amplitude_ >> 6;
        if (tone_amplitude_ < 64 && tone_amplitude_) {
          --tone_amplitude_;
        }
      }
    }
  
    // Generate a distorted sine wave with fluctuating frequency.    
    int32_t digit;
    if (voice_) {
      uint16_t integral = phase_ >> 16;
      uint16_t fractional = phase_ & 0xffff;
      if (integral < voice_digits[digit_ + 1]) {
        uint8_t mask_a = integral * 53;
        uint8_t mask_b = mask_a + 53;
        int32_t a = static_cast<int32_t>(wav_digits[integral] ^ mask_a);
        int32_t b = static_cast<int32_t>(wav_digits[integral + 1] ^ mask_b);
        digit = (a << 8) + (((b - a) * fractional) >> 8);
        digit -= 32768;
        phase_ += phase_increment;
        gate_ = true;
      } else {
        digit = 0;
        gate_ = false;
      }
    } else {
      phase_ += phase_increment + (lp_noise_ << 10);
      digit = Interpolate1022(wav_sine, phase_);
      digit = digit * tone_amplitude_ >> 16;
      gate_ = tone_amplitude_ > 0;
    }
    digit = digit + ((digit - ((digit + 4096) ^ 0x055a)) * distortion_ >> 15);
    
    // Generate narrow-band noise.
    int32_t random_sample = Random::GetSample();
    lp_noise_ += (random_sample - lp_noise_) >> 6;
    noise_phase_ += 238370685;
    int32_t noise = lp_noise_ * Interpolate1022(wav_sine, noise_phase_) >> 12;

    // Generate an interference tone.
    interference_phase_ += 710101260;
    noise += distortion_ * wav_sine[interference_phase_ >> 22] >> 18;

    // Mix signal and noise.
    int32_t inner_sample = digit + ((noise - digit) * noise_ >> 15);

    if (random_sample >= 32767 - (noise_ >> 7)) {
      inner_sample = 0;
    }

    // Final ringmod.
    ringmod_phase_ += 38654706 + (38654706 * drift_ >> 10);
    int32_t ringmod = Interpolate1022(wav_sine, ringmod_phase_) >> 1;
    ringmod = ringmod * inner_sample >> 15;
    inner_sample += ringmod * distortion_ >> 15;
    CLIP(inner_sample)

    // And a pass of wavefolding...
    inner_sample = Interpolate1022(
        wav_fold_sine,
        inner_sample * 8192 + (1UL << 31));

    int32_t outer_sample ;
    outer_sample = lp_.Process<SVF_MODE_LP>(
        hp_.Process<SVF_MODE_HP>((inner_sample + previous_inner_sample_) >> 1));
    *out++ = (previous_outer_sample_ + outer_sample) >> 1;
    *out++ = outer_sample;
    previous_outer_sample_ = outer_sample;

    outer_sample = lp_.Process<SVF_MODE_LP>(
        hp_.Process<SVF_MODE_HP>(inner_sample));
    *out++ = (previous_outer_sample_ + outer_sample) >> 1;
    *out++ = outer_sample;
    previous_outer_sample_ = outer_sample;

    previous_inner_sample_ = inner_sample;
  }
}

}  // namespace peaks
