shithub: leaf

Download patch

ref: a90cbc5b8eac9a63d3e0bbe8f326af7adf44b395
parent: 635257887ab374a68acf386814df0c59aeec41a1
author: spiricom <jeff@snyderphonics.com>
date: Mon Apr 27 17:17:28 EDT 2020

wavefolder edits

binary files a/.DS_Store b/.DS_Store differ
--- a/LEAF/Inc/leaf-distortion.h
+++ b/LEAF/Inc/leaf-distortion.h
@@ -80,7 +80,7 @@
     {
         double Ln1;
         double Fn1;
-        float xn1;
+        double xn1;
 
         double RL;
         double R;
@@ -92,9 +92,16 @@
         double d;
 
         // Antialiasing error threshold
-        double thresh;
+        double AAthresh;
         double half_a;
         double longthing;
+
+        double LambertThresh, w, expw, p, r, s, myerr;
+        double l;
+        double u, Ln, Fn;
+        double tempsDenom;
+        double tempErrDenom;
+        double tempOutDenom;
 
     } _tLockhartWavefolder;
     
--- a/LEAF/Inc/leaf-filters.h
+++ b/LEAF/Inc/leaf-filters.h
@@ -417,9 +417,13 @@
 			float fs;    // sample-rate
 			float fc;    // characteristic frequency
 			float G;     // gain
+			float invG;		//1/gain
 			float B;     // bandwidth (in octaves)
 			float m;     // morph parameter (0...1)
 
+			float sr;    //local sampling rate of filter (may be different from leaf sr if oversampled)
+			float inv_sr;
+
 	    } _tVZFilter;
 
 	    typedef _tVZFilter* tVZFilter;
@@ -428,7 +432,7 @@
 		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);
-
+		void 	tVZFilter_setSampleRate  (tVZFilter* const, float sampleRate);
 		float   tVZFilter_tick           	(tVZFilter* const, float input);
 		void   tVZFilter_calcCoeffs           (tVZFilter* const);
 		void   tVZFilter_setBandwidth        	(tVZFilter* const, float bandWidth);
@@ -436,6 +440,37 @@
 		void   tVZFilter_setGain          		(tVZFilter* const, float gain);
 		void   tVZFilter_setType          		(tVZFilter* const, VZFilterType type);
 		float 	tVZFilter_BandwidthToR		(tVZFilter* const vf, float B);
+
+
+
+
+		//diode ladder filter
+		typedef struct _tDiodeFilter
+		    {
+
+			 	 float f;
+			 	 float r;
+			 	 float Vt;
+			 	 float n;
+			 	 float gamma;
+			 	 float zi;
+			 	 float g0inv;
+			 	 float g1inv;
+			 	 float g2inv;
+			 	 float s0, s1, s2, s3;
+
+		    } _tDiodeFilter;
+
+		    typedef _tDiodeFilter* tDiodeFilter;
+
+		    void    tDiodeFilter_init           (tDiodeFilter* const, float freq, float Q);
+			void    tDiodeFilter_free           (tDiodeFilter* const);
+			void    tDiodeFilter_initToPool     (tDiodeFilter* const, float freq, float Q, tMempool* const);
+			void    tDiodeFilter_freeFromPool   (tDiodeFilter* const, tMempool* const);
+
+			float   tDiodeFilter_tick           	(tDiodeFilter* const, float input);
+			void    tDiodeFilter_setFreq     (tDiodeFilter* const vf, float cutoff);
+			void    tDiodeFilter_setQ     (tDiodeFilter* const vf, float resonance);
 
 
 #ifdef __cplusplus
--- a/LEAF/Inc/leaf-math.h
+++ b/LEAF/Inc/leaf-math.h
@@ -145,10 +145,21 @@
     void LEAF_crossfade(float fade, float* volumes);
 
 
+    float LEAF_tanh(float x);
+    void LEAF_generate_sine(float* buffer, int size);
+    void LEAF_generate_sawtooth(float* buffer, float basefreq, int size);
+    void LEAF_generate_triangle(float* buffer, float basefreq, int size);
+    void LEAF_generate_square(float* buffer, float basefreq, int size);
+    float LEAF_midiToFrequency(float f);
 
