// ----
// ---- file   : stereo_waveshaper_ms_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 stereo_waveshaper_ms_1.cpp -o stereo_waveshaper_ms_1.o
// ---- created: 31May2025 14:22:44
// ----
// ----
// ----

// (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 STEREO  defined

#define PARAM_DRYWET             0
#define PARAM_IOGAIN             1
#define PARAM_PAN_MS             2
#define PARAM_PAN                3
#define PARAM_VOL                4
#define NUM_PARAMS               5
#ifndef CYCLE_SKIP_UI
static const char *loc_param_names[NUM_PARAMS
#if defined(_MSC_VER) && 0 == NUM_PARAMS
                                   +1
#endif
                                   ] = {
#if defined(_MSC_VER) && 0 == NUM_PARAMS
                                   ""
#else
   "drywet",                  // 0: DRYWET
   "iogain",                  // 1: IOGAIN
   "pan_ms",                  // 2: PAN_MS
   "pan",                     // 3: PAN
   "vol",                     // 4: VOL

#endif
};
#endif // CYCLE_SKIP_UI
static float loc_param_resets[NUM_PARAMS
#if defined(_MSC_VER) && 0 == NUM_PARAMS
                                   +1
#endif
                              ] = {
#if defined(_MSC_VER) && 0 == NUM_PARAMS
   0.0f
#else
   0.77f,                     // 0: DRYWET
   0.5f,                      // 1: IOGAIN
   0.5f,                      // 2: PAN_MS
   0.5f,                      // 3: PAN
   0.5f,                      // 4: VOL

#endif
};

#define MOD_DRYWET               0
#define MOD_IOGAIN               1
#define MOD_PAN_MS               2
#define MOD_PAN                  3
#define MOD_VOL                  4
#define NUM_MODS                 5
#ifndef CYCLE_SKIP_UI
static const char *loc_mod_names[NUM_MODS
#if defined(_MSC_VER) && 0 == NUM_MODS
   +1
#endif
                                 ] = {
#if defined(_MSC_VER) && 0 == NUM_MODS
   ""
#else
   "drywet",               // 0: DRYWET
   "iogain",               // 1: IOGAIN
   "pan_ms",               // 2: PAN_MS
   "pan",                  // 3: PAN
   "vol",                  // 4: VOL

#endif
};
#endif // CYCLE_SKIP_UI

typedef struct stereo_waveshaper_ms_1_info_s {
   st_plugin_info_t base;
} stereo_waveshaper_ms_1_info_t;

typedef struct stereo_waveshaper_ms_1_shared_s {
   st_plugin_shared_t base;
   float params[NUM_PARAMS
#if defined(_MSC_VER) && 0 == NUM_PARAMS
                +1
#endif
                ];

} stereo_waveshaper_ms_1_shared_t;

typedef struct stereo_waveshaper_ms_1_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
#if defined(_MSC_VER) && 0 == NUM_MODS
                          +1
