// ----
// ---- file   : osc_rotor_2.cpp
// ---- author : 
// ---- 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_rotor_2.cpp -o osc_rotor_2.o
// ---- created: 28Nov2024 13:10:31
// ----
// ----
// ----

// (note) pre-define CYCLE_SKIP_UI to skip all code used only for UI/editing purposes (e.g. when compiling for standalone replay)
// #define CYCLE_SKIP_UI  defined

#include <plugin.h>

#include <stdlib.h>
#include <math.h>
#include <string.h>
#define OVERSAMPLE_FACTOR  4.0f

#define PARAM_COARSE             0
#define PARAM_FINE               1
#define PARAM_EXP                2
#define PARAM_MUL                3
#define PARAM_ADD                4
#define PARAM_SAW_LPF            5
#define PARAM_PUL_LPF            6
#define PARAM_WINDOW             7
#define NUM_PARAMS               8
#ifndef CYCLE_SKIP_UI
static const char *loc_param_names[NUM_PARAMS] = {
   "coarse",                  // 0: COARSE
   "fine",                    // 1: FINE
   "exp",                     // 2: EXP
   "mul",                     // 3: MUL
   "add",                     // 4: ADD
   "saw_lpf",                 // 5: SAW_LPF
   "pul_lpf",                 // 6: PUL_LPF
   "window",                  // 7: WINDOW

};
#endif // CYCLE_SKIP_UI
static float loc_param_resets[NUM_PARAMS] = {
   0.5f,                      // 0: COARSE
   0.5f,                      // 1: FINE
   0.5f,                      // 2: EXP
   1.0f,                      // 3: MUL
   0.5f,                      // 4: ADD
   0.0f,                      // 5: SAW_LPF
   0.0f,                      // 6: PUL_LPF
   0.0f,                      // 7: WINDOW

};

#define MOD_FINE                 0
#define MOD_EXP                  1
#define MOD_MUL                  2
#define MOD_ADD                  3
#define MOD_SAW_LPF              4
#define MOD_PUL_LPF              5
#define MOD_WINDOW               6
#define NUM_MODS                 7
#ifndef CYCLE_SKIP_UI
static const char *loc_mod_names[NUM_MODS] = {
   "fine",                 // 0: FINE
   "exp",                  // 1: EXP
   "mul",                  // 2: MUL
   "add",                  // 3: ADD
   "saw_lpf",              // 4: SAW_LPF
   "pul_lpf",              // 5: PUL_LPF
   "window",               // 6: WINDOW

};
#endif // CYCLE_SKIP_UI

typedef struct osc_rotor_2_info_s {
   st_plugin_info_t base;
} osc_rotor_2_info_t;

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

} osc_rotor_2_shared_t;

