// ----
// ---- file   : osc_sin_int_1.cpp
// ---- author : bsp
// ---- legal  : Distributed under terms of the MIT license (https://opensource.org/licenses/MIT)
// ----
// ----          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.
// ----
// ---- info   : auto-generated by "Cycle"
// ----           $ g++ -Wall -Wno-unused-function -Wno-unused-variable -I../../tksampler -c osc_sin_int_1.cpp -o osc_sin_int_1.o
// ---- created: 08Aug2023 12:40:47
// ----
// ----
// ----

#include <plugin.h>

#include <stdlib.h>
#include <math.h>
#include <string.h>




#define PARAM_VSYNC              0
#define PARAM_PW                 1
#define PARAM_CUTOFF             2
#define PARAM_RES                3
#define PARAM_KBDAMT             4
#define NUM_PARAMS               5
static const char *loc_param_names[NUM_PARAMS] = {
   "vsync",                   // 0: VSYNC
   "pw",                      // 1: PW
   "cutoff",                  // 2: CUTOFF
   "res",                     // 3: RES
   "kbdamt",                  // 4: KBDAMT

};
static float loc_param_resets[NUM_PARAMS] = {
   0.5f,                      // 0: VSYNC
   0.5f,                      // 1: PW
   1.0f,                      // 2: CUTOFF
   1.0f,                      // 3: RES
   0.5f,                      // 4: KBDAMT

};

#define MOD_VSYNC                0
#define MOD_PW                   1
#define MOD_CUTOFF               2
#define MOD_RES                  3
#define NUM_MODS                 4
static const char *loc_mod_names[NUM_MODS] = {
   "vsync",        // 0: VSYNC
   "pw",           // 1: PW
   "cutoff",       // 2: CUTOFF
   "res",          // 3: RES

};

typedef struct osc_sin_int_1_info_s {
   st_plugin_info_t base;
} osc_sin_int_1_info_t;

typedef struct osc_sin_int_1_shared_s {
   st_plugin_shared_t base;
   float params[NUM_PARAMS];

} osc_sin_int_1_shared_t;

typedef struct osc_sin_int_1_voice_s {
   st_plugin_voice_t base;
   float             sample_rate;
   float             note_speed_fixed;
   float             note_speed_cur;
   float             note_speed_inc;
   float             note_cur;
   float             note_inc;
   float             mods[NUM_MODS];
   float mod_vsync_cur;
   float mod_vsync_inc;
   float mod_pw_cur;
   float mod_pw_inc;
   float mod_cutoff_cur;
   float mod_cutoff_inc;
   float mod_res_cur;
   float mod_res_inc;

   float tmp1_kbd;
   float tmp2_off;
   float tmp3_scl;
   short tmp4_out_i;
   short tmp5_tri_phase;
   short tmp6_tri_speed;
   short tmp7_tri_tmp;
   short tmp8_freq;
   short tmp9_vsync;
   short tmp10_svf_lp;
   short tmp11_svf_hp;
   short tmp12_svf_bp;
   short tmp13_res;
   float tmp14_f2i;
   short tmp15_freq;
   float var_x;
   float var_v_vsync;
   float var_v_cutoff;

} osc_sin_int_1_voice_t;

#define loop(X)  for(unsigned int i = 0u; i < (X); i++)
#define clamp(a,b,c) (((a)<(b))?(b):(((a)>(c))?(c):(a)))

static inline float mathLerpf(float _a, float _b, float _t) { return _a + (_b - _a) * _t; }
static inline float mathClampf(float a, float b, float c) { return (((a)<(b))?(b):(((a)>(c))?(c):(a))); }
static inline float mathMinf(float a, float b) { return (a<b)?a:b; }
static inline float mathMaxf(float a, float b) { return (a>b)?a:b; }
static inline float mathAbsMaxf(float _x, float _y) { return ( ( (_x<0.0f)?-_x:_x)>((_y<0.0f)?-_y:_y)?_x:_y ); }
static inline float mathAbsMinf(float _x, float _y) { return ( ((_x<0.0f)?-_x:_x)<((_y<0.0f)?-_y:_y)?_x:_y ); }

