// ----
// ---- file   : env_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 env_2.cpp -o env_2.o
// ---- created: 23Nov2024 11:05:42
// ----
// ----
// ----

// (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_AD                 0
#define PARAM_S                  1
#define PARAM_R                  2
#define NUM_PARAMS               3
#ifndef CYCLE_SKIP_UI
static const char *loc_param_names[NUM_PARAMS] = {
   "ad",                      // 0: AD
   "s",                       // 1: S
   "r",                       // 2: R

};
#endif // CYCLE_SKIP_UI
static float loc_param_resets[NUM_PARAMS] = {
   0.5f,                      // 0: AD
   0.5f,                      // 1: S
   0.5f,                      // 2: R

};

#define MOD_AD                   0
#define MOD_S                    1
#define MOD_R                    2
#define MOD_GATE                 3
#define NUM_MODS                 4
#ifndef CYCLE_SKIP_UI
static const char *loc_mod_names[NUM_MODS] = {
   "ad",                   // 0: AD
   "s",                    // 1: S
   "r",                    // 2: R
   "gate",                 // 3: GATE

};
#endif // CYCLE_SKIP_UI

typedef struct env_2_info_s {
   st_plugin_info_t base;
} env_2_info_t;

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

} env_2_shared_t;

typedef struct env_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_ad_cur;
   float mod_ad_inc;
   float mod_s_cur;
   float mod_s_inc;
   float mod_r_cur;
   float mod_r_inc;
   float mod_gate_cur;
   float mod_gate_inc;

   float tmp1;
   float tmp2;
   float tmp3;
   float tmp4_sin_phase;
   float tmp5;
   float tmp6;
   float tmp7_env_last_gate;
   float tmp8_env_pos;
   float tmp9_env_rate;
   float tmp10;
   float tmp11;
   float tmp12;
   float tmp13;
   short tmp14;
   short tmp15;
   float tmp16;
   float tmp17;
   float tmp18;
   float tmp19_slw_last;
   float var_x;
   float var_v_ad;
   float var_v_s;
   float var_v_r;
   float sr_factor;
   float add_sr_0;  // 0.05f
   float add_sr_1;  // 0.1f

} env_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] = {
0, 4, 7, 10, 15, 21, 29, 38, 49, 61, 75, 90, 108, 128, 150, 175, 203, 233, 267, 304, 345, 390, 440, 494, 553, 617, 687, 760, 838, 920, 1002, 1086, 
1168, 1247, 1322, 1393, 1460, 1521, 1577, 1629, 1676, 1718, 1757, 1793, 1825, 1854, 1880, 1903, 1925, 1943, 1960, 1976, 1989, 2001, 2010, 2019, 2027, 2033, 2038, 2042, 2045, 2046, 2047, 2047, 
2047, 2046, 2045, 2045, 2044, 2043, 2041, 2040, 2039, 2037, 2035, 2032, 2030, 2028, 2025, 2022, 2019, 2015, 2012, 2008, 2003, 1999, 1994, 1988, 1983, 1977, 1970, 1963, 1956, 1948, 1940, 1931, 
1922, 1913, 1902, 1892, 1881, 1869, 1857, 1844, 1831, 1818, 1804, 1790, 1776, 1762, 1747, 1733, 1719, 1704, 1690, 1676, 1662, 1648, 1634, 1621, 1607, 1594, 1582, 1569, 1557, 1545, 1534, 1523, 
1511, 1501, 1490, 1480, 1470, 1460, 1450, 1440, 1431, 1422, 1413, 1405, 1396, 1388, 1380, 1372, 1364, 1356, 1349, 1341, 1334, 1327, 1320, 1313, 1307, 1300, 1294, 1287, 1281, 1275, 1269, 1263, 
1257, 1252, 1246, 1241, 1235, 1230, 1225, 1220, 1214, 1209, 1205, 1200, 1195, 1190, 1186, 1181, 1177, 1172, 1168, 1163, 1159, 1155, 1150, 1146, 1142, 1138, 1133, 1129, 1125, 1120, 1116, 1112, 
1108, 1104, 1099, 1095, 1091, 1087, 1083, 1079, 1075, 1070, 1066, 1062, 1058, 1054, 1050, 1046, 1042, 1038, 1034, 1030, 1027, 1023, 1019, 1015, 1011, 1007, 1003, 1000, 996, 992, 988, 984, 
981, 977, 973, 970, 966, 962, 959, 955, 951, 948, 944, 941, 937, 933, 930, 926, 923, 919, 916, 912, 909, 906, 902, 899, 895, 892, 889, 885, 882, 879, 875, 872, 
869, 865, 862, 859, 856, 852, 849, 846, 843, 840, 836, 833, 830, 827, 824, 821, 818, 815, 812, 809, 805, 802, 799, 796, 793, 790, 787, 784, 782, 779, 776, 773, 
770, 767, 764, 761, 758, 755, 753, 750, 747, 744, 741, 738, 736, 733, 730, 727, 725, 722, 719, 716, 714, 711, 708, 706, 703, 700, 698, 695, 692, 690, 687, 685, 
682, 679, 677, 674, 672, 669, 667, 664, 662, 659, 657, 654, 652, 649, 647, 644, 642, 639, 637, 634, 632, 630, 627, 625, 622, 620, 618, 615, 613, 611, 608, 606, 
604, 601, 599, 597, 594, 592, 590, 588, 585, 583, 581, 579, 576, 574, 572, 570, 567, 565, 563, 561, 559, 557, 554, 552, 550, 548, 546, 544, 542, 540, 537, 535, 
533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 509, 507, 505, 503, 501, 499, 497, 495, 493, 491, 489, 487, 485, 483, 481, 479, 477, 476, 474, 472, 
470, 468, 466, 464, 462, 461, 459, 457, 455, 453, 451, 450, 448, 446, 444, 442, 441, 439, 437, 435, 434, 432, 430, 428, 427, 425, 423, 421, 420, 418, 416, 415, 
413, 411, 409, 408, 406, 404, 403, 401, 399, 398, 396, 395, 393, 391, 390, 388, 386, 385, 383, 382, 380, 378, 377, 375, 374, 372, 371, 369, 367, 366, 364, 363, 
361, 360, 358, 357, 355, 354, 352, 351, 349, 348, 346, 345, 343, 342, 340, 339, 337, 336, 334, 333, 332, 330, 329, 327, 326, 324, 323, 322, 320, 319, 317, 316, 
315, 313, 312, 310, 309, 308, 306, 305, 304, 302, 301, 300, 298, 297, 296, 294, 293, 292, 290, 289, 288, 286, 285, 284, 283, 281, 280, 279, 277, 276, 275, 274, 
272, 271, 270, 269, 267, 266, 265, 264, 263, 261, 260, 259, 258, 257, 255, 254, 253, 252, 251, 249, 248, 247, 246, 245, 244, 242, 241, 240, 239, 238, 237, 236, 
234, 233, 232, 231, 230, 229, 228, 227, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, 214, 213, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 
200, 199, 198, 197, 196, 195, 194, 193, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, 179, 179, 178, 177, 176, 175, 174, 173, 172, 171, 170, 
169, 168, 167, 167, 166, 165, 164, 163, 162, 161, 160, 159, 159, 158, 157, 156, 155, 154, 153, 152, 152, 151, 150, 149, 148, 147, 147, 146, 145, 144, 143, 142, 
142, 141, 140, 139, 138, 138, 137, 136, 135, 134, 134, 133, 132, 131, 130, 130, 129, 128, 127, 127, 126, 125, 124, 124, 123, 122, 121, 121, 120, 119, 118, 118, 
117, 116, 116, 115, 114, 113, 113, 112, 111, 111, 110, 109, 108, 108, 107, 106, 106, 105, 104, 104, 103, 102, 102, 101, 100, 100, 99, 98, 98, 97, 96, 96, 
95, 94, 94, 93, 93, 92, 91, 91, 90, 89, 89, 88, 88, 87, 86, 86, 85, 85, 84, 83, 83, 82, 82, 81, 80, 80, 79, 79, 78, 78, 77, 76, 
76, 75, 75, 74, 74, 73, 72, 72, 71, 71, 70, 70, 69, 69, 68, 68, 67, 67, 66, 66, 65, 65, 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 
59, 58, 58, 58, 57, 57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 47, 46, 46, 45, 45, 
44, 44, 44, 43, 43, 42, 42, 42, 41, 41, 40, 40, 40, 39, 39, 38, 38, 38, 37, 37, 37, 36, 36, 35, 35, 35, 34, 34, 34, 33, 33, 32, 
32, 32, 31, 31, 31, 30, 30, 30, 29, 29, 29, 28, 28, 28, 27, 27, 27, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 23, 23, 23, 22, 22, 
22, 22, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19, 19, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 
14, 13, 13, 13, 13, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 
7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 
2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
};