typedef struct osc_rotor_2_voice_s {
   st_plugin_voice_t base;
   float             sample_rate;
   float             bpm;
   float             velocity;
   float             note_speed_fixed;
   float             note_speed_cur;
   float             note_speed_inc;
   float             note_cur;
   float             note_inc;
   float             mods[NUM_MODS];
   float mod_fine_cur;
   float mod_fine_inc;
   float mod_exp_cur;
   float mod_exp_inc;
   float mod_mul_cur;
   float mod_mul_inc;
   float mod_add_cur;
   float mod_add_inc;
   float mod_saw_lpf_cur;
   float mod_saw_lpf_inc;
   float mod_pul_lpf_cur;
   float mod_pul_lpf_inc;
   float mod_window_cur;
   float mod_window_inc;

   float tmp1_sin_phase;
   float tmp2;
   float tmp3;
   float tmp4_tri_phase;
   float tmp5_saw_phase;
   float tmp6_svf_lp;
   float tmp7_svf_hp;
   float tmp8_svf_bp;
   float tmp9;
   float tmp10;
   float tmp11_pul_phase;
   float tmp12_svf_lp;
   float tmp13_svf_hp;
   float tmp14_svf_bp;
   float tmp15_pha_phase;
   short tmp16;
   short tmp17;
   float tmp18_sin_phase;
   float tmp19_tri_phase;
   float tmp20;
   float var_x;
   float var_t;

   float lut_oscs[4];
   float sr_factor;

} osc_rotor_2_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 frac(float _x) { return _x - ((int)_x); }

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);
}

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 short curve_0[1024] = {
2048, 1977, 1930, 1893, 1860, 1831, 1804, 1779, 1755, 1733, 1712, 1692, 1672, 1654, 1635, 1618, 1601, 1584, 1568, 1553, 1537, 1522, 1507, 1493, 1479, 1465, 1451, 1438, 1425, 1412, 1399, 1387, 
1374, 1362, 1350, 1338, 1326, 1315, 1304, 1292, 1281, 1270, 1259, 1249, 1238, 1227, 1217, 1207, 1196, 1186, 1176, 1167, 1157, 1147, 1138, 1128, 1119, 1109, 1100, 1091, 1082, 1073, 1064, 1055, 
1046, 1038, 1029, 1020, 1012, 1003, 995, 987, 979, 970, 962, 954, 946, 939, 931, 923, 915, 908, 900, 892, 885, 878, 870, 863, 856, 848, 841, 834, 827, 820, 813, 806, 
800, 793, 786, 780, 773, 766, 760, 753, 747, 741, 734, 728, 722, 716, 710, 704, 698, 692, 686, 680, 674, 668, 663, 657, 651, 646, 640, 635, 630, 624, 619, 614, 
608, 603, 598, 593, 588, 583, 578, 574, 569, 564, 559, 555, 550, 546, 541, 537, 533, 528, 524, 520, 516, 512, 508, 504, 500, 496, 492, 489, 485, 482, 478, 475, 
471, 468, 465, 462, 459, 456, 453, 450, 447, 445, 442, 439, 437, 435, 432, 430, 428, 425, 423, 421, 419, 417, 415, 413, 411, 409, 407, 405, 403, 401, 399, 398, 
396, 394, 393, 391, 389, 388, 386, 384, 383, 381, 380, 378, 377, 375, 374, 373, 371, 370, 368, 367, 366, 364, 363, 362, 360, 359, 358, 357, 355, 354, 353, 352, 
350, 349, 348, 347, 346, 345, 343, 342, 341, 340, 339, 338, 337, 336, 335, 334, 332, 331, 330, 329, 328, 327, 326, 325, 324, 323, 322, 321, 320, 319, 318, 317, 
316, 315, 314, 313, 312, 311, 311, 310, 309, 308, 307, 306, 305, 304, 303, 302, 301, 300, 299, 298, 298, 297, 296, 295, 294, 293, 292, 291, 290, 289, 289, 288, 
287, 286, 285, 284, 283, 282, 281, 281, 280, 279, 278, 277, 276, 275, 274, 273, 273, 272, 271, 270, 269, 268, 267, 266, 265, 265, 264, 263, 262, 261, 260, 259, 
258, 257, 257, 256, 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 
228, 227, 226, 225, 224, 224, 223, 222, 220, 219, 218, 217, 216, 215, 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 199, 198, 197, 196, 
195, 194, 193, 192, 191, 189, 188, 187, 186, 185, 184, 182, 181, 180, 179, 178, 176, 175, 174, 173, 172, 170, 169, 168, 167, 165, 164, 163, 161, 160, 159, 157, 
156, 155, 153, 152, 151, 149, 148, 147, 145, 144, 142, 141, 140, 138, 137, 135, 134, 132, 131, 130, 128, 127, 125, 123, 122, 120, 119, 117, 116, 114, 113, 111, 
109, 108, 106, 104, 103, 101, 100, 98, 96, 94, 93, 91, 89, 87, 86, 84, 82, 80, 79, 77, 75, 73, 71, 69, 67, 65, 64, 62, 60, 58, 56, 54, 
52, 50, 48, 46, 44, 42, 39, 37, 35, 33, 31, 29, 27, 24, 22, 20, 18, 15, 13, 11, 9, 6, 4, 1, 0, -2, -5, -7, -10, -12, -15, -17, 
-20, -22, -25, -27, -30, -33, -35, -38, -40, -43, -45, -48, -50, -53, -55, -58, -60, -63, -66, -68, -71, -73, -76, -78, -81, -83, -86, -88, -91, -94, -96, -99, 
-101, -104, -106, -109, -111, -114, -117, -119, -122, -124, -127, -129, -132, -135, -137, -140, -142, -145, -148, -150, -153, -155, -158, -161, -163, -166, -169, -171, -174, -176, -179, -182, 
-184, -187, -190, -192, -195, -198, -201, -203, -206, -209, -211, -214, -217, -220, -222, -225, -228, -231, -233, -236, -239, -242, -245, -247, -250, -253, -256, -259, -261, -264, -267, -270, 
-273, -276, -279, -282, -285, -287, -290, -293, -296, -299, -302, -305, -308, -311, -314, -317, -320, -323, -326, -329, -332, -335, -338, -342, -345, -348, -351, -354, -357, -360, -364, -367, 
-370, -373, -376, -380, -383, -386, -389, -393, -396, -399, -403, -406, -409, -413, -416, -420, -423, -426, -430, -433, -437, -440, -444, -447, -451, -454, -458, -461, -465, -469, -472, -476, 
-480, -483, -487, -491, -494, -498, -502, -506, -510, -513, -517, -521, -525, -529, -533, -537, -541, -545, -549, -553, -557, -561, -565, -569, -573, -577, -582, -586, -590, -594, -599, -603, 
-607, -611, -616, -620, -625, -629, -634, -638, -643, -647, -652, -656, -661, -666, -670, -675, -680, -685, -689, -694, -699, -704, -709, -714, -719, -724, -729, -734, -739, -744, -749, -755, 
-760, -765, -770, -776, -781, -787, -792, -798, -803, -809, -814, -820, -826, -832, -837, -843, -849, -855, -861, -867, -873, -879, -885, -892, -898, -904, -910, -917, -923, -930, -936, -943, 
-950, -956, -963, -970, -977, -984, -991, -998, -1005, -1012, -1019, -1026, -1033, -1039, -1046, -1053, -1060, -1067, -1073, -1080, -1087, -1093, -1100, -1107, -1113, -1120, -1126, -1133, -1139, -1146, -1152, -1159, 
-1165, -1171, -1178, -1184, -1190, -1196, -1203, -1209, -1215, -1221, -1227, -1233, -1239, -1245, -1251, -1257, -1263, -1269, -1275, -1281, -1287, -1293, -1299, -1304, -1310, -1316, -1322, -1327, -1333, -1338, -1344, -1350, 
-1355, -1361, -1366, -1372, -1377, -1382, -1388, -1393, -1398, -1404, -1409, -1414, -1420, -1425, -1430, -1435, -1440, -1445, -1450, -1455, -1460, -1465, -1470, -1475, -1480, -1485, -1490, -1495, -1500, -1504, -1509, -1514, 
-1519, -1523, -1528, -1533, -1537, -1542, -1547, -1551, -1556, -1560, -1565, -1569, -1574, -1578, -1582, -1587, -1591, -1595, -1600, -1604, -1608, -1613, -1617, -1621, -1625, -1629, -1634, -1638, -1642, -1646, -1650, -1654, 
-1658, -1662, -1666, -1670, -1674, -1678, -1682, -1686, -1689, -1693, -1697, -1701, -1705, -1708, -1712, -1716, -1720, -1723, -1727, -1731, -1734, -1738, -1742, -1745, -1749, -1752, -1756, -1759, -1763, -1766, -1770, -1773, 
-1777, -1780, -1784, -1787, -1791, -1794, -1797, -1801, -1804, -1807, -1810, -1814, -1817, -1820, -1824, -1827, -1830, -1833, -1836, -1840, -1843, -1846, -1849, -1852, -1855, -1858, -1861, -1864, -1867, -1870, -1873, -1876, 
-1879, -1882, -1885, -1888, -1891, -1894, -1897, -1900, -1903, -1906, -1909, -1912, -1915, -1917, -1920, -1923, -1926, -1929, -1931, -1934, -1937, -1940, -1943, -1945, -1948, -1951, -1953, -1956, -1959, -1961, -1964, -1967, 
-1969, -1972, -1975, -1977, -1980, -1983, -1985, -1988, -1990, -1993, -1996, -1998, -2001, -2003, -2006, -2008, -2011, -2013, -2016, -2018, -2021, -2023, -2026, -2028, -2031, -2033, -2035, -2038, -2040, -2043, -2045, -2047, 
};

