/*
    SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
    http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#include "SC_PlugIn.h"
#include "function_attributes.h"
#include <limits>
#include <string.h>

static InterfaceTable* ft;

struct BufUnit : public Unit {
    SndBuf* m_buf;
    float m_fbufnum;
};

struct TableLookup : public BufUnit {
    double m_cpstoinc, m_radtoinc;
    int32 mTableSize;
    int32 m_lomask;
};

struct DegreeToKey : public BufUnit {
    int32 mPrevIndex;
    float mPrevKey;
    int32 mOctave;
};

struct Select : public Unit {};

struct TWindex : public Unit {
    int32 m_prevIndex;
    float m_trig;
    float m_maxSum;
};

struct Index : public BufUnit {};

struct IndexL : public BufUnit {};

struct WrapIndex : public BufUnit {};

struct FoldIndex : public BufUnit {};

struct IndexInBetween : public BufUnit {};

struct DetectIndex : public BufUnit {
    float mPrev;
    float mPrevIn;
};

struct Shaper : public BufUnit {
    float mOffset;
    float mPrevIn;
};

struct FSinOsc : public Unit {
    double m_b1, m_y1, m_y2, m_freq;
};

struct PSinGrain : public Unit {
    double m_b1, m_y1, m_y2;
    double m_level, m_slope, m_curve;
    int32 mCounter;
};

struct Osc : public TableLookup {
    int32 m_phase;
    float m_phasein;
};

struct SinOsc : public TableLookup {
    int32 m_phase;
    float m_phasein;
};

struct SinOscFB : public TableLookup {
    int32 m_phase;
    float m_prevout, m_feedback;
};

struct OscN : public TableLookup {
    int32 m_phase;
    float m_phasein;
};

struct COsc : public TableLookup {
    int32 m_phase1, m_phase2;
};

struct VOsc : public Unit {
    double m_cpstoinc, m_radtoinc;
    int32 mTableSize;
    int32 m_lomask;
    int32 m_phase, m_phaseoffset;
    float m_phasein, m_bufpos;
};

struct VOsc3 : public Unit {
    double m_cpstoinc;
    int32 mTableSize;
    int32 m_lomask;
    int32 m_phase1, m_phase2, m_phase3;
    float m_bufpos;
};

struct Formant : public Unit {
    int32 m_phase1, m_phase2, m_phase3;
    double m_cpstoinc;
};


struct Blip : public Unit {
    int32 m_phase, m_numharm, m_N;
    float m_freqin, m_scale;
    double m_cpstoinc;
};

struct Saw : public Unit {
    int32 m_phase, m_N;
    float m_freqin, m_scale, m_y1;
    double m_cpstoinc;
};

struct Pulse : public Unit {
    int32 m_phase, m_phaseoff, m_N;
    float m_freqin, m_scale, m_y1;
    double m_cpstoinc;
};

struct Klang : public Unit {
    float* m_coefs;
    int32 m_numpartials;
};

struct Klank : public Unit {
    float* m_coefs;
    float* m_buf;
    float m_x1, m_x2;
    int32 m_numpartials;
};


#define xlomask8 0x000003FC
#define xlomask9 0x000007FC
#define xlomask10 0x00000FFC
#define xlomask11 0x00001FFC
#define xlomask12 0x00003FFC
#define xlomask13 0x00007FFC

#define xlomask8i 0x000007F8
#define xlomask9i 0x00000FF8
#define xlomask10i 0x00001FF8
#define xlomask11i 0x00003FF8
#define xlomask12i 0x00007FF8
#define xlomask13i 0x0000FFF8

#define onecyc13 0x20000000


//////////////////////////////////////////////////////////////////////////////////////////////////

extern "C" {

void DegreeToKey_Ctor(DegreeToKey* unit);
void DegreeToKey_next_1(DegreeToKey* unit, int inNumSamples);
void DegreeToKey_next_k(DegreeToKey* unit, int inNumSamples);
void DegreeToKey_next_a(DegreeToKey* unit, int inNumSamples);

void Select_Ctor(Select* unit);
void Select_next_1(Select* unit, int inNumSamples);
void Select_next_k(Select* unit, int inNumSamples);
void Select_next_a(Select* unit, int inNumSamples);

void TWindex_Ctor(TWindex* unit);
void TWindex_next_k(TWindex* unit, int inNumSamples);
void TWindex_next_a(TWindex* unit, int inNumSamples);

void Index_Ctor(Index* unit);
void Index_next_1(Index* unit, int inNumSamples);
void Index_next_k(Index* unit, int inNumSamples);
void Index_next_a(Index* unit, int inNumSamples);

void IndexL_Ctor(IndexL* unit);
void IndexL_next_1(IndexL* unit, int inNumSamples);
void IndexL_next_k(IndexL* unit, int inNumSamples);
void IndexL_next_a(IndexL* unit, int inNumSamples);

void FoldIndex_Ctor(FoldIndex* unit);
void FoldIndex_next_1(FoldIndex* unit, int inNumSamples);
void FoldIndex_next_k(FoldIndex* unit, int inNumSamples);
void FoldIndex_next_a(FoldIndex* unit, int inNumSamples);

void WrapIndex_Ctor(WrapIndex* unit);
void WrapIndex_next_1(WrapIndex* unit, int inNumSamples);
void WrapIndex_next_k(WrapIndex* unit, int inNumSamples);
void WrapIndex_next_a(WrapIndex* unit, int inNumSamples);

void Shaper_Ctor(Shaper* unit);
void Shaper_next_1(Shaper* unit, int inNumSamples);
void Shaper_next_k(Shaper* unit, int inNumSamples);
void Shaper_next_a(Shaper* unit, int inNumSamples);


void DetectIndex_Ctor(DetectIndex* unit);
void DetectIndex_next_1(DetectIndex* unit, int inNumSamples);
void DetectIndex_next_k(DetectIndex* unit, int inNumSamples);
void DetectIndex_next_a(DetectIndex* unit, int inNumSamples);

void IndexInBetween_Ctor(IndexInBetween* unit);
void IndexInBetween_next_1(IndexInBetween* unit, int inNumSamples);
void IndexInBetween_next_k(IndexInBetween* unit, int inNumSamples);
void IndexInBetween_next_a(IndexInBetween* unit, int inNumSamples);


void FSinOsc_Ctor(FSinOsc* unit);
void FSinOsc_next(FSinOsc* unit, int inNumSamples);
void FSinOsc_next_i(FSinOsc* unit, int inNumSamples);

void PSinGrain_Ctor(PSinGrain* unit);
void PSinGrain_next(PSinGrain* unit, int inNumSamples);

void SinOsc_Ctor(SinOsc* unit);
void SinOsc_next_ikk(SinOsc* unit, int inNumSamples);
void SinOsc_next_ika(SinOsc* unit, int inNumSamples);
void SinOsc_next_iak(SinOsc* unit, int inNumSamples);
void SinOsc_next_iaa(SinOsc* unit, int inNumSamples);

void Osc_Ctor(Osc* unit);
void Osc_next_ikk(Osc* unit, int inNumSamples);
void Osc_next_ika(Osc* unit, int inNumSamples);
void Osc_next_iak(Osc* unit, int inNumSamples);
void Osc_next_iaa(Osc* unit, int inNumSamples);

void OscN_Ctor(OscN* unit);
void OscN_next_nkk(OscN* unit, int inNumSamples);
void OscN_next_nka(OscN* unit, int inNumSamples);
void OscN_next_nak(OscN* unit, int inNumSamples);
void OscN_next_naa(OscN* unit, int inNumSamples);

void COsc_Ctor(COsc* unit);
void COsc_next(COsc* unit, int inNumSamples);

void VOsc_Ctor(VOsc* unit);
void VOsc_next_ikk(VOsc* unit, int inNumSamples);
void VOsc_next_ika(VOsc* unit, int inNumSamples);

void VOsc3_Ctor(VOsc3* unit);
void VOsc3_next_ik(VOsc3* unit, int inNumSamples);

void Formant_Ctor(Formant* unit);
void Formant_next(Formant* unit, int inNumSamples);

void Blip_Ctor(Blip* unit);
void Blip_next(Blip* unit, int inNumSamples);

void Saw_Ctor(Saw* unit);
void Saw_next(Saw* unit, int inNumSamples);

void Pulse_Ctor(Pulse* unit);
void Pulse_next(Pulse* unit, int inNumSamples);

void Klang_Dtor(Klang* unit);
void Klang_Ctor(Klang* unit);
void Klang_next(Klang* unit, int inNumSamples);

void Klank_Dtor(Klank* unit);
void Klank_Ctor(Klank* unit);
void Klank_next(Klank* unit, int inNumSamples);
}

//////////////////////////////////////////////////////////////////////////////////////////////////

force_inline bool UnitGetTable(BufUnit* unit, int inNumSamples, const SndBuf*& buf, const float*& bufData,
                               int& tableSize) {
    float fbufnum = ZIN0(0);
    if (fbufnum != unit->m_fbufnum) {
        uint32 bufnum = (uint32)fbufnum;
        World* world = unit->mWorld;
        if (bufnum >= world->mNumSndBufs) {
            uint32 localBufNum = bufnum - world->mNumSndBufs;
            Graph* parent = unit->mParent;
            if (localBufNum <= parent->localBufNum)
                unit->m_buf = parent->mLocalSndBufs + localBufNum;
            else {
                bufnum = 0;
                unit->m_buf = world->mSndBufs + bufnum;
            }
        } else
            unit->m_buf = world->mSndBufs + bufnum;

        unit->m_fbufnum = fbufnum;
    }
    buf = unit->m_buf;
    if (!buf) {
        ClearUnitOutputs(unit, inNumSamples);
        return false;
    }

    bufData = buf->data;
    if (!bufData) {
        ClearUnitOutputs(unit, inNumSamples);
        return false;
    }
    tableSize = buf->samples;
    return true;
}

#define GET_TABLE                                                                                                      \
    const SndBuf* buf;                                                                                                 \
    const float* bufData;                                                                                              \
    int tableSize;                                                                                                     \
    do {                                                                                                               \
        bool tableValid = UnitGetTable(unit, inNumSamples, buf, bufData, tableSize);                                   \
        if (!tableValid)                                                                                               \
            return;                                                                                                    \
    } while (0);

static inline bool verify_wavetable(Unit* unit, const char* name, int tableSize, int inNumSamples) {
    // phase computation is not precise for large wavetables.
    if (tableSize > 131072) {
        if (unit->mWorld->mVerbosity >= -1)
            Print("Warning: wave table too big (%s)\n", name);
        ClearUnitOutputs(unit, inNumSamples);
        return false;
    }

    if (!ISPOWEROFTWO(tableSize)) {
        if (unit->mWorld->mVerbosity >= -1)
            Print("Warning: size of wavetable not a power of two (%s)\n", name);
        ClearUnitOutputs(unit, inNumSamples);
        return false;
    }

    return true;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

/*
void TableLookup_SetTable(TableLookup* unit, int32 inSize, float* inTable)
{
    unit->mTable0 = inTable;
    unit->mTable1 = inTable + 1;
    unit->mTableSize = inSize;
    unit->mMaxIndex = unit->mTableSize - 1;
    unit->mFMaxIndex = unit->mMaxIndex;
    unit->m_radtoinc = unit->mTableSize * (rtwopi * 65536.);
    unit->m_cpstoinc = unit->mTableSize * SAMPLEDUR * 65536.;
    //Print("TableLookup_SetTable unit->m_radtoinc %g %d %g\n", m_radtoinc, unit->mTableSize, rtwopi);
}
*/

////////////////////////////////////////////////////////////////////////////////////////////////////////

void DegreeToKey_Ctor(DegreeToKey* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(DegreeToKey_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(DegreeToKey_next_a);
    } else {
        SETCALC(DegreeToKey_next_k);
    }
    unit->mOctave = (int32)ZIN0(2);
    unit->mPrevIndex = std::numeric_limits<int32>::max();
    unit->mPrevKey = 0.;
    DegreeToKey_next_1(unit, 1);
}

void DegreeToKey_next_1(DegreeToKey* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    int32 key, oct;

    int32 octave = unit->mOctave;
    float val;

    int32 index = (int32)floor(ZIN0(1));
    if (index == unit->mPrevIndex) {
        val = unit->mPrevKey;
    } else if (index < 0) {
        unit->mPrevIndex = index;
        key = tableSize + index % tableSize;
        oct = (index + 1) / tableSize - 1;
        val = unit->mPrevKey = table[key] + octave * oct;
    } else if (index > maxindex) {
        unit->mPrevIndex = index;
        key = index % tableSize;
        oct = index / tableSize;
        val = unit->mPrevKey = table[key] + octave * oct;
    } else {
        unit->mPrevIndex = index;
        val = unit->mPrevKey = table[index];
    }
    ZOUT0(0) = val;
}

void DegreeToKey_next_k(DegreeToKey* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);

    int32 key, oct;
    float octave = unit->mOctave;
    float val;

    int32 index = (int32)floor(ZIN0(1));
    if (index == unit->mPrevIndex) {
        val = unit->mPrevKey;
    } else if (index < 0) {
        unit->mPrevIndex = index;
        key = tableSize + index % tableSize;
        oct = (index + 1) / tableSize - 1;
        val = unit->mPrevKey = table[key] + octave * oct;
    } else if (index > maxindex) {
        unit->mPrevIndex = index;
        key = index % tableSize;
        oct = index / tableSize;
        val = unit->mPrevKey = table[key] + octave * oct;
    } else {
        unit->mPrevIndex = index;
        val = unit->mPrevKey = table[index];
    }
    LOOP1(inNumSamples, ZXP(out) = val;);
}


void DegreeToKey_next_a(DegreeToKey* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);
    int32 previndex = unit->mPrevIndex;
    float prevkey = unit->mPrevKey;
    int32 key, oct;

    float octave = unit->mOctave;

    LOOP1(
        inNumSamples, int32 index = (int32)floor(ZXP(in));
        if (index == previndex) { ZXP(out) = prevkey; } else if (index < 0) {
            previndex = index;
            key = tableSize + index % tableSize;
            oct = (index + 1) / tableSize - 1;
            ZXP(out) = prevkey = table[key] + octave * oct;
        } else if (index > maxindex) {
            previndex = index;
            key = index % tableSize;
            oct = index / tableSize;
            ZXP(out) = prevkey = table[key] + octave * oct;
        } else {
            previndex = index;
            ZXP(out) = prevkey = table[index];
        });
    unit->mPrevIndex = previndex;
    unit->mPrevKey = prevkey;
}

