// ----
// ---- file   : CustomSynthGenerator.tks
// ---- 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: 
// ----          
// ----          
// ----
// ----
// ----

module MCustomSynthGenerator;

use tkcustomsynth;


class CustomSynthGenerator : ST_Generator {

   define String TYPE_CUSTOM = "CUSTOM";

   define int PARAM_CUSTOM_NONE         =  0;
   define int PARAM_CUSTOM_ALTVOLUME    =  1;
   define int PARAM_CUSTOM_PULSEWIDTH   =  2;
   define int PARAM_CUSTOM_BRILLIANCE   =  3;

   define int NUM_CUSTOM_PARAMS         =  4;

   public static StringArray custom_userctl_param_names = [
      "None",                 //  0
      "Alt. Volume",          //  1
      "Pulse width",          //  2
      "Brilliance"            //  3
   ];

   public static StringArray custom_userctl_param_infos = [
      // 0: None:
      "None",

      // 1: Alt. Volume
      " 0x00..0x7f (=> 0 .. 1)\n\n"
      "  - modulates the volume"
      ,

      // 2: Pulse width
      " 0x00..0xff (=> 0 .. 1)\n\n"
      "  - modulates the pulse width"
      ,

      // 3: Brilliance
      " 0x00..0xff (=> 0 .. 1)\n\n"
      "  - crossfades between pulse (1.0) and sine (0.0)"
      ,

      // last entry (dummy):
      ""
   ];

   public static IntArray custom_userctl_param_layoutstyles = [
      0,                                // none
      ControllerEventLayout.STYLE_HEX,  // Alt. Volume
      ControllerEventLayout.STYLE_HEX,  // Pulse width
      ControllerEventLayout.STYLE_HEX,  // Brilliance

      0//dummy
   ];

   public static IntArray custom_userctl_param_layoutranges = [
      0,                                     // none
      ControllerEventLayout.RANGE_0_127,     // Alt. Volume
      ControllerEventLayout.RANGE_0_255,     // Pulse width
      ControllerEventLayout.RANGE_0_255,     // Brilliance

      // ,     // 
      // ControllerEventLayout.RANGE_M64_P63,   // 
      // ControllerEventLayout.RANGE_M128_P127, // 

      0//dummy
   ];

   // Synth instance
   CustomSynth synth;
   
   // Maximum number of samples per generator mixbuffer
   //  (note) the number of elements actually being used depends on the number of samples requested, see Audio.tks
   define int MAX_GENERATOR_MIXBUF_SIZE = 4096*2;
   FloatArray mix_buf;

   CustomSynthVoice *controller_voice; // never allocated, can be used for generator-wide controller updates

   SyModuleExtSample *fx_mod; // Reference to fx patch module


   public virtual init(int _polyphony) {
      // overrides ST_Generator::init

      // Initialize synth
      synth.mixRate   = Audio.mix_rate;
      synth.freqTable = Audio.default_freq_table;
      synth.volume    = (1.0f / _polyphony);

      setPolyphony(_polyphony);

      mix_buf.alloc(MAX_GENERATOR_MIXBUF_SIZE);
      mix_buf.useAll();

      controller_voice <= new CustomSynthVoice;
      controller_voice.init(this);
   }

   public virtual needsExtSampleFXModule() : boolean {
      return true;
   }

   public virtual setFXModule(SyModuleExtSample _fxMod) {
      // Set reference to fx module
      fx_mod <= _fxMod;
   }

   public virtual getFXModule() {
      return fx_mod;
   }

   public virtual setName(String _name) {
      ST_Generator::setName(_name);

      if(null != fx_mod)
      {
         fx_mod.setName(_name);
      }
   }

   public method setPolyphony(int _polyphony) {
      if(_polyphony < 1)
         _polyphony = 1;
      else if(_polyphony > 256)
         _polyphony = 256;

      synth.allocVoices(_polyphony);

      // Allocate voices pointer array
      voices.alloc(_polyphony);
      voices.useAll();

      // Initialize all voices
      allocVoices();
   }

   public virtual getTypeName() : String {
      // overrides ST_Generator::getTypeName
      return TYPE_CUSTOM;
   }

   public virtual getInfo() : String {
      // overrides ST_Generator::getInfo
      return "CustomSynth ("+voices.numElements+" voices)";
   }