static inline float winLinear(const float *_s, float _index) {
   int idx = (int)_index;
   float r = _index - (float)idx;
   return mathLerpf(_s[idx], _s[idx+1], r);
}

#ifdef AMIGA
static inline int mulsw(short b,short c) {
   int a;
   asm("mulsw %2,%0":"=d"(a): "0"(c),"d"(b): "cc");
   return a;
}
#else
static inline int mulsw(short b,short c) {
  return b * c;
}
#endif // AMIGA
#define Dmulsw_shr(A,B) (mulsw(A, B) >> 11)


static float ffrac_s(float _f) { int i; if(_f >= 0.0f) { i = (int)_f; return _f - (float)i; } else { i = (int)-_f; return 1.0f - (-_f - (float)i); } }

static float loc_bipolar_to_scale(const float _t, const float _div, const float _mul) {
   // t (-1..1) => /_div .. *_mul
   
   float s;

   if(_t < 0.0f)
   {
      s = (1.0f / _div);
      s = 1.0f + (s - 1.0f) * -_t;
      if(s < 0.0f)
         s = 0.0f;
   }
   else
   {
      s = _mul;
      s = 1.0f + (s - 1.0f) * _t;
   }
   
   return s;
}

static void loc_prepare(st_plugin_voice_t *_voice);

void loc_prepare(st_plugin_voice_t *_voice) {
   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_sin_int_1_shared_t);

   float out = 0.0f;
   // -------- lane "prepare" modIdx=0
   
   // -- mod="$m_vsync" dstVar=out
   out = voice->mod_vsync_cur;
   
   // -- mod="fma" dstVar=out
   out = (out * 2.0f) + -1.0f;
   
   // -- mod="bts" dstVar=out
   out = loc_bipolar_to_scale(out, 2.0f, 48.0f);
   
   // -- mod="sto v_vsync" dstVar=out
   voice->var_v_vsync = out;
   
   // -- mod="$m_cutoff" dstVar=out
   out = voice->mod_cutoff_cur;
   
   // -- mod="kbd" dstVar=out
   out *= 127.0f;
   
   // ---- mod="kbd" input "kbd" seq 1/1
   
   // -- mod="$p_kbdamt" dstVar=voice->tmp1_kbd
   voice->tmp1_kbd = shared->params[PARAM_KBDAMT] * 2.0f - 1.0f;
   
   // ---- mod="kbd" input "off" seq 1/1
   
   // -- mod="0" dstVar=voice->tmp2_off
   voice->tmp2_off = 0.0f;
   voice->tmp3_scl = 4;
   out += (voice->note_cur + (voice->tmp2_off * 12.0f) - 64.0f) * voice->tmp1_kbd;
   out = clamp(out, 0.0f, 127.0f);
   #ifdef OVERSAMPLE_FACTOR
   out = ((440.0f/32.0f)*expf( ((out-9.0f)/12.0f)*logf(2.0f) )) / (voice->sample_rate * OVERSAMPLE_FACTOR);
   #else
   out = ((440.0f/32.0f)*expf( ((out-9.0f)/12.0f)*logf(2.0f) )) / voice->sample_rate;
   #endif
   out *= voice->tmp3_scl;
   
   // -- mod="sto v_cutoff" dstVar=out
   voice->var_v_cutoff = out;
} /* end prepare */

static const char *ST_PLUGIN_API loc_get_param_name(st_plugin_info_t *_info,
                                                    unsigned int      _paramIdx
                                                    ) {
   (void)_info;
   return loc_param_names[_paramIdx];
}

static float ST_PLUGIN_API loc_get_param_reset(st_plugin_info_t *_info,
                                               unsigned int      _paramIdx
                                               ) {
   (void)_info;
   return loc_param_resets[_paramIdx];
}