////////////////////////////////////////////////////////////////////////////////////

void Select_Ctor(Select* unit) {
    if (BUFLENGTH == 1) {
        SETCALC(Select_next_1);
    } else if (INRATE(0) == calc_FullRate) {
        SETCALC(Select_next_a);
    } else {
        SETCALC(Select_next_k);
    }
    Select_next_1(unit, 1);
}

void Select_next_1(Select* unit, int inNumSamples) {
    int32 maxindex = unit->mNumInputs - 1;
    int32 index = (int32)ZIN0(0) + 1;
    index = sc_clip(index, 1, maxindex);
    ZOUT0(0) = ZIN0(index);
}

void Select_next_k(Select* unit, int inNumSamples) {
    int32 maxindex = unit->mNumInputs - 1;
    int32 index = (int32)ZIN0(0) + 1;
    index = sc_clip(index, 1, maxindex);

    float* out = OUT(0);
    float* in = IN(index);

    Copy(inNumSamples, out, in);
}

void Select_next_a(Select* unit, int inNumSamples) {
    int32 maxindex = unit->mNumInputs - 1;

    float* out = ZOUT(0);
    float* in0 = ZIN(0);
    float** in = unit->mInBuf;

    for (int i = 0; i < inNumSamples; ++i) {
        int32 index = (int32)ZXP(in0) + 1;
        index = sc_clip(index, 1, maxindex);
        ZXP(out) = in[index][i];
    }
}
////////////////////////////////////////////////////////////////////////////////////

static int32 TWindex_chooseNewIndex(TWindex* unit) {
    int maxindex = unit->mNumInputs;
    int32 index = maxindex;
    float normalize = ZIN0(1);
    float maxSum = unit->m_maxSum;

    if (maxSum < 0.f) {
        maxSum = 0.f;
        if (normalize == 1) {
            for (int32 k = 2; k < maxindex; ++k) {
                maxSum += ZIN0(k);
            }
        } else
            maxSum = 1.f;
        unit->m_maxSum = maxSum;
    }
    RGen& rgen = *unit->mParent->mRGen;

    float max = maxSum * rgen.frand();
    float sum = 0.f;
    for (int32 k = 2; k < maxindex; ++k) {
        sum += ZIN0(k);
        if (sum >= max) {
            index = k - 2;
            break;
        }
    }
    return index;
}

void TWindex_Ctor(TWindex* unit) {
    if (INRATE(0) == calc_FullRate) {
        SETCALC(TWindex_next_a);
    } else {
        SETCALC(TWindex_next_k);
    }
    unit->m_maxSum = -1.f; // trigger update of maxSum
    int32 index = TWindex_chooseNewIndex(unit);
    OUT0(0) = index;
    unit->m_prevIndex = index;
    // Ensure a first-sample trigger doesn't cause another rand val
    // so initialization sample will equal first output sample
    unit->m_trig = 1.f;
}

void TWindex_next_k(TWindex* unit, int inNumSamples) {
    float trig = ZIN0(0);
    float* out = ZOUT(0);
    int32 index;
    unit->m_maxSum = -1.f;
    if (trig > 0.f && unit->m_trig <= 0.f) {
        index = TWindex_chooseNewIndex(unit);
        unit->m_prevIndex = index;
    } else {
        index = unit->m_prevIndex;
    }
    LOOP1(inNumSamples, ZXP(out) = index;)
    unit->m_trig = trig;
}

void TWindex_next_a(TWindex* unit, int inNumSamples) {
    float* trig = ZIN(0);
    float* out = ZOUT(0);
    int32 index;
    float curtrig;
    unit->m_maxSum = -1.f;

    LOOP1(
        inNumSamples, curtrig = ZXP(trig); if (curtrig > 0.f && unit->m_trig <= 0.f) {
            index = TWindex_chooseNewIndex(unit);
            unit->m_prevIndex = index;
        } else index = unit->m_prevIndex;

        ZXP(out) = index; unit->m_trig = curtrig;)
}


////////////////////////////////////////////////////////////////////////////////////

void Index_Ctor(Index* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(Index_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(Index_next_a);
    } else {
        SETCALC(Index_next_k);
    }
    Index_next_1(unit, 1);
}

void Index_next_1(Index* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    int32 index = (int32)ZIN0(1);
    index = sc_clip(index, 0, maxindex);
    ZOUT0(0) = table[index];
}

void Index_next_k(Index* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);

    int32 index = (int32)ZIN0(1);

    index = sc_clip(index, 0, maxindex);
    float val = table[index];
    LOOP1(inNumSamples, ZXP(out) = val;);
}


void Index_next_a(Index* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);

    LOOP1(inNumSamples, int32 index = (int32)ZXP(in); index = sc_clip(index, 0, maxindex); ZXP(out) = table[index];);
}


////////////////////////////////////////////////////////////////////////////////////

void IndexL_Ctor(IndexL* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(IndexL_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(IndexL_next_a);
    } else {
        SETCALC(IndexL_next_k);
    }
    IndexL_next_1(unit, 1);
}

void IndexL_next_1(IndexL* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float findex = ZIN0(1);
    float frac = sc_frac(findex);

    int32 index = (int32)findex;
    index = sc_clip(index, 0, maxindex);

    float a = table[index];
    float b = table[sc_clip(index + 1, 0, maxindex)];
    ZOUT0(0) = lininterp(frac, a, b);
}

void IndexL_next_k(IndexL* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);

    float findex = ZIN0(1);
    float frac = sc_frac(findex);

    int32 index = (int32)findex;
    index = sc_clip(index, 0, maxindex);


    float a = table[index];
    float b = table[sc_clip(index + 1, 0, maxindex)];
    float val = lininterp(frac, a, b);

    LOOP1(inNumSamples, ZXP(out) = val;);
}


void IndexL_next_a(IndexL* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);

    LOOP1(inNumSamples, float findex = ZXP(in); float frac = sc_frac(findex);
          int32 i1 = sc_clip((int32)findex, 0, maxindex); int32 i2 = sc_clip(i1 + 1, 0, maxindex); float a = table[i1];
          float b = table[i2]; ZXP(out) = lininterp(frac, a, b););
}


////////////////////////////////////////////////////////////////////////////////////


void FoldIndex_Ctor(FoldIndex* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(FoldIndex_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(FoldIndex_next_a);
    } else {
        SETCALC(FoldIndex_next_k);
    }
    FoldIndex_next_1(unit, 1);
}

void FoldIndex_next_1(FoldIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    int32 index = (int32)ZIN0(1);
    index = sc_fold(index, 0, maxindex);
    ZOUT0(0) = table[index];
}

void FoldIndex_next_k(FoldIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    int32 index = (int32)ZIN0(1);
    float* out = ZOUT(0);

    index = sc_fold(index, 0, maxindex);
    float val = table[index];
    LOOP1(inNumSamples, ZXP(out) = val;);
}


void FoldIndex_next_a(FoldIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);

    LOOP1(inNumSamples, int32 index = (int32)ZXP(in); index = sc_fold(index, 0, maxindex); ZXP(out) = table[index];);
}


////////////////////////////////////////////////////////////////////////////////////

void WrapIndex_Ctor(WrapIndex* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(WrapIndex_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(WrapIndex_next_a);
    } else {
        SETCALC(WrapIndex_next_k);
    }
    WrapIndex_next_1(unit, 1);
}

void WrapIndex_next_1(WrapIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    int32 index = (int32)floor(ZIN0(1));
    index = sc_wrap(index, 0, maxindex);
    ZOUT0(0) = table[index];
}

void WrapIndex_next_k(WrapIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);

    int32 index = (int32)ZIN0(1);
    index = sc_wrap(index, 0, maxindex);
    float val = table[index];
    LOOP1(inNumSamples, ZXP(out) = val;);
}


void WrapIndex_next_a(WrapIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);

    LOOP1(inNumSamples, int32 index = (int32)ZXP(in); index = sc_wrap(index, 0, maxindex); ZXP(out) = table[index];);
}

////////////////////////////////////////////////////////////////////////////////////

static float IndexInBetween_FindIndex(const float* table, float in, int32 maxindex) {
    for (int32 i = 0; i <= maxindex; i++) {
        if (table[i] > in) {
            if (i == 0) {
                return 0.f;
            } else {
                return ((in - table[i - 1]) / (table[i] - table[i - 1]) + i - 1);
            }
        }
    }
    return (float)maxindex;
}

void IndexInBetween_Ctor(IndexInBetween* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(IndexInBetween_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(IndexInBetween_next_a);
    } else {
        SETCALC(IndexInBetween_next_k);
    }
    IndexInBetween_next_1(unit, 1);
}

void IndexInBetween_next_1(IndexInBetween* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float in = ZIN0(1);
    ZOUT0(0) = IndexInBetween_FindIndex(table, in, maxindex);
}

void IndexInBetween_next_k(IndexInBetween* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float in = ZIN0(1);

    float val = IndexInBetween_FindIndex(table, in, maxindex);
    LOOP1(inNumSamples, ZXP(out) = val;);
}


void IndexInBetween_next_a(IndexInBetween* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);

    LOOP1(inNumSamples, ZXP(out) = IndexInBetween_FindIndex(table, ZXP(in), maxindex););
}


////////////////////////////////////////////////////////////////////////////////////

static int32 DetectIndex_FindIndex(const float* table, float in, int32 maxindex) {
    int32 index;
    for (index = 0; index <= maxindex; index += 1) {
        if (table[index] == in) {
            return index;
        }
    }
    return -1;
}

void DetectIndex_Ctor(DetectIndex* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(DetectIndex_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(DetectIndex_next_a);
    } else {
        SETCALC(DetectIndex_next_k);
    }
    unit->mPrev = -1.f;
    // ensure in != unit->mPrevIn on first frame
    unit->mPrevIn = std::numeric_limits<float>::quiet_NaN();
    DetectIndex_next_1(unit, 1);
}

void DetectIndex_next_1(DetectIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float in = ZIN0(1);
    int32 index;
    if (in == unit->mPrevIn) {
        index = (int32)unit->mPrev;
    } else {
        index = DetectIndex_FindIndex(table, in, maxindex);
        unit->mPrev = index;
        unit->mPrevIn = in;
    }
    ZOUT0(0) = (float)index;
}

void DetectIndex_next_k(DetectIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float in = ZIN0(1);
    int32 index;
    float val;
    if (in == unit->mPrevIn) {
        index = (int32)unit->mPrev;
    } else {
        index = DetectIndex_FindIndex(table, in, maxindex);
        unit->mPrev = index;
        unit->mPrevIn = in;
    };
    val = (float)index;
    LOOP1(inNumSamples, ZXP(out) = val;);
}


void DetectIndex_next_a(DetectIndex* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    int32 maxindex = tableSize - 1;

    float* out = ZOUT(0);
    float* in = ZIN(1);
    float prev = unit->mPrevIn;
    int32 prevIndex = (int32)unit->mPrev;
    float inval;

    LOOP1(
        inNumSamples, inval = ZXP(in);
        if (inval != prev) { prevIndex = DetectIndex_FindIndex(table, inval, maxindex); } prev = inval;
        ZXP(out) = (float)prevIndex;);

    unit->mPrev = prevIndex;
    unit->mPrevIn = inval;
}


////////////////////////////////////////////////////////////////////////////////////

void Shaper_Ctor(Shaper* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    if (BUFLENGTH == 1) {
        SETCALC(Shaper_next_1);
    } else if (INRATE(1) == calc_FullRate) {
        SETCALC(Shaper_next_a);
    } else {
        SETCALC(Shaper_next_k);
    }
    unit->mPrevIn = ZIN0(1);
    Shaper_next_1(unit, 1);
}

float force_inline ShaperPerform(const float* table0, const float* table1, float in, float offset, float fmaxindex) {
    float findex = offset + in * offset;
    findex = sc_clip(findex, 0.f, fmaxindex);
    int32 index = (int32)findex;
    float pfrac = findex - (index - 1);
    index <<= 3;
    float val1 = *(const float*)((const char*)table0 + index);
    float val2 = *(const float*)((const char*)table1 + index);
    float val = val1 + val2 * pfrac;
    return val;
}

void Shaper_next_1(Shaper* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table0 = bufData;
    const float* table1 = table0 + 1;
    float fmaxindex = (float)(tableSize >> 1) - 0.001;
    float offset = tableSize * 0.25;

    ZOUT0(0) = ShaperPerform(table0, table1, ZIN0(1), offset, fmaxindex);
}

void Shaper_next_k(Shaper* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table0 = bufData;
    const float* table1 = table0 + 1;
    float fmaxindex = (float)(tableSize >> 1) - 0.001;
    float offset = tableSize * 0.25;

    float* out = ZOUT(0);
    float fin = ZIN0(1);

    if (fin == unit->mPrevIn) {
        LOOP1(inNumSamples, ZXP(out) = ShaperPerform(table0, table1, fin, offset, fmaxindex););
    } else {
        float phaseinc = (fin - unit->mPrevIn) * offset;
        unit->mPrevIn = fin;

        LOOP1(inNumSamples, ZXP(out) = ShaperPerform(table0, table1, fin, offset, fmaxindex); fin += phaseinc;);
    }
}

