/*
	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;
};

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_ak(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(0) == 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];
	}
}
////////////////////////////////////////////////////////////////////////////////////

void TWindex_Ctor(TWindex *unit)
{
	if (INRATE(0) == calc_FullRate) {
		SETCALC(TWindex_next_ak); // todo : ar
	} else {
		SETCALC(TWindex_next_k);
	}
	unit->m_prevIndex = 0;
	unit->m_trig = -1.f; // make it trigger the first time
	TWindex_next_k(unit, 1);
}


void TWindex_next_k(TWindex *unit, int inNumSamples)
{

	int maxindex = unit->mNumInputs;
	int32 index = maxindex;
	float sum = 0.f;
	float maxSum = 0.f;
	float normalize = ZIN0(1); // switch normalisation on or off
	float trig = ZIN0(0);
	float *out = ZOUT(0);
	if (trig > 0.f && unit->m_trig <= 0.f) {
			if(normalize == 1) {
				for (int32 k=2; k<maxindex; ++k) { maxSum += ZIN0(k); }
			} else {
				maxSum = 1.f;
			}
			RGen& rgen = *unit->mParent->mRGen;
			float max = maxSum * rgen.frand();

			for (int32 k=2; k<maxindex; ++k) {
				sum += ZIN0(k);
				if(sum >= max) {
					index = k - 2;
					break;
				}
			}

			unit->m_prevIndex = index;
	} else {
		index = unit->m_prevIndex;
	}

	LOOP1(inNumSamples,
			ZXP(out) = index;
	)
	unit->m_trig = trig;
}

void TWindex_next_ak(TWindex *unit, int inNumSamples)
{
	int maxindex = unit->mNumInputs;
	int32 index = maxindex;

	float sum = 0.f;
	float maxSum = 0.f;
	float normalize = ZIN0(1);//switch normalisation on or off
	float *trig = ZIN(0);
	float *out = ZOUT(0);
	float curtrig;
	if(normalize == 1) {
		for (int32 k=2; k<maxindex; ++k) { maxSum += ZIN0(k); }
	} else
		maxSum = 1.f;
	RGen& rgen = *unit->mParent->mRGen;

	LOOP1(inNumSamples,
		curtrig = ZXP(trig);
		if (curtrig > 0.f && unit->m_trig <= 0.f) {
			float max = maxSum * rgen.frand();
			for (int32 k=2; k<maxindex; ++k) {
				sum += ZIN0(k);
							if(sum >= max) {
								index = k - 2;
								break;
							}
			}

			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;
	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(0);
	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)
{
	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 = 2. * cos(w);
	unit->m_y1 = sin(iphase);
	unit->m_y2 = sin(iphase - w);

	ZOUT0(0) = unit->m_y1;
}

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;
	//Print("y %g %g  b1 %g\n", y1, y2, b1);
	//Print("%d %d\n", unit->mRate->mFilterLoops, unit->mRate->mFilterRemain);
	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;
	);
	//Print("y %g %g  b1 %g\n", y1, y2, b1);
	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;
	//Print("y %g %g  b1 %g\n", y1, y2, b1);
	//Print("%d %d\n", unit->mRate->mFilterLoops, unit->mRate->mFilterRemain);
	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;
	);
	//Print("y %g %g  b1 %g\n", y1, y2, b1);
	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 */
	unit->m_b1 = 2. * cos(w);
	unit->m_y1 = 0.f;
	unit->m_y2 = -sin(w) * amp;
	ZOUT0(0) = 0.f;
}


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;

	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 = 0;
	} else {
		if (INRATE(1) == calc_FullRate) {
			//Print("next_ika\n");
			SETCALC(SinOsc_next_ika);
			unit->m_phase = 0;
		} else {
			SETCALC(SinOsc_next_ikk);
			unit->m_phase = (int32)(unit->m_phasein * unit->m_radtoinc);
		}
	}

	SinOsc_next_ikk(unit, 1);
}


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

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

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)
{
	//Print("next_ik\n");
	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);
}


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

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

	if (INRATE(1) == calc_FullRate) {
		if (INRATE(2) == calc_FullRate) {
			//Print("next_iaa\n");
			SETCALC(Osc_next_iaa);
			unit->m_phase = 0;
		} else {
			//Print("next_iak\n");
			SETCALC(Osc_next_iak);
			unit->m_phase = 0;
		}
	} else {
		if (INRATE(2) == calc_FullRate) {
			//Print("next_ika\n");
			SETCALC(Osc_next_ika);
			unit->m_phase = 0;
		} else {
			//Print("next_ikk\n");
			SETCALC(Osc_next_ikk);
			unit->m_phase = (int32)(unit->m_phasein * unit->m_radtoinc);
		}
	}

	Osc_next_ikk(unit, 1);
}

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);
	//Print("OscN_Ctor\n");
	if (INRATE(1) == calc_FullRate) {
		if (INRATE(2) == calc_FullRate) {
			//Print("next_naa\n");
			SETCALC(OscN_next_naa);
			unit->m_phase = 0;
		} else {
			//Print("next_nak\n");
			SETCALC(OscN_next_nak);
			unit->m_phase = 0;
		}
	} else {
		if (INRATE(2) == calc_FullRate) {
			//Print("next_nka\n");
			SETCALC(OscN_next_nka);
			unit->m_phase = 0;
		} else {
			//Print("next_nkk\n");
			SETCALC(OscN_next_nkk);
			unit->m_phase = (int32)(unit->m_phasein * unit->m_radtoinc);
		}
	}

	OscN_next_nkk(unit, 1);
}


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


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

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

	VOsc_next_ikk(unit, 1);
}

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

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

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

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;

	ZOUT0(0) = 0.f;
}

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)
{
	unit->m_numpartials = (unit->mNumInputs - 2)/3;

	int numcoefs = unit->m_numpartials * 3;
	unit->m_coefs = (float*)RTAlloc(unit->mWorld, numcoefs * sizeof(float));

	if (!unit->m_coefs) {
		Print("Klang: RT memory allocation failed\n");
		SETCALC(ClearUnitOutputs);
		return 0.f;
	}

	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 += *++coefs = level * sin(phase);		// y1
			        *++coefs = level * sin(phase - w);	// y2
		} else {
			outf += *++coefs = 0.f;						// y1
			        *++coefs = level * -sin(w);	// y2
		}
		*++coefs = 2. * cos(w);		// b1
	}
	return outf;
}