+
     float fast_mtof(float f);
 
+    double fastexp(double x);
+
     float fastexpf(float x);
+
+    double fasterexp(double x);
 
     float fasterexpf(float x);
 
--- a/LEAF/Src/leaf-distortion.c
+++ b/LEAF/Src/leaf-distortion.c
@@ -17,7 +17,7 @@
 #include "../Inc/leaf-tables.h"
 
 //testing
-//#include "gpio.h"
+#include "gpio.h"
 
 #endif
 
@@ -353,26 +353,7 @@
 
 void tLockhartWavefolder_init(tLockhartWavefolder* const wf)
 {
-    _tLockhartWavefolder* w = *wf = (_tLockhartWavefolder*) leaf_alloc(sizeof(_tLockhartWavefolder));
-    
-    w->Ln1 = 0.0;
-    w->Fn1 = 0.0;
-    w->xn1 = 0.0f;
-
-    w->RL = 7.5e3;
-    w->R = 15e3;
-    w->VT = 26e-3;
-    w->Is = 10e-16;
-
-    w->a = 2.0*w->RL/w->R;
-    w->b = (w->R+2.0*w->RL)/(w->VT*w->R);
-    w->d = (w->RL*w->Is)/w->VT;
-    w->half_a = 0.5 * w->a;
-    w->longthing = (0.5*w->VT/w->b);
-
-
-    // Antialiasing error threshold
-    w->thresh = 10e-10;
+	tLockhartWavefolder_initToPool   (wf,  &leaf.mempool);
 }
 
 void tLockhartWavefolder_free(tLockhartWavefolder* const wf)
@@ -389,7 +370,7 @@
     
     w->Ln1 = 0.0;
     w->Fn1 = 0.0;
-    w->xn1 = 0.0f;
+    w->xn1 = 0.0;
     
     w->RL = 7.5e3;
     w->R = 15e3;
@@ -404,7 +385,26 @@
     
     
     // Antialiasing error threshold
-    w->thresh = 10e-10;
+    w->AAthresh = 10e-10; //10
+
+	w->LambertThresh = 10e-10; //12  //was 8
+
+
+    w->w = 0.0f;
+    w->expw = 0.0f;
+    w->p = 0.0f;
+    w->r = 0.0f;
+    w->s= 0.0f;
+    w->myerr = 0.0f;
+    w->l = 0.0f;
+    w->u = 0.0f;
+    w->Ln = 0.0f;
+	w->Fn = 0.0f;
+    w->tempsDenom = 0.0f;
+    w->tempErrDenom = 0.0f;
+    w->tempOutDenom = 0.0f;
+
+
 }
 
 void    tLockhartWavefolder_freeFromPool (tLockhartWavefolder* const wf, tMempool* const mp)
@@ -415,71 +415,182 @@
     mpool_free(w, m);
 }
 
