// ----
// ---- file   : SynthVoice.cpp
// ---- author : Bastian Spiegel <bs@tkscript.de>
// ---- legal  : (c) 2011 by Bastian Spiegel. 
// ----          Distributed under terms of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL). See 
// ----          http://www.gnu.org/licenses/licenses.html#LGPL or COPYING for further information.
// ----
// ---- info   : This is part of the "syntracker" midi sequencer.
// ----
// ---- created: 06Apr2011
// ----          
// ---- changed: 
// ----          
// ----
// ----

#include "tkcustomsynth.h"


SynthVoice::SynthVoice(void) {
   synth = NULL;

   reset();
}

SynthVoice::~SynthVoice() {
}

void SynthVoice::init(CustomSynth *_synth) {
   synth = _synth;
}

void SynthVoice::reset(void) {
   base_note   = 12.0f;
   freq        = 0.0f;
   volume      = 1.0f;
   alt_volume  = 1.0f;
   panning     = 0.0f;
   pulse_width = 0.5f;
   brilliance  = 1.0f;

   phase = 0.0f;

   b_active = 0;
}

void SynthVoice::render(sF32 *d, sUI numFrames) {

   // Calc effective, clipped volume
   sF32 aVol = synth->volume * volume * alt_volume;
   if(aVol < 0.0f)
   {
      aVol = 0.0f;
   }

   // Calc effective, clipped panning
   sF32 aPan = panning;
   if(aPan < -1.0f)
   {
      aPan = -1.0f;
   }
   else if(aPan > 1.0f)
   {
      aPan = 1.0f;
   }

   sF32 ap = ((1.0f - aPan) * 0.5f);
   sF32 cVolL = aVol * ap;
   sF32 cVolR = aVol * (1.0f - ap);

   ////printf("xxx SynthVoice::render: cVol=(%f; %f) synth->vol=%f vol=%f alt_vol=%f\n", cVolL, cVolR, synth->volume, volume, alt_volume);

   // Calc effective, clipped frequency
   sF32 aFreq = base_note + freq;
   if(aFreq < 1.0f)
   {
      aFreq = 1.0f;
   }
   else if(aFreq > 126.0f)
   {
      aFreq = 126.0f;
   }

   // Convert normalized "note-space" frequency to Hz
   sUI intNote = (sUI) aFreq;
   sF32 frac = aFreq - (sF32)intNote;

   sF32 f = synth->freq_table[intNote] * (1.0f - frac) + synth->freq_table[intNote + 1] * frac;

   sF32 w = f / synth->mix_rate;

   // Calc effective, clipped "brilliance"
   sF32 aBrilliance = brilliance;
   if(aBrilliance < 0.0f)
   {
      aBrilliance = 0.0f;
   }
   else if(aBrilliance > 1.0f)
   {
      aBrilliance = 1.0f;
   }

   // Render the voice
   sUI i;
   for(i=0; i<numFrames; i++)
   {
      sF32 smp;

      // Calc pulse
      if(phase >= pulse_width)
      {
         smp = -1.0f;
      }
      else
      {
         smp =  1.0f;
      }

      // Calc sine
      sF32 s = (sF32) sin(sM_PI*2 * phase);

      // Crossfade between sine and pulse
      smp = (1.0f - brilliance) * s + brilliance * smp;

      // Store L/R samples
      d[0] += smp * cVolL;
      d[1] += smp * cVolR;

      phase += w;
      if(phase >= 1.0f)
      {
         phase -= 1.0f;
      }

      // Next frame
      d += 2;
   }
}