#define USE_CYCLE_SINE_TBL  defined
static float cycle_sine_tbl_f[16384];

static float mathLogf(const float _x) {
   union {
      float f;
      unsigned int u;
   } bx;
   bx.f = _x;
   const unsigned int ex = bx.u >> 23;
   const signed int t = (signed int)ex - (signed int)127;
   const unsigned int s = (t < 0) ? (-t) : t;
   bx.u = 1065353216u | (bx.u & 8388607u);
   return
      -1.7417939f + (2.8212026f + (-1.4699568f + (0.44717955f - 0.056570851f * bx.f)*bx.f)*bx.f)*bx.f
      + 0.6931471806f * t;
}

static float mathPowerf(float _x, float _y) {
   float r;
   if(_y != 0.0f)
   {
      if(_x < 0.0f)
      {
         r = (float)( -expf(_y*mathLogf(-_x)) );
      }
      else if(_x > 0.0f)
      {
         r = (float)( expf(_y*mathLogf(_x)) );
      }
      else
      {
         r = 0.0f;
      }
   }
   else
   {
      r = 1.0f;
   }
   return Dstplugin_fix_denorm_32(r);
}

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_update_lut_oscs(st_plugin_voice_t *_voice, float *_d);

void loc_update_lut_oscs(st_plugin_voice_t *_voice, float *_d) {
   ST_PLUGIN_VOICE_CAST(osc_rotor_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_rotor_2_shared_t);
   float x = 0.0f;
   loop(4)
   {
      float out = x;
      // -------- lane "lut_oscs" modIdx=0 class=0
      
      // -- mod="0" dstVar=out
      out = 0.0f;
      _d[i] = out;

      // Next x
      x += 0.333333f;
   } // loop 4
} /* end loc_update_lut_oscs() */