void Shaper_next_a(Shaper* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table0 = bufData;
    const float* table1 = table0 + 1;
    float fmaxindex = (float)(tableSize >> 1) - 0.001;
    float offset = tableSize * 0.25;

    float* out = ZOUT(0);
    const float* in = ZIN(1);

    LOOP1(inNumSamples, float fin = ZXP(in); ZXP(out) = ShaperPerform(table0, table1, fin, offset, fmaxindex););
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

void FSinOsc_Ctor(FSinOsc* unit) {
    double b1, y1, y2;
    if (INRATE(0) == calc_ScalarRate)
        SETCALC(FSinOsc_next_i);
    else
        SETCALC(FSinOsc_next);
    unit->m_freq = ZIN0(0);
    float iphase = ZIN0(1);
    float w = unit->m_freq * unit->mRate->mRadiansPerSample;
    unit->m_b1 = b1 = 2. * cos(w);
    unit->m_y1 = y1 = sin(iphase - w);
    unit->m_y2 = y2 = sin(iphase - 2 * w);

    ZOUT0(0) = b1 * y1 - y2;
}

void FSinOsc_next(FSinOsc* unit, int inNumSamples) {
    float* out = ZOUT(0);
    double freq = ZIN0(0);
    double b1;
    if (freq != unit->m_freq) {
        unit->m_freq = freq;
        double w = freq * unit->mRate->mRadiansPerSample;
        unit->m_b1 = b1 = 2.f * cos(w);
    } else {
        b1 = unit->m_b1;
    }
    double y0;
    double y1 = unit->m_y1;
    double y2 = unit->m_y2;
    LOOP(unit->mRate->mFilterLoops, ZXP(out) = y0 = b1 * y1 - y2; ZXP(out) = y2 = b1 * y0 - y1;
         ZXP(out) = y1 = b1 * y2 - y0;);
    LOOP(unit->mRate->mFilterRemain, ZXP(out) = y0 = b1 * y1 - y2; y2 = y1; y1 = y0;);
    unit->m_y1 = y1;
    unit->m_y2 = y2;
}

void FSinOsc_next_i(FSinOsc* unit, int inNumSamples) {
    float* __restrict__ out = ZOUT(0);
    double b1 = unit->m_b1;

    double y0;
    double y1 = unit->m_y1;
    double y2 = unit->m_y2;
    LOOP(unit->mRate->mFilterLoops, y0 = b1 * y1 - y2; y2 = b1 * y0 - y1; y1 = b1 * y2 - y0; ZXP(out) = y0;
         ZXP(out) = y2; ZXP(out) = y1;);
    LOOP(unit->mRate->mFilterRemain, ZXP(out) = y0 = b1 * y1 - y2; y2 = y1; y1 = y0;);
    unit->m_y1 = y1;
    unit->m_y2 = y2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

void PSinGrain_Ctor(PSinGrain* unit) {
    SETCALC(PSinGrain_next);
    float freq = ZIN0(0);
    float dur = ZIN0(1);
    float amp = ZIN0(2);

    float w = freq * unit->mRate->mRadiansPerSample;
    float sdur = SAMPLERATE * dur;

    float rdur = 1.f / sdur;
    float rdur2 = rdur * rdur;

    unit->m_level = 0.f;
    unit->m_slope = 4.0 * (rdur - rdur2); // ampslope
    unit->m_curve = -8.0 * rdur2; // ampcurve
    unit->mCounter = (int32)(sdur + .5);

    /* calc feedback param and initial conditions */
    double b1, y1, y2;
    unit->m_b1 = b1 = 2. * cos(w);
    unit->m_y1 = y1 = -sin(w) * amp;
    unit->m_y2 = y2 = -sin(w + w) * amp;

    ZOUT0(0) = b1 * y1 - y2;
}


void PSinGrain_next(PSinGrain* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float y0;
    float y1 = unit->m_y1;
    float y2 = unit->m_y2;
    float b1 = unit->m_b1;
    float level = unit->m_level;
    float slope = unit->m_slope;
    float curve = unit->m_curve;
    int32 counter = unit->mCounter;
    int32 remain = inNumSamples;
    int32 nsmps;
    do {
        if (counter <= 0) {
            nsmps = remain;
            remain = 0;
            LOOP(nsmps, ZXP(out) = 0.f;); // can't use Clear bcs might not be aligned
        } else {
            nsmps = sc_min(remain, counter);
            remain -= nsmps;
            counter -= nsmps;
            if (nsmps == inNumSamples) {
                nsmps = unit->mRate->mFilterLoops;
                LOOP(nsmps, y0 = b1 * y1 - y2; ZXP(out) = y0 * level; level += slope; slope += curve; y2 = b1 * y0 - y1;
                     ZXP(out) = y2 * level; level += slope; slope += curve; y1 = b1 * y2 - y0; ZXP(out) = y1 * level;
                     level += slope; slope += curve;);
                nsmps = unit->mRate->mFilterRemain;
                LOOP(nsmps, y0 = b1 * y1 - y2; y2 = y1; y1 = y0; ZXP(out) = y0 * level; level += slope;
                     slope += curve;);
            } else {
                LOOP(nsmps, y0 = b1 * y1 - y2; y2 = y1; y1 = y0; ZXP(out) = y0 * level; level += slope;
                     slope += curve;);
            }
            if (counter == 0) {
                NodeEnd(&unit->mParent->mNode);
            }
        }
    } while (remain > 0);
    unit->mCounter = counter;
    unit->m_level = level;
    unit->m_slope = slope;
    unit->m_y1 = y1;
    unit->m_y2 = y2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename OscType, int FreqInputIndex>
force_inline void Osc_ikk_perform(OscType* unit, const float* table0, const float* table1, int inNumSamples) {
    float* out = ZOUT(0);
    float freqin = ZIN0(FreqInputIndex);
    float phasein = ZIN0(FreqInputIndex + 1);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    int32 freq = (int32)(unit->m_cpstoinc * freqin);
    int32 phaseinc = freq + (int32)(CALCSLOPE(phasein, unit->m_phasein) * unit->m_radtoinc);
    unit->m_phasein = phasein;

    LOOP1(inNumSamples, ZXP(out) = lookupi1(table0, table1, phase, lomask); phase += phaseinc;);
    unit->m_phase = phase;
}

void SinOsc_next_ikk(SinOsc* unit, int inNumSamples) {
    float* table0 = ft->mSineWavetable;
    float* table1 = table0 + 1;

    Osc_ikk_perform<SinOsc, 0>(unit, table0, table1, inNumSamples);
}


template <typename OscType, int FreqInputIndex>
force_inline void Osc_ika_perform(OscType* unit, const float* table0, const float* table1, int inNumSamples) {
    float* out = ZOUT(0);
    float freqin = ZIN0(FreqInputIndex);
    float* phasein = ZIN(FreqInputIndex + 1);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    int32 freq = (int32)(unit->m_cpstoinc * freqin);
    float radtoinc = unit->m_radtoinc;
    LOOP1(inNumSamples, int32 phaseoffset = phase + (int32)(radtoinc * ZXP(phasein));
          ZXP(out) = lookupi1(table0, table1, phaseoffset, lomask); phase += freq;);
    unit->m_phase = phase;
}

void SinOsc_next_ika(SinOsc* unit, int inNumSamples) {
    const float* table0 = ft->mSineWavetable;
    const float* table1 = table0 + 1;
    Osc_ika_perform<SinOsc, 0>(unit, table0, table1, inNumSamples);
}


template <typename OscType, int FreqInputIndex>
force_inline void Osc_iaa_perform(OscType* unit, const float* table0, const float* table1, int inNumSamples) {
    float* out = ZOUT(0);
    float* freqin = ZIN(FreqInputIndex);
    float* phasein = ZIN(FreqInputIndex + 1);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    float cpstoinc = unit->m_cpstoinc;
    float radtoinc = unit->m_radtoinc;
    LOOP1(inNumSamples, float phaseIn = ZXP(phasein); float freqIn = ZXP(freqin);
          int32 phaseoffset = phase + (int32)(radtoinc * phaseIn);
          float z = lookupi1(table0, table1, phaseoffset, lomask); phase += (int32)(cpstoinc * freqIn); ZXP(out) = z;);
    unit->m_phase = phase;
}

void SinOsc_next_iaa(SinOsc* unit, int inNumSamples) {
    const float* table0 = ft->mSineWavetable;
    const float* table1 = table0 + 1;

    Osc_iaa_perform<SinOsc, 0>(unit, table0, table1, inNumSamples);
}


template <typename OscType, int FreqInputIndex>
force_inline void Osc_iak_perform(OscType* unit, const float* table0, const float* table1, int inNumSamples) {
    float* out = ZOUT(0);
    float* freqin = ZIN(FreqInputIndex);
    float phasein = ZIN0(FreqInputIndex + 1);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    float cpstoinc = unit->m_cpstoinc;
    float radtoinc = unit->m_radtoinc;
    float phasemod = unit->m_phasein;

    if (phasein != phasemod) {
        float phaseslope = CALCSLOPE(phasein, phasemod);

        LOOP1(inNumSamples, int32 pphase = phase + (int32)(radtoinc * phasemod); phasemod += phaseslope;
              float z = lookupi1(table0, table1, pphase, lomask); phase += (int32)(cpstoinc * ZXP(freqin));
              ZXP(out) = z;);
    } else {
        LOOP1(inNumSamples, int32 pphase = phase + (int32)(radtoinc * phasemod);
              float z = lookupi1(table0, table1, pphase, lomask); phase += (int32)(cpstoinc * ZXP(freqin));
              ZXP(out) = z;);
    }
    unit->m_phase = phase;
    unit->m_phasein = phasein;
}

void SinOsc_next_iak(SinOsc* unit, int inNumSamples) {
    float* table0 = ft->mSineWavetable;
    float* table1 = table0 + 1;

    Osc_iak_perform<SinOsc, 0>(unit, table0, table1, inNumSamples);
}


template <typename OscType, int FreqInputIndex>
force_inline void Osc_iai_perform(OscType* unit, const float* table0, const float* table1, int inNumSamples) {
    float* out = ZOUT(0);
    float* freqin = ZIN(FreqInputIndex);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    float cpstoinc = unit->m_cpstoinc;
    float radtoinc = unit->m_radtoinc;
    float phasemod = unit->m_phasein;

    LOOP1(inNumSamples, int32 pphase = phase + (int32)(radtoinc * phasemod);
          float z = lookupi1(table0, table1, pphase, lomask); phase += (int32)(cpstoinc * ZXP(freqin)); ZXP(out) = z;);
    unit->m_phase = phase;
}

void SinOsc_next_iai(SinOsc* unit, int inNumSamples) {
    float* table0 = ft->mSineWavetable;
    float* table1 = table0 + 1;
    Osc_iai_perform<SinOsc, 0>(unit, table0, table1, inNumSamples);
}


void SinOsc_Ctor(SinOsc* unit) {
    int tableSize2 = ft->mSineSize;
    unit->m_phasein = ZIN0(1);
    unit->m_radtoinc = tableSize2 * (rtwopi * 65536.);
    unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
    unit->m_lomask = (tableSize2 - 1) << 3;

    int32 initPhase;
    if (INRATE(0) == calc_FullRate) {
        if (INRATE(1) == calc_FullRate)
            SETCALC(SinOsc_next_iaa);
        else if (INRATE(1) == calc_BufRate)
            SETCALC(SinOsc_next_iak);
        else
            SETCALC(SinOsc_next_iai);

        unit->m_phase = initPhase = 0;
        SinOsc_next_iaa(unit, 1);
    } else {
        if (INRATE(1) == calc_FullRate) {
            SETCALC(SinOsc_next_ika);
            unit->m_phase = initPhase = 0;
            SinOsc_next_iaa(unit, 1);
        } else {
            SETCALC(SinOsc_next_ikk);
            unit->m_phase = initPhase = (int32)(unit->m_phasein * unit->m_radtoinc);
            SinOsc_next_ikk(unit, 1);
        }
    }
    unit->m_phase = initPhase;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////!!!

void SinOscFB_next_kk(SinOscFB* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freqin = ZIN0(0);

    float feedback = unit->m_feedback;
    float nextFeedback = ZIN0(1) * unit->m_radtoinc;

    float* table0 = ft->mSineWavetable;
    float* table1 = table0 + 1;

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;
    float prevout = unit->m_prevout;
    float feedback_slope = CALCSLOPE(nextFeedback, feedback);
    int32 freq = (int32)(unit->m_cpstoinc * freqin);

    LooP(inNumSamples) {
        prevout = lookupi1(table0, table1, phase + (int32)(feedback * prevout), lomask);
        ZXP(out) = prevout;
        phase += freq;
        feedback += feedback_slope;
    }
    unit->m_phase = phase;
    unit->m_prevout = prevout;
    unit->m_feedback = feedback;
}

void SinOscFB_Ctor(SinOscFB* unit) {
    SETCALC(SinOscFB_next_kk);

    int tableSize2 = ft->mSineSize;
    unit->m_lomask = (tableSize2 - 1) << 3;
    unit->m_radtoinc = tableSize2 * (rtwopi * 65536.);
    unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
    unit->m_prevout = 0.;
    unit->m_feedback = ZIN0(1) * unit->m_radtoinc;

    unit->m_phase = 0;

    SinOscFB_next_kk(unit, 1);
    unit->m_phase = 0;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////

void Osc_Ctor(Osc* unit) {
    unit->mTableSize = -1;

    float fbufnum = ZIN0(0);
    uint32 bufnum = (uint32)fbufnum;
    World* world = unit->mWorld;

    SndBuf* buf;
    if (bufnum >= world->mNumSndBufs) {
        int localBufNum = bufnum - world->mNumSndBufs;
        Graph* parent = unit->mParent;
        if (localBufNum <= parent->localBufNum) {
            buf = unit->m_buf = parent->mLocalSndBufs + localBufNum;
        } else {
            buf = unit->m_buf = world->mSndBufs;
        }
    } else {
        buf = unit->m_buf = world->mSndBufs + bufnum;
    }

    int tableSize = buf->samples;
    int tableSize2 = tableSize >> 1;
    unit->m_radtoinc = tableSize2 * (rtwopi * 65536.); // Osc, OscN, PMOsc

    unit->m_phasein = ZIN0(2);

    int32 initphase;
    if (INRATE(1) == calc_FullRate) {
        if (INRATE(2) == calc_FullRate) {
            SETCALC(Osc_next_iaa);
        } else {
            SETCALC(Osc_next_iak);
        }
        unit->m_phase = initphase = 0;
        Osc_next_iaa(unit, 1);
    } else {
        if (INRATE(2) == calc_FullRate) {
            SETCALC(Osc_next_ika);
            unit->m_phase = initphase = 0;
            Osc_next_iaa(unit, 1);
        } else {
            SETCALC(Osc_next_ikk);
            unit->m_phase = initphase = (int32)(unit->m_phasein * unit->m_radtoinc);
            Osc_next_ikk(unit, 1);
        }
    }
    unit->m_phase = initphase;
}

force_inline bool Osc_get_table(Osc* unit, const float*& table0, const float*& table1, int inNumSamples) {
    const SndBuf* buf;
    const float* bufData;
    int tableSize;
    bool tableValid = UnitGetTable(unit, inNumSamples, buf, bufData, tableSize);
    if (!tableValid)
        return false;

    table0 = bufData;
    table1 = table0 + 1;
    if (tableSize != unit->mTableSize) {
        unit->mTableSize = tableSize;
        int tableSize2 = tableSize >> 1;
        unit->m_lomask = (tableSize2 - 1) << 3; // Osc, OscN, COsc, COsc, COsc2, OscX4, OscX2
        unit->m_radtoinc = tableSize2 * (rtwopi * 65536.); // Osc, OscN, PMOsc
        // Osc, OscN, PMOsc, COsc, COsc2, OscX4, OscX2
        unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
    }
    if (!verify_wavetable(unit, "Osc", tableSize, inNumSamples))
        return false;

    return true;
}

void Osc_next_ikk(Osc* unit, int inNumSamples) {
    const float* table0;
    const float* table1;
    bool tableValid = Osc_get_table(unit, table0, table1, inNumSamples);
    if (!tableValid)
        return;

    Osc_ikk_perform<Osc, 1>(unit, table0, table1, inNumSamples);
}

void Osc_next_ika(Osc* unit, int inNumSamples) {
    const float* table0;
    const float* table1;
    bool tableValid = Osc_get_table(unit, table0, table1, inNumSamples);
    if (!tableValid)
        return;

    Osc_ika_perform<Osc, 1>(unit, table0, table1, inNumSamples);
}

void Osc_next_iaa(Osc* unit, int inNumSamples) {
    const float* table0;
    const float* table1;
    bool tableValid = Osc_get_table(unit, table0, table1, inNumSamples);
    if (!tableValid)
        return;

    Osc_iaa_perform<Osc, 1>(unit, table0, table1, inNumSamples);
}


void Osc_next_iak(Osc* unit, int inNumSamples) {
    const float* table0;
    const float* table1;
    bool tableValid = Osc_get_table(unit, table0, table1, inNumSamples);
    if (!tableValid)
        return;

    Osc_iak_perform<Osc, 1>(unit, table0, table1, inNumSamples);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////

void OscN_Ctor(OscN* unit) {
    unit->mTableSize = -1;

    float fbufnum = ZIN0(0);
    uint32 bufnum = (uint32)fbufnum;
    World* world = unit->mWorld;
    SndBuf* buf;
    if (bufnum >= world->mNumSndBufs) {
        int localBufNum = bufnum - world->mNumSndBufs;
        Graph* parent = unit->mParent;
        if (localBufNum <= parent->localBufNum) {
            buf = unit->m_buf = parent->mLocalSndBufs + localBufNum;
        } else {
            buf = unit->m_buf = world->mSndBufs;
        }
    } else {
        buf = unit->m_buf = world->mSndBufs + bufnum;
    }

    int tableSize = buf->samples;
    unit->m_radtoinc = tableSize * (rtwopi * 65536.);

    unit->m_phasein = ZIN0(2);
    int32 initphase;
    if (INRATE(1) == calc_FullRate) {
        if (INRATE(2) == calc_FullRate) {
            SETCALC(OscN_next_naa);
        } else {
            SETCALC(OscN_next_nak);
        }
        unit->m_phase = initphase = 0;
        OscN_next_naa(unit, 1);
    } else {
        if (INRATE(2) == calc_FullRate) {
            SETCALC(OscN_next_nka);
            unit->m_phase = initphase = 0;
            OscN_next_naa(unit, 1);
        } else {
            SETCALC(OscN_next_nkk);
            unit->m_phase = initphase = (int32)(unit->m_phasein * unit->m_radtoinc);
            OscN_next_nkk(unit, 1);
        }
    }
    unit->m_phase = initphase;
}


void OscN_next_nkk(OscN* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    if (tableSize != unit->mTableSize) {
        unit->mTableSize = tableSize;
        unit->m_lomask = (tableSize - 1) << 2;
        unit->m_radtoinc = tableSize * (rtwopi * 65536.);
        unit->m_cpstoinc = tableSize * SAMPLEDUR * 65536.;
    }

    if (!verify_wavetable(unit, "OscN", tableSize, inNumSamples))
        return;

    float* out = ZOUT(0);
    float freqin = ZIN0(1);
    float phasein = ZIN0(2);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    int32 freq = (int32)(unit->m_cpstoinc * freqin);
    int32 phaseinc = freq + (int32)(CALCSLOPE(phasein, unit->m_phasein) * unit->m_radtoinc);
    unit->m_phasein = phasein;

    LOOP1(inNumSamples, ZXP(out) = *(float*)((char*)table + ((phase >> xlobits) & lomask)); phase += phaseinc;);
    unit->m_phase = phase;
}


void OscN_next_nka(OscN* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    if (tableSize != unit->mTableSize) {
        unit->mTableSize = tableSize;
        unit->m_lomask = (tableSize - 1) << 2;
        unit->m_radtoinc = tableSize * (rtwopi * 65536.);
        unit->m_cpstoinc = tableSize * SAMPLEDUR * 65536.;
    }

    if (!verify_wavetable(unit, "OscN", tableSize, inNumSamples))
        return;

    float* out = ZOUT(0);
    float freqin = ZIN0(1);
    float* phasein = ZIN(2);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    int32 freq = (int32)(unit->m_cpstoinc * freqin);
    float radtoinc = unit->m_radtoinc;
    LOOP1(inNumSamples, int32 pphase = phase + (int32)(radtoinc * ZXP(phasein));
          ZXP(out) = *(float*)((char*)table + ((pphase >> xlobits) & lomask)); phase += freq;);
    unit->m_phase = phase;
}

void OscN_next_naa(OscN* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    if (tableSize != unit->mTableSize) {
        unit->mTableSize = tableSize;
        unit->m_lomask = (tableSize - 1) << 2;
        unit->m_radtoinc = tableSize * (rtwopi * 65536.);
        unit->m_cpstoinc = tableSize * SAMPLEDUR * 65536.;
    }

    if (!verify_wavetable(unit, "OscN", tableSize, inNumSamples))
        return;

    float* out = ZOUT(0);
    float* freqin = ZIN(1);
    float* phasein = ZIN(2);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    float cpstoinc = unit->m_cpstoinc;
    float radtoinc = unit->m_radtoinc;
    LOOP1(inNumSamples, int32 pphase = phase + (int32)(radtoinc * ZXP(phasein));
          float z = *(float*)((char*)table + ((pphase >> xlobits) & lomask)); phase += (int32)(cpstoinc * ZXP(freqin));
          ZXP(out) = z;);
    unit->m_phase = phase;
}


void OscN_next_nak(OscN* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table = bufData;
    if (tableSize != unit->mTableSize) {
        unit->mTableSize = tableSize;
        unit->m_lomask = (tableSize - 1) << 2;
        unit->m_radtoinc = tableSize * (rtwopi * 65536.);
        unit->m_cpstoinc = tableSize * SAMPLEDUR * 65536.;
    }

    if (!verify_wavetable(unit, "OscN", tableSize, inNumSamples))
        return;

    float* out = ZOUT(0);
    float* freqin = ZIN(1);
    float phasein = ZIN0(2);

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    float cpstoinc = unit->m_cpstoinc;
    float radtoinc = unit->m_radtoinc;
    float phasemod = unit->m_phasein;
    float phaseslope = CALCSLOPE(phasein, phasemod);

    LOOP1(inNumSamples, int32 pphase = phase + (int32)(radtoinc * phasemod); phasemod += phaseslope;
          float z = *(float*)((char*)table + ((pphase >> xlobits) & lomask)); phase += (int32)(cpstoinc * ZXP(freqin));
          ZXP(out) = z;);
    unit->m_phase = phase;
    unit->m_phasein = phasein;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

void COsc_Ctor(COsc* unit) {
    unit->m_fbufnum = std::numeric_limits<float>::quiet_NaN();
    SETCALC(COsc_next);
    unit->m_phase1 = 0;
    unit->m_phase2 = 0;
    unit->mTableSize = -1;
    COsc_next(unit, 1);
    unit->m_phase1 = 0;
    unit->m_phase2 = 0;
}


void COsc_next(COsc* unit, int inNumSamples) {
    // get table
    GET_TABLE
    const float* table0 = bufData;
    const float* table1 = table0 + 1;
    if (tableSize != unit->mTableSize) {
        unit->mTableSize = tableSize;
        int tableSize2 = tableSize >> 1;
        unit->m_lomask = (tableSize2 - 1) << 3; // Osc, OscN, COsc, COsc, COsc2, OscX4, OscX2
        // Osc, OscN, PMOsc, COsc, COsc2, OscX4, OscX2
        unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;
    }
    if (!verify_wavetable(unit, "COsc", tableSize, inNumSamples))
        return;

    float* out = ZOUT(0);
    float freqin = ZIN0(1);
    float beats = ZIN0(2) * 0.5f;

    int32 phase1 = unit->m_phase1;
    int32 phase2 = unit->m_phase2;
    int32 lomask = unit->m_lomask;

    int32 cfreq = (int32)(unit->m_cpstoinc * freqin);
    int32 beatf = (int32)(unit->m_cpstoinc * beats);
    int32 freq1 = cfreq + beatf;
    int32 freq2 = cfreq - beatf;
    LOOP1(inNumSamples, float a = lookupi1(table0, table1, phase1, lomask);
          float b = lookupi1(table0, table1, phase2, lomask); ZXP(out) = a + b; phase1 += freq1; phase2 += freq2;);
    unit->m_phase1 = phase1;
    unit->m_phase2 = phase2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

static inline const SndBuf* VOscGetBuf(int& bufnum, World* world, Unit* unit) {
    if (bufnum < 0)
        bufnum = 0;

    const SndBuf* bufs;
    if (bufnum + 1 >= world->mNumSndBufs) {
        int localBufNum = bufnum - world->mNumSndBufs;
        Graph* parent = unit->mParent;
        if (localBufNum <= parent->localBufNum) {
            bufs = parent->mLocalSndBufs + localBufNum;
        } else {
            bufnum = 0;
            bufs = world->mSndBufs + bufnum;
        }
    } else {
        if (bufnum >= world->mNumSndBufs)
            bufnum = 0;
        bufs = world->mSndBufs + sc_max(0, bufnum);
    }
    return bufs;
}

void VOsc_Ctor(VOsc* unit) {
    float nextbufpos = ZIN0(0);
    unit->m_bufpos = nextbufpos;
    int bufnum = sc_floor(nextbufpos);
    World* world = unit->mWorld;

    const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);

    int tableSize = bufs[0].samples;

    unit->mTableSize = tableSize;
    int tableSize2 = tableSize >> 1;
    unit->m_lomask = (tableSize2 - 1) << 3;
    unit->m_radtoinc = tableSize2 * (rtwopi * 65536.);
    unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;

    unit->m_phasein = ZIN0(2);
    unit->m_phaseoffset = (int32)(unit->m_phasein * unit->m_radtoinc);

    double initphase;
    if (INRATE(2) == calc_FullRate) {
        SETCALC(VOsc_next_ika);
        unit->m_phase = initphase = 0;
        VOsc_next_ika(unit, 1);
    } else {
        SETCALC(VOsc_next_ikk);
        unit->m_phase = initphase = unit->m_phaseoffset;
        VOsc_next_ikk(unit, 1);
    }
    unit->m_phase = initphase;
}

void VOsc_next_ikk(VOsc* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float nextbufpos = ZIN0(0);
    float freqin = ZIN0(1);
    float phasein = ZIN0(2);

    float prevbufpos = unit->m_bufpos;
    float bufdiff = nextbufpos - prevbufpos;

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    int32 freq = (int32)(unit->m_cpstoinc * freqin);
    int32 phaseinc = freq + (int32)(CALCSLOPE(phasein, unit->m_phasein) * unit->m_radtoinc);
    unit->m_phasein = phasein;
    int tableSize = unit->mTableSize;
    float cur = prevbufpos;
    World* world = unit->mWorld;

    if (bufdiff == 0.f) {
        float level = cur - sc_floor(cur);
        int32 bufnum = (int)sc_floor(cur);

        const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
        if (!verify_wavetable(unit, "VOsc", tableSize, inNumSamples))
            return;

        const float* table0 = bufs[0].data;
        const float* table2 = bufs[1].data;
        if (!table0 || !table2 || tableSize != bufs[0].samples || tableSize != bufs[1].samples) {
            ClearUnitOutputs(unit, inNumSamples);
            return;
        }

        const float* table1 = table0 + 1;
        const float* table3 = table2 + 1;

        LOOP1(inNumSamples, float pfrac = PhaseFrac1(phase); uint32 index = ((phase >> xlobits1) & lomask);
              float val0 = *(float*)((char*)table0 + index); float val1 = *(float*)((char*)table1 + index);
              float val2 = *(float*)((char*)table2 + index); float val3 = *(float*)((char*)table3 + index);
              float a = val0 + val1 * pfrac; float b = val2 + val3 * pfrac; ZXP(out) = a + level * (b - a);
              phase += phaseinc;);
    } else {
        int nsmps;
        int donesmps = 0;
        int remain = inNumSamples;
        while (remain) {
            float level = cur - sc_floor(cur);

            float cut;
            if (bufdiff > 0.) {
                cut = sc_min(nextbufpos, sc_floor(cur + 1.f));
            } else {
                cut = sc_max(nextbufpos, sc_ceil(cur - 1.f));
            }

            float sweepdiff = cut - cur;
            if (cut == nextbufpos)
                nsmps = remain;
            else {
                float sweep = (float)inNumSamples / bufdiff;
                nsmps = (int)sc_floor(sweep * sweepdiff + 0.5f) - donesmps;
                nsmps = sc_clip(nsmps, 1, remain);
            }

            float slope = sweepdiff / (float)nsmps;

            int32 bufnum = (int32)sc_floor(cur);
            const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
            if (!verify_wavetable(unit, "VOsc", tableSize, inNumSamples))
                return;

            const float* table0 = bufs[0].data;
            const float* table2 = bufs[1].data;
            if (!table0 || !table2 || tableSize != bufs[0].samples || tableSize != bufs[1].samples) {
                ClearUnitOutputs(unit, inNumSamples);
                return;
            }

            const float* table1 = table0 + 1;
            const float* table3 = table2 + 1;

            LOOP(nsmps, float pfrac = PhaseFrac1(phase); uint32 index = ((phase >> xlobits1) & lomask);
                 float val0 = *(float*)((char*)table0 + index); float val1 = *(float*)((char*)table1 + index);
                 float val2 = *(float*)((char*)table2 + index); float val3 = *(float*)((char*)table3 + index);
                 float a = val0 + val1 * pfrac; float b = val2 + val3 * pfrac; ZXP(out) = a + level * (b - a);
                 phase += phaseinc; level += slope;);
            donesmps += nsmps;
            remain -= nsmps;
            cur = cut;
        }
    }
    unit->m_bufpos = nextbufpos;
    unit->m_phase = phase;
}

void VOsc_next_ika(VOsc* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float nextbufpos = ZIN0(0);
    float freqin = ZIN0(1);
    float* phasein = ZIN(2);

    float prevbufpos = unit->m_bufpos;
    float bufdiff = nextbufpos - prevbufpos;

    int32 phase = unit->m_phase;
    int32 lomask = unit->m_lomask;

    int32 freq = (int32)(unit->m_cpstoinc * freqin);
    int32 phaseinc = freq;
    int tableSize = unit->mTableSize;
    float cur = prevbufpos;
    World* world = unit->mWorld;

    if (bufdiff == 0.f) {
        float level = cur - sc_floor(cur);
        int32 bufnum = (int)sc_floor(cur);

        const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
        if (!verify_wavetable(unit, "VOsc", tableSize, inNumSamples))
            return;

        const float* table0 = bufs[0].data;
        const float* table2 = bufs[1].data;
        if (!table0 || !table2 || tableSize != bufs[0].samples || tableSize != bufs[1].samples) {
            ClearUnitOutputs(unit, inNumSamples);
            return;
        }

        const float* table1 = table0 + 1;
        const float* table3 = table2 + 1;

        LOOP1(inNumSamples, int32 pphase = phase + (int32)(ZXP(phasein) * unit->m_radtoinc);
              float pfrac = PhaseFrac1(pphase); uint32 index = ((pphase >> xlobits1) & lomask);
              float val0 = *(float*)((char*)table0 + index); float val1 = *(float*)((char*)table1 + index);
              float val2 = *(float*)((char*)table2 + index); float val3 = *(float*)((char*)table3 + index);
              float a = val0 + val1 * pfrac; float b = val2 + val3 * pfrac; ZXP(out) = a + level * (b - a);
              phase += phaseinc;);
    } else {
        int nsmps;
        int donesmps = 0;
        int remain = inNumSamples;
        while (remain) {
            float level = cur - sc_floor(cur);

            float cut;
            if (bufdiff > 0.) {
                cut = sc_min(nextbufpos, sc_floor(cur + 1.f));
            } else {
                cut = sc_max(nextbufpos, sc_ceil(cur - 1.f));
            }

            float sweepdiff = cut - cur;
            if (cut == nextbufpos)
                nsmps = remain;
            else {
                float sweep = (float)inNumSamples / bufdiff;
                nsmps = (int)sc_floor(sweep * sweepdiff + 0.5f) - donesmps;
                nsmps = sc_clip(nsmps, 1, remain);
            }

            float slope = sweepdiff / (float)nsmps;

            int32 bufnum = (int32)sc_floor(cur);
            const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
            if (!verify_wavetable(unit, "VOsc", tableSize, inNumSamples))
                return;

            const float* table0 = bufs[0].data;
            const float* table2 = bufs[1].data;
            if (!table0 || !table2 || tableSize != bufs[0].samples || tableSize != bufs[1].samples) {
                ClearUnitOutputs(unit, inNumSamples);
                return;
            }

            const float* table1 = table0 + 1;
            const float* table3 = table2 + 1;

            LOOP(nsmps, int32 pphase = phase + (int32)(ZXP(phasein) * unit->m_radtoinc);
                 float pfrac = PhaseFrac1(pphase); uint32 index = ((pphase >> xlobits1) & lomask);
                 float val0 = *(float*)((char*)table0 + index); float val1 = *(float*)((char*)table1 + index);
                 float val2 = *(float*)((char*)table2 + index); float val3 = *(float*)((char*)table3 + index);
                 float a = val0 + val1 * pfrac; float b = val2 + val3 * pfrac; ZXP(out) = a + level * (b - a);
                 phase += phaseinc; level += slope;);
            donesmps += nsmps;
            remain -= nsmps;
            cur = cut;
        }
    }
    unit->m_bufpos = nextbufpos;
    unit->m_phase = phase;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

void VOsc3_Ctor(VOsc3* unit) {
    SETCALC(VOsc3_next_ik);

    float nextbufpos = ZIN0(0);
    unit->m_bufpos = nextbufpos;
    int32 bufnum = (int32)sc_floor(nextbufpos);
    World* world = unit->mWorld;

    const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
    int tableSize = bufs[0].samples;

    unit->mTableSize = tableSize;
    int tableSize2 = tableSize >> 1;
    unit->m_lomask = (tableSize2 - 1) << 3;
    unit->m_cpstoinc = tableSize2 * SAMPLEDUR * 65536.;

    unit->m_phase1 = 0;
    unit->m_phase2 = 0;
    unit->m_phase3 = 0;

    VOsc3_next_ik(unit, 1);
    unit->m_phase1 = 0;
    unit->m_phase2 = 0;
    unit->m_phase3 = 0;
}

void VOsc3_next_ik(VOsc3* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float nextbufpos = ZIN0(0);
    float freq1in = ZIN0(1);
    float freq2in = ZIN0(2);
    float freq3in = ZIN0(3);

    float prevbufpos = unit->m_bufpos;
    float bufdiff = nextbufpos - prevbufpos;

    int32 phase1 = unit->m_phase1;
    int32 phase2 = unit->m_phase2;
    int32 phase3 = unit->m_phase3;

    int32 freq1 = (int32)(unit->m_cpstoinc * freq1in);
    int32 freq2 = (int32)(unit->m_cpstoinc * freq2in);
    int32 freq3 = (int32)(unit->m_cpstoinc * freq3in);

    int32 lomask = unit->m_lomask;
    int tableSize = unit->mTableSize;
    float cur = prevbufpos;
    World* world = unit->mWorld;

    if (bufdiff == 0.f) {
        float level = cur - (int)cur;

        int bufnum = (int)cur;

        const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
        if (!verify_wavetable(unit, "VOsc3", tableSize, inNumSamples))
            return;

        const float* table0 = bufs[0].data;
        const float* table2 = bufs[1].data;
        if (!table0 || !table2 || tableSize != bufs[0].samples || tableSize != bufs[1].samples) {
            ClearUnitOutputs(unit, inNumSamples);
            return;
        }

        const float* table1 = table0 + 1;
        const float* table3 = table2 + 1;

        LOOP1(inNumSamples,

              float pfrac1 = PhaseFrac1(phase1);
              float pfrac2 = PhaseFrac1(phase2); float pfrac3 = PhaseFrac1(phase3);

              int index1 = ((phase1 >> xlobits1) & lomask); int index2 = ((phase2 >> xlobits1) & lomask);
              int index3 = ((phase3 >> xlobits1) & lomask);

              phase1 += freq1; phase2 += freq2; phase3 += freq3;

              float val10 = *(float*)((char*)table0 + index1); float val11 = *(float*)((char*)table1 + index1);
              float val12 = *(float*)((char*)table2 + index1); float val13 = *(float*)((char*)table3 + index1);
              float a = val10 + val11 * pfrac1; float b = val12 + val13 * pfrac1;

              float val20 = *(float*)((char*)table0 + index2); float val21 = *(float*)((char*)table1 + index2);
              float val22 = *(float*)((char*)table2 + index2); float val23 = *(float*)((char*)table3 + index2);
              a += val20 + val21 * pfrac2; b += val22 + val23 * pfrac2;

              float val30 = *(float*)((char*)table0 + index3); float val31 = *(float*)((char*)table1 + index3);
              float val32 = *(float*)((char*)table2 + index3); float val33 = *(float*)((char*)table3 + index3);
              a += val30 + val31 * pfrac3; b += val32 + val33 * pfrac3;

              ZXP(out) = a + level * (b - a););
    } else {
        int nsmps;
        int donesmps = 0;
        int remain = inNumSamples;
        do {
            float level = cur - sc_trunc(cur);

            float cut;
            if (bufdiff >= 0.)
                cut = sc_min(nextbufpos, sc_trunc(cur + 1.f));
            else
                cut = sc_max(nextbufpos, sc_ceil(cur - 1.f));

            float sweepdiff = cut - cur;
            if (cut == nextbufpos)
                nsmps = remain;
            else {
                float sweep = (float)inNumSamples / bufdiff;
                nsmps = sc_floor(sweep * sweepdiff + 0.5f) - donesmps;
                nsmps = sc_clip(nsmps, 1, remain);
            }

            float slope = sweepdiff / (float)nsmps;

            int bufnum = (int)cur;

            const SndBuf* bufs = VOscGetBuf(bufnum, world, unit);
            if (!verify_wavetable(unit, "VOsc3", tableSize, inNumSamples))
                return;

            const float* table0 = bufs[0].data;
            const float* table2 = bufs[1].data;
            if (!table0 || !table2 || tableSize != bufs[0].samples || tableSize != bufs[1].samples) {
                ClearUnitOutputs(unit, inNumSamples);
                return;
            }

            const float* table1 = table0 + 1;
            const float* table3 = table2 + 1;

            LOOP(nsmps,

                 float pfrac1 = PhaseFrac1(phase1);
                 float pfrac2 = PhaseFrac1(phase2); float pfrac3 = PhaseFrac1(phase3);

                 int index1 = ((phase1 >> xlobits1) & lomask); int index2 = ((phase2 >> xlobits1) & lomask);
                 int index3 = ((phase3 >> xlobits1) & lomask);

                 phase1 += freq1; phase2 += freq2; phase3 += freq3;

                 float val10 = *(float*)((char*)table0 + index1); float val11 = *(float*)((char*)table1 + index1);
                 float val12 = *(float*)((char*)table2 + index1); float val13 = *(float*)((char*)table3 + index1);
                 float a = val10 + val11 * pfrac1; float b = val12 + val13 * pfrac1;

                 float val20 = *(float*)((char*)table0 + index2); float val21 = *(float*)((char*)table1 + index2);
                 float val22 = *(float*)((char*)table2 + index2); float val23 = *(float*)((char*)table3 + index2);
                 a += val20 + val21 * pfrac2; b += val22 + val23 * pfrac2;

                 float val30 = *(float*)((char*)table0 + index3); float val31 = *(float*)((char*)table1 + index3);
                 float val32 = *(float*)((char*)table2 + index3); float val33 = *(float*)((char*)table3 + index3);
                 a += val30 + val31 * pfrac3; b += val32 + val33 * pfrac3;

                 ZXP(out) = a + level * (b - a); level += slope;);
            donesmps += nsmps;
            remain -= nsmps;
            cur = cut;
        } while (remain);
    }
    unit->m_bufpos = nextbufpos;
    unit->m_phase1 = phase1;
    unit->m_phase2 = phase2;
    unit->m_phase3 = phase3;
}

//////////////////////////////////////////////////////////////////////////////////////////

void Formant_Ctor(Formant* unit) {
    SETCALC(Formant_next);
    unit->m_cpstoinc = ft->mSineSize * SAMPLEDUR * 65536.;
    unit->m_phase1 = 0;
    unit->m_phase2 = 0;
    unit->m_phase3 = 0;

    Formant_next(unit, 1);
    unit->m_phase1 = 0;
    unit->m_phase2 = 0;
    unit->m_phase3 = 0;
}

#define tqcyc13 0x18000000

void Formant_next(Formant* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freq1in = ZIN0(0);
    float freq2in = ZIN0(1);
    float freq3in = ZIN0(2);

    int32 phase1 = unit->m_phase1;
    int32 phase2 = unit->m_phase2;
    int32 phase3 = unit->m_phase3;
    float cpstoinc = unit->m_cpstoinc;
    int32 freq1 = (int32)(cpstoinc * freq1in);
    int32 freq2 = (int32)(cpstoinc * freq2in);
    int32 freq3 = (int32)(cpstoinc * freq3in);
    float* sine = ft->mSine;
    int32 formfreq = sc_max(freq1, freq3);
    LOOP1(
        inNumSamples,
        if (phase3 < onecyc13) {
            ZXP(out) = (*(float*)((char*)sine + (((phase3 + tqcyc13) >> xlobits) & xlomask13)) + 1.f)
                * *(float*)((char*)sine + ((phase2 >> xlobits) & xlomask13));
            phase3 += formfreq;
        } else { ZXP(out) = 0.f; } phase1 += freq1;
        phase2 += freq2; if (phase1 > onecyc13) {
            phase1 -= onecyc13;
            phase2 = phase1 * freq2 / freq1;
            phase3 = phase1 * freq3 / freq1;
        });

    unit->m_phase1 = phase1;
    unit->m_phase2 = phase2;
    unit->m_phase3 = phase3;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

inline float lookup13(float* table, int32 pphase) {
    float pfrac = PhaseFrac(pphase);
    float* tbl = (float*)((char*)table + ((pphase >> xlobits) & xlomask13));
    return lininterp(pfrac, tbl[0], tbl[1]);
}

void Blip_Ctor(Blip* unit) {
    SETCALC(Blip_next);
    unit->m_freqin = ZIN0(0);
    unit->m_numharm = (int32)ZIN0(1);

    unit->m_cpstoinc = ft->mSineSize * SAMPLEDUR * 65536. * 0.5;
    int32 N = unit->m_numharm;
    int32 maxN = (int32)((SAMPLERATE * 0.5) / unit->m_freqin);
    if (N > maxN)
        N = maxN;
    if (N < 1)
        N = 1;
    unit->m_N = N;
    unit->m_scale = 0.5 / N;
    unit->m_phase = 0;

    Blip_next(unit, 1);
    unit->m_N = N;
    unit->m_scale = 0.5 / N;
    unit->m_phase = 0;
}

void Blip_next(Blip* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freqin = ZIN0(0);
    int numharm = (int32)ZIN0(1);

    int32 phase = unit->m_phase;

    float* numtbl = ft->mSine;
    float* dentbl = ft->mCosecant;

    int32 freq, N, prevN;
    float scale, prevscale;
    bool crossfade;
    if (numharm != unit->m_numharm || freqin != unit->m_freqin) {
        N = numharm;
        int32 maxN = (int32)((SAMPLERATE * 0.5) / freqin);
        if (N > maxN) {
            float maxfreqin;
            N = maxN;
            maxfreqin = sc_max(unit->m_freqin, freqin);
            freq = (int32)(unit->m_cpstoinc * maxfreqin);
        } else {
            if (N < 1) {
                N = 1;
            }
            freq = (int32)(unit->m_cpstoinc * freqin);
        }
        crossfade = N != unit->m_N;
        prevN = unit->m_N;
        prevscale = unit->m_scale;
        unit->m_N = N;
        unit->m_scale = scale = 0.5 / N;
    } else {
        N = unit->m_N;
        freq = (int32)(unit->m_cpstoinc * freqin);
        scale = unit->m_scale;
        crossfade = false;
    }
    int32 N2 = 2 * N + 1;

    if (crossfade) {
        int32 prevN2 = 2 * prevN + 1;
        float xfade_slope = unit->mRate->mSlopeFactor;
        float xfade = 0.f;
        LOOP1(
            inNumSamples, float* tbl = (float*)((char*)dentbl + ((phase >> xlobits) & xlomask13)); float t0 = tbl[0];
            float t1 = tbl[1]; if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    ZXP(out) = 1.f;
                } else {
                    int32 rphase = phase * prevN2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n1 = (numer / denom - 1.f) * prevscale;

                    rphase = phase * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n2 = (numer / denom - 1.f) * scale;

                    ZXP(out) = lininterp(xfade, n1, n2);
                }
            } else {
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;

                int32 rphase = phase * prevN2;
                pfrac = PhaseFrac(rphase);
                float* tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n1 = (numer * denom - 1.f) * prevscale;

                rphase = phase * N2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n2 = (numer * denom - 1.f) * scale;

                ZXP(out) = lininterp(xfade, n1, n2);
            } phase += freq;
            xfade += xfade_slope;);
    } else {
        // hmm, if freq is above sr/4 then revert to sine table osc w/ no interpolation ?
        // why bother, it isn't a common choice for a fundamental.
        LOOP1(
            inNumSamples, float* tbl = (float*)((char*)dentbl + ((phase >> xlobits) & xlomask13)); float t0 = tbl[0];
            float t1 = tbl[1]; if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    ZXP(out) = 1.f;
                } else {
                    int32 rphase = phase * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    ZXP(out) = (numer / denom - 1.f) * scale;
                }
            } else {
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                int32 rphase = phase * N2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                ZXP(out) = (numer * denom - 1.f) * scale;
            } phase += freq;);
    }

    unit->m_phase = phase;
    unit->m_freqin = freqin;
    unit->m_numharm = numharm;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////


void Saw_Ctor(Saw* unit) {
    SETCALC(Saw_next);
    unit->m_freqin = ZIN0(0);

    unit->m_cpstoinc = ft->mSineSize * SAMPLEDUR * 65536. * 0.5;
    unit->m_N = (int32)((SAMPLERATE * 0.5) / unit->m_freqin);
    unit->m_scale = 0.5 / unit->m_N;
    unit->m_phase = 0;
    unit->m_y1 = -0.46f;

    Saw_next(unit, 1);
    unit->m_scale = 0.5 / unit->m_N;
    unit->m_phase = 0;
    unit->m_y1 = -0.46f;
}

void Saw_next(Saw* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freqin = ZIN0(0);

    int32 phase = unit->m_phase;
    float y1 = unit->m_y1;

    float* numtbl = ft->mSine;
    float* dentbl = ft->mCosecant;

    int32 freq, N, prevN;
    float scale, prevscale;
    bool crossfade;
    if (freqin != unit->m_freqin) {
        N = (int32)((SAMPLERATE * 0.5) / freqin);
        if (N != unit->m_N) {
            float maxfreqin;
            maxfreqin = sc_max(unit->m_freqin, freqin);
            freq = (int32)(unit->m_cpstoinc * maxfreqin);
            crossfade = true;
        } else {
            freq = (int32)(unit->m_cpstoinc * freqin);
            crossfade = false;
        }
        prevN = unit->m_N;
        prevscale = unit->m_scale;
        unit->m_N = N;
        unit->m_scale = scale = 0.5 / N;
    } else {
        N = unit->m_N;
        freq = (int32)(unit->m_cpstoinc * freqin);
        scale = unit->m_scale;
        crossfade = false;
    }
    int32 N2 = 2 * N + 1;

    if (crossfade) {
        int32 prevN2 = 2 * prevN + 1;
        float xfade_slope = unit->mRate->mSlopeFactor;
        float xfade = 0.f;
        LOOP1(
            inNumSamples, float* tbl = (float*)((char*)dentbl + ((phase >> xlobits) & xlomask13)); float t0 = tbl[0];
            float t1 = tbl[1]; if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    ZXP(out) = y1 = 1.f + 0.999f * y1;
                } else {
                    int32 rphase = phase * prevN2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n1 = (numer / denom - 1.f) * prevscale;

                    rphase = phase * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n2 = (numer / denom - 1.f) * scale;

                    ZXP(out) = y1 = n1 + xfade * (n2 - n1) + 0.999f * y1;
                }
            } else {
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;

                int32 rphase = phase * prevN2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n1 = (numer * denom - 1.f) * prevscale;

                rphase = phase * N2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n2 = (numer * denom - 1.f) * scale;

                ZXP(out) = y1 = n1 + xfade * (n2 - n1) + 0.999f * y1;
            } phase += freq;
            xfade += xfade_slope;);
    } else {
        // hmm, if freq is above sr/4 then revert to sine table osc ?
        // why bother, it isn't a common choice for a fundamental.
        LOOP1(
            inNumSamples, float* tbl = (float*)((char*)dentbl + ((phase >> xlobits) & xlomask13)); float t0 = tbl[0];
            float t1 = tbl[1]; if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    ZXP(out) = y1 = 1.f + 0.999f * y1;
                } else {
                    int32 rphase = phase * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    ZXP(out) = y1 = (numer / denom - 1.f) * scale + 0.999f * y1;
                }
            } else {
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                int32 rphase = phase * N2;
                pfrac = PhaseFrac(rphase);
                float* tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                ZXP(out) = y1 = (numer * denom - 1.f) * scale + 0.999f * y1;
            } phase += freq;);
    }

    unit->m_y1 = y1;
    unit->m_phase = phase;
    unit->m_freqin = freqin;
}


////////////////////////////////////////////////////////////////////////////////////////////////////////


void Pulse_Ctor(Pulse* unit) {
    SETCALC(Pulse_next);
    unit->m_freqin = ZIN0(0);

    unit->m_cpstoinc = ft->mSineSize * SAMPLEDUR * 65536. * 0.5;
    unit->m_N = (int32)((SAMPLERATE * 0.5) / unit->m_freqin);
    unit->m_scale = 0.5 / unit->m_N;
    unit->m_phase = 0;
    unit->m_phaseoff = 0;
    unit->m_y1 = 0.f;
    ZOUT0(0) = 0.f;
}

void Pulse_next(Pulse* unit, int inNumSamples) {
    float* out = ZOUT(0);
    float freqin = ZIN0(0);
    float duty = ZIN0(1);

    int32 phase = unit->m_phase;
    float y1 = unit->m_y1;

    float* numtbl = ft->mSine;
    float* dentbl = ft->mCosecant;

    int32 freq, N, prevN;
    float scale, prevscale;
    bool crossfade;

    if (freqin != unit->m_freqin) {
        N = (int32)((SAMPLERATE * 0.5) / freqin);
        if (N != unit->m_N) {
            float maxfreqin;
            maxfreqin = sc_max(unit->m_freqin, freqin);
            freq = (int32)(unit->m_cpstoinc * maxfreqin);
            crossfade = true;
        } else {
            freq = (int32)(unit->m_cpstoinc * freqin);
            crossfade = false;
        }
        prevN = unit->m_N;
        prevscale = unit->m_scale;
        unit->m_N = N;
        unit->m_scale = scale = 0.5 / N;
    } else {
        N = unit->m_N;
        freq = (int32)(unit->m_cpstoinc * freqin);
        scale = unit->m_scale;
        crossfade = false;
    }
    int32 N2 = 2 * N + 1;

    int32 phaseoff = unit->m_phaseoff;
    int32 next_phaseoff = (int32)(duty * (1L << 28));
    int32 phaseoff_slope = (int32)((next_phaseoff - phaseoff) * unit->mRate->mSlopeFactor);
    unit->m_phaseoff = next_phaseoff;
    float rscale = 1.f / scale + 1.f;
    float pul1, pul2;

    if (crossfade) {
        int32 prevN2 = 2 * prevN + 1;
        float xfade_slope = unit->mRate->mSlopeFactor;
        float xfade = 0.f;
        LOOP1(
            inNumSamples, float* tbl = (float*)((char*)dentbl + ((phase >> xlobits) & xlomask13)); float t0 = tbl[0];
            float t1 = tbl[1]; if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    pul1 = 1.f;
                } else {
                    int32 rphase = phase * prevN2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n1 = (numer / denom - 1.f) * prevscale;

                    rphase = phase * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n2 = (numer / denom - 1.f) * scale;

                    pul1 = lininterp(xfade, n1, n2);
                }
            } else {
                float pfrac = PhaseFrac(phase);
                float denom = lininterp(pfrac, t0, t1);

                int32 rphase = phase * prevN2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n1 = (numer * denom - 1.f) * prevscale;

                rphase = phase * N2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n2 = (numer * denom - 1.f) * scale;

                pul1 = lininterp(xfade, n1, n2);
            }

            int32 phase2 = phase + phaseoff;
            tbl = (float*)((char*)dentbl + ((phase2 >> xlobits) & xlomask13)); t0 = tbl[0]; t1 = tbl[1];
            if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase2 >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase2);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    pul2 = 1.f;
                } else {
                    int32 rphase = phase2 * prevN2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n1 = (numer / denom - 1.f) * prevscale;

                    rphase = phase2 * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    numer = lininterp(pfrac, tbl[0], tbl[1]);
                    float n2 = (numer / denom - 1.f) * scale;

                    pul2 = lininterp(xfade, n1, n2);
                }
            } else {
                float pfrac = PhaseFrac(phase2);
                float denom = t0 + (t1 - t0) * pfrac;

                int32 rphase = phase2 * prevN2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n1 = (numer * denom - 1.f) * prevscale;

                rphase = phase2 * N2;
                pfrac = PhaseFrac(rphase);
                tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                numer = lininterp(pfrac, tbl[0], tbl[1]);
                float n2 = (numer * denom - 1.f) * scale;

                pul2 = lininterp(xfade, n1, n2);
            }

            ZXP(out) = y1 = pul1 - pul2 + 0.999f * y1;
            phase += freq; phaseoff += phaseoff_slope; xfade += xfade_slope;);
    } else {
        LOOP1(
            inNumSamples, float* tbl = (float*)((char*)dentbl + ((phase >> xlobits) & xlomask13)); float t0 = tbl[0];
            float t1 = tbl[1]; if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    pul1 = rscale;
                } else {
                    int32 rphase = phase * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    pul1 = numer / denom;
                }
            } else {
                float pfrac = PhaseFrac(phase);
                float denom = t0 + (t1 - t0) * pfrac;
                int32 rphase = phase * N2;
                pfrac = PhaseFrac(rphase);
                float* tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);
                pul1 = (numer * denom);
            }

            int32 phase2 = phase + phaseoff;
            tbl = (float*)((char*)dentbl + ((phase2 >> xlobits) & xlomask13)); t0 = tbl[0]; t1 = tbl[1];
            if (t0 == kBadValue || t1 == kBadValue) {
                tbl = (float*)((char*)numtbl + ((phase2 >> xlobits) & xlomask13));
                t0 = tbl[0];
                t1 = tbl[1];
                float pfrac = PhaseFrac(phase2);
                float denom = t0 + (t1 - t0) * pfrac;
                if (std::abs(denom) < 0.0005f) {
                    pul2 = rscale;
                } else {
                    int32 rphase = phase2 * N2;
                    pfrac = PhaseFrac(rphase);
                    tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                    float numer = lininterp(pfrac, tbl[0], tbl[1]);
                    pul2 = numer / denom;
                }
            } else {
                float pfrac = PhaseFrac(phase2);
                float denom = t0 + (t1 - t0) * pfrac;
                int32 rphase = phase2 * N2;
                pfrac = PhaseFrac(rphase);
                float* tbl = (float*)((char*)numtbl + ((rphase >> xlobits) & xlomask13));
                float numer = lininterp(pfrac, tbl[0], tbl[1]);

                pul2 = (numer * denom);
            }

            ZXP(out) = y1 = (pul1 - pul2) * scale + 0.999f * y1;
            phase += freq; phaseoff += phaseoff_slope;);
    }

    unit->m_y1 = y1;
    unit->m_phase = phase;
    unit->m_freqin = freqin;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