-double tLockhartWavefolderLambert(double x, double ln)
+
+
+double tLockhartWavefolderLambert(tLockhartWavefolder* const wf, double x, double ln)
 {
-    double thresh, w, expw, p, r, s, err;
-    // Error threshold
-    thresh = 10e-12;
+	_tLockhartWavefolder* mwf = *wf;
+
+
     // Initial guess (use previous value)
-    w = ln;
+	mwf->w = ln;
     
     // Haley's method (Sec. 4.2 of the paper)
-    for(int i=0; i<1000; i+=1) {
+    for(int i=0; i<3000; i+=1) { //1000
         
-        expw = exp(w);
-        
-        p = w*expw - x;
-        r = (w+1.0)*expw;
-        s = (w+2.0)/(2.0*(w+1.0));
-        err = (p/(r-(p*s)));
-        
-        if (fabs(err)<thresh) {
+    	mwf->expw = exp(mwf->w);
+    	/*
+        if (isinf(mwf->expw) || isnan(mwf->expw))
+        {
+        	mwf->expw = 10e-5;
+        	LEAF_error();
+        }
+        */
+    	mwf->p = mwf->w*mwf->expw - x;
+    	/*
+        if (isinf(mwf->p) || isnan(mwf->p))
+        {
+        	mwf->p = 10e-5;
+        	LEAF_error();
+        }
+        */
+    	mwf->r = (mwf->w+1.0)*mwf->expw;
+    	/*
+        if (isinf(mwf->r) || isnan(mwf->r))
+        {
+        	mwf->r = 10e-5;
+        	LEAF_error();
+        }
+        */
+    	mwf->tempsDenom = (2.0*(mwf->w+1.0));
+    	/*
+        if ((mwf->tempsDenom == 0.0) || isinf(mwf->tempsDenom) || isnan(mwf->tempsDenom))
+        {
+        	mwf->tempsDenom = 10e-5;
+        	LEAF_error();
+        }
+        */
+        mwf->s = (mwf->w+2.0)/mwf->tempsDenom;
+        /*
+        if (isnan(mwf->s) || isinf(mwf->s))
+        {
+        	mwf->s = 10e-5;
+        	LEAF_error();
+        }
+        */
+        mwf->tempErrDenom = (mwf->r-(mwf->p*mwf->s));
+         /*
+        if ((mwf->tempErrDenom == 0.0) || isinf(mwf->tempErrDenom) || isnan(mwf->tempErrDenom))
+        {
+        	mwf->tempErrDenom = 10e-5;
+        	LEAF_error();
+        }
+        */
+        mwf->myerr = (mwf->p/mwf->tempErrDenom);
+         /*
+        if (isnan(mwf->myerr) || isinf(mwf->myerr))
+        {
+        	mwf->myerr = 10e-5;
+        	LEAF_error();
+        }
+        */
 
+        if ((fabs(mwf->myerr))<mwf->LambertThresh) {
+
         	break;
         }
-        
-        w = w - err;
-        if (i == 999)
+
+        mwf->w = mwf->w - mwf->myerr;
+
+         /*
+        if (isinf(mwf->w) || isnan(mwf->w))
         {
-        	//HAL_GPIO_WritePin(GPIOG, GPIO_PIN_7, GPIO_PIN_SET);
+        	mwf->w = 10e-5;
+        	LEAF_error();
         }
+*/
 
     }
-    return w;
+    return mwf->w;
 }
 
-float tLockhartWavefolder_tick(tLockhartWavefolder* const wf, float samp)
+float tLockhartWavefolder_tick(tLockhartWavefolder* const wf, float in)
 {
     _tLockhartWavefolder* w = *wf;
-    
+
     float out = 0.0f;
     
     // Compute Antiderivative
-    int l = (samp > 0.0f) - (samp < 0.0f);
-    double u = w->d*exp(l*w->b*samp);
-    double Ln = tLockhartWavefolderLambert(u,w->Ln1);
-    double Fn = w->longthing*(Ln*(Ln + 2.0)) - w->half_a*samp*samp;
-    
+    w->l = (in > 0.0) - (in < 0.0);
+    w->u = w->d*exp(w->l*w->b*in);
+    /*
+    if ((w->u == 0.0) || isinf(w->u) || isnan(w->u))
+    {
+    	w->u = 10e-5;
+    	LEAF_error();
+    }
+    */
+
+    w->Ln = tLockhartWavefolderLambert(wf,w->u,w->Ln1);
+    /*
+    if ((w->Ln == 0.0) || isinf(w->Ln) || isnan(w->Ln))
+	{
+		w->Ln = 10e-5;
+		LEAF_error();
+	}
+*/
+    w->Fn = (w->longthing*(w->Ln*(w->Ln + 2.0))) - (w->half_a*in*in);
+    /*
+    if ((w->Fn == 0.0) || isinf(w->Fn) || isnan(w->Fn))
+	{
+		w->Fn = 10e-5;
+		LEAF_error();
+	}
+	*/
     // Check for ill-conditioning
-    if (fabs(samp-w->xn1)<w->thresh) {
+
+    if (fabs(in-w->xn1)<w->AAthresh)
+    {
         
         // Compute Averaged Wavefolder Output
-        float xn = 0.5f*(samp+w->xn1);
-        u = w->d*exp(l*w->b*xn);
-        Ln = tLockhartWavefolderLambert(u,w->Ln1);
-        out = (float) (l*w->VT*Ln - w->a*xn);
+    	double xn = 0.5*(in+w->xn1);
+    	w->u = w->d*exp(w->l*w->b*xn);
+        /*
+    	if ((w->u == 0.0) || isinf(w->u) || isnan(w->u))
+        {
+        	w->u = 10e-5;
+        	LEAF_error();
 
+        }
+        */
+    	w->Ln = tLockhartWavefolderLambert(wf,w->u,w->Ln1);
+    	/*
+        if ((w->Ln == 0.0) || isinf(w->Ln) || isnan(w->Ln))
+    	{
+    		w->Ln = 10e-5;
+    		LEAF_error();
+    	}
+    	*/
+        out = (float)((w->l*w->VT*w->Ln) - (w->a*xn));
+
     }
-    else {
-        
+    else
+    {
+
         // Apply AA Form
-        out = (float) ((Fn-w->Fn1)/(samp-w->xn1));
+    	w->tempOutDenom = (in-w->xn1);
+    	/*
+    	if ((w->tempOutDenom == 0.0) || isinf(w->tempOutDenom))
+    	{
+    		w->tempOutDenom = 10e-5;
+    		LEAF_error();
+    	}
+    	*/
+        out = ((w->Fn-w->Fn1)/w->tempOutDenom);
+        /*
+        if (isinf(out) || isnan(out))
+		{
+        	out = 10e-5;
+        	LEAF_error();
+		}
+		*/
+
     }
-    
+
     // Update States
-    w->Ln1 = Ln;
-    w->Fn1 = Fn;
-    w->xn1 = samp;
+    w->Ln1 = w->Ln;
+    w->Fn1 = w->Fn;
+    w->xn1 = (double)in;
     
     return out;
 }
--- a/LEAF/Src/leaf-filters.c
+++ b/LEAF/Src/leaf-filters.c
@@ -17,7 +17,7 @@
 #include "../Inc/leaf-filters.h"
 #include "../Inc/leaf-tables.h"
 #include "../leaf.h"
-
+#include "tim.h"
 #endif
 
 // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ OnePole Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ //
@@ -1398,6 +1398,8 @@
 	f->m    = 0.0f;
 	f->s1 = 0.0f;
 	f->s2 = 0.0f;
+	f->sr = leaf.sampleRate;
+	f->inv_sr = leaf.invSampleRate;
 	tVZFilter_calcCoeffs(vf);
 
 
@@ -1409,6 +1411,13 @@
 		 mpool_free(f, m);
 }
 
+void 	tVZFilter_setSampleRate  (tVZFilter* const vf, float sampleRate)
+{
+	_tVZFilter* f = *vf;
+	f->sr = sampleRate;
+	f->inv_sr = 1.0f/sampleRate;
+}
+
 float   tVZFilter_tick           	(tVZFilter* const vf, float in)
 {
 	_tVZFilter* f = *vf;
@@ -1444,13 +1453,13 @@
 {
 
 	_tVZFilter* f = *vf;
-	f->g = tanf(PI * f->fc * leaf.invSampleRate);  // embedded integrator gain (Fig 3.11)
+	f->g = tanf(PI * f->fc * f->inv_sr);  // 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->R2 = f->invG;  // can we use an arbitrary value here, for example R2 = 1?
 		  f->cL = 1.0f;
 		  f->cB = f->R2;
 		  f->cH = 1.0f;
@@ -1458,19 +1467,19 @@
 		break;
 	  case Lowpass:
 		{
-			f->R2 = 1.0f / f->G;
+			f->R2 = f->invG;
 			f->cL = 1.0f; f->cB = 0.0f; f->cH = 0.0f;
 		}
 		break;
 	  case Highpass:
 		{
-			f->R2 = 1.0f / f->G;
+			f->R2 = f->invG;
 			f->cL = 0.0f; f->cB = 0.0f; f->cH = 1.0f;
 		}
 		break;
 	  case BandpassSkirt:
 		{
-			f->R2 = 1.0f / f->G;
+			f->R2 = f->invG;
 			f->cL = 0.0f; f->cB = 1.0f; f->cH = 0.0f;
 		}
 		break;
@@ -1489,7 +1498,7 @@
 	  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 wl = tanf(PI*fl*f->inv_sr);   // 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
@@ -1523,7 +1532,7 @@
 		// experimental - maybe we must find better curves for cL, cB, cH:
 	  case Morph:
 		{
-			f->R2 = 1.0f / f->G;
+			f->R2 = f->invG;
 		  float x  = 2.0f*f->m-1.0f;
 
 		  f->cL = maximum(-x, 0.0f); /*cL *= cL;*/
@@ -1562,6 +1571,7 @@
 {
 	_tVZFilter* f = *vf;
 	f->G = LEAF_clip(0.000001f, gain, 100.0f);
+	f->invG = 1.0f/f->G;
 	tVZFilter_calcCoeffs(vf);
 }
 
@@ -1583,9 +1593,148 @@
 {
 	_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 gl = tanf(PI*fl*f->inv_sr);   // 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));
 }
+
+
+
+void    tDiodeFilter_init           (tDiodeFilter* const vf, float cutoff, float resonance)
+{
+	tDiodeFilter_initToPool(vf, cutoff, resonance, &leaf.mempool);
+}
+
+void    tDiodeFilter_free           (tDiodeFilter* const vf)
+{
+	tDiodeFilter_freeFromPool(vf, &leaf.mempool);
+}
+void    tDiodeFilter_initToPool     (tDiodeFilter* const vf, float cutoff, float resonance, tMempool* const mp)
+{
+
+	 _tMempool* m = *mp;
+	 _tDiodeFilter* f = *vf = (_tDiodeFilter*) mpool_alloc(sizeof(_tDiodeFilter), m);
+	 // initialization (the resonance factor is between 0 and 8 according to the article)
+	 f->f = tan(PI * cutoff/leaf.sampleRate);
+	 f->r = (7.f * resonance + 0.5f);
+	 f->Vt = 0.5f;
+	 f->n = 1.836f;
+	 f->zi = 0.0f; //previous input value
+	 f->gamma = f->Vt*f->n;
+	 f->s0 = 0.01f;
+	 f->s1 = 0.02f;
+	 f->s2 = 0.03f;
+	 f->s3 = 0.04f;
+	 f->g0inv = 1.f/(2.f*f->Vt);
+	 f->g1inv = 1.f/(2.f*f->gamma);
+	 f->g2inv = 1.f/(6.f*f->gamma);
+
+
+}
+void    tDiodeFilter_freeFromPool   (tDiodeFilter* const vf, tMempool* const mp)
+{
+	 _tMempool* m = *mp;
+	 _tDiodeFilter* f = *vf = (_tDiodeFilter*) mpool_alloc(sizeof(_tDiodeFilter), m);
+	 mpool_free(f, m);
+}
+
+float tanhXdX(float x)
+{
+    float a = x*x;
+    // IIRC I got this as Pade-approx for tanh(sqrt(x))/sqrt(x)
+    return ((a + 105.0f)*a + 945.0f) / ((15.0f*a + 420.0f)*a + 945.0f);
+}
+
+float   tDiodeFilter_tick           	(tDiodeFilter* const vf, float in)
+{
+	_tDiodeFilter* f = *vf;
+
+	// the input x[n+1] is given by 'in', and x[n] by zi
+	// input with half delay
+	float ih = 0.5f * (in + f->zi);
+
+	// evaluate the non-linear factors
+	float t0 = f->f*tanhXdX((ih - f->r * f->s3)*f->g0inv)*f->g0inv;
+	float t1 = f->f*tanhXdX((f->s1-f->s0)*f->g1inv)*f->g1inv;
+	float t2 = f->f*tanhXdX((f->s2-f->s1)*f->g1inv)*f->g1inv;
+	float t3 = f->f*tanhXdX((f->s3-f->s2)*f->g1inv)*f->g1inv;
+	float t4 = f->f*tanhXdX((f->s3)*f->g2inv)*f->g2inv;
+
+
+
+	// This formula gives the result for y3 thanks to MATLAB
+	float y3 = (f->s2 + f->s3 + t2*(f->s1 + f->s2 + f->s3 + t1*(f->s0 + f->s1 + f->s2 + f->s3 + t0*in)) + t1*(2.0f*f->s2 + 2.0f*f->s3))*t3 + f->s3 + 2.0f*f->s3*t1 + t2*(2.0f*f->s3 + 3.0f*f->s3*t1);
+	if (isnan(y3))
+	{
+		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
+	}
+	float tempy3denom = (t4 + t1*(2.0f*t4 + 4.0f) + t2*(t4 + t1*(t4 + f->r*t0 + 4.0f) + 3.0f) + 2.0f)*t3 + t4 + t1*(2.0f*t4 + 2.0f) + t2*(2.0f*t4 + t1*(3.0f*t4 + 3.0f) + 2.0f) + 1.0f;
+	if (isnan(tempy3denom))
+	{
+		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
+	}
+	if (tempy3denom == 0.0f)
+	{
+		tempy3denom = 0.000001f;
+	}
+	y3 = y3 / tempy3denom;
+	if (isnan(y3))
+	{
+		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
+	}
+	if (t1 == 0.0f)
+	{
+		t1 = 0.000001f;
+	}
+	if (t2 == 0.0f)
+	{
+		t2 = 0.000001f;
+	}
+	if (t3 == 0.0f)
+	{
+		t3 = 0.000001f;
+	}
+	// Other outputs
+	float y2 = (f->s3 - (1+t4+t3)*y3) / (-t3);
+	float y1 = (f->s2 - (1+t3+t2)*y2 + t3*y3) / (-t2);
+	float y0 = (f->s1 - (1+t2+t1)*y1 + t2*y2) / (-t1);
+	float xx = (in - f->r*y3);
+
+	// update state
+	f->s0 += 2.0f * (t0*xx + t1*(y1-y0));
+	if (isnan(f->s0))
+	{
+		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
+	}
+
+	if (isinf(f->s0))
+	{
+		__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, 400);
+	}
+	f->s1 += 2.0f * (t2*(y2-y1) - t1*(y1-y0));
+	f->s2 += 2.0f * (t3*(y3-y2) - t2*(y2-y1));
+	f->s3 += 2.0f * (-t4*(y3) - t3*(y3-y2));
+
+	f->zi = in;
+	return y3*f->r;
+
+}
+
+
+
+void    tDiodeFilter_setFreq     (tDiodeFilter* const vf, float cutoff)
+{
+	 _tDiodeFilter* f = *vf;
+	 f->f = tanf(PI * LEAF_clip(10.0f, cutoff, 20000.0f)*leaf.invSampleRate);
+}
+
+
+void    tDiodeFilter_setQ     (tDiodeFilter* const vf, float resonance)
+{
+	 _tDiodeFilter* f = *vf;
+	 f->r = LEAF_clip(0.5, (7.f * resonance + 0.5f), 8.0f);
+
+}
+
--- a/LEAF/Src/leaf-math.c
+++ b/LEAF/Src/leaf-math.c
@@ -90,6 +90,13 @@
     return alias.f;
 }
 
+double fastexp(double x) {
+  x = 1.0 + (x * 0.0009765625);
+  x *= x; x *= x; x *= x; x *= x;
+  x *= x; x *= x; x *= x; x *= x;
+  x *= x; x *= x;
+  return x;
+}
 
 float fastexpf(float x) {
   x = 1.0f + (x * 0.0009765625f);
@@ -99,8 +106,15 @@
   return x;
 }
 
+double fasterexp(double x) {
+  x = 1.0 + (x * 0.00390625);
+  x *= x; x *= x; x *= x; x *= x;
+  x *= x; x *= x; x *= x; x *= x;
+  return x;
+}
+
 float fasterexpf(float x) {
-  x = 1.0 + (x * 0.00390625f);
+  x = 1.0f + (x * 0.00390625f);
   x *= x; x *= x; x *= x; x *= x;
   x *= x; x *= x; x *= x; x *= x;
   return x;