#ifndef CYCLE_SKIP_UI
static unsigned int loc_copy_chars(char *_d, const unsigned int _dSize, const char *_s) {
   unsigned int r = 0u;
   if(NULL != _d)
   {
      // Write min(dSize,len(s)) characters to 'd'
      char *d = _d;
      if(NULL != _s)
      {
         const char *s = _s;
         for(;;)
         {
            if(r < _dSize)
            {
               char c = s[r++];
               *d++ = c;
               if(0 == c)
                  break;
            }
            else
            {
               if(r > 0u)
                  d[-1] = 0;  // force ASCIIZ
               break;
            }
         }
      }
      else
      {
         if(_dSize > 0u)
         {
            _d[0] = 0;
            r = 1u;
         }
      }
   }
   else
   {
      // Return total length of source string
      if(NULL != _s)
      {
         const char *s = _s;
         for(;;)
         {
            char c = s[r++];
            if(0 == c)
               break;
         }
      }
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static unsigned int loc_copy_floats(float              *_d,
                                    const unsigned int  _dSize,
                                    const float        *_s,
                                    const unsigned int  _sNum
                                    ) {
   unsigned int r = 0u;
   if(NULL != _d)
   {
      // Write min(dSize,sNum) characters to 'd'
      if(NULL != _s)
      {
         unsigned int num = (_sNum > _dSize) ? _dSize : _sNum;
         memcpy((void*)_d, (const void*)_s, num * sizeof(float));
         r = num;
      }
   }
   else
   {
      // Query total number of presets
      r = _sNum;
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
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];
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static const char *ST_PLUGIN_API loc_get_param_group_name(st_plugin_info_t *_info,
                                                          unsigned int      _paramGroupIdx
                                                          ) {
   (void)_info;
   const char *r = NULL;
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static unsigned int ST_PLUGIN_API loc_get_param_group_idx(st_plugin_info_t *_info,
                                                          unsigned int      _paramIdx
                                                          ) {
   (void)_info;
   unsigned int r = ~0u;
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static const char *ST_PLUGIN_API loc_get_param_section_name(st_plugin_info_t *_info,
                                                            unsigned int      _paramSectionIdx
                                                            ) {
   (void)_info;
   const char *r = NULL;
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static unsigned int ST_PLUGIN_API loc_get_param_section_idx(st_plugin_info_t *_info,
                                                            unsigned int      _paramIdx
                                                            ) {
   (void)_info;
   unsigned int r = ~0u;
   return r;
}
#endif // CYCLE_SKIP_UI

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

static float ST_PLUGIN_API loc_get_param_value(st_plugin_shared_t *_shared,
                                               unsigned int        _paramIdx
                                               ) {
   ST_PLUGIN_SHARED_CAST(osc_rotor_2_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_rotor_2_shared_t);
   shared->params[_paramIdx] = _value;
}

#ifndef CYCLE_SKIP_UI
static unsigned int ST_PLUGIN_API loc_query_dynamic_param_preset_values(st_plugin_shared_t *_shared,
                                                                        const unsigned int  _paramIdx,
                                                                        float              *_retValues,
                                                                        const unsigned int  _retValuesSize
                                                                        ) {
   ST_PLUGIN_SHARED_CAST(osc_rotor_2_shared_t);
   unsigned int r = 0u;
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static unsigned int ST_PLUGIN_API loc_query_dynamic_param_preset_name(st_plugin_shared_t *_shared,
                                                                      const unsigned int  _paramIdx,
                                                                      const unsigned int  _presetIdx,
                                                                      char               *_retBuf,
                                                                      const unsigned int  _retBufSize
                                                                      ) {
   ST_PLUGIN_SHARED_CAST(osc_rotor_2_shared_t);
   unsigned int r = 0u;
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static unsigned int ST_PLUGIN_API loc_get_array_param_size(st_plugin_info_t   *_info,
                                                           const unsigned int  _paramIdx
                                                           ) {
   unsigned int r = 0u;
   switch(_paramIdx)
   {
      default:
         break;
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static unsigned int ST_PLUGIN_API loc_get_array_param_num_variations(st_plugin_info_t   *_info,
                                                                     const unsigned int  _paramIdx
                                                                     ) {
   unsigned int r = 0u;
   switch(_paramIdx)
   {
      default:
         break;
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static float * ST_PLUGIN_API loc_get_array_param_variation_ptr(st_plugin_shared_t *_shared,
                                                               const unsigned int  _paramIdx,
                                                               const unsigned int  _variationIdx
                                                               ) {
   ST_PLUGIN_SHARED_CAST(osc_rotor_2_shared_t);
   float *r = NULL;
   switch(_paramIdx)
   {
      default:
         break;
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static void ST_PLUGIN_API loc_set_array_param_edit_variation_idx(st_plugin_shared_t *_shared,
                                                                 const unsigned int  _paramIdx,
                                                                 const int           _variationIdx
                                                                 ) {
   ST_PLUGIN_SHARED_CAST(osc_rotor_2_shared_t);
   switch(_paramIdx)
   {
      default:
         break;
   }
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static const char *ST_PLUGIN_API loc_get_array_param_element_name(st_plugin_info_t   *_info,
                                                                  const unsigned int  _paramIdx,
                                                                  const unsigned int  _elementIdx
                                                                  ) {
   ST_PLUGIN_INFO_CAST(osc_rotor_2_info_t);
   const char *r = NULL;
   switch(_paramIdx)
   {
      default:
         break;
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static int ST_PLUGIN_API loc_get_array_param_element_value_range(st_plugin_info_t   *_info,
                                                                 const unsigned int  _paramIdx,
                                                                 const unsigned int  _elementIdx,
                                                                 float              *_retStorageMin,
                                                                 float              *_retStorageMax,
                                                                 float              *_retDisplayMin,
                                                                 float              *_retDisplayMax,
                                                                 unsigned int       *_retDisplayPrecision
                                                                 ) {
   (void)_elementIdx;
   switch(_paramIdx)
   {
      default:
         break;
   }
   return 0;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static float ST_PLUGIN_API loc_get_array_param_element_reset(st_plugin_info_t   *_info,
                                                             const unsigned int  _paramIdx,
                                                             const unsigned int  _elementIdx
                                                             ) {
   ST_PLUGIN_INFO_CAST(osc_rotor_2_info_t);
   float r = -999999.0f/*INVALID_VALUE*/;
   switch(_paramIdx)
   {
      default:
         break;
   }
   return r;
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
static void ST_PLUGIN_API loc_update_lut(st_plugin_info_t   *_info,
                                         unsigned int        _idx,
                                         const float        *_src,
                                         const unsigned int  _srcNumElements
                                         ) {
   (void)_info;
   unsigned int copySz = 0u;
   (void)copySz;
   switch(_idx)
   {
      default:
         break;
      case 0: // "c_mod_ws"
         copySz = Dstplugin_min(_srcNumElements, 1024);
         for(unsigned int i = 0u; i < copySz; i++)
            curve_0[i] = (short)(_src[i] * 2048);
         break;
   }
}
#endif // CYCLE_SKIP_UI

#ifndef CYCLE_SKIP_UI
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];
}
#endif // CYCLE_SKIP_UI

static void ST_PLUGIN_API loc_set_sample_rate(st_plugin_voice_t *_voice,
                                              float              _sampleRate
                                              ) {
   ST_PLUGIN_VOICE_CAST(osc_rotor_2_voice_t);
   if(_sampleRate != voice->sample_rate)
   {
      voice->sr_factor = 12000.0f / _sampleRate;
   }
   voice->sample_rate = _sampleRate;
}

static void ST_PLUGIN_API loc_set_bpm(st_plugin_voice_t *_voice,
                                      float              _bpm
                                      ) {
   ST_PLUGIN_VOICE_CAST(osc_rotor_2_voice_t);
   voice->bpm = _bpm;
}

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_rotor_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_rotor_2_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 = (261.63f/*C-5*/ / (voice->sample_rate * OVERSAMPLE_FACTOR));
#else
      voice->note_speed_fixed = (261.63f/*C-5*/ / voice->sample_rate);
#endif // OVERSAMPLE_FACTOR
      voice->velocity = _vel;
      voice->tmp1_sin_phase = 0.0f;
      voice->tmp4_tri_phase = 0.0f;
      voice->tmp5_saw_phase = 0.0f;
      voice->tmp6_svf_lp = 0.0f;
      voice->tmp7_svf_hp = 0.0f;
      voice->tmp8_svf_bp = 0.0f;
      voice->tmp11_pul_phase = 0.0f;
      voice->tmp12_svf_lp = 0.0f;
      voice->tmp13_svf_hp = 0.0f;
      voice->tmp14_svf_bp = 0.0f;
      voice->tmp15_pha_phase = 0.0f;
      voice->tmp18_sin_phase = 0.0f;
      voice->tmp19_tri_phase = 0.0f;
      voice->var_x = 0.0f;
      voice->var_t = 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_rotor_2_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_rotor_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_rotor_2_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 modfine         = shared->params[PARAM_FINE        ]                       + voice->mods[MOD_FINE         ];
   float modexp          = shared->params[PARAM_EXP         ]                       + voice->mods[MOD_EXP          ];
   float modmul          = shared->params[PARAM_MUL         ]                       + voice->mods[MOD_MUL          ];
   float modadd          = shared->params[PARAM_ADD         ]                       + voice->mods[MOD_ADD          ];
   float modsaw_lpf      = shared->params[PARAM_SAW_LPF     ]                       + voice->mods[MOD_SAW_LPF      ];
   float modpul_lpf      = shared->params[PARAM_PUL_LPF     ]                       + voice->mods[MOD_PUL_LPF      ];
   float modwindow       = shared->params[PARAM_WINDOW      ]                       + voice->mods[MOD_WINDOW       ];

   if(_numFrames > 0u)
   {
      // lerp
#ifdef OVERSAMPLE_FACTOR
      float recBlockSize = (1.0f / (_numFrames * OVERSAMPLE_FACTOR));
#else
      float recBlockSize = (1.0f / _numFrames);
#endif // OVERSAMPLE_FACTOR
      voice->note_speed_inc = (noteSpeed - voice->note_speed_cur) * recBlockSize;
      voice->note_inc       = (_note - voice->note_cur) * recBlockSize;
      voice->mod_fine_inc         = (modfine            - voice->mod_fine_cur          ) * recBlockSize;
      voice->mod_exp_inc          = (modexp             - voice->mod_exp_cur           ) * recBlockSize;
      voice->mod_mul_inc          = (modmul             - voice->mod_mul_cur           ) * recBlockSize;
      voice->mod_add_inc          = (modadd             - voice->mod_add_cur           ) * recBlockSize;
      voice->mod_saw_lpf_inc      = (modsaw_lpf         - voice->mod_saw_lpf_cur       ) * recBlockSize;
      voice->mod_pul_lpf_inc      = (modpul_lpf         - voice->mod_pul_lpf_cur       ) * recBlockSize;
      voice->mod_window_inc       = (modwindow          - voice->mod_window_cur        ) * recBlockSize;
   }
   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_fine_cur         = modfine;
      voice->mod_fine_inc         = 0.0f;
      voice->mod_exp_cur          = modexp;
      voice->mod_exp_inc          = 0.0f;
      voice->mod_mul_cur          = modmul;
      voice->mod_mul_inc          = 0.0f;
      voice->mod_add_cur          = modadd;
      voice->mod_add_inc          = 0.0f;
      voice->mod_saw_lpf_cur      = modsaw_lpf;
      voice->mod_saw_lpf_inc      = 0.0f;
      voice->mod_pul_lpf_cur      = modpul_lpf;
      voice->mod_pul_lpf_inc      = 0.0f;
      voice->mod_window_cur       = modwindow;
      voice->mod_window_inc       = 0.0f;
   }

   // printf("xxx note_cur=%f\n", voice->note_cur);
   // printf("xxx prepare_block: numFrames=%u moda=%f\n", _numFrames, moda);
   // printf("xxx voice->voicebus_idx_0=%u voice->base.voice_bus_read_offset=%u\n", voice->voicebus_idx_0, voice->base.voice_bus_read_offset);
}

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_rotor_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(osc_rotor_2_shared_t);

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

#ifdef OVERSAMPLE_FACTOR
      for(unsigned int osi = 0u; osi < OVERSAMPLE_FACTOR; osi++)
#endif // OVERSAMPLE_FACTOR
      {
#ifdef STEREO
         out = outL = inL;
         outR = inR;
#else
         out = inL;
#endif // STEREO
         float tmp_f;
         float tmp2_f;
         
         // ========
         // ======== lane "out" modIdx=0 class=sin
         // ========
         
         // -- mod="sin" dstVar=out
         
         // ---- mod="sin" input "freq" seq 1/1
         
         // -- mod="1" dstVar=voice->tmp3/*sin_freq*/
         voice->tmp3/*sin_freq*/ = 1.0f;
         voice->tmp2/*sin_speed*/ = voice->note_speed_cur * voice->tmp3/*sin_freq*/;
         voice->tmp3/*sin_tmp*/ = (voice->tmp1_sin_phase);
         voice->tmp3/*sin_tmp*/ = ffrac_s(voice->tmp3/*sin_tmp*/);
         out = cycle_sine_tbl_f[(unsigned short)(16384 * voice->tmp3/*sin_tmp*/)&16383u];
         voice->tmp1_sin_phase = ffrac_s(voice->tmp1_sin_phase + voice->tmp2/*sin_speed*/);
         
         // -- mod="sto t" dstVar=out
         voice->var_t = out;
         
         // -- mod="0" dstVar=out
         out = 0.0f;
         
         // -- mod="sta oscs 1 0 t 1 0" dstVar=out
         voice->lut_oscs[((unsigned int)((out * 1.0f + 0.0f) * 4)) & 3] = voice->var_t * 1.0f + 0.0f;
         
         // -- mod="tri" dstVar=out
         
         // ---- mod="tri" input "freq" seq 1/1
         
         // -- mod="1" dstVar=voice->tmp3/*tri_freq*/
         voice->tmp3/*tri_freq*/ = 1.0f;
         voice->tmp2/*tri_speed*/ = voice->note_speed_cur * voice->tmp3/*tri_freq*/;
         voice->tmp3/*tri_tmp*/ = (voice->tmp4_tri_phase);
         voice->tmp3/*tri_tmp*/ = ffrac_s(voice->tmp3/*tri_tmp*/);
         out = (voice->tmp3/*tri_tmp*/ < 0.5f) ? (-1.0 + voice->tmp3/*tri_tmp*/ * 4.0f) : (1.0 - (voice->tmp3/*tri_tmp*/ - 0.5f)*4);
         voice->tmp4_tri_phase = ffrac_s(voice->tmp4_tri_phase + voice->tmp2/*tri_speed*/);
         
         // -- mod="sto t" dstVar=out
         voice->var_t = out;
         
         // -- mod="0.25" dstVar=out
         out = 0.25f;
         
         // -- mod="sta oscs 1 0 t 1 0" dstVar=out
         voice->lut_oscs[((unsigned int)((out * 1.0f + 0.0f) * 4)) & 3] = voice->var_t * 1.0f + 0.0f;
         
         // -- mod="saw" dstVar=out
         
         // ---- mod="saw" input "freq" seq 1/1
         
         // -- mod="1" dstVar=voice->tmp3/*saw_freq*/
         voice->tmp3/*saw_freq*/ = 1.0f;
         voice->tmp2/*saw_speed*/ = voice->note_speed_cur * voice->tmp3/*saw_freq*/;
         voice->tmp3/*saw_tmp*/ = (voice->tmp5_saw_phase);
         voice->tmp3/*saw_tmp*/ = ffrac_s(voice->tmp3/*saw_tmp*/);
         out = 1.0 - (voice->tmp3/*saw_tmp*/ * 2.0f);
         voice->tmp5_saw_phase = ffrac_s(voice->tmp5_saw_phase + voice->tmp2/*saw_speed*/);
         
         // -- mod="svf" dstVar=out
         
         // ---- mod="svf" input "freq" seq 1/1
         
         // -- mod="$m_saw_lpf" dstVar=voice->tmp2/*addsr_freq*/
         voice->tmp2/*addsr_freq*/ = voice->mod_saw_lpf_cur;
         
         // -- mod="kbd" dstVar=voice->tmp2/*addsr_freq*/
         
         // ---- mod="kbd" input "amt" seq 1/1
         
         // -- mod="100" dstVar=voice->tmp3/*amt*/
         voice->tmp3/*amt*/ = 100.0f;
         voice->tmp9/*off*/ = 0;
         voice->tmp2/*addsr_freq*/ *= 127.0f;
         voice->tmp10/*scl*/ = 4;
         voice->tmp2/*addsr_freq*/ += (voice->note_cur + (voice->tmp9/*off*/ * 12.0f) - 64.0f) * voice->tmp3/*amt*/;
         voice->tmp2/*addsr_freq*/ = clamp(voice->tmp2/*addsr_freq*/, 0.0f, 127.0f);
         #ifdef OVERSAMPLE_FACTOR
         voice->tmp2/*addsr_freq*/ = ((440.0f/32.0f)*expf( ((voice->tmp2/*addsr_freq*/-9.0f)/12.0f)*logf(2.0f) )) / (voice->sample_rate * OVERSAMPLE_FACTOR);
         #else
         voice->tmp2/*addsr_freq*/ = ((440.0f/32.0f)*expf( ((voice->tmp2/*addsr_freq*/-9.0f)/12.0f)*logf(2.0f) )) / voice->sample_rate;
         #endif
         voice->tmp2/*addsr_freq*/ *= voice->tmp10/*scl*/;
         voice->tmp2/*addsr_freq*/ *= voice->sr_factor;
         voice->tmp6_svf_lp = voice->tmp6_svf_lp + (voice->tmp8_svf_bp * voice->tmp2/*addsr_freq*/);
         voice->tmp7_svf_hp = out - voice->tmp6_svf_lp - (voice->tmp8_svf_bp * 1.0f);
         voice->tmp8_svf_bp = voice->tmp8_svf_bp + (voice->tmp7_svf_hp * voice->tmp2/*addsr_freq*/);
         out = voice->tmp6_svf_lp;
         
         // -- mod="sto t" dstVar=out
         voice->var_t = out;
         
         // -- mod="0.5" dstVar=out
         out = 0.5f;
         
         // -- mod="sta oscs 1 0 t 1 0" dstVar=out
         voice->lut_oscs[((unsigned int)((out * 1.0f + 0.0f) * 4)) & 3] = voice->var_t * 1.0f + 0.0f;
         
         // -- mod="pul" dstVar=out
         
         // ---- mod="pul" input "freq" seq 1/1
         
         // -- mod="1" dstVar=voice->tmp3/*pul_freq*/
         voice->tmp3/*pul_freq*/ = 1.0f;
         voice->tmp2/*pul_speed*/ = voice->note_speed_cur * voice->tmp3/*pul_freq*/;
         voice->tmp3/*pul_tmp*/ = (voice->tmp11_pul_phase);
         voice->tmp3/*pul_tmp*/ = ffrac_s(voice->tmp3/*pul_tmp*/);
         out = (voice->tmp3/*pul_tmp*/ >= 0.5) ? 1.0f : -1.0f;
         voice->tmp11_pul_phase = ffrac_s(voice->tmp11_pul_phase + voice->tmp2/*pul_speed*/);
         
         // -- mod="svf" dstVar=out
         
         // ---- mod="svf" input "freq" seq 1/1
         
         // -- mod="$m_pul_lpf" dstVar=voice->tmp2/*addsr_freq*/
         voice->tmp2/*addsr_freq*/ = voice->mod_pul_lpf_cur;
         
         // -- mod="kbd" dstVar=voice->tmp2/*addsr_freq*/
         
         // ---- mod="kbd" input "amt" seq 1/1
         
         // -- mod="100" dstVar=voice->tmp3/*amt*/
         voice->tmp3/*amt*/ = 100.0f;
         voice->tmp9/*off*/ = 0;
         voice->tmp2/*addsr_freq*/ *= 127.0f;
         voice->tmp10/*scl*/ = 4;
         voice->tmp2/*addsr_freq*/ += (voice->note_cur + (voice->tmp9/*off*/ * 12.0f) - 64.0f) * voice->tmp3/*amt*/;
         voice->tmp2/*addsr_freq*/ = clamp(voice->tmp2/*addsr_freq*/, 0.0f, 127.0f);
         #ifdef OVERSAMPLE_FACTOR
         voice->tmp2/*addsr_freq*/ = ((440.0f/32.0f)*expf( ((voice->tmp2/*addsr_freq*/-9.0f)/12.0f)*logf(2.0f) )) / (voice->sample_rate * OVERSAMPLE_FACTOR);
         #else
         voice->tmp2/*addsr_freq*/ = ((440.0f/32.0f)*expf( ((voice->tmp2/*addsr_freq*/-9.0f)/12.0f)*logf(2.0f) )) / voice->sample_rate;
         #endif
         voice->tmp2/*addsr_freq*/ *= voice->tmp10/*scl*/;
         voice->tmp2/*addsr_freq*/ *= voice->sr_factor;
         voice->tmp12_svf_lp = voice->tmp12_svf_lp + (voice->tmp14_svf_bp * voice->tmp2/*addsr_freq*/);
         voice->tmp13_svf_hp = out - voice->tmp12_svf_lp - (voice->tmp14_svf_bp * 1.0f);
         voice->tmp14_svf_bp = voice->tmp14_svf_bp + (voice->tmp13_svf_hp * voice->tmp2/*addsr_freq*/);
         out = voice->tmp12_svf_lp;
         
         // -- mod="sto t" dstVar=out
         voice->var_t = out;
         
         // -- mod="0.75" dstVar=out
         out = 0.75f;
         
         // -- mod="sta oscs 1 0 t 1 0" dstVar=out
         voice->lut_oscs[((unsigned int)((out * 1.0f + 0.0f) * 4)) & 3] = voice->var_t * 1.0f + 0.0f;
         
         // -- mod="pha" dstVar=out
         
         // ---- mod="pha" input "freq" seq 1/1
         
         // -- mod="$p_coarse" dstVar=voice->tmp3/*pha_freq*/
         voice->tmp3/*pha_freq*/ = shared->params[PARAM_COARSE];
         
         // -- mod="32" dstVar=voice->tmp3/*pha_freq*/
         voice->tmp3/*pha_freq*/ *= 32.0f;
         
         // -- mod="itg" dstVar=voice->tmp3/*pha_freq*/
         voice->tmp3/*pha_freq*/ = (float)(int)voice->tmp3/*pha_freq*/;
         
         // -- mod="16" dstVar=voice->tmp3/*pha_freq*/
         voice->tmp3/*pha_freq*/ -= 16.0f;
         
         // -- mod="$m_fine" dstVar=voice->tmp3/*pha_freq*/
         voice->tmp9/*seq*/ = voice->mod_fine_cur;
         
         // -- mod="fma" dstVar=voice->tmp9/*seq*/
         voice->tmp9/*seq*/ = (voice->tmp9/*seq*/ * 2.0f) + -1.0f;
         voice->tmp3/*pha_freq*/ += voice->tmp9/*seq*/;
         voice->tmp2/*pha_speed*/ = voice->note_speed_cur * voice->tmp3/*pha_freq*/;
         voice->tmp3/*pha_tmp*/ = (voice->tmp15_pha_phase);
         out = ffrac_s(voice->tmp3/*pha_tmp*/);
         voice->tmp15_pha_phase = ffrac_s(voice->tmp15_pha_phase + voice->tmp2/*pha_speed*/);
         
         // -- mod="pow" dstVar=out
         
         // ---- mod="pow" input "exp" seq 1/1
         
         // -- mod="$m_exp" dstVar=voice->tmp2/*exp*/
         voice->tmp2/*exp*/ = voice->mod_exp_cur;
         
         // -- mod="fma" dstVar=voice->tmp2/*exp*/
         voice->tmp2/*exp*/ = (voice->tmp2/*exp*/ * 2.0f) + -1.0f;
         
         // -- mod="bts" dstVar=voice->tmp2/*exp*/
         voice->tmp2/*exp*/ = loc_bipolar_to_scale(voice->tmp2/*exp*/, 8.0f, 8.0f);
         out = mathPowerf(out, voice->tmp2/*exp*/);
         
         // -- mod="$m_mul" dstVar=out
         voice->tmp2/*seq*/ = voice->mod_mul_cur;
         
         // -- mod="fma" dstVar=voice->tmp2/*seq*/
         voice->tmp2/*seq*/ = (voice->tmp2/*seq*/ * 2.0f) + -1.0f;
         out *= voice->tmp2/*seq*/;
         
         // -- mod="$m_add" dstVar=out
         voice->tmp2/*seq*/ = voice->mod_add_cur;
         
         // -- mod="fma" dstVar=voice->tmp2/*seq*/
         voice->tmp2/*seq*/ = (voice->tmp2/*seq*/ * 2.0f) + -1.0f;
         out += voice->tmp2/*seq*/;
         
         // -- mod="lut" dstVar=out
         voice->tmp2/*lut_f*/ = (out * 4);
         voice->tmp16/*lut_idx_a*/ = (int)voice->tmp2/*lut_f*/;
         voice->tmp3/*lut_frac*/ = voice->tmp2/*lut_f*/ - voice->tmp16/*lut_idx_a*/;
         voice->tmp9/*lut_a*/ = voice->lut_oscs[ ((unsigned int)voice->tmp16/*lut_idx_a*/)      & 3];
         voice->tmp10/*lut_b*/ = voice->lut_oscs[(((unsigned int)voice->tmp16/*lut_idx_a*/) + 1) & 3];
         out = voice->tmp9/*lut_a*/ + (voice->tmp10/*lut_b*/ - voice->tmp9/*lut_a*/) * voice->tmp3/*lut_frac*/;
         
         // -- mod="ipl" dstVar=out
         voice->tmp2/*b*/ = out;
         
         // ---- mod="ipl" input "b" seq 1/1
         
         // -- mod="sin" dstVar=voice->tmp2/*b*/
         voice->tmp9/*sin_freq*/ = 1;
         voice->tmp3/*sin_speed*/ = voice->note_speed_cur * voice->tmp9/*sin_freq*/;
         voice->tmp9/*sin_tmp*/ = (voice->tmp18_sin_phase);
         voice->tmp9/*sin_tmp*/ = ffrac_s(voice->tmp9/*sin_tmp*/);
         voice->tmp2/*b*/ = cycle_sine_tbl_f[(unsigned short)(16384 * voice->tmp9/*sin_tmp*/)&16383u];
         voice->tmp18_sin_phase = ffrac_s(voice->tmp18_sin_phase + voice->tmp3/*sin_speed*/);
         voice->tmp3/*t*/ = out;
         
         // ---- mod="ipl" input "t" seq 1/1
         
         // -- mod="1" dstVar=voice->tmp3/*t*/
         voice->tmp3/*t*/ = 1.0f;
         
         // -- mod="tri" dstVar=voice->tmp3/*t*/
         
         // ---- mod="tri" input "freq" seq 1/1
         
         // -- mod="8" dstVar=voice->tmp20/*tri_freq*/
         voice->tmp20/*tri_freq*/ = 8.0f;
         voice->tmp10/*tri_speed*/ = voice->note_speed_cur * voice->tmp20/*tri_freq*/;
         voice->tmp20/*tri_tmp*/ = (voice->tmp19_tri_phase);
         voice->tmp20/*tri_tmp*/ = ffrac_s(voice->tmp20/*tri_tmp*/);
         voice->tmp9/*seq*/ = (voice->tmp20/*tri_tmp*/ < 0.5f) ? (-1.0 + voice->tmp20/*tri_tmp*/ * 4.0f) : (1.0 - (voice->tmp20/*tri_tmp*/ - 0.5f)*4);
         voice->tmp19_tri_phase = ffrac_s(voice->tmp19_tri_phase + voice->tmp10/*tri_speed*/);
         
         // -- mod="fma" dstVar=voice->tmp9/*seq*/
         voice->tmp9/*seq*/ = (voice->tmp9/*seq*/ * 0.5f) + 0.5f;
         voice->tmp3/*t*/ -= voice->tmp9/*seq*/;
         
         // -- mod="$m_window" dstVar=voice->tmp3/*t*/
         voice->tmp9/*seq*/ = voice->mod_window_cur;
         voice->tmp3/*t*/ *= voice->tmp9/*seq*/;
         out = out + (voice->tmp2/*b*/ - out) * voice->tmp3/*t*/;
   
         /* end calc */

#ifdef OVERSAMPLE_FACTOR
#ifdef STEREO
         outOSL += outL;
         outOSR += outR;
#else
         outOS += out;
#endif // STEREO
#endif // OVERSAMPLE_FACTOR
         voice->note_speed_cur += voice->note_speed_inc;
         voice->note_cur       += voice->note_inc;
         voice->mod_fine_cur       += voice->mod_fine_inc;
         voice->mod_exp_cur        += voice->mod_exp_inc;
         voice->mod_mul_cur        += voice->mod_mul_inc;
         voice->mod_add_cur        += voice->mod_add_inc;
         voice->mod_saw_lpf_cur    += voice->mod_saw_lpf_inc;
         voice->mod_pul_lpf_cur    += voice->mod_pul_lpf_inc;
         voice->mod_window_cur     += voice->mod_window_inc;
      }
#ifdef OVERSAMPLE_FACTOR
      // Apply lowpass filter before downsampling
      //   (note) normalized Fc = F/Fs = 0.442947 / sqrt(oversample_factor^2 - 1)
#ifdef STEREO
      outL = outOSL * (1.0f / OVERSAMPLE_FACTOR);
      outR = outOSR * (1.0f / OVERSAMPLE_FACTOR);
#else
      out = outOS * (1.0f / OVERSAMPLE_FACTOR);
#endif // STEREO
#endif // OVERSAMPLE_FACTOR
#ifdef STEREO
      outL = Dstplugin_fix_denorm_32(outL);
      outR = Dstplugin_fix_denorm_32(outR);
#else
      out = Dstplugin_fix_denorm_32(out);
#endif // STEREO
#ifdef STEREO
      _samplesOut[k]      = outL;
      _samplesOut[k + 1u] = outR;
#else
      _samplesOut[k]      = out;
      _samplesOut[k + 1u] = out;
#endif // STEREO

      // Next frame
      k += 2u;
#ifdef STEREO
      j += 2u;
#else
      j += jStep;
#endif // STEREO
   } /* loop numFrames */
}

static st_plugin_shared_t *ST_PLUGIN_API loc_shared_new(st_plugin_info_t *_info) {
   osc_rotor_2_shared_t *ret = (osc_rotor_2_shared_t *)malloc(sizeof(osc_rotor_2_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, unsigned int _voiceIdx) {
   osc_rotor_2_voice_t *voice = (osc_rotor_2_voice_t *)malloc(sizeof(osc_rotor_2_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_rotor_2_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

#ifdef USE_CYCLE_HSE_TBL
#endif // USE_CYCLE_HSE_TBL

extern "C" {
st_plugin_info_t *osc_rotor_2_init(void) {
   osc_rotor_2_info_t *ret = (osc_rotor_2_info_t *)malloc(sizeof(osc_rotor_2_info_t));

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

      ret->base.api_version = ST_PLUGIN_API_VERSION;
      ret->base.id          = "osc_rotor_2_0xc80d8cb5";  // unique id. don't change this in future builds.
      // (note) don't set author/name/short_name when CYCLE_SKIP_UI is defined ?
      ret->base.author      = "";
      ret->base.name        = "osc_rotor_2";
      ret->base.short_name  = "osc_rotor_2";
      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;
#ifndef CYCLE_SKIP_UI
      ret->base.get_param_name                      = &loc_get_param_name;
      ret->base.get_param_group_name                = &loc_get_param_group_name;
      ret->base.get_param_group_idx                 = &loc_get_param_group_idx;
      ret->base.get_param_section_name              = &loc_get_param_section_name;
      ret->base.get_param_section_idx               = &loc_get_param_section_idx;
      ret->base.get_param_reset                     = &loc_get_param_reset;
      ret->base.query_dynamic_param_preset_values   = &loc_query_dynamic_param_preset_values;
      ret->base.query_dynamic_param_preset_name     = &loc_query_dynamic_param_preset_name;
      ret->base.get_array_param_size                = &loc_get_array_param_size;
      ret->base.get_array_param_num_variations      = &loc_get_array_param_num_variations;
      ret->base.get_array_param_variation_ptr       = &loc_get_array_param_variation_ptr;
      ret->base.set_array_param_edit_variation_idx  = &loc_set_array_param_edit_variation_idx;
      ret->base.get_array_param_element_name        = &loc_get_array_param_element_name;
      ret->base.get_array_param_element_value_range = &loc_get_array_param_element_value_range;
      ret->base.get_array_param_element_reset       = &loc_get_array_param_element_reset;
      ret->base.update_lut                          = &loc_update_lut;
#endif // CYCLE_SKIP_UI
      ret->base.get_param_value                     = &loc_get_param_value;
      ret->base.set_param_value                     = &loc_set_param_value;
#ifndef CYCLE_SKIP_UI
      ret->base.get_mod_name                        = &loc_get_mod_name;
#endif // CYCLE_SKIP_UI
      ret->base.set_sample_rate                     = &loc_set_sample_rate;
      ret->base.set_bpm                             = &loc_set_bpm;
      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_rotor_2_init();
   }
   return NULL;
}
#endif // STFX_SKIP_MAIN_INIT

} // extern "C"