static float Klang_SetCoefs(Klang* unit) {
    float freqscale = ZIN0(0) * unit->mRate->mRadiansPerSample;
    float freqoffset = ZIN0(1) * unit->mRate->mRadiansPerSample;

    float outf = 0.;
    float* coefs = unit->m_coefs - 1;

    for (int i = 0, j = 2; i < unit->m_numpartials; ++i, j += 3) {
        float w = ZIN0(j) * freqscale + freqoffset;
        float level = ZIN0(j + 1);
        float phase = ZIN0(j + 2);

        if (phase != 0.f) {
            outf += level * sin(phase);
            *++coefs = level * sin(phase - w); // y1
            *++coefs = level * sin(phase - w - w); // y2
        } else {
            *++coefs = level * -sin(w); // y1
            *++coefs = level * -sin(w + w); // y2
        }
        *++coefs = 2. * cos(w); // b1
    }
    return outf;
}

void Klang_Ctor(Klang* unit) {
    SETCALC(Klang_next);
    unit->m_numpartials = (unit->mNumInputs - 2) / 3;
    int numcoefs = unit->m_numpartials * 3;
    unit->m_coefs = (float*)RTAlloc(unit->mWorld, numcoefs * sizeof(float));
    ClearUnitIfMemFailed(unit->m_coefs);
    ZOUT0(0) = Klang_SetCoefs(unit);
}