static float ST_PLUGIN_API loc_get_param_value(st_plugin_shared_t *_shared,
                                               unsigned int        _paramIdx
                                               ) {
   ST_PLUGIN_SHARED_CAST(osc_sin_int_1_shared_t);
   return shared->params[_paramIdx];
}

static void ST_PLUGIN_API loc_set_param_value(st_plugin_shared_t *_shared,
                                              unsigned int        _paramIdx,
                                              float               _value
                                              ) {
   ST_PLUGIN_SHARED_CAST(osc_sin_int_1_shared_t);
   shared->params[_paramIdx] = _value;
}

static const char *ST_PLUGIN_API loc_get_mod_name(st_plugin_info_t *_info,
                                                  unsigned int      _modIdx
                                                  ) {
   (void)_info;
   return loc_mod_names[_modIdx];
}

static void ST_PLUGIN_API loc_set_sample_rate(st_plugin_voice_t *_voice,
                                              float              _sampleRate
                                              ) {
   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);
   voice->sample_rate = _sampleRate;
}

static void ST_PLUGIN_API loc_note_on(st_plugin_voice_t  *_voice,
                                      int                 _bGlide,
                                      unsigned char       _note,
                                      float               _vel
                                      ) {
   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_sin_int_1_shared_t);
   (void)_bGlide;
   (void)_note;
   (void)_vel;
   if(!_bGlide)
   {
      memset((void*)voice->mods, 0, sizeof(voice->mods));
#ifdef OVERSAMPLE_FACTOR
      voice->note_speed_fixed = (0.5f / (voice->sample_rate * OVERSAMPLE_FACTOR));
#else
      voice->note_speed_fixed = (0.5f / voice->sample_rate);
#endif // OVERSAMPLE_FACTOR


      voice->tmp5_tri_phase = 0;
      voice->tmp10_svf_lp = 0;
      voice->tmp11_svf_hp = 0;
      voice->tmp12_svf_bp = 0;

      voice->var_x = 0.0f;
      voice->var_v_vsync = 0.0f;
      voice->var_v_cutoff = 0.0f;


   }
}

static void ST_PLUGIN_API loc_set_mod_value(st_plugin_voice_t *_voice,
                                            unsigned int       _modIdx,
                                            float              _value,
                                            unsigned           _frameOffset
                                            ) {
   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);
   (void)_frameOffset;
   voice->mods[_modIdx] = _value;
}

static void ST_PLUGIN_API loc_prepare_block(st_plugin_voice_t *_voice,
                                            unsigned int       _numFrames,
                                            float              _freqHz,
                                            float              _note,
                                            float              _vol,
                                            float              _pan
                                            ) {
   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_sin_int_1_shared_t);
   (void)_note;
   (void)_vol;
   (void)_pan;

#ifdef OVERSAMPLE_FACTOR
   float noteSpeed = _freqHz / (voice->sample_rate * OVERSAMPLE_FACTOR);
#else
   float noteSpeed = _freqHz / voice->sample_rate;
