ref: ffa74901871dab0d6d96ba82414d800748cb01c1
parent: 08efd6ad79ec698103bcfc7567e036b4d1d9ef10
author: spiricom <jeff@snyderphonics.com>
date: Thu Apr 23 09:22:01 EDT 2020
added vz filters
binary files a/.DS_Store b/.DS_Store differ
binary files a/LEAF/.DS_Store b/LEAF/.DS_Store differ
--- a/LEAF/Inc/leaf-envelopes.h
+++ b/LEAF/Inc/leaf-envelopes.h
@@ -161,8 +161,36 @@
void tRamp_setDest (tRamp* const, float dest);
void tRamp_setVal (tRamp* const, float val);
+ ///=============
+ /* Ramp Updown*/
+ typedef struct _tRampUpDown {
+ float upInc;
+ float downInc;
+ float inv_sr_ms;
+ float minimum_time;
+ float curr,dest;
+ float upTime;
+ float downTime;
+ int samples_per_tick;
+
+ } _tRampUpDown;
+
+ typedef _tRampUpDown* tRampUpDown;
+
+ void tRampUpDown_init (tRampUpDown* const, float upTime, float downTime, int samplesPerTick);
+ void tRampUpDown_free (tRampUpDown* const);
+ void tRampUpDown_initToPool (tRampUpDown* const, float upTime, float downTime, int samplesPerTick, tMempool* const);
+ void tRampUpDown_freeFromPool (tRampUpDown* const, tMempool* const);
+ float tRampUpDown_tick (tRampUpDown* const);
+ float tRampUpDown_sample (tRampUpDown* const);
+ void tRampUpDown_setUpTime (tRampUpDown* const, float upTime);
+ void tRampUpDown_setDownTime (tRampUpDown* const, float downTime);
+ void tRampUpDown_setDest (tRampUpDown* const, float dest);
+ void tRampUpDown_setVal (tRampUpDown* const, float val);
+
+ ///=========================
//Slide (based on Max/MSP's slide~)
typedef struct _tSlide {
float prevOut;
--- a/LEAF/Inc/leaf-filters.h
+++ b/LEAF/Inc/leaf-filters.h
@@ -240,6 +240,8 @@
SVFTypeBandpass,
SVFTypeNotch,
SVFTypePeak,
+ SVFTypeLowShelf,
+ SVFTypeHighShelf
} SVFType;
typedef struct _tSVF
@@ -247,7 +249,7 @@
SVFType type;
float cutoff, Q;
float ic1eq,ic2eq;
- float g,k,a1,a2,a3;
+ float g,k,a1,a2,a3,cH,cB,cL, kAmount;
} _tSVF;
@@ -376,6 +378,65 @@
void tMedianFilter_freeFromPool (tMedianFilter* const, tMempool* const);
float tMedianFilter_tick (tMedianFilter* const, float input);
+
+
+
+
+
+ //Vadim Zavalishin style from VA book (from implementation in RSlib posted to kvr forum)
+
+ typedef enum VZFilterType
+ {
+ Highpass = 0,
+ Lowpass,
+ BandpassSkirt,
+ BandpassPeak,
+ BandReject,
+ Bell,
+ Lowshelf,
+ Highshelf,
+ Morph,
+ Bypass,
+ Allpass
+ } VZFilterType;
+
+
+ typedef struct _tVZFilter
+ {
+ VZFilterType type;
+ // state:
+ float s1, s2;
+
+ // filter coefficients:
+ float g; // embedded integrator gain
+ float R2; // twice the damping coefficient (R2 == 2*R == 1/Q)
+ float h; // factor for feedback (== 1/(1+2*R*g+g*g))
+ float cL, cB, cH; // coefficients for low-, band-, and highpass signals
+
+ // parameters:
+ float fs; // sample-rate
+ float fc; // characteristic frequency
+ float G; // gain
+ float B; // bandwidth (in octaves)
+ float m; // morph parameter (0...1)
+
+ } _tVZFilter;
+
+ typedef _tVZFilter* tVZFilter;
+
+ void tVZFilter_init (tVZFilter* const, VZFilterType type, float freq, float Q);
+ void tVZFilter_free (tVZFilter* const);
+ void tVZFilter_initToPool (tVZFilter* const, VZFilterType type, float freq, float Q, tMempool* const);
+ void tVZFilter_freeFromPool (tVZFilter* const, tMempool* const);
+
+ float tVZFilter_tick (tVZFilter* const, float input);
+ void tVZFilter_calcCoeffs (tVZFilter* const);
+ void tVZFilter_setBandwidth (tVZFilter* const, float bandWidth);
+ void tVZFilter_setFreq (tVZFilter* const, float freq);
+ void tVZFilter_setGain (tVZFilter* const, float gain);
+ void tVZFilter_setType (tVZFilter* const, VZFilterType type);
+ float tVZFilter_BandwidthToR (tVZFilter* const vf, float B);
+
#ifdef __cplusplus
}
--- a/LEAF/Inc/leaf-math.h
+++ b/LEAF/Inc/leaf-math.h
@@ -33,7 +33,7 @@
float f;
int i;
};
-
+
#define SQRT8 2.82842712475f
#define WSCALE 1.30612244898f
#define PI (3.14159265358979f)
@@ -90,6 +90,7 @@
#define TWO_TO_32 4294967296.0f
#define INV_TWO_TO_32 0.000000000232831f
+#define ONE_OVER_SQRT2 0.707106781186548f
#define LOGTEN 2.302585092994
@@ -170,6 +171,14 @@
float atodb(float a);
float dbtoa(float db);
+
+ float fastdbtoa(float db);
+
+ float maximum (float num1, float num2);
+
+ float minimum (float num1, float num2);
+
+
//==============================================================================
#ifdef __cplusplus
--- a/LEAF/Src/leaf-analysis.c
+++ b/LEAF/Src/leaf-analysis.c
@@ -609,7 +609,7 @@
int n, tindex = s->timeindex;
int framesize = s->framesize;
int mask = framesize - 1;
- float norm = 1.0f / sqrtf((float)(framesize * 2));
+ float norm = 1. / sqrt((float)(framesize * 2));
float *inputbuf = s->inputbuf;
float *processbuf = s->processbuf;
--- a/LEAF/Src/leaf-effects.c
+++ b/LEAF/Src/leaf-effects.c
@@ -1209,7 +1209,7 @@
{
_tRetune* r = *rt;
- return (r->inputPeriod * leaf.invSampleRate);
+ return r->inputPeriod;
}
float tRetune_getInputFreq(tRetune* const rt)
@@ -1216,7 +1216,7 @@
{
_tRetune* r = *rt;
- return 1.0f/(r->inputPeriod * leaf.invSampleRate);
+ return 1.0f/r->inputPeriod;
}
//============================================================================================================
--- a/LEAF/Src/leaf-envelopes.c
+++ b/LEAF/Src/leaf-envelopes.c
@@ -700,6 +700,151 @@
}
+
+/* RampUpDown */
+void tRampUpDown_init(tRampUpDown* const r, float upTime, float downTime, int samples_per_tick)
+{
+ tRampUpDown_initToPool(r, upTime, downTime, samples_per_tick, &leaf.mempool);
+}
+
+void tRampUpDown_free(tRampUpDown* const r)
+{
+ tRampUpDown_freeFromPool(r, &leaf.mempool);
+}
+
+void tRampUpDown_initToPool(tRampUpDown* const r, float upTime, float downTime, int samples_per_tick, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tRampUpDown* ramp = *r = (_tRampUpDown*) mpool_alloc(sizeof(_tRampUpDown), m);
+
+ ramp->inv_sr_ms = 1.0f/(leaf.sampleRate*0.001f);
+ ramp->minimum_time = ramp->inv_sr_ms * samples_per_tick;
+ ramp->curr = 0.0f;
+ ramp->dest = 0.0f;
+
+ if (upTime < ramp->minimum_time)
+ {
+ ramp->upTime = ramp->minimum_time;
+ }
+ else
+ {
+ ramp->upTime = upTime;
+ }
+
+ if (downTime < ramp->minimum_time)
+ {
+ ramp->downTime = ramp->minimum_time;
+ }
+ else
+ {
+ ramp->downTime = downTime;
+ }
+
+ ramp->samples_per_tick = samples_per_tick;
+ ramp->upInc = ((ramp->dest - ramp->curr) / ramp->upTime * ramp->inv_sr_ms) * (float)ramp->samples_per_tick;
+ ramp->downInc = ((ramp->dest - ramp->curr) / ramp->downTime * ramp->inv_sr_ms) * (float)ramp->samples_per_tick;
+}
+
+void tRampUpDown_freeFromPool (tRampUpDown* const r, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tRampUpDown* ramp = *r;
+
+ mpool_free(ramp, m);
+}
+
+void tRampUpDown_setUpTime(tRampUpDown* const ramp, float upTime)
+{
+ _tRampUpDown* r = *ramp;
+
+ if (upTime < r->minimum_time)
+ {
+ r->upTime = r->minimum_time;
+ }
+ else
+ {
+ r->upTime = upTime;
+ }
+ r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick;
+}
+
+
+void tRampUpDown_setDownTime(tRampUpDown* const ramp, float downTime)
+{
+ _tRampUpDown* r = *ramp;
+
+ if (downTime < r->minimum_time)
+ {
+ r->downTime = r->minimum_time;
+ }
+ else
+ {
+ r->downTime = downTime;
+ }
+ r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick;
+}
+
+void tRampUpDown_setDest(tRampUpDown* const ramp, float dest)
+{
+ _tRampUpDown* r = *ramp;
+ r->dest = dest;
+ r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick;
+ r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick;
+}
+
+void tRampUpDown_setVal(tRampUpDown* const ramp, float val)
+{
+ _tRampUpDown* r = *ramp;
+ r->curr = val;
+ r->upInc = ((r->dest - r->curr) / r->upTime * r->inv_sr_ms) * (float)r->samples_per_tick;
+ r->downInc = ((r->dest - r->curr) / r->downTime * r->inv_sr_ms) * (float)r->samples_per_tick;
+}
+
+float tRampUpDown_tick(tRampUpDown* const ramp)
+{
+ _tRampUpDown* r = *ramp;
+ float test;
+
+ if (r->dest < r->curr)
+ {
+ test = r->curr + r->downInc;
+ if (test > r->dest)
+ {
+ r->curr = test;
+ }
+ else
+ {
+ r->downInc = 0.0f;
+ r->curr = r->dest;
+ }
+ }
+ else if (r->dest > r->curr)
+ {
+ test = r->curr + r->upInc;
+ if (test < r->dest)
+ {
+ r->curr = test;
+ }
+ else
+ {
+ r->upInc = 0.0f;
+ r->curr = r->dest;
+ }
+ }
+ return r->curr;
+}
+
+float tRampUpDown_sample(tRampUpDown* const ramp)
+{
+ _tRampUpDown* r = *ramp;
+ return r->curr;
+}
+
+
+
+
+
+
/* Exponential Smoother */
void tExpSmooth_init(tExpSmooth* const expsmooth, float val, float factor)
{ // factor is usually a value between 0 and 0.1. Lower value is slower. 0.01 for example gives you a smoothing time of about 10ms
--- a/LEAF/Src/leaf-filters.c
+++ b/LEAF/Src/leaf-filters.c
@@ -855,6 +855,51 @@
svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k));
svf->a2 = svf->g*svf->a1;
svf->a3 = svf->g*svf->a2;
+
+ if (type == SVFTypeLowpass)
+ {
+ svf->cH = 0.0f;
+ svf->cB = 0.0f;
+ svf->kAmount = 0.0f;
+ svf->cL = 1.0f;
+ }
+ else if (type == SVFTypeBandpass)
+ {
+ svf->cH = 0.0f;
+ svf->cB = 1.0f;
+ svf->kAmount = 0.0f;
+ svf->cL = 0.0f;
+ }
+
+ else if (type == SVFTypeHighpass)
+ {
+ svf->cH = 1.0f;
+ svf->cB = svf->k * -1.0f;
+ svf->kAmount = 1.0f;
+ svf->cL = -1.0f;
+ }
+
+ else if (type == SVFTypeNotch)
+ {
+ svf->cH = 1.0f;
+ svf->cB = svf->k * -1.0f;
+ svf->kAmount = 1.0f;
+ svf->cL = 0.0f;
+ }
+
+
+ else if (type == SVFTypePeak)
+ {
+ svf->cH = 1.0f;
+ svf->cB = svf->k * -1.0f;
+ svf->kAmount = 1.0f;
+ svf->cL = -2.0f;
+ }
+ // or maybe this?
+ /*
+ * hp=1 bp=A/Q (where A is 10^(G/40) and G is gain in decibels) and lp = 1
+ */
+
}
void tSVF_free(tSVF* const svff)
@@ -879,6 +924,9 @@
svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k));
svf->a2 = svf->g*svf->a1;
svf->a3 = svf->g*svf->a2;
+ svf->cH = 0.0f;
+ svf->cB = 0.0f;
+ svf->cL = 1.0f;
}
void tSVF_freeFromPool (tSVF* const svff, tMempool* const mp)
@@ -900,13 +948,7 @@
svf->ic1eq = (2.0f * v1) - svf->ic1eq;
svf->ic2eq = (2.0f * v2) - svf->ic2eq;
- if (svf->type == SVFTypeLowpass) return v2;
- else if (svf->type == SVFTypeBandpass) return v1;
- else if (svf->type == SVFTypeHighpass) return v0 - (svf->k * v1) - v2;
- else if (svf->type == SVFTypeNotch) return v0 - (svf->k * v1);
- else if (svf->type == SVFTypePeak) return v0 - (svf->k * v1) - (2.0f * v2);
- else return 0.0f;
-
+ return (v0 * svf->cH) + (svf->kAmount * svf->k * v1 * svf->cB) + (v2 * svf->cL);
}
void tSVF_setFreq(tSVF* const svff, float freq)
@@ -924,11 +966,22 @@
_tSVF* svf = *svff;
svf->k = 1.0f/Q;
+
svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k));
svf->a2 = svf->g * svf->a1;
svf->a3 = svf->g * svf->a2;
}
+void tSVF_setFreqAndQ(tSVF* const svff, float freq, float Q)
+{
+ _tSVF* svf = *svff;
+ svf->k = 1.0f/Q;
+ svf->g = tanf(PI * freq * leaf.invSampleRate);
+ svf->a1 = 1.0f/(1.0f + svf->g * (svf->g + svf->k));
+ svf->a2 = svf->g * svf->a1;
+ svf->a3 = svf->g * svf->a2;
+}
+
// Efficient version of tSVF where frequency is set based on 12-bit integer input for lookup in tanh wavetable.
void tEfficientSVF_init(tEfficientSVF* const svff, SVFType type, uint16_t input, float Q)
{
@@ -1324,3 +1377,215 @@
}
+void tVZFilter_init (tVZFilter* const vf, VZFilterType type, float freq, float bandWidth)
+{
+ tVZFilter_initToPool(vf, type, freq, bandWidth, &leaf.mempool);
+}
+
+void tVZFilter_free (tVZFilter* const vf)
+{
+ tVZFilter_freeFromPool(vf, &leaf.mempool);
+}
+void tVZFilter_initToPool (tVZFilter* const vf, VZFilterType type, float freq, float bandWidth, tMempool* const mp)
+{
+
+ _tMempool* m = *mp;
+ _tVZFilter* f = *vf = (_tVZFilter*) mpool_alloc(sizeof(_tVZFilter), m);
+ f->fc = freq;
+ f->type = type;
+ f->G = ONE_OVER_SQRT2;
+ f->B = bandWidth;
+ f->m = 0.0f;
+ f->s1 = 0.0f;
+ f->s2 = 0.0f;
+ tVZFilter_calcCoeffs(vf);
+
+
+}
+void tVZFilter_freeFromPool (tVZFilter* const vf, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tVZFilter* f = *vf = (_tVZFilter*) mpool_alloc(sizeof(_tVZFilter), m);
+ mpool_free(f, m);
+}
+
+float tVZFilter_tick (tVZFilter* const vf, float in)
+{
+ _tVZFilter* f = *vf;
+
+ float yL, yB, yH;
+
+ // compute highpass output via Eq. 5.1:
+ yH = (in - f->R2*f->s1 - f->g*f->s1 - f->s2) * f->h;
+
+ // compute bandpass output by applying 1st integrator to highpass output:
+ yB = tanhf(f->g*yH) + f->s1;
+ f->s1 = f->g*yH + yB; // state update in 1st integrator
+
+ // compute lowpass output by applying 2nd integrator to bandpass output:
+ yL = tanhf(f->g*yB) + f->s2;
+ f->s2 = f->g*yB + yL; // state update in 2nd integrator
+
+ //according to the Vadim paper, we could add saturation to this model by adding a tanh in the integration stage.
+ //
+ //seems like that might look like this:
+ // y = tanh(g*x) + s; // output computation
+ // s = g*x + y; // state update
+
+ //instead of this:
+ // y = g*x + s; // output computation
+ // s = g*x + y; // state update
+
+ return f->cL*yL + f->cB*yB + f->cH*yH;
+
+}
+
+void tVZFilter_calcCoeffs (tVZFilter* const vf)
+{
+
+ _tVZFilter* f = *vf;
+ f->g = tanf(PI * f->fc * leaf.invSampleRate); // embedded integrator gain (Fig 3.11)
+
+ switch( f->type )
+ {
+ case Bypass:
+ {
+ f->R2 = 1.0f / f->G; // can we use an arbitrary value here, for example R2 = 1?
+ f->cL = 1.0f;
+ f->cB = f->R2;
+ f->cH = 1.0f;
+ }
+ break;
+ case Lowpass:
+ {
+ f->R2 = 1.0f / f->G;
+ f->cL = 1.0f; f->cB = 0.0f; f->cH = 0.0f;
+ }
+ break;
+ case Highpass:
+ {
+ f->R2 = 1.0f / f->G;
+ f->cL = 0.0f; f->cB = 0.0f; f->cH = 1.0f;
+ }
+ break;
+ case BandpassSkirt:
+ {
+ f->R2 = 1.0f / f->G;
+ f->cL = 0.0f; f->cB = 1.0f; f->cH = 0.0f;
+ }
+ break;
+ case BandpassPeak:
+ {
+ f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B);
+ f->cL = 0.0f; f->cB = f->R2; f->cH = 0.0f;
+ }
+ break;
+ case BandReject:
+ {
+ f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B);
+ f->cL = 1.0f; f->cB = 0.0f; f->cH = 1.0f;
+ }
+ break;
+ case Bell:
+ {
+ float fl = f->fc*powf(2.0f, (-f->B)*0.5f); // lower bandedge frequency (in Hz)
+ float wl = tanf(PI*fl*leaf.invSampleRate); // warped radian lower bandedge frequency /(2*fs)
+ float r = f->g/wl;
+ r *= r; // warped frequency ratio wu/wl == (wc/wl)^2 where wu is the
+ // warped upper bandedge, wc the center
+ f->R2 = 2.0f*sqrtf(((r*r+1.0f)/r-2.0f)/(4.0f*f->G));
+ f->cL = 1.0f; f->cB = f->R2*f->G; f->cH = 1.0f;
+ }
+ break;
+ case Lowshelf:
+ {
+ float A = sqrtf(f->G);
+ f->g /= sqrtf(A); // scale SVF-cutoff frequency for shelvers
+ f->R2 = 2*sinhf(f->B*logf(2.0f)*0.5f);
+ f->cL = f->G; f->cB = f->R2*A; f->cH = 1.0f;
+ }
+ break;
+ case Highshelf:
+ {
+ float A = sqrtf(f->G);
+ f->g *= sqrtf(A); // scale SVF-cutoff frequency for shelvers
+ f->R2 = 2.0f*sinhf(f->B*logf(2.0f)*0.5f);
+ f->cL = 1.0f; f->cB = f->R2*A; f->cH = f->G;
+ }
+ break;
+ case Allpass:
+ {
+ f->R2 = 2.0f*tVZFilter_BandwidthToR(vf, f->B);
+ f->cL = 1.0f; f->cB = -f->R2; f->cH = 1.0f;
+ }
+ break;
+
+ // experimental - maybe we must find better curves for cL, cB, cH:
+ case Morph:
+ {
+ f->R2 = 1.0f / f->G;
+ float x = 2.0f*f->m-1.0f;
+
+ f->cL = maximum(-x, 0.0f); /*cL *= cL;*/
+ f->cH = minimum( x, 0.0f); /*cH *= cH;*/
+ f->cB = 1.0f-x*x;
+
+ // bottom line: we need to test different versions for how they feel when tweaking the
+ // morph parameter
+
+ // this scaling ensures constant magnitude at the cutoff point (we divide the coefficients by
+ // the magnitude response value at the cutoff frequency and scale back by the gain):
+ float s = f->G * sqrtf((f->R2*f->R2) / (f->cL*f->cL + f->cB*f->cB + f->cH*f->cH - 2.0f*f->cL*f->cH));
+ f->cL *= s; f->cB *= s; f->cH *= s;
+ }
+ break;
+
+ }
+
+ f->h = 1.0f / (1.0f + f->R2*f->g + f->g*f->g); // factor for feedback precomputation
+}
+
+
+void tVZFilter_setBandwidth (tVZFilter* const vf, float B)
+{
+ _tVZFilter* f = *vf;
+ f->B = LEAF_clip(0.0f, B, 100.0f);
+ tVZFilter_calcCoeffs(vf);
+}
+void tVZFilter_setFreq (tVZFilter* const vf, float freq)
+{
+ _tVZFilter* f = *vf;
+ f->fc = LEAF_clip(0.0f, freq, 0.5f*leaf.sampleRate);
+ tVZFilter_calcCoeffs(vf);
+}
+void tVZFilter_setGain (tVZFilter* const vf, float gain)
+{
+ _tVZFilter* f = *vf;
+ f->G = LEAF_clip(0.000001f, gain, 100.0f);
+ tVZFilter_calcCoeffs(vf);
+}
+
+void tVZFilter_setMorph (tVZFilter* const vf, float morph)
+{
+ _tVZFilter* f = *vf;
+ f->m = LEAF_clip(0.0f, morph, 1.0f);
+ tVZFilter_calcCoeffs(vf);
+}
+
+void tVZFilter_setType (tVZFilter* const vf, VZFilterType type)
+{
+ _tVZFilter* f = *vf;
+ f->type = type;
+ tVZFilter_calcCoeffs(vf);
+}
+
+float tVZFilter_BandwidthToR(tVZFilter* const vf, float B)
+{
+ _tVZFilter* f = *vf;
+ float fl = f->fc*powf(2.0f, -B*0.5f); // lower bandedge frequency (in Hz)
+ float gl = tanf(PI*fl*leaf.invSampleRate); // warped radian lower bandedge frequency /(2*fs)
+ float r = gl/f->g; // ratio between warped lower bandedge- and center-frequencies
+ // unwarped: r = pow(2, -B/2) -> approximation for low
+ // center-frequencies
+ return sqrtf((1.0f-r*r)*(1.0f-r*r)/(4.0f*r*r));
+}
--- a/LEAF/Src/leaf-math.c
+++ b/LEAF/Src/leaf-math.c
@@ -514,4 +514,21 @@
}
+float fastdbtoa(float db)
+{
+ //return powf(10.0f, db * 0.05f);
+ return expf(0.115129254649702f * db); //faster version from http://openaudio.blogspot.com/2017/02/faster-log10-and-pow.html
+}
+
+
+float maximum (float num1, float num2)
+{
+ return (num1 > num2 ) ? num1 : num2;
+}
+
+float minimum (float num1, float num2)
+{
+ return (num1 < num2 ) ? num1 : num2;
+}
+
--- a/LEAF/Src/leaf-oscillators.c
+++ b/LEAF/Src/leaf-oscillators.c
@@ -227,7 +227,7 @@
float out = 0.0f;
int idx = (int)(c->phase * SQR_TABLE_SIZE);
-
+
// Wavetable synthesis
out = __leaf_table_squarewave[c->oct+1][idx] +
(__leaf_table_squarewave[c->oct][idx] - __leaf_table_squarewave[c->oct+1][idx]) * c->w;