void Klang_Dtor(Klang* unit) { RTFree(unit->mWorld, unit->m_coefs); }

void Klang_next(Klang* unit, int inNumSamples) {
    float* out0 = ZOUT(0);

    float* out;
    float y0_0, y1_0, y2_0, b1_0;
    float y0_1, y1_1, y2_1, b1_1;
    float y0_2, y1_2, y2_2, b1_2;
    float y0_3, y1_3, y2_3, b1_3;
    float outf;

    float* coefs = unit->m_coefs - 1;
    int32 numpartials = unit->m_numpartials;

    switch (numpartials & 3) {
    case 3:
        y1_0 = *++coefs;
        y2_0 = *++coefs;
        b1_0 = *++coefs;
        y1_1 = *++coefs;
        y2_1 = *++coefs;
        b1_1 = *++coefs;
        y1_2 = *++coefs;
        y2_2 = *++coefs;
        b1_2 = *++coefs;

        out = out0;
        LOOP(unit->mRate->mFilterLoops, outf = y0_0 = b1_0 * y1_0 - y2_0; outf += y0_1 = b1_1 * y1_1 - y2_1;
             outf += y0_2 = b1_2 * y1_2 - y2_2; ZXP(out) = outf;

             outf = y2_0 = b1_0 * y0_0 - y1_0; outf += y2_1 = b1_1 * y0_1 - y1_1; outf += y2_2 = b1_2 * y0_2 - y1_2;
             ZXP(out) = outf;

             outf = y1_0 = b1_0 * y2_0 - y0_0; outf += y1_1 = b1_1 * y2_1 - y0_1; outf += y1_2 = b1_2 * y2_2 - y0_2;
             ZXP(out) = outf;);
        LOOP(unit->mRate->mFilterRemain, outf = y0_0 = b1_0 * y1_0 - y2_0; outf += y0_1 = b1_1 * y1_1 - y2_1;
             outf += y0_2 = b1_2 * y1_2 - y2_2; y2_0 = y1_0; y1_0 = y0_0; y2_1 = y1_1; y1_1 = y0_1; y2_2 = y1_2;
             y1_2 = y0_2; ZXP(out) = outf;);
        coefs -= 9;
        *++coefs = y1_0;
        *++coefs = y2_0;
        ++coefs;
        *++coefs = y1_1;
        *++coefs = y2_1;
        ++coefs;
        *++coefs = y1_2;
        *++coefs = y2_2;
        ++coefs;
        break;
    case 2:
        y1_0 = *++coefs;
        y2_0 = *++coefs;
        b1_0 = *++coefs;
        y1_1 = *++coefs;
        y2_1 = *++coefs;
        b1_1 = *++coefs;

        out = out0;
        LOOP(unit->mRate->mFilterLoops, outf = y0_0 = b1_0 * y1_0 - y2_0; outf += y0_1 = b1_1 * y1_1 - y2_1;
             ZXP(out) = outf;

             outf = y2_0 = b1_0 * y0_0 - y1_0; outf += y2_1 = b1_1 * y0_1 - y1_1; ZXP(out) = outf;

             outf = y1_0 = b1_0 * y2_0 - y0_0; outf += y1_1 = b1_1 * y2_1 - y0_1; ZXP(out) = outf;);
        LOOP(unit->mRate->mFilterRemain, outf = y0_0 = b1_0 * y1_0 - y2_0; outf += y0_1 = b1_1 * y1_1 - y2_1;
             y2_0 = y1_0; y1_0 = y0_0; y2_1 = y1_1; y1_1 = y0_1; ZXP(out) = outf;);

        coefs -= 6;
        *++coefs = y1_0;
        *++coefs = y2_0;
        ++coefs;
        *++coefs = y1_1;
        *++coefs = y2_1;
        ++coefs;
        break;
    case 1:
        y1_0 = *++coefs;
        y2_0 = *++coefs;
        b1_0 = *++coefs;

        out = out0;
        LOOP(unit->mRate->mFilterLoops, ZXP(out) = y0_0 = b1_0 * y1_0 - y2_0;

             ZXP(out) = y2_0 = b1_0 * y0_0 - y1_0;

             ZXP(out) = y1_0 = b1_0 * y2_0 - y0_0;);
        LOOP(unit->mRate->mFilterRemain, ZXP(out) = y0_0 = b1_0 * y1_0 - y2_0; y2_0 = y1_0; y1_0 = y0_0;);

        coefs -= 3;
        *++coefs = y1_0;
        *++coefs = y2_0;
        ++coefs;
        break;
    case 0:
        out = out0;
        ZClear(inNumSamples, out);
        break;
    }

    int32 imax = numpartials >> 2;

    for (int i = 0; i < imax; ++i) {
        y1_0 = *++coefs;
        y2_0 = *++coefs;
        b1_0 = *++coefs;
        y1_1 = *++coefs;
        y2_1 = *++coefs;
        b1_1 = *++coefs;
        y1_2 = *++coefs;
        y2_2 = *++coefs;
        b1_2 = *++coefs;
        y1_3 = *++coefs;
        y2_3 = *++coefs;
        b1_3 = *++coefs;

        out = out0;
        LOOP(unit->mRate->mFilterLoops, outf = y0_0 = b1_0 * y1_0 - y2_0; outf += y0_1 = b1_1 * y1_1 - y2_1;
             outf += y0_2 = b1_2 * y1_2 - y2_2; outf += y0_3 = b1_3 * y1_3 - y2_3; ZXP(out) += outf;

             outf = y2_0 = b1_0 * y0_0 - y1_0; outf += y2_1 = b1_1 * y0_1 - y1_1; outf += y2_2 = b1_2 * y0_2 - y1_2;
             outf += y2_3 = b1_3 * y0_3 - y1_3; ZXP(out) += outf;

             outf = y1_0 = b1_0 * y2_0 - y0_0; outf += y1_1 = b1_1 * y2_1 - y0_1; outf += y1_2 = b1_2 * y2_2 - y0_2;
             outf += y1_3 = b1_3 * y2_3 - y0_3; ZXP(out) += outf;);
        LOOP(unit->mRate->mFilterRemain, outf = y0_0 = b1_0 * y1_0 - y2_0; outf += y0_1 = b1_1 * y1_1 - y2_1;
             outf += y0_2 = b1_2 * y1_2 - y2_2; outf += y0_3 = b1_3 * y1_3 - y2_3; y2_0 = y1_0; y1_0 = y0_0;
             y2_1 = y1_1; y1_1 = y0_1; y2_2 = y1_2; y1_2 = y0_2; y2_3 = y1_3; y1_3 = y0_3; ZXP(out) += outf;);
        coefs -= 12;
        *++coefs = y1_0;
        *++coefs = y2_0;
        ++coefs;
        *++coefs = y1_1;
        *++coefs = y2_1;
        ++coefs;
        *++coefs = y1_2;
        *++coefs = y2_2;
        ++coefs;
        *++coefs = y1_3;
        *++coefs = y2_3;
        ++coefs;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

static void Klank_SetCoefs(Klank* unit) {
    float freqscale = ZIN0(1) * unit->mRate->mRadiansPerSample;
    float freqoffset = ZIN0(2) * unit->mRate->mRadiansPerSample;
    float decayscale = ZIN0(3);
    float* coefs = unit->m_coefs;
    int numpartials = unit->m_numpartials;
    float sampleRate = SAMPLERATE;

    for (int i = 0, j = 4; i < numpartials; ++i, j += 3) {
        float w = ZIN0(j) * freqscale + freqoffset;
        float level = ZIN0(j + 1);
        float time = ZIN0(j + 2) * decayscale;

        float R = time == 0.f ? 0.f : exp(log001 / (time * sampleRate));
        float twoR = 2.f * R;
        float R2 = R * R;
        float cost = (twoR * cos(w)) / (1.f + R2);

        int k = 20 * (i >> 2) + (i & 3);
        coefs[k + 0] = 0.f; // y1
        coefs[k + 4] = 0.f; // y2
        coefs[k + 8] = twoR * cost; // b1
        coefs[k + 12] = -R2; // b2
        coefs[k + 16] = level * 0.25; // a0
    }
}

void Klank_Ctor(Klank* unit) {
    SETCALC(Klank_next);

    unit->m_x1 = unit->m_x2 = 0.f;
    int numpartials = (unit->mNumInputs - 4) / 3;
    unit->m_numpartials = numpartials;
    int numcoefs = ((numpartials + 3) & ~3) * 5;
    unit->m_coefs = (float*)RTAlloc(unit->mWorld, (numcoefs + FULLBUFLENGTH) * sizeof(float));
    ClearUnitIfMemFailed(unit->m_coefs);
    unit->m_buf = unit->m_coefs + numcoefs;
    Klank_SetCoefs(unit);

    // generate initial sample
    int filtLoops = unit->mRate->mFilterLoops;
    int filtRemain = unit->mRate->mFilterRemain;
    unit->mRate->mFilterLoops = 0; // supress filter loop
    unit->mRate->mFilterRemain = 1; // just go through 1 iteration
    Klank_next(unit, 1);
    unit->mRate->mFilterLoops = filtLoops;
    unit->mRate->mFilterRemain = filtRemain;

    // reset state for first sample
    unit->m_x1 = unit->m_x2 = 0.f;
    Klank_SetCoefs(unit);
}

void Klank_Dtor(Klank* unit) { RTFree(unit->mWorld, unit->m_coefs); }

void Klank_next(Klank* unit, int inNumSamples) {
    float* out0 = ZOUT(0);
    float* in0 = ZIN(0);

    float *in, *out;
    float inf;
    float y0_0, y1_0, y2_0, a0_0, b1_0, b2_0;
    float y0_1, y1_1, y2_1, a0_1, b1_1, b2_1;
    float y0_2, y1_2, y2_2, a0_2, b1_2, b2_2;
    float y0_3, y1_3, y2_3, a0_3, b1_3, b2_3;

    int32 numpartials = unit->m_numpartials;
    int32 imax = numpartials >> 2;

    float* coefs = unit->m_coefs + imax * 20;

    switch (numpartials & 3) {
    case 3:
        y1_0 = coefs[0];
        y2_0 = coefs[4];
        b1_0 = coefs[8];
        b2_0 = coefs[12];
        a0_0 = coefs[16];
        y1_1 = coefs[1];
        y2_1 = coefs[5];
        b1_1 = coefs[9];
        b2_1 = coefs[13];
        a0_1 = coefs[17];
        y1_2 = coefs[2];
        y2_2 = coefs[6];
        b1_2 = coefs[10];
        b2_2 = coefs[14];
        a0_2 = coefs[18];

        in = in0;
        out = unit->m_buf - 1;
        LooP(unit->mRate->mFilterLoops) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            y0_1 = inf + b1_1 * y1_1 + b2_1 * y2_1;
            y0_2 = inf + b1_2 * y1_2 + b2_2 * y2_2;
            *++out = a0_0 * y0_0 + a0_1 * y0_1 + a0_2 * y0_2;

            inf = *++in;
            y2_0 = inf + b1_0 * y0_0 + b2_0 * y1_0;
            y2_1 = inf + b1_1 * y0_1 + b2_1 * y1_1;
            y2_2 = inf + b1_2 * y0_2 + b2_2 * y1_2;
            *++out = a0_0 * y2_0 + a0_1 * y2_1 + a0_2 * y2_2;

            inf = *++in;
            y1_0 = inf + b1_0 * y2_0 + b2_0 * y0_0;
            y1_1 = inf + b1_1 * y2_1 + b2_1 * y0_1;
            y1_2 = inf + b1_2 * y2_2 + b2_2 * y0_2;
            *++out = a0_0 * y1_0 + a0_1 * y1_1 + a0_2 * y1_2;
        }
        LooP(unit->mRate->mFilterRemain) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            y0_1 = inf + b1_1 * y1_1 + b2_1 * y2_1;
            y0_2 = inf + b1_2 * y1_2 + b2_2 * y2_2;
            *++out = a0_0 * y0_0 + a0_1 * y0_1 + a0_2 * y0_2;
            y2_0 = y1_0;
            y1_0 = y0_0;
            y2_1 = y1_1;
            y1_1 = y0_1;
            y2_2 = y1_2;
            y1_2 = y0_2;
        }
        coefs[0] = zapgremlins(y1_0);
        coefs[4] = zapgremlins(y2_0);
        coefs[1] = zapgremlins(y1_1);
        coefs[5] = zapgremlins(y2_1);
        coefs[2] = zapgremlins(y1_2);
        coefs[6] = zapgremlins(y2_2);
        break;
    case 2:
        y1_0 = coefs[0];
        y2_0 = coefs[4];
        b1_0 = coefs[8];
        b2_0 = coefs[12];
        a0_0 = coefs[16];
        y1_1 = coefs[1];
        y2_1 = coefs[5];
        b1_1 = coefs[9];
        b2_1 = coefs[13];
        a0_1 = coefs[17];

        in = in0;
        out = unit->m_buf - 1;
        LooP(unit->mRate->mFilterLoops) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            y0_1 = inf + b1_1 * y1_1 + b2_1 * y2_1;
            *++out = a0_0 * y0_0 + a0_1 * y0_1;

            inf = *++in;
            y2_0 = inf + b1_0 * y0_0 + b2_0 * y1_0;
            y2_1 = inf + b1_1 * y0_1 + b2_1 * y1_1;
            *++out = a0_0 * y2_0 + a0_1 * y2_1;

            inf = *++in;
            y1_0 = inf + b1_0 * y2_0 + b2_0 * y0_0;
            y1_1 = inf + b1_1 * y2_1 + b2_1 * y0_1;
            *++out = a0_0 * y1_0 + a0_1 * y1_1;
        }
        LooP(unit->mRate->mFilterRemain) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            y0_1 = inf + b1_1 * y1_1 + b2_1 * y2_1;
            *++out = a0_0 * y0_0 + a0_1 * y0_1;
            y2_0 = y1_0;
            y1_0 = y0_0;
            y2_1 = y1_1;
            y1_1 = y0_1;
        }
        coefs[0] = zapgremlins(y1_0);
        coefs[4] = zapgremlins(y2_0);
        coefs[1] = zapgremlins(y1_1);
        coefs[5] = zapgremlins(y2_1);
        break;
    case 1:
        y1_0 = coefs[0];
        y2_0 = coefs[4];
        b1_0 = coefs[8];
        b2_0 = coefs[12];
        a0_0 = coefs[16];

        in = in0;
        out = unit->m_buf - 1;
        LooP(unit->mRate->mFilterLoops) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            *++out = a0_0 * y0_0;

            inf = *++in;
            y2_0 = inf + b1_0 * y0_0 + b2_0 * y1_0;
            *++out = a0_0 * y2_0;

            inf = *++in;
            y1_0 = inf + b1_0 * y2_0 + b2_0 * y0_0;
            *++out = a0_0 * y1_0;
        }
        LooP(unit->mRate->mFilterRemain) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            *++out = a0_0 * y0_0;
            y2_0 = y1_0;
            y1_0 = y0_0;
        }
        /*
        coefs[0] = y1_0;	coefs[4] = y2_0;
        */
        coefs[0] = zapgremlins(y1_0);
        coefs[4] = zapgremlins(y2_0);
        break;
    case 0:
        out = unit->m_buf - 1;
        LooP(inNumSamples) { *++out = 0.f; }
        break;
    }

    coefs = unit->m_coefs;

    for (int i = 0; i < imax; ++i) {
        y1_0 = coefs[0];
        y2_0 = coefs[4];
        b1_0 = coefs[8];
        b2_0 = coefs[12];
        a0_0 = coefs[16];
        y1_1 = coefs[1];
        y2_1 = coefs[5];
        b1_1 = coefs[9];
        b2_1 = coefs[13];
        a0_1 = coefs[17];
        y1_2 = coefs[2];
        y2_2 = coefs[6];
        b1_2 = coefs[10];
        b2_2 = coefs[14];
        a0_2 = coefs[18];
        y1_3 = coefs[3];
        y2_3 = coefs[7];
        b1_3 = coefs[11];
        b2_3 = coefs[15];
        a0_3 = coefs[19];

        in = in0;
        out = unit->m_buf - 1;
        LooP(unit->mRate->mFilterLoops) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            y0_1 = inf + b1_1 * y1_1 + b2_1 * y2_1;
            y0_2 = inf + b1_2 * y1_2 + b2_2 * y2_2;
            y0_3 = inf + b1_3 * y1_3 + b2_3 * y2_3;
            *++out += a0_0 * y0_0 + a0_1 * y0_1 + a0_2 * y0_2 + a0_3 * y0_3;

            inf = *++in;
            y2_0 = inf + b1_0 * y0_0 + b2_0 * y1_0;
            y2_1 = inf + b1_1 * y0_1 + b2_1 * y1_1;
            y2_2 = inf + b1_2 * y0_2 + b2_2 * y1_2;
            y2_3 = inf + b1_3 * y0_3 + b2_3 * y1_3;
            *++out += a0_0 * y2_0 + a0_1 * y2_1 + a0_2 * y2_2 + a0_3 * y2_3;

            inf = *++in;
            y1_0 = inf + b1_0 * y2_0 + b2_0 * y0_0;
            y1_1 = inf + b1_1 * y2_1 + b2_1 * y0_1;
            y1_2 = inf + b1_2 * y2_2 + b2_2 * y0_2;
            y1_3 = inf + b1_3 * y2_3 + b2_3 * y0_3;
            *++out += a0_0 * y1_0 + a0_1 * y1_1 + a0_2 * y1_2 + a0_3 * y1_3;
        }
        LooP(unit->mRate->mFilterRemain) {
            inf = *++in;
            y0_0 = inf + b1_0 * y1_0 + b2_0 * y2_0;
            y0_1 = inf + b1_1 * y1_1 + b2_1 * y2_1;
            y0_2 = inf + b1_2 * y1_2 + b2_2 * y2_2;
            y0_3 = inf + b1_3 * y1_3 + b2_3 * y2_3;
            *++out += a0_0 * y0_0 + a0_1 * y0_1 + a0_2 * y0_2 + a0_3 * y0_3;
            y2_0 = y1_0;
            y1_0 = y0_0;
            y2_1 = y1_1;
            y1_1 = y0_1;
            y2_2 = y1_2;
            y1_2 = y0_2;
            y2_3 = y1_3;
            y1_3 = y0_3;
        }
        coefs[0] = zapgremlins(y1_0);
        coefs[4] = zapgremlins(y2_0);
        coefs[1] = zapgremlins(y1_1);
        coefs[5] = zapgremlins(y2_1);
        coefs[2] = zapgremlins(y1_2);
        coefs[6] = zapgremlins(y2_2);
        coefs[3] = zapgremlins(y1_3);
        coefs[7] = zapgremlins(y2_3);
        coefs += 20;
    }

    float x0;
    float x1 = unit->m_x1;
    float x2 = unit->m_x2;

    in = unit->m_buf - 1;
    out = out0;
    LooP(unit->mRate->mFilterLoops) {
        x0 = *++in;
        *++out = x0 - x2;
        x2 = *++in;
        *++out = x2 - x1;
        x1 = *++in;
        *++out = x1 - x0;
    }
    LooP(unit->mRate->mFilterRemain) {
        x0 = *++in;
        *++out = x0 - x2;
        x2 = x1;
        x1 = x0;
    }

    unit->m_x1 = x1;
    unit->m_x2 = x2;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