#endif // OVERSAMPLE_FACTOR

   float modvsync        = shared->params[PARAM_VSYNC       ]                       + voice->mods[MOD_VSYNC        ];
   float modpw           = shared->params[PARAM_PW          ]                       + voice->mods[MOD_PW           ];
   float modcutoff       = shared->params[PARAM_CUTOFF      ]                       + voice->mods[MOD_CUTOFF       ];
   float modres          = shared->params[PARAM_RES         ]                       + voice->mods[MOD_RES          ];

   if(_numFrames > 0u)
   {
      // lerp
      float recBlockSize = (1.0f / _numFrames);
      voice->note_speed_inc = (noteSpeed - voice->note_speed_cur) * recBlockSize;
      voice->note_inc       = (_note - voice->note_cur) * recBlockSize;
      voice->mod_vsync_inc        = (modvsync           - voice->mod_vsync_cur         ) * recBlockSize;
      voice->mod_pw_inc           = (modpw              - voice->mod_pw_cur            ) * recBlockSize;
      voice->mod_cutoff_inc       = (modcutoff          - voice->mod_cutoff_cur        ) * recBlockSize;
      voice->mod_res_inc          = (modres             - voice->mod_res_cur           ) * recBlockSize;

      loc_prepare(&voice->base);
   }
   else
   {
      // initial params/modulation (first block, not rendered)
      voice->note_speed_cur = noteSpeed;
      voice->note_speed_inc = 0.0f;
      voice->note_cur       = _note;
      voice->note_inc       = 0.0f;
      voice->mod_vsync_cur        = modvsync;
      voice->mod_vsync_inc        = 0.0f;
      voice->mod_pw_cur           = modpw;
      voice->mod_pw_inc           = 0.0f;
      voice->mod_cutoff_cur       = modcutoff;
      voice->mod_cutoff_inc       = 0.0f;
      voice->mod_res_cur          = modres;
      voice->mod_res_inc          = 0.0f;

      loc_prepare(&voice->base);
   }

   // printf("xxx note_cur=%f\n", voice->note_cur);
}

static void ST_PLUGIN_API loc_process_replace(st_plugin_voice_t  *_voice,
                                              int                 _bMonoIn,
                                              const float        *_samplesIn,
                                              float              *_samplesOut, 
                                              unsigned int        _numFrames
                                              ) {
   (void)_bMonoIn;
   (void)_samplesIn;

   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_sin_int_1_shared_t);

   // Mono output (replicate left to right channel)
   unsigned int j = 0u;
   unsigned int jStep = _bMonoIn ? 1u : 2u;
   unsigned int k = 0u;
   for(unsigned int i = 0u; i < _numFrames; i++)
   {
      float out = _samplesIn[j];
      j += jStep;
#ifdef OVERSAMPLE_FACTOR
      float outOS = 0.0f;
#endif // OVERSAMPLE_FACTOR

#ifdef OVERSAMPLE_FACTOR
      for(unsigned int osi = 0u; osi < OVERSAMPLE_FACTOR; osi++)
#endif // OVERSAMPLE_FACTOR
      {
      float tmp_f;
      float tmp2_f;
      
      // ========
      // ======== lane "out" modIdx=0
      // ========
      
      // -- mod="int" dstVar=out
      voice->tmp4_out_i = (short)(out * 2048);
      {
      
      // -- mod="tri" dstVar=voice->tmp4_out_i
      voice->tmp8_freq = 2048;
      voice->tmp6_tri_speed = mulsw((short)(voice->note_speed_cur * 16384), voice->tmp8_freq) >> 11;
      
      // ---- mod="tri " input "vsync" seq 1/1
      
      // -- mod="$v_vsync" dstVar=voice->tmp9_vsync
      voice->tmp9_vsync = (short)(voice->var_v_vsync * 2048);
      voice->tmp7_tri_tmp = (voice->tmp5_tri_phase);
      voice->tmp7_tri_tmp = mulsw(voice->tmp7_tri_tmp, voice->tmp9_vsync) >> 11;
      voice->tmp7_tri_tmp = voice->tmp7_tri_tmp & 0x3FFF;
      voice->tmp4_out_i = (voice->tmp7_tri_tmp < 8192) ? ((voice->tmp7_tri_tmp >> 1) - 2048) : ((2048*3) - (voice->tmp7_tri_tmp >> 1));
      voice->tmp5_tri_phase = (voice->tmp5_tri_phase + voice->tmp6_tri_speed) & 0x3FFF;
      
      // -- mod="svf" dstVar=voice->tmp4_out_i
      
      // ---- mod="svf " input "res" seq 1/1
      
      // -- mod="$m_res" dstVar=voice->tmp13_res
      voice->tmp14_f2i = (float)(voice->tmp13_res) / 2048;  // FloatFallback: I2F
      voice->tmp14_f2i = voice->mod_res_cur;
      voice->tmp13_res = (int)(voice->tmp14_f2i * 2048);  // FloatFallback: F2I
      
      // ---- mod="svf " input "freq" seq 1/1
      
      // -- mod="$v_cutoff" dstVar=voice->tmp15_freq
      voice->tmp15_freq = (short)(voice->var_v_cutoff * 2048);
      voice->tmp10_svf_lp = voice->tmp10_svf_lp + (mulsw(voice->tmp12_svf_bp, voice->tmp15_freq) >> 11);
      voice->tmp11_svf_hp = voice->tmp4_out_i - voice->tmp10_svf_lp - (mulsw(voice->tmp12_svf_bp, voice->tmp13_res) >> 11);
      voice->tmp12_svf_bp = voice->tmp12_svf_bp + (mulsw(voice->tmp11_svf_hp, voice->tmp15_freq) >> 11);
      voice->tmp4_out_i = voice->tmp10_svf_lp;
      }
      out = voice->tmp4_out_i * 0.000488281;

      /* end calc */

#ifdef OVERSAMPLE_FACTOR
         outOS += out;
#endif // OVERSAMPLE_FACTOR
      }
#ifdef OVERSAMPLE_FACTOR
      // Apply lowpass filter before downsampling
      //   (note) normalized Fc = F/Fs = 0.442947 / sqrt(oversample_factorČ - 1)
      out = outOS * (1.0f / OVERSAMPLE_FACTOR);
#endif // OVERSAMPLE_FACTOR
      _samplesOut[k]      = out;
      _samplesOut[k + 1u] = out;

      // Next frame
      k += 2u;
      voice->note_speed_cur += voice->note_speed_inc;
      voice->mod_vsync_cur         += voice->mod_vsync_inc;
      voice->mod_pw_cur            += voice->mod_pw_inc;
      voice->mod_cutoff_cur        += voice->mod_cutoff_inc;
      voice->mod_res_cur           += voice->mod_res_inc;
   } /* loop numFrames */
}