#define USE_CYCLE_SINE_TBL  defined
static float cycle_sine_tbl_f[16384];

static void loc_prepare(st_plugin_voice_t *_voice);

void loc_prepare(st_plugin_voice_t *_voice) {
   ST_PLUGIN_VOICE_CAST(env_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(env_2_shared_t);

   float out = 0.0f;
   (void)out;
   // -------- lane "prepare" modIdx=0 class=$m_ad
   
   // -- mod="$m_ad" dstVar=out
   out = voice->mod_ad_cur;
   
   // -- mod="clp" dstVar=out
   if(out > 1.0f) out = 1.0f;
   else if(out < 0.0f) out = 0.0f;
   
   // -- mod="pow" dstVar=out
   out = out * out * out;
   
   // -- mod="kbd" dstVar=out
   
   // ---- mod="kbd" input "amt" seq 1/1
   
   // -- mod="1" dstVar=voice->tmp1/*amt*/
   voice->tmp1/*amt*/ = 1.0f;
   voice->tmp2/*off*/ = 0;
   voice->tmp3/*rate*/  = (voice->note_cur + (voice->tmp2/*off*/ * 12.0f) - 60.0f) * voice->tmp1/*amt*/;
   out *= powf(2.0f, voice->tmp3/*rate*/ * (1.0f / 12.0f));
   
   // -- mod="sto v_ad" dstVar=out
   voice->var_v_ad = out;
   
   // -- mod="$m_s" dstVar=out
   out = voice->mod_s_cur;
   
   // -- mod="sto v_s" dstVar=out
   voice->var_v_s = out;
   
   // -- mod="$m_r" dstVar=out
   out = voice->mod_r_cur;
   
   // -- mod="clp" dstVar=out
   if(out > 1.0f) out = 1.0f;
   else if(out < 0.0f) out = 0.0f;
   
   // -- mod="pow" dstVar=out
   out = out * out * out;
   
   // -- mod="kbd" dstVar=out
   
   // ---- mod="kbd" input "amt" seq 1/1
   
   // -- mod="1" dstVar=voice->tmp1/*amt*/
   voice->tmp1/*amt*/ = 1.0f;
   voice->tmp2/*off*/ = 0;
   voice->tmp3/*rate*/  = (voice->note_cur + (voice->tmp2/*off*/ * 12.0f) - 60.0f) * voice->tmp1/*amt*/;
   out *= powf(2.0f, voice->tmp3/*rate*/ * (1.0f / 12.0f));
   
   // -- mod="sto v_r" dstVar=out
   voice->var_v_r = out;
} /* end prepare */


#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(env_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(env_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(env_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(env_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(env_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(env_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(env_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(env_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: // "env"
         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(env_2_voice_t);
   if(_sampleRate != voice->sample_rate)
   {
      voice->sr_factor = 12000.0f / _sampleRate;
      voice->add_sr_0 = voice->sr_factor * 0.05f;
      voice->add_sr_1 = voice->sr_factor * 0.1f;
   }
   voice->sample_rate = _sampleRate;
}