static void normalize_samples(int size, float* data, float peak) {
    float maxamp = 0.f;
    for (int i = 0; i < size; ++i) {
        float absamp = std::abs(data[i]);
        if (absamp > maxamp)
            maxamp = absamp;
    }
    if (maxamp != 0.f && maxamp != peak) {
        float ampfac = peak / maxamp;
        for (int i = 0; i < size; ++i) {
            data[i] *= ampfac;
        }
    }
}

static void normalize_wsamples(int size, float* data, float peak) {
    float maxamp = 0.f;
    for (int i = 0; i < size; i += 2) {
        float absamp = std::abs(data[i] + data[i + 1]);
        if (absamp > maxamp)
            maxamp = absamp;
    }
    if (maxamp != 0.f && maxamp != peak) {
        float ampfac = peak / maxamp;
        for (int i = 0; i < size; ++i) {
            data[i] *= ampfac;
        }
    }
}

static void add_partial(int size, float* data, double partial, double amp, double phase) {
    if (amp == 0.0)
        return;
    double w = (partial * 2.0 * 3.1415926535897932384626433832795) / (double)size;
    for (int i = 0; i < size; ++i) {
        data[i] += amp * sin(phase);
        phase += w;
    }
}

static void add_wpartial(int size, float* data, double partial, double amp, double phase) {
    if (amp == 0.0)
        return;
    int size2 = size >> 1;
    double w = (partial * 2.0 * 3.1415926535897932384626433832795) / (double)size2;
    double cur = amp * sin(phase);
    phase += w;
    for (int i = 0; i < size; i += 2) {
        double next = amp * sin(phase);
        data[i] += 2 * cur - next;
        data[i + 1] += next - cur;
        cur = next;
        phase += w;
    }
}