static st_plugin_shared_t *ST_PLUGIN_API loc_shared_new(st_plugin_info_t *_info) {
   osc_sin_int_1_shared_t *ret = (osc_sin_int_1_shared_t *)malloc(sizeof(osc_sin_int_1_shared_t));
   if(NULL != ret)
   {
      memset((void*)ret, 0, sizeof(*ret));
      ret->base.info  = _info;
      memcpy((void*)ret->params, (void*)loc_param_resets, NUM_PARAMS * sizeof(float));
   }
   return &ret->base;
}

static void ST_PLUGIN_API loc_shared_delete(st_plugin_shared_t *_shared) {
   free((void*)_shared);
}

static st_plugin_voice_t *ST_PLUGIN_API loc_voice_new(st_plugin_info_t *_info) {
   osc_sin_int_1_voice_t *voice = (osc_sin_int_1_voice_t *)malloc(sizeof(osc_sin_int_1_voice_t));
   if(NULL != voice)
   {
      memset((void*)voice, 0, sizeof(*voice));
      voice->base.info = _info;
   }
   return &voice->base;
}

static void ST_PLUGIN_API loc_voice_delete(st_plugin_voice_t *_voice) {
   ST_PLUGIN_VOICE_CAST(osc_sin_int_1_voice_t);

   free((void*)_voice);
}

static void ST_PLUGIN_API loc_plugin_exit(st_plugin_info_t *_info) {
   free((void*)_info);
}