static void ST_PLUGIN_API loc_set_bpm(st_plugin_voice_t *_voice,
                                      float              _bpm
                                      ) {
   ST_PLUGIN_VOICE_CAST(env_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(env_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(env_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->tmp4_sin_phase = 0.0f;
      voice->tmp7_env_last_gate = 0.0f;
      voice->tmp8_env_pos = 0.0f;
      voice->tmp9_env_rate = 0.0f;
      voice->tmp19_slw_last = 0.0f;
      voice->var_x = 0.0f;
      voice->var_v_ad = 0.0f;
      voice->var_v_s = 0.0f;
      voice->var_v_r = 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(env_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(env_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(env_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 modad           = shared->params[PARAM_AD          ]                       + voice->mods[MOD_AD           ];
   float mods            = shared->params[PARAM_S           ]                       + voice->mods[MOD_S            ];
   float modr            = shared->params[PARAM_R           ]                       + voice->mods[MOD_R            ];
   float modgate         = voice->mods[MOD_GATE         ];

   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_ad_inc           = (modad              - voice->mod_ad_cur            ) * recBlockSize;
      voice->mod_s_inc            = (mods               - voice->mod_s_cur             ) * recBlockSize;
      voice->mod_r_inc            = (modr               - voice->mod_r_cur             ) * recBlockSize;
      voice->mod_gate_inc         = (modgate            - voice->mod_gate_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_ad_cur           = modad;
      voice->mod_ad_inc           = 0.0f;
      voice->mod_s_cur            = mods;
      voice->mod_s_inc            = 0.0f;
      voice->mod_r_cur            = modr;
      voice->mod_r_inc            = 0.0f;
      voice->mod_gate_cur         = modgate;
      voice->mod_gate_inc         = 0.0f;
      loc_prepare(&voice->base);
   }

   // 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(env_2_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(env_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
         voice->tmp6/*sin_freq*/ = 1;
         voice->tmp5/*sin_speed*/ = voice->note_speed_cur * voice->tmp6/*sin_freq*/;
         voice->tmp6/*sin_tmp*/ = (voice->tmp4_sin_phase);
         voice->tmp6/*sin_tmp*/ = ffrac_s(voice->tmp6/*sin_tmp*/);
         out = cycle_sine_tbl_f[(unsigned short)(16384 * voice->tmp6/*sin_tmp*/)&16383u];
         voice->tmp4_sin_phase = ffrac_s(voice->tmp4_sin_phase + voice->tmp5/*sin_speed*/);
         
         // -- mod="$m_gate" dstVar=out
         voice->tmp5/*seq*/ = voice->mod_gate_cur;
         
         // -- mod="env" dstVar=voice->tmp5/*seq*/
         // env sDstVar="voice->tmp5/*seq*/"
         
         // ---- mod="env" input "s" seq 1/1
         
         // -- mod="$v_s" dstVar=voice->tmp6/*env_suspos*/
         voice->tmp6/*env_suspos*/ = voice->var_v_s;
         
         // ---- mod="env" input "scl" seq 1/1
         
         // -- mod="0.01" dstVar=voice->tmp10/*env_loop_scl*/
         voice->tmp10/*env_loop_scl*/ = 0.01f;
         
         // ---- mod="env" input "ad" seq 1/1
         
         // -- mod="$v_ad" dstVar=voice->tmp11/*addsr_ad*/
         voice->tmp11/*addsr_ad*/ = voice->var_v_ad;
         voice->tmp11/*addsr_ad*/ *= voice->sr_factor;
         
         // ---- mod="env" input "r" seq 1/1
         
         // -- mod="$v_r" dstVar=voice->tmp12/*addsr_r*/
         voice->tmp12/*addsr_r*/ = voice->var_v_r;
         voice->tmp12/*addsr_r*/ *= voice->sr_factor;
         if(voice->tmp5/*seq*/ > 0.001f && voice->tmp7_env_last_gate <= 0.001f)
         {
            voice->tmp8_env_pos = 0.0f;
            voice->tmp7_env_last_gate = 1.0f;
         }
         voice->tmp5/*seq*/ = voice->tmp8_env_pos;
         
         // -- mod="lut" dstVar=voice->tmp5/*seq*/
         voice->tmp13/*lut_f*/ = (voice->tmp5/*seq*/ * 1024);
         voice->tmp16/*lut_frac*/ = voice->tmp13/*lut_f*/ - (int)voice->tmp13/*lut_f*/;
         voice->tmp14/*lut_idx_a*/ = (int)voice->tmp13/*lut_f*/;
         if(voice->tmp14/*lut_idx_a*/ < 0)
         {
            voice->tmp14/*lut_idx_a*/ = 0;
            voice->tmp15/*lut_idx_b*/ = 0;
            voice->tmp16/*lut_frac*/ = 0.0f;
         }
         else if(voice->tmp14/*lut_idx_a*/ > 1023)
         {
            voice->tmp14/*lut_idx_a*/ = 1023;
            voice->tmp15/*lut_idx_b*/ = 1023;
         }
         else
         {
            voice->tmp15/*lut_idx_b*/ = voice->tmp14/*lut_idx_a*/ + 1;
            if(voice->tmp15/*lut_idx_b*/ > 1023)
               voice->tmp15/*lut_idx_b*/ = 1023;
         }
         voice->tmp17/*lut_a*/ = curve_0[(unsigned int)voice->tmp14/*lut_idx_a*/] * (1.0f / 2048);
         voice->tmp18/*lut_b*/ = curve_0[(unsigned int)voice->tmp15/*lut_idx_b*/] * (1.0f / 2048);
         voice->tmp5/*seq*/ = voice->tmp17/*lut_a*/ + (voice->tmp18/*lut_b*/ - voice->tmp17/*lut_a*/) * voice->tmp16/*lut_frac*/;
         if(voice->tmp8_env_pos < voice->tmp6/*env_suspos*/)
           voice->tmp9_env_rate = voice->tmp10/*env_loop_scl*/ * voice->tmp11/*addsr_ad*/;
         else
           voice->tmp9_env_rate = voice->tmp10/*env_loop_scl*/ * voice->tmp12/*addsr_r*/;
         {
            voice->tmp13/*seq_loop_lastpos*/ = voice->tmp8_env_pos;
            voice->tmp8_env_pos += voice->tmp9_env_rate;
            if(voice->tmp8_env_pos > voice->tmp6/*env_suspos*/ && voice->tmp13/*seq_loop_lastpos*/ < voice->tmp6/*env_suspos*/ && (0.0f != voice->tmp11/*addsr_ad*/))
            {
               voice->tmp8_env_pos = voice->tmp6/*env_suspos*/ + (voice->tmp8_env_pos - voice->tmp6/*env_suspos*/) * voice->tmp10/*env_loop_scl*/ * (voice->tmp12/*addsr_r*/ / voice->tmp11/*addsr_ad*/);
            }
            if(voice->tmp8_env_pos >= 1.0f)
            {
               voice->tmp8_env_pos = ffrac_s(voice->tmp8_env_pos);
            }
         }
         
         // -- mod="slw" dstVar=voice->tmp5/*seq*/
         if(voice->tmp19_slw_last < voice->tmp5/*seq*/)
            voice->tmp5/*seq*/ = voice->tmp19_slw_last + (voice->tmp5/*seq*/ - voice->tmp19_slw_last) * voice->add_sr_0;
         else
            voice->tmp5/*seq*/ = voice->tmp19_slw_last + (voice->tmp5/*seq*/ - voice->tmp19_slw_last) * voice->add_sr_1;
         voice->tmp19_slw_last = voice->tmp5/*seq*/;
         out *= voice->tmp5/*seq*/;
         
         // -- mod="clp" dstVar=out
         if(out > 0.999f) out = 0.999f;
         else if(out < -0.999f) out = -0.999f;
   
         /* 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_ad_cur         += voice->mod_ad_inc;
         voice->mod_s_cur          += voice->mod_s_inc;
         voice->mod_r_cur          += voice->mod_r_inc;
         voice->mod_gate_cur       += voice->mod_gate_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) {
   env_2_shared_t *ret = (env_2_shared_t *)malloc(sizeof(env_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) {
   env_2_voice_t *voice = (env_2_voice_t *)malloc(sizeof(env_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(env_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 *env_2_init(void) {
   env_2_info_t *ret = (env_2_info_t *)malloc(sizeof(env_2_info_t));

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

      ret->base.api_version = ST_PLUGIN_API_VERSION;
      ret->base.id          = "env_2_0x1ac13c73";  // 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        = "env_2";
      ret->base.short_name  = "env_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 env_2_init();
   }
   return NULL;
}
#endif // STFX_SKIP_MAIN_INIT

} // extern "C"