static void add_chebyshev(int size, float* data, double partial, double amp) {
    if (amp == 0.0)
        return;
    double w = 2.0 / (double)size;
    double phase = -1.0;
    double offset = -amp * cos(partial * pi2);
    for (int i = 0; i < size; ++i) {
        data[i] += amp * cos(partial * acos(phase)) + offset;
        phase += w;
    }
}

static void add_wchebyshev(int size, float* data, double partial, double amp) {
    if (amp == 0.0)
        return;
    int size2 = size >> 1;
    double w = 2.0 / (double)size2;
    double phase = -1.0;
    double offset = -amp * cos(partial * pi2);
    double cur = amp * cos(partial * acos(phase)) + offset;
    phase += w;
    for (int i = 0; i < size; i += 2) {
        double next = amp * cos(partial * acos(phase)) + offset;
        data[i] += 2 * cur - next;
        data[i + 1] += next - cur;
        cur = next;
        phase += w;
    }
}

static void cantorFill(int size, float* data) // long offset, double amp)
{
    //	if (amp == 0.0) return;
    for (int i = 0; i < (size); ++i) {
        int j = i;
        float flag = 1.f;
        while ((j > 0) && (flag == 1.f)) {
            if (j % 3 == 1) {
                flag = 0.f;
            }
            j = j / 3;
        }
        if (flag) {
            data[i] += 1.f;
        }
    }
}