#ifdef USE_CYCLE_SINE_TBL
static void loc_calc_sine_tbl(void) {
#define QCOS_BITS 16
#define QCOS_ONE  (1 << QCOS_BITS)
#define QCOS_MASK (QCOS_ONE - 1)
   // (note) same as in TSR "C" implementation
   float *qcos = cycle_sine_tbl_f + 4096; // quarter cos tbl

   // calc quarter cos tbl
   int k = 0;
   for(int a = 0; a < (QCOS_ONE/4); a += (QCOS_ONE/16384)/*4*/)  // 4096 elements
   {
      int x = a - ((int)(QCOS_ONE * 0.25 + 0.5)) + ((a + ((int)(QCOS_ONE * 0.25 + 0.5)))&~QCOS_MASK);
      x = ((x * ((x<0?-x:x) - ((int)(QCOS_ONE * 0.5 + 0.5)))) >> QCOS_BITS) << 4;
      x += (((((int)(QCOS_ONE * 0.225 + 0.5)) * x) >> QCOS_BITS) * ((x<0?-x:x) - QCOS_ONE)) >> QCOS_BITS;
      qcos[k++] = x / float(QCOS_ONE);
   }

   // 0°..90° (rev qtbl)
   int j = 4096;
   k = 0;
   loop(4096)
      cycle_sine_tbl_f[k++] = qcos[--j];

   // 90..180° = cos tbl
   k += 4096;

   // 180°..360° (y-flip first half of tbl)
   j = 0;
   loop(8192)
      cycle_sine_tbl_f[k++] = -cycle_sine_tbl_f[j++];

#ifdef USE_CYCLE_SINE_TBL_I
   loop(16384)
      cycle_sine_tbl_i[i] = (short)(cycle_sine_tbl_f[i] * 2048.0f/*FX_ONE*/);
#endif // USE_CYCLE_SINE_TBL_I
}
#endif // USE_CYCLE_SINE_TBL

extern "C" {
st_plugin_info_t *osc_sin_int_1_init(void) {
   osc_sin_int_1_info_t *ret = (osc_sin_int_1_info_t *)malloc(sizeof(osc_sin_int_1_info_t));

   if(NULL != ret)
   {
      memset(ret, 0, sizeof(*ret));

      ret->base.api_version = ST_PLUGIN_API_VERSION;
      ret->base.id          = "osc_sin_int_1";  // unique id. don't change this in future builds.
      ret->base.author      = "bsp";
      ret->base.name        = "osc sin integer";
      ret->base.short_name  = "osc sin integer";
      ret->base.flags       = ST_PLUGIN_FLAG_OSC;
      ret->base.category    = ST_PLUGIN_CAT_UNKNOWN;
      ret->base.num_params  = NUM_PARAMS;
      ret->base.num_mods    = NUM_MODS;

      ret->base.shared_new       = &loc_shared_new;
      ret->base.shared_delete    = &loc_shared_delete;
      ret->base.voice_new        = &loc_voice_new;
      ret->base.voice_delete     = &loc_voice_delete;
      ret->base.get_param_name   = &loc_get_param_name;
      ret->base.get_param_reset  = &loc_get_param_reset;
      ret->base.get_param_value  = &loc_get_param_value;
      ret->base.set_param_value  = &loc_set_param_value;
      ret->base.get_mod_name     = &loc_get_mod_name;
      ret->base.set_sample_rate  = &loc_set_sample_rate;
      ret->base.note_on          = &loc_note_on;
      ret->base.set_mod_value    = &loc_set_mod_value;
      ret->base.prepare_block    = &loc_prepare_block;
      ret->base.process_replace  = &loc_process_replace;
      ret->base.plugin_exit      = &loc_plugin_exit;

#ifdef USE_CYCLE_SINE_TBL
      loc_calc_sine_tbl();
#endif // USE_CYCLE_SINE_TBL
   }

   return &ret->base;
}

#ifndef STFX_SKIP_MAIN_INIT
ST_PLUGIN_APICALL st_plugin_info_t *ST_PLUGIN_API st_plugin_init(unsigned int _pluginIdx) {
   switch(_pluginIdx)
   {
      case 0u:
         return osc_sin_int_1_init();
   }
   return NULL;
}
#endif // STFX_SKIP_MAIN_INIT

} // extern "C"