void Klang_Ctor(Klang *unit)
{
	SETCALC(Klang_next);
	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)
{
	int numpartials = (unit->mNumInputs - 4) / 3;
	unit->m_numpartials = numpartials;

	int numcoefs = ((unit->m_numpartials + 3) & ~3) * 5;
	unit->m_coefs = (float*)RTAlloc(unit->mWorld, (numcoefs + unit->mWorld->mBufLength) * sizeof(float));
	if (!unit->m_coefs) {
		Print("Klang: RT memory allocation failed\n");
		SETCALC(ClearUnitOutputs);
		return;
	}

	unit->m_buf = unit->m_coefs + numcoefs;

	float freqscale = ZIN0(1) * unit->mRate->mRadiansPerSample;
	float freqoffset = ZIN0(2) * unit->mRate->mRadiansPerSample;
	float decayscale = ZIN0(3);

	float* coefs = unit->m_coefs;

	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
		//Print("coefs %d  %g %g %g\n", i, twoR * cost, -R2, ampf * 0.25);
	}
}

void Klank_Ctor(Klank *unit)
{
	SETCALC(Klank_next);
	unit->m_x1 = unit->m_x2 = 0.f;
	Klank_SetCoefs(unit);
	ZOUT0(0) = 0.f;
}

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];

			//Print("rcoefs %g %g %g %g %g\n", y1_0, y2_0, b1_0, b2_0, a0_0);
			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;
				//Print("out %g %g %g\n", y0_0, y2_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;
				//Print("out %g\n", 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) Fill(size, data, 0.);
	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) Fill(size, data, 0.);
	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) Fill(size, data, 0.);
	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) Fill(size, data, 0.);
	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);
}

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