enum { flag_Normalize = 1, flag_Wavetable = 2, flag_Clear = 4 };

void ChebyFill(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    if (buf->channels != 1)
        return;

    int flags = msg->geti();

    int size = buf->samples;
    int byteSize = size * sizeof(float);
    float* data = (float*)malloc(byteSize);

    if (flags & flag_Clear)
        Clear(size, data);
    else
        memcpy(data, buf->data, byteSize);

    for (int partial = 1; msg->remain(); partial++) {
        double amp = msg->getf();
        if (flags & flag_Wavetable)
            add_wchebyshev(size, data, partial, amp);
        else
            add_chebyshev(size, data, partial, amp);
    }

    if (flags & flag_Normalize) {
        if (flags & flag_Wavetable)
            normalize_wsamples(size, data, 1.);
        else
            normalize_samples(size, data, 1.);
    }

    memcpy(buf->data, data, byteSize);
    free(data);
}


void SineFill1(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    if (buf->channels != 1)
        return;

    int flags = msg->geti();

    int size = buf->samples;
    int byteSize = size * sizeof(float);
    float* data = (float*)malloc(byteSize);

    if (flags & flag_Clear)
        Clear(size, data);
    else
        memcpy(data, buf->data, byteSize);

    for (int partial = 1; msg->remain(); partial++) {
        double amp = msg->getf();
        if (flags & flag_Wavetable)
            add_wpartial(size, data, partial, amp, 0.);
        else
            add_partial(size, data, partial, amp, 0.);
    }

    if (flags & flag_Normalize) {
        if (flags & flag_Wavetable)
            normalize_wsamples(size, data, 1.);
        else
            normalize_samples(size, data, 1.);
    }

    memcpy(buf->data, data, byteSize);
    free(data);
}

void SineFill2(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    if (buf->channels != 1)
        return;

    int flags = msg->geti();

    int size = buf->samples;
    int byteSize = size * sizeof(float);
    float* data = (float*)malloc(byteSize);

    if (flags & flag_Clear)
        Clear(size, data);
    else
        memcpy(data, buf->data, byteSize);

    while (msg->remain()) {
        double partial = msg->getf();
        double amp = msg->getf();
        if (flags & flag_Wavetable)
            add_wpartial(size, data, partial, amp, 0.);
        else
            add_partial(size, data, partial, amp, 0.);
    }
    if (flags & flag_Normalize) {
        if (flags & flag_Wavetable)
            normalize_wsamples(size, data, 1.);
        else
            normalize_samples(size, data, 1.);
    }

    memcpy(buf->data, data, byteSize);
    free(data);
}

void SineFill3(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    if (buf->channels != 1)
        return;

    int flags = msg->geti();

    int size = buf->samples;
    int byteSize = size * sizeof(float);
    float* data = (float*)malloc(byteSize);

    if (flags & flag_Clear)
        Clear(size, data);
    else
        memcpy(data, buf->data, byteSize);

    while (msg->remain()) {
        double partial = msg->getf();
        double amp = msg->getf();
        double phase = msg->getf();
        if (flags & flag_Wavetable)
            add_wpartial(size, data, partial, amp, phase);
        else
            add_partial(size, data, partial, amp, phase);
    }
    if (flags & flag_Normalize) {
        if (flags & flag_Wavetable)
            normalize_wsamples(size, data, 1.);
        else
            normalize_samples(size, data, 1.);
    }

    memcpy(buf->data, data, byteSize);
    free(data);
}

void NormalizeBuf(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    float newmax;
    if (msg->remain() != 0) {
        newmax = msg->getf();
    } else {
        newmax = 1.f;
    }
    float* data = buf->data;
    int size = buf->samples;
    normalize_samples(size, data, newmax);
}

void NormalizeWaveBuf(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    float newmax;
    if (msg->remain() != 0) {
        newmax = msg->getf();
    } else {
        newmax = 1.f;
    }
    float* data = buf->data;
    int size = buf->samples;
    normalize_wsamples(size, data, newmax);
}

void CopyBuf(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    int frames1 = buf->frames;
    int channels1 = buf->channels;

    int toPos = msg->geti();
    uint32 bufnum2 = msg->geti();
    int fromPos = msg->geti();
    int length = msg->geti();

    if (bufnum2 >= world->mNumSndBufs)
        bufnum2 = 0;
    SndBuf* buf2 = world->mSndBufs + bufnum2;
    int frames2 = buf2->frames;
    int channels2 = buf2->channels;

    if (channels1 != channels2)
        return;

    fromPos = sc_clip(fromPos, 0, frames2 - 1);
    toPos = sc_clip(toPos, 0, frames1 - 1);

    int maxLength = sc_min(frames2 - fromPos, frames1 - toPos);
    if (length < 0) {
        length = maxLength;
    } else {
        length = sc_min(length, maxLength);
    }

    if (length <= 0)
        return;

    int numbytes = length * sizeof(float) * channels1;
    float* data1 = buf->data + toPos * channels1;
    float* data2 = buf2->data + fromPos * channels2;

    if ((((char*)data1 + numbytes) > (char*)data2) || (((char*)data2 + numbytes) > (char*)data1)) {
        memmove(data1, data2, numbytes);
    } else {
        memcpy(data1, data2, numbytes);
    }
}

void CantorFill(World* world, struct SndBuf* buf, struct sc_msg_iter* msg) {
    float* data = buf->data;
    int size = buf->samples;
    //	double offset = msg->getf();
    //	double amp = msg->getf();
    //	long offs = (long) offset;
    cantorFill(size, data);
}


////////////////////////////////////////////////////////////////////////////////////////////////////////

PluginLoad(Osc) {
    ft = inTable;

    DefineSimpleUnit(DegreeToKey);
    DefineSimpleUnit(Select);
    DefineSimpleUnit(TWindex);
    DefineSimpleUnit(Index);
    DefineSimpleUnit(IndexL);
    DefineSimpleUnit(FoldIndex);
    DefineSimpleUnit(WrapIndex);
    DefineSimpleUnit(IndexInBetween);
    DefineSimpleUnit(DetectIndex);
    DefineSimpleUnit(Shaper);
    DefineSimpleUnit(FSinOsc);
    DefineSimpleUnit(PSinGrain);
    DefineSimpleUnit(SinOsc);
    DefineSimpleUnit(SinOscFB);
    DefineSimpleUnit(VOsc);
    DefineSimpleUnit(VOsc3);
    DefineSimpleUnit(Osc);
    DefineSimpleUnit(OscN);
    DefineSimpleUnit(COsc);
    DefineSimpleUnit(Formant);
    DefineSimpleUnit(Blip);
    DefineSimpleUnit(Saw);
    DefineSimpleUnit(Pulse);
    DefineDtorUnit(Klang);
    DefineDtorUnit(Klank);

    DefineBufGen("cheby", ChebyFill);
    DefineBufGen("sine1", SineFill1);
    DefineBufGen("sine2", SineFill2);
    DefineBufGen("sine3", SineFill3);
    DefineBufGen("normalize", NormalizeBuf);
    DefineBufGen("wnormalize", NormalizeWaveBuf);
    DefineBufGen("copy", CopyBuf);
    DefineBufGen("cantorFill", CantorFill);
}

//////////////////////////////////////////////////////////////////////////////////////////////////
