diff CloneChannel/plugins/common/gen_dsp/genlib_ops.h @ 12:24d60bdea349

ClonneChannel
author prymula <prymula76@outlook.com>
date Thu, 08 Feb 2024 20:24:27 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CloneChannel/plugins/common/gen_dsp/genlib_ops.h	Thu Feb 08 20:24:27 2024 +0100
@@ -0,0 +1,1371 @@
+/*******************************************************************************************************************
+Copyright (c) 2012 Cycling '74
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+and associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies
+or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*******************************************************************************************************************/
+
+#ifndef GENLIB_OPS_H
+#define GENLIB_OPS_H 1
+
+#include "genlib_common.h"	// common to common code and any host code
+#include "genlib.h"			// this file is different for different "hosts"
+
+#include <cmath>
+
+//////////// genlib_ops.h ////////////
+
+// system constants
+#define GENLIB_DBL_EPSILON (__DBL_EPSILON__)
+
+#define GENLIB_PI (3.14159265358979323846264338327950288f)
+#define GENLIB_PI_OVER_2 (1.57079632679489661923132169163975144f)
+#define GENLIB_PI_OVER_4 (0.785398163397448309615660845819875721f)
+#define GENLIB_1_OVER_LOG_2 (1.442695040888963f)
+
+#define GENLIB_NO_DENORM_TEST 1
+
+// assumes v is a 64-bit double:
+#define GENLIB_IS_NAN_DOUBLE(v)			(((((uint32_t *)&(v))[1])&0x7fe00000)==0x7fe00000) 
+#define GENLIB_FIX_NAN_DOUBLE(v)		((v)=GENLIB_IS_NAN_DOUBLE(v)?0.:(v))
+
+#ifdef GENLIB_NO_DENORM_TEST
+	#define GENLIB_IS_DENORM_DOUBLE(v)	(v)
+	#define GENLIB_FIX_DENORM_DOUBLE(v)	(v)
+#else
+	#define GENLIB_IS_DENORM_DOUBLE(v)	((((((uint32_t *)&(v))[1])&0x7fe00000)==0)&&((v)!=0.))
+	#define GENLIB_FIX_DENORM_DOUBLE(v)	((v)=GENLIB_IS_DENORM_DOUBLE(v)?0.f:(v))
+#endif
+
+#define GENLIB_QUANT(f1,f2)			(floor((f1)*(f2)+0.5)/(f2))
+
+inline double genlib_isnan(double v) { return GENLIB_IS_NAN_DOUBLE(v); }
+inline double fixnan(double v) { return GENLIB_FIX_NAN_DOUBLE(v); }
+inline double fixdenorm(double v) { return GENLIB_FIX_DENORM_DOUBLE(v); }
+inline double isdenorm(double v) { return GENLIB_IS_DENORM_DOUBLE(v); }
+
+inline double safemod(double f, double m) {
+	if (m > GENLIB_DBL_EPSILON || m < -GENLIB_DBL_EPSILON) {
+		if (m<0) 
+			m = -m; // modulus needs to be absolute value		
+		if (f>=m) {
+			if (f>=(m*2.)) {
+				double d = f / m;
+				d = d - (long) d;
+				f = d * m;
+			} 
+			else {
+				f -= m;
+			}
+		} 
+		else if (f<=(-m)) {
+			if (f<=(-m*2.)) {
+				double d = f / m;
+				d = d - (long) d;
+				f = d * m;
+			}
+			 else {
+				f += m;
+			}
+		}
+	} else {
+		f = 0.0; //don't divide by zero
+	}
+	return f;
+}
+
+
+inline double safediv(double num, double denom) {
+	return denom == 0. ? 0. : num/denom;
+}
+
+// fixnan for case of negative base and non-integer exponent:
+inline double safepow(double base, double exponent) {
+	return fixnan(pow(base, exponent));
+}
+
+inline double absdiff(double a, double b) { return fabs(a-b); }
+
+inline double exp2(double v) { return pow(2., v); }
+
+inline double trunc(double v) {
+	double epsilon = (v<0.0) * -2 * 1E-9 + 1E-9;
+	// copy to long so it gets truncated (probably cheaper than floor())
+	long val = v + epsilon;
+	return val;
+}
+
+inline t_sample sign(t_sample v) { 
+	return v > t_sample(0) ? t_sample(1) : v < t_sample(0) ? t_sample(-1) : t_sample(0); 
+}
+
+inline long is_poweroftwo(long x) {
+	return (x & (x - 1)) == 0;
+}
+
+inline uint64_t next_power_of_two(uint64_t v) {
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v |= v >> 32;
+    v++;
+    return v;
+}
+
+inline t_sample fold(t_sample v, t_sample lo1, t_sample hi1){
+	t_sample lo;
+	t_sample hi;
+	if(lo1 == hi1){ return lo1; }
+	if (lo1 > hi1) {	
+		hi = lo1; lo = hi1;
+	} else {
+		lo = lo1; hi = hi1;
+	}
+	const t_sample range = hi - lo;
+	long numWraps = 0;
+	if(v >= hi){
+		v -= range;
+		if(v >= hi){
+			numWraps = (long)((v - lo)/range);
+			v -= range * (t_sample)numWraps;
+		}
+		numWraps++;
+	} else if(v < lo){
+		v += range;
+		if(v < lo){
+			numWraps = (long)((v - lo)/range) - 1;
+			v -= range * (t_sample)numWraps;
+		}
+		numWraps--;
+	}
+	if(numWraps & 1) v = hi + lo - v;	// flip sign for odd folds
+	return v;
+}
+
+inline double wrap(double v, double lo1, double hi1){
+	double lo;
+	double hi;
+	if(lo1 == hi1) return lo1;
+	if (lo1 > hi1) {	
+		hi = lo1; lo = hi1;
+	} else {
+		lo = lo1; hi = hi1;
+	}
+	const double range = hi - lo;
+	if (v >= lo && v < hi) return v;
+	if (range <= 0.000000001) return lo;	// no point... 
+	const long numWraps = long((v-lo)/range) - (v < lo);
+	return v - range * double(numWraps);
+}
+
+// this version gives far better performance when wrapping is relatively rare
+// and typically double of wraps is very low (>1%)
+// but catastrophic if wraps is high (1000%+)
+inline t_sample genlib_wrapfew(t_sample v, t_sample lo, t_sample hi){
+	const t_sample range = hi - lo;
+	while (v >= hi) v -= range;
+	while (v < lo) v += range;
+	return v;
+}
+
+inline t_sample phasewrap(t_sample val) {
+	const t_sample twopi = GENLIB_PI*2.;
+	const t_sample oneovertwopi = 1./twopi;
+	if (val>= twopi || val <= twopi) {
+		t_sample d = val * oneovertwopi;	//multiply faster
+		d = d - (long)d;
+		val = d * twopi;
+	}	
+	if (val > GENLIB_PI) val -= twopi;
+	if (val < -GENLIB_PI) val += twopi;
+	return val;
+}
+
+/// 8th order Taylor series approximation to a cosine.
+/// r must be in [-pi, pi].
+inline t_sample genlib_cosT8(t_sample r) {
+	const t_sample t84 = 56.;
+	const t_sample t83 = 1680.;
+	const t_sample t82 = 20160.;
+	const t_sample t81 = 2.4801587302e-05;
+	const t_sample t73 = 42.;
+	const t_sample t72 = 840.;
+	const t_sample t71 = 1.9841269841e-04;
+	if(r < GENLIB_PI_OVER_4 && r > -GENLIB_PI_OVER_4){
+		t_sample rr = r*r;
+		return 1. - rr * t81 * (t82 - rr * (t83 - rr * (t84 - rr)));
+	}
+	else if(r > 0.){
+		r -= GENLIB_PI_OVER_2;
+		t_sample rr = r*r;
+		return -r * (1. - t71 * rr * (t72 - rr * (t73 - rr)));
+	}
+	else{
+		r += GENLIB_PI_OVER_2;
+		t_sample rr = r*r;
+		return r * (1. - t71 * rr * (t72 - rr * (t73 - rr)));
+	}
+}
+
+//inline double genlib_sin_fast(const double r){
+//	const double y = (4./GENLIB_PI) * r + (-4./(GENLIB_PI*GENLIB_PI)) * r * fabs(r);
+//	return 0.225 * (y * fabs(y) - y) + y;   // Q * y + P * y * abs(y)
+//}
+//
+//inline t_sample genlib_sinP7(t_sample n){
+//	t_sample nn = n*n;
+//	return n * (t_sample(3.138982) + nn * (t_sample(-5.133625) + nn * (t_sample(2.428288) - nn * t_sample(0.433645))));
+//}
+//
+//inline t_sample genlib_sinP9(t_sample n){
+//	t_sample nn = n*n;
+//	return n * (GENLIB_PI + nn * (t_sample(-5.1662729) + nn * (t_sample(2.5422065) + nn * (t_sample(-0.5811243) + nn * t_sample(0.0636716)))));
+//}
+//
+//inline t_sample genlib_sinT7(t_sample r){
+//	const t_sample t84 = 56.;
+//	const t_sample t83 = 1680.;
+//	const t_sample t82 = 20160.;
+//	const t_sample t81 = 2.4801587302e-05;
+//	const t_sample t73 = 42.;
+//	const t_sample t72 = 840.;
+//	const t_sample t71 = 1.9841269841e-04;
+//	if(r < GENLIB_PI_OVER_4 && r > -GENLIB_PI_OVER_4){
+//		t_sample rr = r*r;
+//		return r * (1. - t71 * rr * (t72 - rr * (t73 - rr)));
+//	}
+//	else if(r > 0.){
+//		r -= GENLIB_PI_OVER_2;
+//		t_sample rr = r*r;
+//		return t_sample(1.) - rr * t81 * (t82 - rr * (t83 - rr * (t84 - rr)));
+//	}
+//	else{
+//		r += GENLIB_PI_OVER_2;
+//		t_sample rr = r*r;
+//		return t_sample(-1.) + rr * t81 * (t82 - rr * (t83 - rr * (t84 - rr)));
+//	}
+//}
+
+// use these if r is not known to be in [-pi, pi]:
+inline t_sample genlib_cosT8_safe(t_sample r) { return genlib_cosT8(phasewrap(r)); }
+//inline double genlib_sin_fast_safe(double r) { return genlib_sin_fast(phasewrap(r)); }
+//inline t_sample genlib_sinP7_safe(t_sample r) { return genlib_sinP7(phasewrap(r)); }
+//inline t_sample genlib_sinP9_safe(t_sample r) { return genlib_sinP9(phasewrap(r)); }
+//inline t_sample genlib_sinT7_safe(t_sample r) { return genlib_sinT7(phasewrap(r)); }
+
+
+
+/*=====================================================================*
+ *                   Copyright (C) 2011 Paul Mineiro                   *
+ * All rights reserved.                                                *
+ *                                                                     *
+ * Redistribution and use in source and binary forms, with             *
+ * or without modification, are permitted provided that the            *
+ * following conditions are met:                                       *
+ *                                                                     *
+ *     * Redistributions of source code must retain the                *
+ *     above copyright notice, this list of conditions and             *
+ *     the following disclaimer.                                       *
+ *                                                                     *
+ *     * Redistributions in binary form must reproduce the             *
+ *     above copyright notice, this list of conditions and             *
+ *     the following disclaimer in the documentation and/or            *
+ *     other materials provided with the distribution.                 *
+ *                                                                     *
+ *     * Neither the name of Paul Mineiro nor the names                *
+ *     of other contributors may be used to endorse or promote         *
+ *     products derived from this software without specific            *
+ *     prior written permission.                                       *
+ *                                                                     *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND              *
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,         *
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES               *
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE             *
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER               *
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,                 *
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES            *
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE           *
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR                *
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF          *
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           *
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY              *
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE             *
+ * POSSIBILITY OF SUCH DAMAGE.                                         *
+ *                                                                     *
+ * Contact: Paul Mineiro <paul@mineiro.com>                            *
+ *=====================================================================*/
+
+inline float genlib_fastersin (float x) {
+	static const float fouroverpi = 1.2732395447351627f;
+	static const float fouroverpisq = 0.40528473456935109f;
+	static const float q = 0.77633023248007499f;
+	union { float f; uint32_t i; } p = { 0.22308510060189463f };
+	union { float f; uint32_t i; } vx = { x };
+	uint32_t sign = vx.i & 0x80000000;
+	vx.i &= 0x7FFFFFFF;
+	float qpprox = fouroverpi * x - fouroverpisq * x * vx.f;
+	p.i |= sign;
+	return qpprox * (q + p.f * qpprox);
+}
+
+inline float genlib_fastercos (float x) {
+	static const float twooverpi = 0.63661977236758134f;
+	static const float p = 0.54641335845679634f;
+	union { float f; uint32_t i; } vx = { x };
+	vx.i &= 0x7FFFFFFF;
+	float qpprox = 1.0f - twooverpi * vx.f;
+	return qpprox + p * qpprox * (1.0f - qpprox * qpprox);
+}
+
+inline float genlib_fastersinfull (float x) {
+	static const float twopi = 6.2831853071795865f;
+	static const float invtwopi = 0.15915494309189534f;
+	int k = x * invtwopi;
+	float half = (x < 0) ? -0.5f : 0.5f;
+	return genlib_fastersin ((half + k) * twopi - x);
+}
+ 
+inline float genlib_fastercosfull (float x) {
+	static const float halfpi = 1.5707963267948966f;
+	return genlib_fastersinfull (x + halfpi);
+}
+ 
+inline float genlib_fastertanfull (float x) {
+	static const float twopi = 6.2831853071795865f;
+	static const float invtwopi = 0.15915494309189534f;
+	int k = x * invtwopi;
+	float half = (x < 0) ? -0.5f : 0.5f;
+	float xnew = x - (half + k) * twopi;
+	return genlib_fastersin (xnew) / genlib_fastercos (xnew);
+}
+
+
+#define cast_uint32_t static_cast<uint32_t>
+inline float genlib_fasterpow2 (float p) {
+	float clipp = (p < -126) ? -126.0f : p;
+	union { uint32_t i; float f; } v = { cast_uint32_t ( (1 << 23) * (clipp + 126.94269504f) ) };
+	return v.f;
+}
+
+inline float genlib_fasterexp (float p) {
+	return genlib_fasterpow2 (1.442695040f * p);
+}
+
+inline float genlib_fasterlog2 (float x) {
+	union { float f; uint32_t i; } vx = { x };
+	float y = vx.i;
+	y *= 1.1920928955078125e-7f;
+	return y - 126.94269504f;
+}
+
+inline float genlib_fasterpow (float x, float p) {
+	return genlib_fasterpow2(p * genlib_fasterlog2 (x));
+}
+
+////////////////////////////////////////////////////////////////
+
+inline double fastertanfull(double x) {
+	return (double)genlib_fastertanfull((float)x);
+}
+
+inline double fastersinfull(double x) {
+	return (double)genlib_fastersinfull((float)x);
+}
+
+inline double fastercosfull(double x) {
+	return (double)genlib_fastercosfull((float)x);
+}
+
+inline double fasterexp(double x) {
+	return (double)genlib_fasterexp((float)x);
+}
+
+inline double fasterpow(double x, double p) {
+	return (double)genlib_fasterpow((float)x, (float)p);
+}
+/****************************************************************/
+
+
+
+inline double minimum(double x, double y) { return (y<x?y:x); }
+inline double maximum(double x, double y) { return (x<y?y:x); }
+
+inline t_sample clamp(t_sample x, t_sample minVal, t_sample maxVal) { 
+	return minimum(maximum(x,minVal),maxVal); 
+}
+
+template<typename T>
+inline T smoothstep(double e0, double e1, T x) {
+	T t = clamp( safediv(x-T(e0),T(e1-e0)), 0., 1. );
+	return t*t*(T(3) - T(2)*t);
+}
+
+inline t_sample mix(t_sample x, t_sample y, t_sample a) { 
+	return x+a*(y-x); 
+}
+
+inline double scale(double in, double inlow, double inhigh, double outlow, double outhigh, double power)
+{
+	double value;
+	double inscale = safediv(1., inhigh - inlow);
+	double outdiff = outhigh - outlow;
+	
+	value = (in - inlow) * inscale;
+	if (value > 0.0)
+		value = pow(value, power);
+	else if (value < 0.0)
+		value = -pow(-value, power);
+	value = (value * outdiff) + outlow;
+	
+	return value;
+}
+
+inline t_sample linear_interp(t_sample a, t_sample x, t_sample y) {
+	return x+a*(y-x); 
+}
+
+inline t_sample cosine_interp(t_sample a, t_sample x, t_sample y) {
+	const t_sample a2 = (t_sample(1.)-genlib_cosT8_safe(a*t_sample(GENLIB_PI)))/t_sample(2.);
+	return(x*(t_sample(1.)-a2)+y*a2);
+}
+
+inline t_sample cubic_interp(t_sample a, t_sample w, t_sample x, t_sample y, t_sample z) {
+	const t_sample a2 = a*a;
+	const t_sample f0 = z - y - w + x;
+	const t_sample f1 = w - x - f0;
+	const t_sample f2 = y - w;
+	const t_sample f3 = x;
+	return(f0*a*a2 + f1*a2 + f2*a + f3);
+}
+
+// Breeuwsma catmull-rom spline interpolation
+inline t_sample spline_interp(t_sample a, t_sample w, t_sample x, t_sample y, t_sample z) {
+	const t_sample a2 = a*a;
+	const t_sample f0 = t_sample(-0.5)*w + t_sample(1.5)*x - t_sample(1.5)*y + t_sample(0.5)*z;
+	const t_sample f1 = w - t_sample(2.5)*x + t_sample(2)*y - t_sample(0.5)*z;
+	const t_sample f2 = t_sample(-0.5)*w + t_sample(0.5)*y;
+	return(f0*a*a2 + f1*a2 + f2*a + x);
+}
+
+template<typename T1, typename T2>
+inline T1 neqp(T1 x, T2 y) { 
+	return ((((x) != T1(y))) ? (x) : T1(0));
+}
+
+template<typename T1, typename T2>
+inline T1 gtp(T1 x, T2 y) { return ((((x) > T1(y))) ? (x) : T1(0)); }
+template<typename T1, typename T2>
+inline T1 gtep(T1 x, T2 y) { return ((((x) >= T1(y))) ? (x) : T1(0)); }
+template<typename T1, typename T2>
+inline T1 ltp(T1 x, T2 y) { return ((((x) < T1(y))) ? (x) : T1(0)); }
+template<typename T1, typename T2>
+inline T1 ltep(T1 x, T2 y) { return ((((x) <= T1(y))) ? (x) : T1(0)); }
+
+inline double fract(double x) { double unused; return modf(x, &unused); }
+
+// log2(x) = log(x)/log(2)
+template<typename T>
+inline T log2(T x) { 
+	return log(x)*GENLIB_1_OVER_LOG_2; 
+}
+
+inline double atodb(double in) {
+	return (in <=0.) ? -999. : (20. * log10(in));
+}
+
+inline double dbtoa(double in) {
+	return pow(10., in * 0.05);
+}
+
+inline double ftom(double in, double tuning=440.) {
+	return 69. + 17.31234050465299 * log(safediv(in, tuning));
+}
+
+inline double mtof(double in, double tuning=440.) {
+	return tuning * exp(.057762265 * (in - 69.0));
+}
+
+inline t_sample mstosamps(t_sample ms, t_sample samplerate=44100.) {
+	return samplerate * ms * t_sample(0.001);
+}
+
+inline t_sample sampstoms(t_sample s, t_sample samplerate=44100.) {
+	return t_sample(1000.) * s / samplerate;
+}
+
+inline double triangle(double phase, double p1) {
+	phase = wrap(phase, 0., 1.);
+	p1 = clamp(p1, 0., 1.);
+	if (phase < p1)
+		return (p1) ? phase/p1 : 0.;
+	else
+		return (p1==1.) ? phase : 1. - ((phase - p1) / (1. - p1));
+}
+
+struct Delta {
+	t_sample history;
+	Delta() { reset(); }
+	inline void reset(t_sample init=0) { history=init; }
+	
+	inline t_sample operator()(t_sample in1) {
+		t_sample ret = in1 - history;
+		history = in1;
+		return ret;
+	}
+};
+struct Change {
+	t_sample history;
+	Change() { reset(); }
+	inline void reset(t_sample init=0) { history=init; }
+	
+	inline t_sample operator()(t_sample in1) {
+		t_sample ret = in1 - history;
+		history = in1;
+		return sign(ret);
+	}
+};
+
+struct Rate {
+	t_sample phase, diff, mult, invmult, prev;
+	int wantlock, quant;
+	
+	Rate() { reset(); }
+	
+	inline void reset() {
+		phase = diff = prev = 0;
+		mult = invmult = 1;
+		wantlock = 1;
+		quant = 1;
+	}
+	
+	inline t_sample perform_lock(t_sample in1, t_sample in2) {
+		// did multiplier change?
+		if (in2 != mult && !genlib_isnan(in2)) {
+			mult = in2;
+			invmult = safediv(1., mult);
+			wantlock = 1;
+		}
+		t_sample diff = in1 - prev;
+		
+		if (diff < t_sample(-0.5)) {
+			diff += t_sample(1);
+		} else if (diff > t_sample(0.5)) {
+			diff -= t_sample(1);
+		}
+		
+		if (wantlock) {
+			// recalculate phase
+			phase = (in1 - GENLIB_QUANT(in1, quant)) * invmult 
+						+ GENLIB_QUANT(in1, quant * mult);
+			diff = 0;
+			wantlock = 0;
+		} else {
+			// diff is always between -0.5 and 0.5
+			phase += diff * invmult;
+		}
+		
+		if (phase > t_sample(1.) || phase < t_sample(-0.)) {
+			phase = phase - (long)(phase);
+		}	
+		
+		prev = in1;
+		
+		return phase;
+	}
+	
+	inline t_sample perform_cycle(t_sample in1, t_sample in2) {
+		// did multiplier change?
+		if (in2 != mult && !genlib_isnan(in2)) {
+			mult = in2;
+			invmult = safediv(1., mult);
+			wantlock = 1;
+		}
+		t_sample diff = in1 - prev;
+		
+		if (diff < t_sample(-0.5)) {
+			if (wantlock) {
+				wantlock = 0;
+				phase = in1 * invmult;
+				diff = t_sample(0);
+			} else {
+				diff += t_sample(1);
+			}
+		} else if (diff > t_sample(0.5)) {
+			if (wantlock) {
+				wantlock = 0;
+				phase = in1 * invmult;
+				diff = t_sample(0);
+			} else {
+				diff -= t_sample(1);
+			}
+		}
+		
+		// diff is always between -0.5 and 0.5
+		phase += diff * invmult;
+		
+		if (phase > t_sample(1.) || phase < t_sample(-0.)) {
+			phase = phase - (long)(phase);
+		}	
+		
+		prev = in1;
+		
+		return phase;
+	}
+	
+	inline t_sample perform_off(double in1, double in2) {
+		// did multiplier change?
+		if (in2 != mult && !genlib_isnan(in2)) {
+			mult = in2;
+			invmult = safediv(1., mult);
+			wantlock = 1;
+		}
+		double diff = in1 - prev;
+		
+		if (diff < t_sample(-0.5)) {
+			diff += t_sample(1);
+		} else if (diff > t_sample(0.5)) {
+			diff -= t_sample(1);
+		}
+		
+		phase += diff * invmult;
+		
+		if (phase > t_sample(1.) || phase < t_sample(-0.)) {
+			phase = phase - (long)(phase);
+		}	
+		
+		prev = in1;
+		
+		return phase;
+	}
+};
+
+struct DCBlock {
+	t_sample x1, y1;
+	DCBlock() { reset(); }
+	inline void reset() { x1=0; y1=0; }
+	
+	inline double operator()(t_sample in1) {
+		t_sample y = in1 - x1 + y1*t_sample(0.9997);
+		x1 = in1;
+		y1 = y;
+		return y;
+	}
+};
+
+struct Noise {
+	unsigned long last;
+	static long uniqueTickCount(void) {
+		static long lasttime = 0;
+		long time = genlib_ticks();
+		return (time <= lasttime) ? (++lasttime) : (lasttime = time);
+	}
+	
+	Noise() { reset(); }
+	Noise(double seed) { reset(seed); }
+	void reset() { last = uniqueTickCount() * uniqueTickCount(); }
+	void reset(double seed) { last = seed; }
+	
+	inline t_sample operator()() {
+		last = 1664525L * last + 1013904223L;
+		unsigned long itemp = 0x3f800000 | (0x007fffff & last);
+		unsigned long* itempptr = &itemp;
+		return ((*(float *)itempptr) * 2.f) - 3.f;
+	}
+};
+
+struct Phasor {
+	t_sample phase;
+	Phasor() { reset(); }
+	void reset(t_sample v=0.) { phase=v; }
+	inline double operator()(t_sample freq, t_sample invsamplerate) {
+		const t_sample pincr = freq * invsamplerate;
+		//phase = genlib_wrapfew(phase + pincr, 0., 1.); // faster for low frequencies, but explodes with high frequencies
+		phase = wrap(phase + pincr, 0., 1.);
+		return phase;
+	}
+};
+
+struct PlusEquals {
+	t_sample count;
+	PlusEquals() { reset(); }
+	void reset(t_sample v=0.) { count=v; }
+	
+	// reset post-application mode:
+	inline t_sample post(t_sample incr, t_sample reset, t_sample min, t_sample max) {
+		count = reset ? min : wrap(count+incr, min, max);
+		return count;
+	}
+	inline t_sample post(t_sample incr=1., t_sample reset=0., t_sample min=0.) {
+		count = reset ? min : count+incr;
+		return count;
+	}
+	
+	// reset pre-application mode:
+	inline t_sample pre(t_sample incr, t_sample reset, t_sample min, t_sample max) {
+		count = reset ? min+incr : wrap(count+incr, min, max);
+		return count;
+	}
+	inline t_sample pre(t_sample incr=1., t_sample reset=0., t_sample min=0.) {
+		count = reset ? min+incr : count+incr;
+		return count;
+	}
+};
+
+struct MulEquals {
+	t_sample count;
+	MulEquals() { reset(); }
+	void reset(t_sample v=0.) { count=v; }
+	
+	// reset post-application mode:
+	inline t_sample post(t_sample incr, t_sample reset, t_sample min, t_sample max) {
+		count = reset ? min : wrap(fixdenorm(count*incr), min, max);
+		return count;
+	}
+	inline t_sample post(t_sample incr=1., t_sample reset=0., t_sample min=0.) {
+		count = reset ? min : fixdenorm(count*incr);
+		return count;
+	}
+	
+	// reset pre-application mode:
+	inline t_sample pre(t_sample incr, t_sample reset, t_sample min, t_sample max) {
+		count = reset ? min*incr : wrap(fixdenorm(count*incr), min, max);
+		return count;
+	}
+	inline t_sample pre(t_sample incr=1., t_sample reset=0., t_sample min=0.) {
+		count = reset ? min*incr : fixdenorm(count*incr);
+		return count;
+	}
+};
+
+struct Sah {
+	t_sample prev, output;
+	Sah() { reset(); }
+	void reset(t_sample o=0.) {
+		output = prev = o;
+	}
+	
+	inline t_sample operator()(t_sample in, t_sample trig, t_sample thresh) {
+		if (prev <= thresh && trig > thresh) {
+			output = in;
+		}
+		prev = trig;
+		return output;
+	}
+};
+
+struct Train {
+	t_sample phase, state;
+	Train() { reset(); }
+	void reset(t_sample p=0) { phase = p; state = 0.; }
+	
+	inline t_sample operator()(t_sample pulseinterval, t_sample width, t_sample pulsephase) {
+		if (width <= t_sample(0.)) {
+			state = t_sample(0.);	// no pulse!
+		} else if (width >= 1.) {
+			state = t_sample(1.); // constant pulse!
+		} else {
+			const t_sample interval = maximum(pulseinterval, t_sample(1.));	// >= 1.
+			const t_sample p1 = clamp(pulsephase, t_sample(0.), t_sample(1.));	// [0..1]
+			const t_sample p2 = p1+width;						// (p1..p1+1)
+			const t_sample pincr = t_sample(1.)/interval;			// (0..1]
+			phase += pincr;								// +ve
+			if (state) {	// on:
+				if (phase > p2) {	
+					state = t_sample(0.);				// turn off
+					phase -= (int)(1.+phase-p2);		// wrap phase back down
+				}
+			} else {		// off:
+				if (phase > p1) {			
+					state = t_sample(1.);				// turn on.
+				}
+			}
+		}
+		return state;
+	}	
+};
+
+struct Delay {
+	t_sample * memory;
+	long size, wrap, maxdelay;
+	long reader, writer;
+	
+	t_genlib_data * dataRef;
+	
+	Delay() : memory(0) {
+		size = wrap = maxdelay = 0;
+		reader = writer = 0;
+		dataRef = 0;
+	}
+	~Delay() {
+		if (dataRef != 0) {
+			// store write position for persistence:
+			genlib_data_setcursor(dataRef, writer);
+			// decrement reference count:
+			genlib_data_release(dataRef);
+		}
+	}
+	
+	inline void reset(const char * name, long d) {
+		// if needed, acquire the Data's global reference: 
+		if (dataRef == 0) {
+		
+			void * ref = genlib_obtain_reference_from_string(name);
+			dataRef = genlib_obtain_data_from_reference(ref);
+			if (dataRef == 0) {	
+				genlib_report_error("failed to acquire data");
+				return; 
+			}
+			
+			// scale maxdelay to next highest power of 2:
+			maxdelay = d;
+			size = maxdelay < 2 ? 2 : maxdelay;
+			size = next_power_of_two(size);
+			
+			// first reset should resize the memory:
+			genlib_data_resize(dataRef, size, 1);
+			
+			t_genlib_data_info info;
+			if (genlib_data_getinfo(dataRef, &info) == GENLIB_ERR_NONE) {
+				if (info.dim != size) {
+					// at this point, could resolve by reducing to 
+					// maxdelay = size = next_power_of_two(info.dim+1)/2;
+					// but really, if this happens, it means more than one
+					// object is referring to the same t_gen_dsp_data.
+					// which is probably bad news.
+					genlib_report_error("delay memory size error");
+					memory = 0;
+					return;
+				}
+				memory = info.data;
+				writer = genlib_data_getcursor(dataRef);
+			} else {
+				genlib_report_error("failed to acquire data info");
+			}
+		
+		} else {		
+			// subsequent reset should zero the memory & heads:
+			set_zero64(memory, size);
+			writer = 0;
+		}
+		
+		reader = writer;
+		wrap = size-1;
+	}
+	
+	// called at bufferloop end, updates read pointer time
+	inline void step() {	
+		reader++; 
+		if (reader >= size) reader = 0;
+	}
+	
+	inline void write(t_sample x) {
+		writer = reader;	// update write ptr
+		memory[writer] = x;
+	}	
+	
+	inline t_sample read_step(t_sample d) {
+		// extra half for nice rounding:
+		// min 1 sample delay for read before write (r != w)
+		const t_sample r = t_sample(size + reader) - clamp(d-t_sample(0.5), (reader != writer), maxdelay);	
+		long r1 = long(r);
+		return memory[r1 & wrap];
+	}
+	
+	inline t_sample read_linear(t_sample d) {
+		// min 1 sample delay for read before write (r != w)
+		t_sample c = clamp(d, (reader != writer), maxdelay);
+		const t_sample r = t_sample(size + reader) - c;	
+		long r1 = long(r);
+		long r2 = r1+1;
+		t_sample a = r - (t_sample)r1;
+		t_sample x = memory[r1 & wrap];
+		t_sample y = memory[r2 & wrap];
+		return linear_interp(a, x, y);
+	}
+	
+	inline t_sample read_cosine(t_sample d) {
+		// min 1 sample delay for read before write (r != w)
+		const t_sample r = t_sample(size + reader) - clamp(d, (reader != writer), maxdelay);	
+		long r1 = long(r);
+		long r2 = r1+1;
+		t_sample a = r - (t_sample)r1;
+		t_sample x = memory[r1 & wrap];
+		t_sample y = memory[r2 & wrap];
+		return cosine_interp(a, x, y);
+	}
+	
+	// cubic requires extra sample of compensation:
+	inline t_sample read_cubic(t_sample d) {
+		// min 1 sample delay for read before write (r != w)
+		// plus extra 1 sample compensation for 4-point interpolation
+		const t_sample r = t_sample(size + reader) - clamp(d, t_sample(1.)+t_sample(reader != writer), maxdelay);	
+		long r1 = long(r);
+		long r2 = r1+1;
+		long r3 = r1+2;
+		long r4 = r1+3;
+		t_sample a = r - (t_sample)r1;
+		t_sample w = memory[r1 & wrap];
+		t_sample x = memory[r2 & wrap];
+		t_sample y = memory[r3 & wrap];
+		t_sample z = memory[r4 & wrap];
+		return cubic_interp(a, w, x, y, z);
+	}
+	
+	// spline requires extra sample of compensation:
+	inline t_sample read_spline(t_sample d) {
+		// min 1 sample delay for read before write (r != w)
+		// plus extra 1 sample compensation for 4-point interpolation
+		const t_sample r = t_sample(size + reader) - clamp(d, t_sample(1.)+t_sample(reader != writer), maxdelay);	
+		long r1 = long(r);
+		long r2 = r1+1;
+		long r3 = r1+2;
+		long r4 = r1+3;
+		t_sample a = r - (t_sample)r1;
+		t_sample w = memory[r1 & wrap];
+		t_sample x = memory[r2 & wrap];
+		t_sample y = memory[r3 & wrap];
+		t_sample z = memory[r4 & wrap];
+		return spline_interp(a, w, x, y, z);
+	}
+};
+
+template<typename T=t_sample>
+struct DataInterface {
+	long dim, channels;
+	T * mData;
+	void * mDataReference;		// this was t_symbol *mName
+	int modified;
+	
+	DataInterface() : dim(0), channels(1), mData(0), modified(0) { mDataReference = 0; }
+	
+	// raw reading/writing/overdubbing (internal use only, no bounds checking)
+	inline t_sample read(long index, long channel=0) const { 
+		return mData[channel+index*channels]; 
+	}	
+	inline void write(T value, long index, long channel=0) {
+		mData[channel+index*channels] = value; 
+		modified = 1;
+	}
+	// NO LONGER USED:
+	inline void overdub(T value, long index, long channel=0) { 
+		mData[channel+index*channels] += value; 
+		modified = 1;
+	}
+	
+	// averaging overdub (used by splat)
+	inline void blend(T value, long index, long channel, t_sample alpha) { 
+		long offset = channel+index*channels;
+		const T old = mData[offset];
+		mData[offset] = old + alpha * (value - old); 
+		modified = 1;
+	}
+	
+	// NO LONGER USED:
+	inline void read_ok(long index, long channel=0, bool ok=1) const { 
+		return ok ? mData[channel+index*channels] : T(0); 
+	}	
+	inline void write_ok(T value, long index, long channel=0, bool ok=1) { 
+		if (ok) mData[channel+index*channels] = value; 
+	}
+	inline void overdub_ok(T value, long index, long channel=0, bool ok=1) { 
+		if (ok) mData[channel+index*channels] += value; 
+	}
+		
+	// Bounds strategies:
+	inline long index_clamp(long index) const { return clamp(index, 0, dim-1); } 
+	inline long index_wrap(long index) const { return wrap(index, 0, dim); } 
+	inline long index_fold(long index) const { return fold(index, 0, dim); } 
+	inline bool index_oob(long index) const { return (index < 0 || index >= dim); } 
+	inline bool index_inbounds(long index) const { return (index >=0 && index < dim); } 
+	
+	// channel bounds:
+	inline long channel_clamp(long c) const { return clamp(c, 0, channels-1); }
+	inline long channel_wrap(long c) const { return wrap(c, 0, channels); } 
+	inline long channel_fold(long c) const { return fold(c, 0, channels); } 
+	inline bool channel_oob(long c) const { return (c < 0 || c >= channels); }
+	inline bool channel_inbounds(long c) const { return !channel_oob(c); } 
+
+	// Indexing strategies:
+	// [0..1] -> [0..(dim-1)]
+	inline t_sample phase2index(t_sample phase) const { return phase * t_sample(dim-1); }
+	// [0..1] -> [min..max]
+	inline t_sample subphase2index(t_sample phase, long min, long max) const { 
+		min = index_clamp(min);
+		max = index_clamp(max);
+		return t_sample(min) + phase * t_sample(max-min); 
+	}
+	// [-1..1] -> [0..(dim-1)]
+	inline t_sample signal2index(t_sample signal) const { return phase2index((signal+t_sample(1.)) * t_sample(0.5)); }
+	
+	inline T peek(t_sample index, long channel=0) const {
+		const long i = (long)index;
+		if (index_oob(i) || channel_oob(channel)) {
+			return 0.;
+		} else {
+			return read(i, channel);
+		}
+	}
+	
+	inline T index(double index, long channel=0) const {
+		channel = channel_clamp(channel);
+		// no-interp:
+		long i = (long)index;
+		// bound:
+		i = index_clamp(i);
+		return read(i, channel);
+	}
+	
+	inline T cell(double index, long channel=0) const {
+		channel = channel_clamp(channel);
+		// no-interp:
+		long i = (long)index;
+		// bound:
+		i = index_wrap(i);
+		return read(i, channel);
+	}
+	
+	inline T cycle(t_sample phase, long channel=0) const {
+		channel = channel_clamp(channel);
+		t_sample index = phase2index(phase);
+		// interp:
+		long i1 = (long)index;
+		long i2 = i1+1;
+		const t_sample alpha = index - (t_sample)i1;
+		// bound:
+		i1 = index_wrap(i1);
+		i2 = index_wrap(i2);
+		// interp:
+		T v1 = read(i1, channel);
+		T v2 = read(i2, channel);
+		return mix(v1, v2, alpha);
+	}
+	
+	inline T lookup(t_sample signal, long channel=0) const {
+		channel = channel_clamp(channel);
+		t_sample index = signal2index(signal);
+		// interp:
+		long i1 = (long)index;
+		long i2 = i1+1;
+		t_sample alpha = index - (t_sample)i1;
+		// bound:
+		i1 = index_clamp(i1);
+		i2 = index_clamp(i2);
+		// interp:
+		T v1 = read(i1, channel);
+		T v2 = read(i2, channel);
+		return mix(v1, v2, alpha);
+	}
+	// NO LONGER USED:
+	inline void poke(t_sample value, t_sample index, long channel=0) {
+		const long i = (long)index;
+		if (!(index_oob(i) || channel_oob(channel))) {
+			write(fixdenorm(value), i, channel);
+		} 
+	}
+	// NO LONGER USED:
+	inline void splat_adding(t_sample value, t_sample phase, long channel=0) {
+		const t_sample valuef = fixdenorm(value);
+		channel = channel_clamp(channel);
+		t_sample index = phase2index(phase);
+		// interp:
+		long i1 = (long)index;
+		long i2 = i1+1;
+		const t_sample alpha = index - (double)i1;
+		// bound:
+		i1 = index_wrap(i1);
+		i2 = index_wrap(i2);
+		// interp:
+		overdub(valuef*(1.-alpha), i1, channel);
+		overdub(valuef*alpha,      i2, channel);
+	}
+	// NO LONGER USED:
+	inline void splat(t_sample value, t_sample phase, long channel=0) {
+		const t_sample valuef = fixdenorm(value);
+		channel = channel_clamp(channel);
+		t_sample index = phase2index(phase);
+		// interp:
+		long i1 = (long)index;
+		long i2 = i1+1;
+		const t_sample alpha = index - (t_sample)i1;
+		// bound:
+		i1 = index_wrap(i1);
+		i2 = index_wrap(i2);
+		// interp:
+		const T v1 = read(i1, channel);
+		const T v2 = read(i2, channel);
+		write(v1 + (1.-alpha)*(valuef-v1), i1, channel);
+		write(v2 + (alpha)*(valuef-v2), i2, channel);
+	}
+};
+
+// DATA_MAXIMUM_ELEMENTS * 8 bytes = 256 mb limit
+#define DATA_MAXIMUM_ELEMENTS	(33554432)
+
+struct Data : public DataInterface<t_sample> {
+	t_genlib_data * dataRef;	// a pointer to some external source of the data
+	
+	Data() : DataInterface<t_sample>() {
+		dataRef = 0;
+	}
+	~Data() { 
+		//genlib_report_message("releasing data handle %d", dataRef);
+		if (dataRef != 0) {
+			genlib_data_release(dataRef);
+		}
+	}
+	void reset(const char * name, long s, long c) {
+		// if needed, acquire the Data's global reference: 
+		if (dataRef == 0) {
+			void * ref = genlib_obtain_reference_from_string(name);
+			dataRef = genlib_obtain_data_from_reference(ref);
+			if (dataRef == 0) {	
+				genlib_report_error("failed to acquire data");
+				return; 
+			}
+		}
+		genlib_data_resize(dataRef, s, c);
+		getinfo();
+	}
+	bool setbuffer(void * bufferRef) {
+		//genlib_report_message("set buffer %p", bufferRef);
+		if (dataRef == 0) {
+			// error: no data, or obtain?
+			return false;
+		}
+		genlib_data_setbuffer(dataRef, bufferRef);
+		getinfo();
+		return true;
+	}
+	
+	void getinfo() {
+		t_genlib_data_info info;
+		if (genlib_data_getinfo(dataRef, &info) == GENLIB_ERR_NONE) {
+			mData = info.data;
+			dim = info.dim;
+			channels = info.channels;
+		} else {
+			genlib_report_error("failed to acquire data info");
+		}
+	}
+};
+
+// Used by SineData
+struct DataLocal : public DataInterface<t_sample> {
+	DataLocal() : DataInterface<t_sample>() {}
+	~DataLocal() { 
+		if (mData) sysmem_freeptr(mData);
+		mData = 0;
+	}
+
+	void reset(long s, long c) { 
+		mData=0; 
+		resize(s, c); 
+	}
+		
+	void resize(long s, long c) {
+		if (s * c > DATA_MAXIMUM_ELEMENTS) {
+			s = DATA_MAXIMUM_ELEMENTS/c;
+			genlib_report_message("warning: resizing data to < 256MB");
+		}
+		if (mData) {
+			sysmem_resizeptr(mData, sizeof(t_sample) * s * c);
+		} else {
+			mData = (t_sample *)sysmem_newptr(sizeof(t_sample) * s * c);
+		}
+		if (!mData) {
+			genlib_report_error("out of memory");
+			resize(512, 1);
+			return;
+		} else {
+			dim = s;
+			channels = c;
+		}
+		set_zero64(mData, dim * channels);
+	}
+
+	// copy from a buffer~
+	// resizing is safe only during initialization!
+	bool setbuffer(void *dataReference) {
+		mDataReference = dataReference; // replaced mName
+		bool result = false;
+		t_genlib_buffer * b;
+		t_genlib_buffer_info info;
+		if (mDataReference != 0) {
+			b = (t_genlib_buffer *)genlib_obtain_buffer_from_reference(mDataReference);
+			if (b) {
+				if (genlib_buffer_edit_begin(b)==GENLIB_ERR_NONE) {
+					if (genlib_buffer_getinfo(b, &info)==GENLIB_ERR_NONE) {
+						float * samples = info.b_samples;
+						long frames = info.b_frames;
+						long nchans = info.b_nchans;
+						//long size = info.b_size;
+						//long modtime = info.b_modtime;	// cache & compare?
+						
+						// resizing is safe only during initialization!
+						if (mData == 0) resize(frames, nchans);
+						
+						long frames_safe = frames < dim ? frames : dim;
+						long channels_safe = nchans < channels ? nchans : channels;						
+						// copy:
+						for (int f=0; f<frames_safe; f++) {
+							for (int c=0; c<channels_safe; c++) {
+								t_sample value = samples[c+f*nchans];
+								write(value, f, c);
+							}
+						}
+						result = true;
+					} else {
+						genlib_report_message("couldn't get info for buffer\n"); 
+					}
+					genlib_buffer_edit_end(b, 1);
+				} else {
+					genlib_report_message("buffer locked\n");
+				}
+			}
+		} else {
+			genlib_report_message("buffer reference not valid");
+		}
+		return result;
+	}
+};
+
+struct Buffer : public DataInterface<float> {
+	t_genlib_buffer * mBuf;
+	t_genlib_buffer_info mInfo;
+	float mDummy;		// safe access in case buffer is not valid
+	
+	Buffer() : DataInterface<float>() {}
+	
+	void reset(const char * name) {
+		dim = 1;
+		channels = 1;
+		mData = &mDummy;
+		mDummy = 0.f;
+		mBuf = 0;
+		
+		// call into genlib:
+		mDataReference = genlib_obtain_reference_from_string(name);		
+	}
+	
+	void setbuffer(void * ref) {
+		mDataReference = ref;
+	}
+	
+	void begin() {
+		t_genlib_buffer * b = genlib_obtain_buffer_from_reference(mDataReference);
+		mBuf = 0;
+		if (b) {
+			if (genlib_buffer_perform_begin(b) == GENLIB_ERR_NONE) {
+				mBuf = b;
+			} else {
+				//genlib_report_message ("not a buffer~ %s", mName->s_name);
+			}
+		} else {
+			//genlib_report_message("no object %s\n", mName->s_name);
+		}
+		
+		if (mBuf && genlib_buffer_getinfo(mBuf, &mInfo)==GENLIB_ERR_NONE) {
+			// grab data:
+			mBuf = b;
+			mData = mInfo.b_samples;
+			dim = mInfo.b_frames;
+			channels = mInfo.b_nchans;
+		} else {
+			//genlib_report_message("couldn't get info");
+			mBuf = 0;
+			mData = &mDummy;
+			dim = 1;
+			channels = 1;
+		}
+	}
+	
+	void end() {
+		if (mBuf) {
+			genlib_buffer_perform_end(mBuf);
+			if (modified) {
+				genlib_buffer_dirty(mBuf);
+			}
+			modified = 0;
+		}
+		mBuf = 0;
+	}
+};
+
+struct SineData : public DataLocal {
+	SineData() : DataLocal() {
+		const int costable_size = 1 << 14;	// 14 bit index (noise floor at around -156 dB)
+		mData=0; 
+		resize(costable_size, 1); 
+		for (int i=0; i<dim; i++) {
+			mData[i] = cos(i * GENLIB_PI * 2. / (double)(dim));
+		}
+	}
+	
+	~SineData() { 
+		if (mData) sysmem_freeptr(mData);
+		mData = 0;
+	}
+};
+
+template<typename T>
+inline int dim(const T& data) { return data.dim; }
+
+template<typename T>
+inline int channels(const T& data) { return data.channels; }
+
+// used by cycle when no buffer/data is specified:
+struct SineCycle {
+			
+	uint32_t phasei, pincr;
+	double f2i;
+	
+	void reset(t_sample samplerate, t_sample init = 0) { 
+		phasei = init * t_sample(4294967296.0);
+		pincr = 0;
+		f2i = t_sample(4294967296.0) / samplerate;
+	}
+	
+	inline void freq(t_sample f) {
+		pincr = f * f2i;
+	}
+	
+	inline void phase(t_sample f) {
+		phasei = f * t_sample(4294967296.0);
+	}
+	
+	inline t_sample phase() const {	
+		return phasei * t_sample(0.232830643653869629e-9);
+	}
+	
+	template<typename T>
+	inline t_sample operator()(const DataInterface<T>& buf) {
+		T * data = buf.mData;
+		// divide uint32_t range down to buffer size (32-bit to 14-bit)
+		uint32_t idx = phasei >> 18;
+		// compute fractional portion and divide by 18-bit range
+		const t_sample frac = t_sample(phasei & 262143) * t_sample(3.81471181759574e-6);	
+		// index safely in 14-bit range:
+		const t_sample y0 = data[idx];
+		const t_sample y1 = data[(idx+1) & 16383];
+		const t_sample y = linear_interp(frac, y0, y1);
+		phasei += pincr;
+		return y;
+	}
+};
+
+#endif