#endif
                          ];
   float mod_drywet_cur;
   float mod_drywet_inc;
   float mod_iogain_cur;
   float mod_iogain_inc;
   float mod_pan_ms_cur;
   float mod_pan_ms_inc;
   float mod_pan_cur;
   float mod_pan_inc;
   float mod_vol_cur;
   float mod_vol_inc;

   float tmp1;
   float tmp2;
   float tmp3;
   float tmp4;
   float tmp5;
   float tmp6;
   float tmp7;
   short tmp8;
   short tmp9;
   short tmp10;
   float tmp11;
   float tmp12;
   float tmp13;
   float var_x;
   float var_v_i;
   float var_v_o_m;
   float var_v_o_s;
   float var_v_amp_l;
   float var_v_amp_r;
   float var_prepare__vpan_ms;
   float var_prepare__vpan;
   float var_out__v_mid;
   float var_out__v_side;
   float sr_factor;

} stereo_waveshaper_ms_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 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[512] = {
0, 5, 11, 16, 22, 27, 33, 38, 44, 49, 55, 60, 66, 71, 77, 82, 88, 93, 99, 104, 110, 115, 120, 126, 131, 137, 142, 148, 153, 159, 164, 170, 
175, 181, 186, 192, 197, 203, 208, 214, 219, 224, 230, 235, 241, 246, 252, 257, 263, 268, 274, 279, 284, 290, 295, 301, 306, 312, 317, 323, 328, 333, 339, 344, 
350, 355, 361, 366, 371, 377, 382, 388, 393, 398, 404, 409, 415, 420, 425, 431, 436, 442, 447, 452, 458, 463, 469, 474, 479, 485, 490, 495, 501, 506, 512, 517, 
522, 528, 533, 538, 544, 549, 554, 560, 565, 570, 576, 581, 586, 592, 597, 602, 608, 613, 618, 624, 629, 634, 640, 645, 650, 656, 661, 666, 671, 677, 682, 687, 
693, 698, 703, 708, 714, 719, 724, 729, 735, 740, 745, 750, 756, 761, 766, 771, 777, 782, 787, 792, 798, 803, 808, 813, 818, 824, 829, 834, 839, 844, 850, 855, 
860, 865, 870, 876, 881, 886, 891, 896, 901, 906, 912, 917, 922, 927, 932, 937, 942, 947, 953, 958, 963, 968, 973, 978, 983, 988, 993, 998, 1003, 1008, 1014, 1019, 
1024, 1029, 1034, 1039, 1044, 1049, 1054, 1059, 1064, 1069, 1074, 1079, 1084, 1089, 1094, 1099, 1104, 1109, 1114, 1119, 1123, 1128, 1133, 1138, 1143, 1148, 1153, 1158, 1163, 1168, 1173, 1177, 
1182, 1187, 1192, 1197, 1202, 1206, 1211, 1216, 1221, 1226, 1230, 1235, 1240, 1245, 1250, 1254, 1259, 1264, 1269, 1273, 1278, 1283, 1287, 1292, 1297, 1302, 1306, 1311, 1315, 1320, 1325, 1329, 
1334, 1339, 1343, 1348, 1352, 1357, 1362, 1366, 1371, 1375, 1380, 1384, 1389, 1393, 1398, 1402, 1407, 1411, 1415, 1420, 1424, 1429, 1433, 1437, 1442, 1446, 1451, 1455, 1459, 1464, 1468, 1472, 
1476, 1481, 1485, 1489, 1493, 1498, 1502, 1506, 1510, 1514, 1518, 1523, 1527, 1531, 1535, 1539, 1543, 1547, 1551, 1555, 1559, 1563, 1567, 1571, 1575, 1579, 1583, 1587, 1591, 1595, 1598, 1602, 
1606, 1610, 1614, 1617, 1621, 1625, 1629, 1632, 1636, 1640, 1643, 1647, 1651, 1654, 1658, 1661, 1665, 1668, 1672, 1675, 1679, 1682, 1686, 1689, 1692, 1696, 1699, 1702, 1706, 1709, 1712, 1716, 
1719, 1722, 1725, 1728, 1731, 1735, 1738, 1741, 1744, 1747, 1750, 1753, 1756, 1759, 1762, 1765, 1768, 1771, 1773, 1776, 1779, 1782, 1785, 1787, 1790, 1793, 1796, 1798, 1801, 1804, 1806, 1809, 
1811, 1814, 1816, 1819, 1821, 1824, 1826, 1829, 1831, 1834, 1836, 1838, 1841, 1843, 1845, 1848, 1850, 1852, 1854, 1857, 1859, 1861, 1863, 1865, 1867, 1870, 1872, 1874, 1876, 1878, 1880, 1882, 
1884, 1886, 1888, 1890, 1892, 1894, 1896, 1898, 1900, 1902, 1904, 1905, 1907, 1909, 1911, 1913, 1915, 1917, 1918, 1920, 1922, 1924, 1926, 1927, 1929, 1931, 1933, 1934, 1936, 1938, 1940, 1941, 
1943, 1945, 1946, 1948, 1950, 1951, 1953, 1955, 1956, 1958, 1960, 1961, 1963, 1965, 1966, 1968, 1970, 1971, 1973, 1975, 1976, 1978, 1980, 1981, 1983, 1984, 1986, 1988, 1989, 1991, 1993, 1994, 
1996, 1998, 1999, 2001, 2002, 2004, 2006, 2007, 2009, 2011, 2012, 2014, 2016, 2017, 2019, 2021, 2022, 2024, 2026, 2027, 2029, 2031, 2032, 2034, 2036, 2037, 2039, 2041, 2042, 2044, 2046, 2047, 
};
static short curve_1[512] = {
0, 12, 25, 38, 50, 63, 75, 87, 99, 111, 123, 135, 146, 158, 169, 180, 191, 202, 212, 223, 233, 244, 254, 264, 274, 284, 294, 304, 314, 324, 333, 343, 
352, 361, 371, 380, 389, 398, 407, 416, 425, 434, 443, 452, 461, 469, 478, 487, 495, 504, 512, 521, 529, 537, 546, 554, 562, 571, 579, 587, 595, 603, 611, 620, 
628, 636, 644, 652, 660, 668, 676, 684, 692, 700, 708, 715, 723, 731, 739, 747, 755, 763, 770, 778, 786, 794, 802, 810, 817, 825, 833, 841, 849, 856, 864, 872, 
880, 888, 895, 903, 911, 919, 927, 934, 942, 950, 958, 966, 974, 981, 989, 997, 1005, 1013, 1021, 1029, 1037, 1044, 1052, 1060, 1068, 1076, 1084, 1092, 1100, 1108, 1116, 1124, 
1132, 1140, 1149, 1157, 1165, 1173, 1181, 1189, 1197, 1206, 1214, 1222, 1230, 1239, 1247, 1255, 1264, 1272, 1280, 1289, 1297, 1304, 1312, 1320, 1327, 1335, 1342, 1349, 1356, 1363, 1370, 1376, 
1383, 1389, 1395, 1402, 1408, 1414, 1420, 1425, 1431, 1437, 1442, 1448, 1453, 1458, 1464, 1469, 1474, 1479, 1484, 1489, 1494, 1499, 1503, 1508, 1513, 1517, 1522, 1526, 1530, 1535, 1539, 1543, 
1547, 1551, 1555, 1559, 1563, 1567, 1571, 1575, 1579, 1583, 1586, 1590, 1593, 1597, 1601, 1604, 1608, 1611, 1614, 1618, 1621, 1624, 1628, 1631, 1634, 1637, 1640, 1643, 1646, 1649, 1652, 1655, 
1658, 1661, 1664, 1667, 1670, 1673, 1675, 1678, 1681, 1683, 1686, 1689, 1691, 1694, 1697, 1699, 1702, 1704, 1707, 1709, 1712, 1714, 1716, 1719, 1721, 1723, 1726, 1728, 1730, 1733, 1735, 1737, 
1739, 1741, 1744, 1746, 1748, 1750, 1752, 1754, 1756, 1758, 1760, 1762, 1764, 1766, 1768, 1770, 1772, 1774, 1776, 1778, 1780, 1782, 1783, 1785, 1787, 1789, 1791, 1792, 1794, 1796, 1798, 1799, 
1801, 1803, 1805, 1806, 1808, 1810, 1811, 1813, 1815, 1816, 1818, 1819, 1821, 1823, 1824, 1826, 1827, 1829, 1830, 1832, 1833, 1835, 1836, 1838, 1839, 1841, 1842, 1844, 1845, 1847, 1848, 1849, 
1851, 1852, 1854, 1855, 1856, 1858, 1859, 1860, 1862, 1863, 1864, 1866, 1867, 1868, 1870, 1871, 1872, 1874, 1875, 1876, 1877, 1879, 1880, 1881, 1882, 1884, 1885, 1886, 1887, 1888, 1890, 1891, 
1892, 1893, 1894, 1896, 1897, 1898, 1899, 1900, 1901, 1903, 1904, 1905, 1906, 1907, 1908, 1909, 1910, 1912, 1913, 1914, 1915, 1916, 1917, 1918, 1919, 1920, 1921, 1922, 1924, 1925, 1926, 1927, 
1928, 1929, 1930, 1931, 1932, 1933, 1934, 1935, 1936, 1937, 1938, 1939, 1940, 1941, 1942, 1943, 1944, 1945, 1946, 1947, 1948, 1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 
1960, 1961, 1962, 1963, 1964, 1965, 1966, 1967, 1968, 1969, 1969, 1970, 1971, 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1980, 1981, 1982, 1983, 1984, 1984, 1985, 1986, 1987, 1988, 1989, 
1990, 1991, 1992, 1993, 1994, 1995, 1996, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2016, 2017, 2018, 
2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033, 2034, 2035, 2036, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047, 2047, 
};

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(stereo_waveshaper_ms_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(stereo_waveshaper_ms_1_shared_t);

   float out = 0.0f;
   (void)out;
   // -------- lane "prepare" modIdx=0 modString=$m_pan_ms
   
   // -- mod="$m_pan_ms" dstVar=out
   out = voice->mod_pan_ms_cur;
   
   // -- mod="fma" dstVar=out
   out = (out * 2.0f) + -1.0f;
   
   // -- mod="set prepare__vpan_ms" dstVar=out
   voice->var_prepare__vpan_ms = out;
   
   // -- mod="$m_iogain" dstVar=out
   out = voice->mod_iogain_cur;
   
   // -- mod="fma" dstVar=out
   out = (out * 2.0f) + -1.0f;
   
   // -- mod="bts" dstVar=out
   out = loc_bipolar_to_scale(out, 2.0f, 2.0f);
   
   // -- mod="sto v_i" dstVar=out
   voice->var_v_i = out;
   
   // -- mod="rcp" dstVar=out
   if(0.0f != out)
      out = 1.0f / out;
   
   // -- mod="pre" dstVar=out
   voice->tmp1/*seq*/ = out;
   
   // -- mod="pan" dstVar=voice->tmp1/*seq*/
   voice->tmp2/*seq*/ = voice->tmp1/*seq*/;
   
   // ---- mod="pan" input "pos" seq 1/1
   
   // -- mod="$prepare__vpan_ms" dstVar=voice->tmp3/*pan_pos*/
   voice->tmp3/*pan_pos*/ = voice->var_prepare__vpan_ms;
   voice->tmp2/*seq*/ = (voice->tmp3/*pan_pos*/ < 0.0f) ? 1.0f : (1.0f - voice->tmp3/*pan_pos*/);
   voice->tmp1/*seq*/ *= voice->tmp2/*seq*/;
   
   // -- mod="sto v_o_m" dstVar=voice->tmp1/*seq*/
   voice->var_v_o_m = voice->tmp1/*seq*/;
   // (ignore last output via '.')
   
   // -- mod="pan" dstVar=out
   voice->tmp1/*seq*/ = out;
   
   // ---- mod="pan" input "pos" seq 1/1
   
   // -- mod="$prepare__vpan_ms" dstVar=voice->tmp2/*pan_pos*/
   voice->tmp2/*pan_pos*/ = voice->var_prepare__vpan_ms;
   voice->tmp1/*seq*/ = (voice->tmp2/*pan_pos*/ > 0.0f) ? 1.0f : (1.0f + voice->tmp2/*pan_pos*/);
   out *= voice->tmp1/*seq*/;
   
   // -- mod="sto v_o_s" dstVar=out
   voice->var_v_o_s = out;
   
   // -- mod="$m_pan" dstVar=out
   out = voice->mod_pan_cur;
   
   // -- mod="fma" dstVar=out
   out = (out * 2.0f) + -1.0f;
   
   // -- mod="set prepare__vpan" dstVar=out
   voice->var_prepare__vpan = out;
   
   // -- mod="$m_vol" dstVar=out
   out = voice->mod_vol_cur;
   
   // -- mod="fma" dstVar=out
   out = (out * 2.0f) + -1.0f;
   
   // -- mod="bts" dstVar=out
   out = loc_bipolar_to_scale(out, 4.0f, 4.0f);
   
   // -- mod="pre" dstVar=out
   voice->tmp1/*seq*/ = out;
   
   // -- mod="pan" dstVar=voice->tmp1/*seq*/
   voice->tmp4/*seq*/ = voice->tmp1/*seq*/;
   
   // ---- mod="pan" input "pos" seq 1/1
   
   // -- mod="$prepare__vpan" dstVar=voice->tmp5/*pan_pos*/
   voice->tmp5/*pan_pos*/ = voice->var_prepare__vpan;
   voice->tmp4/*seq*/ = (voice->tmp5/*pan_pos*/ < 0.0f) ? 1.0f : (1.0f - voice->tmp5/*pan_pos*/);
   voice->tmp1/*seq*/ *= voice->tmp4/*seq*/;
   
   // -- mod="sto v_amp_l" dstVar=voice->tmp1/*seq*/
   voice->var_v_amp_l = voice->tmp1/*seq*/;
   // (ignore last output via '.')
   
   // -- mod="pan" dstVar=out
   voice->tmp1/*seq*/ = out;
   
   // ---- mod="pan" input "pos" seq 1/1
   
   // -- mod="$prepare__vpan" dstVar=voice->tmp4/*pan_pos*/
   voice->tmp4/*pan_pos*/ = voice->var_prepare__vpan;
   voice->tmp1/*seq*/ = (voice->tmp4/*pan_pos*/ > 0.0f) ? 1.0f : (1.0f + voice->tmp4/*pan_pos*/);
   out *= voice->tmp1/*seq*/;
   
   // -- mod="sto v_amp_r" dstVar=out
   voice->var_v_amp_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(stereo_waveshaper_ms_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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_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: // "ws_mid"
         copySz = Dstplugin_min(_srcNumElements, 512);
         for(unsigned int i = 0u; i < copySz; i++)
            curve_0[i] = (short)(_src[i] * 2048);
         break;
      case 1: // "ws_side"
         copySz = Dstplugin_min(_srcNumElements, 512);
         for(unsigned int i = 0u; i < copySz; i++)
            curve_1[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(stereo_waveshaper_ms_1_voice_t);
   if(_sampleRate != voice->sample_rate)
   {
      voice->sr_factor = 48000.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(stereo_waveshaper_ms_1_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(stereo_waveshaper_ms_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(stereo_waveshaper_ms_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 = (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->var_x = 0.0f;
      voice->var_v_i = 0.0f;
      voice->var_v_o_m = 0.0f;
      voice->var_v_o_s = 0.0f;
      voice->var_v_amp_l = 0.0f;
      voice->var_v_amp_r = 0.0f;
      voice->var_prepare__vpan_ms = 0.0f;
      voice->var_prepare__vpan = 0.0f;
      voice->var_out__v_mid = 0.0f;
      voice->var_out__v_side = 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(stereo_waveshaper_ms_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(stereo_waveshaper_ms_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(stereo_waveshaper_ms_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 moddrywet       = shared->params[PARAM_DRYWET      ]                       + voice->mods[MOD_DRYWET       ];
   float modiogain       = shared->params[PARAM_IOGAIN      ]                       + voice->mods[MOD_IOGAIN       ];
   float modpan_ms       = shared->params[PARAM_PAN_MS      ]                       + voice->mods[MOD_PAN_MS       ];
   float modpan          = shared->params[PARAM_PAN         ]                       + voice->mods[MOD_PAN          ];
   float modvol          = shared->params[PARAM_VOL         ]                       + voice->mods[MOD_VOL          ];

   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_drywet_inc       = (moddrywet          - voice->mod_drywet_cur        ) * recBlockSize;
      voice->mod_iogain_inc       = (modiogain          - voice->mod_iogain_cur        ) * recBlockSize;
      voice->mod_pan_ms_inc       = (modpan_ms          - voice->mod_pan_ms_cur        ) * recBlockSize;
      voice->mod_pan_inc          = (modpan             - voice->mod_pan_cur           ) * recBlockSize;
      voice->mod_vol_inc          = (modvol             - voice->mod_vol_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_drywet_cur       = moddrywet;
      voice->mod_drywet_inc       = 0.0f;
      voice->mod_iogain_cur       = modiogain;
      voice->mod_iogain_inc       = 0.0f;
      voice->mod_pan_ms_cur       = modpan_ms;
      voice->mod_pan_ms_inc       = 0.0f;
      voice->mod_pan_cur          = modpan;
      voice->mod_pan_inc          = 0.0f;
      voice->mod_vol_cur          = modvol;
      voice->mod_vol_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(stereo_waveshaper_ms_1_voice_t);
   ST_PLUGIN_VOICE_SHARED_CAST(stereo_waveshaper_ms_1_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; (void)tmp_f;
      float tmp2_f; (void)tmp2_f;
      
      // ========
      // ======== lane "out" modIdx=0 modString=rdl
      // ========
      
      // -- mod="rdl" dstVar=out
      out = inL;
      
      // -- mod="rdr" dstVar=out
      voice->tmp6/*seq*/ = out;
      voice->tmp6/*seq*/ = inR;
      out += voice->tmp6/*seq*/;
      
      // -- mod="0.5" dstVar=out
      out *= 0.5f;
      
      // -- mod="ipl" dstVar=out
      voice->tmp6/*b*/ = out;
      
      // ---- mod="ipl" input "b" seq 1/1
      
      // -- mod="$v_i" dstVar=voice->tmp6/*b*/
      voice->tmp7/*seq*/ = voice->tmp6/*b*/;
      voice->tmp7/*seq*/ = voice->var_v_i;
      voice->tmp6/*b*/ *= voice->tmp7/*seq*/;
      
      // -- mod="lut" dstVar=voice->tmp6/*b*/
      voice->tmp8/*lut_bip_sign*/ = (voice->tmp6/*b*/ < 0.0f);
      if(voice->tmp8/*lut_bip_sign*/) voice->tmp6/*b*/ = -voice->tmp6/*b*/;
      voice->tmp7/*lut_f*/ = (voice->tmp6/*b*/ * 512);
      voice->tmp11/*lut_frac*/ = voice->tmp7/*lut_f*/ - (int)voice->tmp7/*lut_f*/;
      voice->tmp9/*lut_idx_a*/ = (int)voice->tmp7/*lut_f*/;
      if(voice->tmp9/*lut_idx_a*/ < 0)
      {
         voice->tmp9/*lut_idx_a*/ = 0;
         voice->tmp10/*lut_idx_b*/ = 0;
         voice->tmp11/*lut_frac*/ = 0.0f;
      }
      else if(voice->tmp9/*lut_idx_a*/ > 511)
      {
         voice->tmp9/*lut_idx_a*/ = 511;
         voice->tmp10/*lut_idx_b*/ = 511;
      }
      else
      {
         voice->tmp10/*lut_idx_b*/ = voice->tmp9/*lut_idx_a*/ + 1;
         if(voice->tmp10/*lut_idx_b*/ > 511)
            voice->tmp10/*lut_idx_b*/ = 511;
      }
      voice->tmp12/*lut_a*/ = curve_0[(unsigned int)voice->tmp9/*lut_idx_a*/] * (1.0f / 2048);
      voice->tmp13/*lut_b*/ = curve_0[(unsigned int)voice->tmp10/*lut_idx_b*/] * (1.0f / 2048);
      voice->tmp6/*b*/ = voice->tmp12/*lut_a*/ + (voice->tmp13/*lut_b*/ - voice->tmp12/*lut_a*/) * voice->tmp11/*lut_frac*/;
      if(voice->tmp8/*lut_bip_sign*/) voice->tmp6/*b*/ = -voice->tmp6/*b*/;
      
      // -- mod="$v_o_m" dstVar=voice->tmp6/*b*/
      voice->tmp7/*seq*/ = voice->tmp6/*b*/;
      voice->tmp7/*seq*/ = voice->var_v_o_m;
      voice->tmp6/*b*/ *= voice->tmp7/*seq*/;
      voice->tmp7/*t*/ = out;
      
      // ---- mod="ipl" input "t" seq 1/1
      
      // -- mod="$m_drywet" dstVar=voice->tmp7/*t*/
      voice->tmp7/*t*/ = voice->mod_drywet_cur;
      out = out + (voice->tmp6/*b*/ - out) * voice->tmp7/*t*/;
      
      // -- mod="set out__v_mid" dstVar=out
      voice->var_out__v_mid = out;
      
      // -- mod="rdl" dstVar=out
      out = inL;
      
      // -- mod="rdr" dstVar=out
      voice->tmp6/*seq*/ = out;
      voice->tmp6/*seq*/ = inR;
      out -= voice->tmp6/*seq*/;
      
      // -- mod="0.5" dstVar=out
      out *= 0.5f;
      
      // -- mod="ipl" dstVar=out
      voice->tmp6/*b*/ = out;
      
      // ---- mod="ipl" input "b" seq 1/1
      
      // -- mod="$v_i" dstVar=voice->tmp6/*b*/
      voice->tmp7/*seq*/ = voice->tmp6/*b*/;
      voice->tmp7/*seq*/ = voice->var_v_i;
      voice->tmp6/*b*/ *= voice->tmp7/*seq*/;
      
      // -- mod="lut" dstVar=voice->tmp6/*b*/
      voice->tmp8/*lut_bip_sign*/ = (voice->tmp6/*b*/ < 0.0f);
      if(voice->tmp8/*lut_bip_sign*/) voice->tmp6/*b*/ = -voice->tmp6/*b*/;
      voice->tmp7/*lut_f*/ = (voice->tmp6/*b*/ * 512);
      voice->tmp11/*lut_frac*/ = voice->tmp7/*lut_f*/ - (int)voice->tmp7/*lut_f*/;
      voice->tmp9/*lut_idx_a*/ = (int)voice->tmp7/*lut_f*/;
      if(voice->tmp9/*lut_idx_a*/ < 0)
      {
         voice->tmp9/*lut_idx_a*/ = 0;
         voice->tmp10/*lut_idx_b*/ = 0;
         voice->tmp11/*lut_frac*/ = 0.0f;
      }
      else if(voice->tmp9/*lut_idx_a*/ > 511)
      {
         voice->tmp9/*lut_idx_a*/ = 511;
         voice->tmp10/*lut_idx_b*/ = 511;
      }
      else
      {
         voice->tmp10/*lut_idx_b*/ = voice->tmp9/*lut_idx_a*/ + 1;
         if(voice->tmp10/*lut_idx_b*/ > 511)
            voice->tmp10/*lut_idx_b*/ = 511;
      }
      voice->tmp12/*lut_a*/ = curve_1[(unsigned int)voice->tmp9/*lut_idx_a*/] * (1.0f / 2048);
      voice->tmp13/*lut_b*/ = curve_1[(unsigned int)voice->tmp10/*lut_idx_b*/] * (1.0f / 2048);
      voice->tmp6/*b*/ = voice->tmp12/*lut_a*/ + (voice->tmp13/*lut_b*/ - voice->tmp12/*lut_a*/) * voice->tmp11/*lut_frac*/;
      if(voice->tmp8/*lut_bip_sign*/) voice->tmp6/*b*/ = -voice->tmp6/*b*/;
      
      // -- mod="$v_o_s" dstVar=voice->tmp6/*b*/
      voice->tmp7/*seq*/ = voice->tmp6/*b*/;
      voice->tmp7/*seq*/ = voice->var_v_o_s;
      voice->tmp6/*b*/ *= voice->tmp7/*seq*/;
      voice->tmp7/*t*/ = out;
      
      // ---- mod="ipl" input "t" seq 1/1
      
      // -- mod="$m_drywet" dstVar=voice->tmp7/*t*/
      voice->tmp7/*t*/ = voice->mod_drywet_cur;
      out = out + (voice->tmp6/*b*/ - out) * voice->tmp7/*t*/;
      
      // -- mod="set out__v_side" dstVar=out
      voice->var_out__v_side = out;
      
      // -- mod="$out__v_mid" dstVar=out
      voice->tmp6/*seq*/ = out;
      voice->tmp6/*seq*/ = voice->var_out__v_mid;
      out += voice->tmp6/*seq*/;
      
      // -- mod="wrl" dstVar=out
      
      // ---- mod="wrl" input "amp" seq 1/1
      
      // -- mod="$v_amp_l" dstVar=voice->tmp6/*wrl_amp*/
      voice->tmp6/*wrl_amp*/ = voice->var_v_amp_l;
      outL = out * voice->tmp6/*wrl_amp*/;
      
      // -- mod="$out__v_mid" dstVar=out
      out = voice->var_out__v_mid;
      
      // -- mod="$out__v_side" dstVar=out
      voice->tmp6/*seq*/ = out;
      voice->tmp6/*seq*/ = voice->var_out__v_side;
      out -= voice->tmp6/*seq*/;
      
      // -- mod="wrr" dstVar=out
      
      // ---- mod="wrr" input "amp" seq 1/1
      
      // -- mod="$v_amp_r" dstVar=voice->tmp6/*wrr_amp*/
      voice->tmp6/*wrr_amp*/ = voice->var_v_amp_r;
      outR = out * voice->tmp6/*wrr_amp*/;
      

      /* 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_drywet_cur     += voice->mod_drywet_inc;
         voice->mod_iogain_cur     += voice->mod_iogain_inc;
         voice->mod_pan_ms_cur     += voice->mod_pan_ms_inc;
         voice->mod_pan_cur        += voice->mod_pan_inc;
         voice->mod_vol_cur        += voice->mod_vol_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) {
   stereo_waveshaper_ms_1_shared_t *ret = (stereo_waveshaper_ms_1_shared_t *)malloc(sizeof(stereo_waveshaper_ms_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, unsigned int _voiceIdx) {
   stereo_waveshaper_ms_1_voice_t *voice = (stereo_waveshaper_ms_1_voice_t *)malloc(sizeof(stereo_waveshaper_ms_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(stereo_waveshaper_ms_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..90deg (rev qtbl)
   int j = 4096;
   k = 0;
   loop(4096)
      cycle_sine_tbl_f[k++] = qcos[--j];

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

   // 180..360deg (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 *stereo_waveshaper_ms_1_init(void) {
   stereo_waveshaper_ms_1_info_t *ret = (stereo_waveshaper_ms_1_info_t *)malloc(sizeof(stereo_waveshaper_ms_1_info_t));

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

      ret->base.api_version = ST_PLUGIN_API_VERSION;
      ret->base.id          = "stereo_waveshaper_ms_1_0x9d32b6dc";  // unique id. don't change this in future builds.
      ret->base.author      = "bsp";
      ret->base.name        = "stereo_waveshaper_ms_1";
      ret->base.short_name  = "stereo_waveshaper_ms_1";
      ret->base.flags       = ST_PLUGIN_FLAG_FX;
      ret->base.category    = ST_PLUGIN_CAT_WAVESHAPER;
      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 stereo_waveshaper_ms_1_init();
   }
   return NULL;
}
#endif // STFX_SKIP_MAIN_INIT

} // extern "C"