   public virtual getPolyphony() : short {
      // overrides ST_Generator::getPolyphony
      return voices.numElements;
   }

   public method getVolume() : float {
      return synth.volume;
   }

   public =replay= method setVolume(float _volume) {
      synth.volume = _volume;
   }

   public virtual getNumParamsGEN() : int { 
      // overrides ST_Generator::getNumParamsGEN
      return NUM_CUSTOM_PARAMS;
   }

   public virtual getParamNameGEN(int _paramId) : String {
      // overrides ST_Generator::getParamNameGEN
      if(ST_Generator.PARAM_NONE <= _paramId < NUM_CUSTOM_PARAMS)
      {
         return custom_userctl_param_names[_paramId];
      }
      return "???";
   }

   public virtual getParamInfoGEN(int _paramId) : String {
      // overrides ST_Generator::getParamInfoGEN
      if(ST_Generator.PARAM_NONE <= _paramId < NUM_CUSTOM_PARAMS)
      {
         return custom_userctl_param_infos.get(_paramId);
      }
      return "";
   }

   public virtual uiGetParamLayoutStyleGEN(int _paramId) : int {
      if(ST_Generator.PARAM_NONE <= _paramId < NUM_CUSTOM_PARAMS)
      {
         return custom_userctl_param_layoutstyles[_paramId];
      }
      return 0;
   }

   public virtual uiGetParamLayoutRangeGEN(int _paramId) : int {
      if(ST_Generator.PARAM_NONE <= _paramId < NUM_CUSTOM_PARAMS)
      {
         return custom_userctl_param_layoutranges[_paramId];
      }
      return 0;
   }

   public virtual getControllerVoice() : ST_Voice {
      return controller_voice;
   }

   public method allocVoices() {
      explain "Allocate and initialize pointerarray elements.";

      int i;
      for(i=0; i<voices.numElements; i++)
      {
         CustomSynthVoice v <= new CustomSynthVoice();
         v.init(this);
         voices[i] = deref v;
      }
   }

   public virtual newInstrument() : ST_Instrument {
      // overrides ST_Generator::newInstrument
      local CustomSynthInstrument ins;
      ins.init();
      return deref ins;
   }

   public =replay= virtual allocVoice(CustomSynthInstrument _ins, float _note, _velocity, int _noteOffTicks, int _trackNr) {
      // overrides ST_Generator::allocVoice

      float vel  = _velocity / 127.0;

      // Simple voice allocation: voice# == track#
      CustomSynthVoice voice <= voices.get(_trackNr);

      if(null != voice)
      {
         voice.voice_nr = _trackNr;

         // Map velocity to voice volume
         synth.setVoiceBaseNote(voice.voice_nr, _note);
         synth.setVoiceVolume(voice.voice_nr, vel);
      }

      // The voice will be started by CustomSynthInstrument::startVoice()
      return voice;
   }

   public virtual renderAudio(int _numFrames) {

      mix_buf.numElements = _numFrames * 2;
      mix_buf.fill(0);

      fx_mod.setSamples(mix_buf);

      synth.render(mix_buf); // adds sample output to existing buffer content
   }

   public virtual renderAudioToExtBuf(FloatArray _mixBuf) {
      // called when bypassing FX
      synth.render(_mixBuf); // adds sample output to existing buffer content
   }

   public virtual reset() {
      // overrides ST_Generator::reset
      CustomSynthVoice *v;
      foreach v in voices
      {
         v.reset();
      }
      synth.resetVoices();
   }

   public =replay= virtual tick() {
      // overrides ST_Generator::tick
      CustomSynthVoice *v;
      int i=0;
      foreach v in voices
      {
         v.tick();
      }
   }

   public virtual getCustomInstrumentDialog() : CustomInstrumentDialog {
      return new EditCustomSynthInstrumentDialog();
   }

   public method loadState(Stream _ifs) : boolean {
      int ver = _ifs.i16;

      synth.volume = _ifs.f32;

      return true;
   }

   public method saveState(Stream _ofs) : boolean {

      // Write version
      _ofs.i16 = 1;

      // Write volume
      _ofs.f32 = synth.volume;

      return true;
   }

}


// Register generator type
Global.AddCustomGeneratorType(CustomSynthGenerator);
