ref: 0659ec34571cfa3b83fc8c4a9719f75ff4126ec2
parent: cb6284e95107db310a8b4193f15fa6e45941a5d7
parent: 8733f5cc1504c08044c19f7d6c4f0dce6d39c349
author: mulshine <mulshine@princeton.edu>
date: Tue May 14 10:04:32 EDT 2019
Merge fixed.
--- a/LEAF/Inc/leaf-delay.h
+++ b/LEAF/Inc/leaf-delay.h
@@ -75,6 +75,8 @@
float tDelayL_tapOut (tDelayL* const, float tapDelay);
float tDelayL_addTo (tDelayL* const, float value, uint32_t tapDelay);
float tDelayL_tick (tDelayL* const, float sample);
+void tDelayL_tickIn (tDelayL* const d, float input);
+float tDelayL_tickOut (tDelayL* const d);
float tDelayL_getLastOut (tDelayL* const);
float tDelayL_getLastIn (tDelayL* const);
--- a/LEAF/Inc/leaf-globals.h
+++ b/LEAF/Inc/leaf-globals.h
@@ -49,15 +49,14 @@
#define MPOLY_NUM_MAX_VOICES 8
#define NUM_OSC 4
#define INV_NUM_OSC (1.0f / NUM_OSC)
-#define PS_FRAME_SIZE 1024 // SNAC_FRAME_SIZE in LEAFCore.h should match (or be smaller than?) this
+#define PS_FRAME_SIZE 1024 // SNAC_FRAME_SIZE in leaf-pitch.h should match (or be smaller than?) this
#define ENV_WINDOW_SIZE 1024
#define ENV_HOP_SIZE 256
-#define NUM_KNOBS 4
#define DELAY_LENGTH 16000 // The maximum delay length of all Delay/DelayL/DelayA components.
// Feel free to change to suit memory constraints or desired delay max length / functionality.
-#define TALKBOX_BUFFER_LENGTH 1600 // Every talkbox instance introduces 5 buffers of this size
+
union unholy_t { /* a union between a float and an integer */
--- a/LEAF/Inc/leaf-mempool.h
+++ b/LEAF/Inc/leaf-mempool.h
@@ -50,7 +50,9 @@
//==============================================================================
+
#define MPOOL_POOL_SIZE 500000
+
#define MPOOL_ALIGN_SIZE (8)
//#define size_t unsigned long
--- /dev/null
+++ b/LEAF/Inc/leaf-oversampler.h
@@ -1,0 +1,55 @@
+//
+// leaf-oversampler.h
+// LEAF
+//
+// Created by Matthew Wang and Joshua Becker on 2/28/19.
+// Copyright © 2019 Princeton University. All rights reserved.
+//
+//==============================================================================
+
+#ifndef LEAF_OVERSAMPLER_H_INCLUDED
+#define LEAF_OVERSAMPLER_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//==============================================================================
+
+#include "leaf-globals.h"
+#include "leaf-math.h"
+#include "leaf-filter.h"
+
+//==============================================================================
+typedef struct _tOversamplerFilter
+{
+ float cutoff, Q;
+ float ic1eq,ic2eq;
+ float g,k,a1,a2,a3;
+} tOversamplerFilter;
+
+typedef struct _tOversampler2x
+{
+ tOversamplerFilter filters[2];
+} tOversampler2x;
+
+void tOversampler2x_init(tOversampler2x* const);
+float tOversampler2x_tick(tOversampler2x* const, float input, float (*nonLinearTick)(float));
+
+typedef struct _tOversampler4x
+{
+ tOversamplerFilter filters[4];
+} tOversampler4x;
+
+void tOversampler4x_init(tOversampler4x* const);
+float tOversampler4x_tick(tOversampler4x* const, float input, float (*nonLinearTick)(float));
+
+//==============================================================================
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LEAF_OVERSAMPLER_H_INCLUDED
+
+//==============================================================================
--- a/LEAF/Inc/leaf-utilities.h
+++ b/LEAF/Inc/leaf-utilities.h
@@ -1,24 +1,28 @@
-/*==============================================================================
+/*
+ ==============================================================================
- leaf-utilities.h
+ LEAFUtilities.h
Created: 20 Jan 2017 12:02:17pm
Author: Michael R Mulshine
-==============================================================================*/
+ ==============================================================================
+*/
-#ifndef LEAF_UTILITIES_H_INCLUDED
-#define LEAF_UTILITIES_H_INCLUDED
-
-#ifdef __cplusplus
-extern "C" {
+#ifndef LEAFUTILITIES_H_INCLUDED
+#define LEAFUTILITIES_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
#endif
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
#include "leaf-globals.h"
#include "leaf-math.h"
+#include "leaf-filter.h"
+#include "leaf-delay.h"
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
// STACK implementation (fixed size)
#define STACK_SIZE 128
@@ -46,7 +50,7 @@
int tStack_next (tStack* const);
int tStack_get (tStack* const, int which);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
/* Ramp */
typedef struct _tRamp {
@@ -68,8 +72,149 @@
int tRamp_setDest (tRamp* const, float dest);
int tRamp_setVal (tRamp* const, float val);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+/* Exponential Smoother */
+typedef struct _tExpSmooth {
+ float factor, oneminusfactor;
+ float curr,dest;
+
+} tExpSmooth;
+
+void tExpSmooth_init (tExpSmooth* const, float val, float factor);
+void tExpSmooth_free (tExpSmooth* const);
+
+float tExpSmooth_tick (tExpSmooth* const);
+float tExpSmooth_sample (tExpSmooth* const);
+int tExpSmooth_setFactor (tExpSmooth* const, float factor);
+int tExpSmooth_setDest (tExpSmooth* const, float dest);
+int tExpSmooth_setVal (tExpSmooth* const, float val);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+/* PowerEnvelopeFollower */
+typedef struct _tPwrFollow {
+ float factor, oneminusfactor;
+ float curr;
+
+} tPwrFollow;
+
+void tPwrFollow_init (tPwrFollow* const, float factor);
+void tPwrFollow_free (tPwrFollow* const);
+float tPwrFollow_tick (tPwrFollow* const, float input);
+float tPwrFollow_sample (tPwrFollow* const);
+int tPwrFollow_setFactor (tPwrFollow* const, float factor);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+//Reed Table - borrowed from STK
+typedef struct _tReedTable {
+ float offset, slope;
+} tReedTable;
+
+void tReedTable_init (tReedTable* const, float offset, float slope);
+void tReedTable_free (tReedTable* const);
+float tReedTable_tick (tReedTable* const, float input);
+float tReedTable_tanh_tick (tReedTable* const p, float input); //tanh softclip version of reed table - replacing the hard clip in original stk code
+void tReedTable_setOffset (tReedTable* const, float offset);
+void tReedTable_setSlope (tReedTable* const, float slope);
+
+
+
+///
+/* Feedback leveller */
+// An auto VCA that you put into a feedback circuit to make it stay at the same level.
+// It can enforce level bidirectionally (amplifying and attenuating as needed) or
+// just attenutating. The former option allows for infinite sustain strings, for example, while
+// The latter option allows for decaying strings, which can never exceed
+// a specific level.
+
+typedef struct _tFBleveller {
+ float targetLevel; // target power level
+ float strength; // how strongly level difference affects the VCA
+ int mode; // 0 for upwards limiting only, 1 for biderctional limiting
+ float curr;
+ tPwrFollow pwrFlw; // internal power follower needed for level tracking
+
+} tFBleveller;
+
+void tFBleveller_init (tFBleveller* const, float targetLevel, float factor, float strength, int mode);
+void tFBleveller_free (tFBleveller* const);
+
+float tFBleveller_tick (tFBleveller* const, float input);
+float tFBleveller_sample (tFBleveller* const);
+int tFBleveller_setTargetLevel (tFBleveller* const, float TargetLevel);
+int tFBleveller_setFactor (tFBleveller* const, float factor);
+int tFBleveller_setMode (tFBleveller* const, int mode); // 0 for upwards limiting only, 1 for biderctional limiting
+int tFBleveller_setStrength (tFBleveller* const, float strength);
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+/* Simple Living String */
+typedef struct _tSimpleLivingString {
+ float freq, waveLengthInSamples; // the frequency of the string, determining delay length
+ float dampFreq; // frequency for the bridge LP filter, in Hz
+ float decay; // amplitude damping factor for the string (only active in mode 0)
+ float levMode;
+ float curr;
+ tDelayL delayLine;
+ tOnePole bridgeFilter;
+ tHighpass DCblocker;
+ tFBleveller fbLev;
+ tExpSmooth wlSmooth;
+} tSimpleLivingString;
+
+void tSimpleLivingString_init (tSimpleLivingString* const, float freq, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode);
+void tSimpleLivingString_free (tSimpleLivingString* const);
+float tSimpleLivingString_tick (tSimpleLivingString* const, float input);
+float tSimpleLivingString_sample (tSimpleLivingString* const);
+int tSimpleLivingString_setFreq (tSimpleLivingString* const, float freq);
+int tSimpleLivingString_setWaveLength (tSimpleLivingString* const, float waveLength); // in samples
+int tSimpleLivingString_setDampFreq (tSimpleLivingString* const, float dampFreq);
+int tSimpleLivingString_setDecay (tSimpleLivingString* const, float decay); // should be near 1.0
+int tSimpleLivingString_setTargetLev (tSimpleLivingString* const, float targetLev);
+int tSimpleLivingString_setLevSmoothFactor (tSimpleLivingString* const, float levSmoothFactor);
+int tSimpleLivingString_setLevStrength (tSimpleLivingString* const, float levStrength);
+int tSimpleLivingString_setLevMode (tSimpleLivingString* const, int levMode);
+
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+/* Living String */
+typedef struct _tLivingString {
+ float freq, waveLengthInSamples; // the frequency of the whole string, determining delay length
+ float pickPos; // the pick position, dividing the string in two, in ratio
+ float prepIndex; // the amount of pressure on the pickpoint of the string (near 0=soft obj, near 1=hard obj)
+ float dampFreq; // frequency for the bridge LP filter, in Hz
+ float decay; // amplitude damping factor for the string (only active in mode 0)
+ float levMode;
+ float curr;
+ tDelayL delLF,delUF,delUB,delLB; // delay for lower/upper/forward/backward part of the waveguide model
+ tOnePole bridgeFilter, nutFilter, prepFilterU, prepFilterL;
+ tHighpass DCblockerL, DCblockerU;
+ tFBleveller fbLevU, fbLevL;
+ tExpSmooth wlSmooth, ppSmooth;
+} tLivingString;
+
+void tLivingString_init (tLivingString* const, float freq, float pickPos, float prepIndex, float dampFreq, float decay,
+ float targetLev, float levSmoothFactor, float levStrength, int levMode);
+void tLivingString_free (tLivingString* const);
+float tLivingString_tick (tLivingString* const, float input);
+float tLivingString_sample (tLivingString* const);
+int tLivingString_setFreq (tLivingString* const, float freq);
+int tLivingString_setWaveLength (tLivingString* const, float waveLength); // in samples
+int tLivingString_setPickPos (tLivingString* const, float pickPos);
+int tLivingString_setPrepIndex (tLivingString* const, float prepIndex);
+int tLivingString_setDampFreq (tLivingString* const, float dampFreq);
+int tLivingString_setDecay (tLivingString* const, float decay); // should be near 1.0
+int tLivingString_setTargetLev (tLivingString* const, float targetLev);
+int tLivingString_setLevSmoothFactor (tLivingString* const, float levSmoothFactor);
+int tLivingString_setLevStrength (tLivingString* const, float levStrength);
+int tLivingString_setLevMode (tLivingString* const, int levMode);
+
+
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
/* Compressor */
typedef struct _tCompressor
{
@@ -86,7 +231,7 @@
void tCompressor_free (tCompressor* const);
float tCompressor_tick (tCompressor* const, float input);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
/* Attack-Decay envelope */
typedef struct _tEnvelope {
@@ -118,7 +263,7 @@
int tEnvelope_loop (tEnvelope* const, oBool loop);
int tEnvelope_on (tEnvelope* const, float velocity);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
/* ADSR */
typedef struct _tADSR
@@ -150,7 +295,7 @@
int tADSR_on (tADSR* const, float velocity);
int tADSR_off (tADSR* const);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
/* Envelope Follower */
typedef struct _tEnvelopeFollower
@@ -168,7 +313,7 @@
int tEnvelopeFollower_decayCoeff (tEnvelopeFollower* const, float decayCoeff);
int tEnvelopeFollower_attackThresh (tEnvelopeFollower* const, float attackThresh);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
/* tAtkDtk */
#define DEFBLOCKSIZE 1024
@@ -219,7 +364,7 @@
// find largest transient in input block, return index of attack
int tAtkDtk_detect (tAtkDtk* const, float *in);
-//==============================================================================
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
// ENV~ from PD, modified for LEAF
#define MAXOVERLAP 32
@@ -244,14 +389,12 @@
float tEnv_tick (tEnv* const);
void tEnv_processBlock (tEnv* const, float* in);
-//==============================================================================
-
-#ifdef __cplusplus
-}
+// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+#ifdef __cplusplus
+}
#endif
-#endif // LEAF_UTILITIES_H_INCLUDED
-
-//==============================================================================
+#endif // LEAFUTILITIES_H_INCLUDED
--- a/LEAF/Inc/leaf-vocoder.h
+++ b/LEAF/Inc/leaf-vocoder.h
@@ -22,7 +22,7 @@
/* tTalkbox */
#define NUM_TALKBOX_PARAM 4
-#define TALKBOX_BUFFER_LENGTH 1024 //1600
+#define TALKBOX_BUFFER_LENGTH 1024 // Every talkbox instance introduces 5 buffers of this size - was originally 1600
typedef struct _tTalkbox
{
--- a/LEAF/Src/leaf-delay.c
+++ b/LEAF/Src/leaf-delay.c
@@ -178,6 +178,30 @@
return d->lastOut;
}
+void tDelayL_tickIn (tDelayL* const d, float input)
+{
+ d->buff[d->inPoint] = input * d->gain;
+
+ // Increment input pointer modulo length.
+ if (++(d->inPoint) == d->maxDelay ) d->inPoint = 0;
+}
+
+float tDelayL_tickOut (tDelayL* const d)
+{
+ uint32_t idx = (uint32_t) d->outPoint;
+
+ d->lastOut = LEAF_interpolate_hermite (d->buff[((idx - 1) + d->maxDelay) % d->maxDelay],
+ d->buff[idx],
+ d->buff[(idx + 1) % d->maxDelay],
+ d->buff[(idx + 2) % d->maxDelay],
+ d->alpha);
+
+ // Increment output pointer modulo length
+ if ( (++d->outPoint) >= d->maxDelay ) d->outPoint = 0;
+
+ return d->lastOut;
+}
+
int tDelayL_setDelay (tDelayL* const d, float delay)
{
d->delay = LEAF_clip(0.0f, delay, d->maxDelay);
--- /dev/null
+++ b/LEAF/Src/leaf-oversampler.c
@@ -1,0 +1,124 @@
+//
+// leaf-oversampler.c
+// LEAF
+//
+// Created by Matthew Wang and Joshua Becker on 2/28/19.
+// Copyright © 2019 Princeton University. All rights reserved.
+//
+
+#if _WIN32 || _WIN64
+
+#include "..\Inc\leaf-oversampler.h"
+#else
+
+#include "../Inc/leaf-oversampler.h"
+
+#endif
+
+// Currently just using a double sample rate version of SVF from leaf-filter.c, may want to implement better filter for oversampling.
+// Quick testing seems to indicate that this filter sufficiently handles most aliasing.
+void tOversamplerFilter_init(tOversamplerFilter* const lpf, float freq, float Q, float sampleRateMultiplier)
+{
+ lpf->ic1eq = 0;
+ lpf->ic2eq = 0;
+
+ float a1,a2,a3,g,k;
+ float scaledInverseSampleRate = leaf.invSampleRate * (1 / sampleRateMultiplier);
+
+ g = tanf(PI * freq * scaledInverseSampleRate);
+ k = 1.0f/Q;
+ a1 = 1.0f/(1.0f+g*(g+k));
+ a2 = g*a1;
+ a3 = g*a2;
+
+ lpf->g = g;
+ lpf->k = k;
+ lpf->a1 = a1;
+ lpf->a2 = a2;
+ lpf->a3 = a3;
+}
+
+float tOversamplerFilter_tick(tOversamplerFilter* const lpf, float v0)
+{
+ float v1,v2,v3;
+ v3 = v0 - lpf->ic2eq;
+ v1 = (lpf->a1 * lpf->ic1eq) + (lpf->a2 * v3);
+ v2 = lpf->ic2eq + (lpf->a2 * lpf->ic1eq) + (lpf->a3 * v3);
+ lpf->ic1eq = (2.0f * v1) - lpf->ic1eq;
+ lpf->ic2eq = (2.0f * v2) - lpf->ic2eq;
+
+ return v2;
+}
+
+// 2X Oversampler
+void tOversampler2x_init(tOversampler2x* const os)
+{
+ tOversamplerFilter_init(&os->filters[0], leaf.sampleRate*0.5f, 0.1f, 2.f);
+ tOversamplerFilter_init(&os->filters[1], leaf.sampleRate*0.5f, 0.1f, 2.f);
+}
+
+float tOversampler2x_tick(tOversampler2x* const os, float input, float (*nonLinearTick)(float))
+{
+ float sample = input;
+ float oversample = 0.f;
+
+ sample = tOversamplerFilter_tick(&os->filters[0], sample);
+ oversample = tOversamplerFilter_tick(&os->filters[0], oversample);
+
+ sample = nonLinearTick(sample);
+ oversample = nonLinearTick(oversample);
+
+ sample = tOversamplerFilter_tick(&os->filters[1], sample);
+ oversample = tOversamplerFilter_tick(&os->filters[1], oversample);
+
+ return sample;
+}
+
+// 4X Oversampler
+void tOversampler4x_init(tOversampler4x* const os)
+{
+ tOversamplerFilter_init(&os->filters[0], leaf.sampleRate*0.25f, 0.1f, 2.f); //sample rate was *0.5 but I assume that was copypaste mistake (copied from 2X filter) - JS
+ tOversamplerFilter_init(&os->filters[1], leaf.sampleRate*0.25f, 0.1f, 4.f);
+ tOversamplerFilter_init(&os->filters[2], leaf.sampleRate*0.25f, 0.1f, 4.f);
+ tOversamplerFilter_init(&os->filters[3] , leaf.sampleRate*0.25f, 0.1f, 2.f);
+}
+
+float tOversampler4x_tick(tOversampler4x* const os, float input, float (*nonLinearTick)(float))
+{
+ float sample = input;
+ float oversample1 = 0.f;
+ float oversample2 = 0.f;
+ float oversample3 = 0.f;
+ // Phase 1:
+ // x = [sample, oversample2]
+ // lpf(x)
+ // Phase 2:
+ // x = [sample, oversample1, oversample2, oversample3]
+ // lpf(dist(lpf(x)))
+ // Phase 3:
+ // x = [sample, oversample2]
+ // lpf(x)
+
+ sample = tOversamplerFilter_tick(&os->filters[0], sample);
+ oversample2 = tOversamplerFilter_tick(&os->filters[0], oversample2);
+
+ sample = tOversamplerFilter_tick(&os->filters[1], sample);
+ oversample1 = tOversamplerFilter_tick(&os->filters[1], oversample1);
+ oversample2 = tOversamplerFilter_tick(&os->filters[1], oversample2);
+ oversample3 = tOversamplerFilter_tick(&os->filters[1], oversample3);
+
+ sample = nonLinearTick(sample);
+ oversample1 = nonLinearTick(oversample1);
+ oversample2 = nonLinearTick(oversample2);
+ oversample3 = nonLinearTick(oversample3);
+
+ sample = tOversamplerFilter_tick(&os->filters[2], sample);
+ oversample1 = tOversamplerFilter_tick(&os->filters[2], oversample1);
+ oversample2 = tOversamplerFilter_tick(&os->filters[2], oversample2);
+ oversample3 = tOversamplerFilter_tick(&os->filters[2], oversample3);
+
+ sample = tOversamplerFilter_tick(&os->filters[3], sample);
+ oversample2 = tOversamplerFilter_tick(&os->filters[3], oversample2);
+
+ return sample;
+}
--- a/LEAF/Src/leaf-utilities.c
+++ b/LEAF/Src/leaf-utilities.c
@@ -1,10 +1,12 @@
-/*==============================================================================
+/*
+ ==============================================================================
- leaf-utilities.c
+ LEAFUtilities.c
Created: 20 Jan 2017 12:02:17pm
Author: Michael R Mulshine
-==============================================================================*/
+ ==============================================================================
+*/
#if _WIN32 || _WIN64
@@ -29,12 +31,12 @@
{
if (f <= -1500.0f) return(0);
else if (f > 1499.0f) return(mtof(1499.0f));
- else return (8.17579891564f * exp(0.0577622650f * f));
+ else return (8.17579891564f * expf(0.0577622650f * f));
}
float ftom(float f)
{
- return (f > 0 ? 17.3123405046f * log(.12231220585f * f) : -1500.0f);
+ return (f > 0 ? 17.3123405046f * logf(.12231220585f * f) : -1500.0f);
}
float powtodb(float f)
@@ -42,7 +44,7 @@
if (f <= 0) return (0);
else
{
- float val = 100 + 10.f/LOGTEN * log(f);
+ float val = 100 + 10.f/LOGTEN * logf(f);
return (val < 0 ? 0 : val);
}
}
@@ -65,7 +67,7 @@
{
if (f > 870.0f)
f = 870.0f;
- return (exp((LOGTEN * 0.1f) * (f-100.0f)));
+ return (expf((LOGTEN * 0.1f) * (f-100.0f)));
}
}
@@ -78,7 +80,7 @@
if (f > 485.0f)
f = 485.0f;
}
- return (exp((LOGTEN * 0.05f) * (f-100.0f)));
+ return (expf((LOGTEN * 0.05f) * (f-100.0f)));
}
/* ---------------- env~ - simple envelope follower. ----------------- */
@@ -103,7 +105,7 @@
for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0;
for (i = 0; i < npoints; i++)
- x->buf[i] = (1.0f - cos((2 * PI * i) / npoints))/npoints;
+ x->buf[i] = (1.0f - cosf((2 * PI * i) / npoints))/npoints;
for (; i < npoints+INITVSTAKEN; i++) x->buf[i] = 0;
x->x_f = 0;
@@ -761,6 +763,8 @@
r->curr += r->inc;
if (((r->curr >= r->dest) && (r->inc > 0.0f)) || ((r->curr <= r->dest) && (r->inc < 0.0f))) r->inc = 0.0f;
+ // Palle: There is a slight risk that you overshoot here and stay on dest+inc, which with a large inc value could be a real problem
+ // I suggest you add: r->curr=r->dest in the true if case
return r->curr;
}
@@ -775,6 +779,417 @@
r->inv_sr_ms = 1.0f / (leaf.sampleRate * 0.001f);
r->inc = ((r->dest-r->curr)/r->time * r->inv_sr_ms)*((float)r->samples_per_tick);
}
+
+
+/* Exponential Smoother */
+void tExpSmooth_init(tExpSmooth* const smooth, 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
+ smooth->curr=val;
+ smooth->dest=val;
+ if (factor<0) factor=0;
+ if (factor>1) factor=1;
+ smooth->factor=factor;
+ smooth->oneminusfactor=1.0f-factor;
+}
+
+int tExpSmooth_setFactor(tExpSmooth* const smooth, 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
+ if (factor<0)
+ factor=0;
+ else
+ if (factor>1) factor=1;
+ smooth->factor=factor;
+ smooth->oneminusfactor=1.0f-factor;
+ return 0;
+}
+
+int tExpSmooth_setDest(tExpSmooth* const smooth, float dest)
+{
+ smooth->dest=dest;
+ return 0;
+}
+
+int tExpSmooth_setVal(tExpSmooth* const smooth, float val)
+{
+ smooth->curr=val;
+ return 0;
+}
+
+float tExpSmooth_tick(tExpSmooth* const smooth)
+{
+ smooth->curr = smooth->factor*smooth->dest+smooth->oneminusfactor*smooth->curr;
+ return smooth->curr;
+}
+
+float tExpSmooth_sample(tExpSmooth* const smooth)
+{
+ return smooth->curr;
+}
+
+
+/* Power Follower */
+
+void tPwrFollow_init(tPwrFollow* const p, float factor)
+{
+ p->curr=0.0f;
+ p->factor=factor;
+ p->oneminusfactor=1.0f-factor;
+}
+
+int tPwrFollow_setFactor(tPwrFollow* const p, float factor)
+{
+ if (factor<0) factor=0;
+ if (factor>1) factor=1;
+ p->factor=factor;
+ p->oneminusfactor=1.0f-factor;
+ return 0;
+}
+
+float tPwrFollow_tick(tPwrFollow* const p, float input)
+{
+ p->curr = p->factor*input*input+p->oneminusfactor*p->curr;
+ return p->curr;
+}
+
+float tPwrFollow_sample(tPwrFollow* const p)
+{
+ return p->curr;
+}
+
+/* Feedback Leveler */
+
+void tFBleveller_init(tFBleveller* const p, float targetLevel, float factor, float strength, int mode)
+{
+ p->curr=0.0f;
+ p->targetLevel=targetLevel;
+ tPwrFollow_init(&p->pwrFlw,factor);
+ p->mode=mode;
+ p->strength=strength;
+}
+
+int tFBleveller_setStrength(tFBleveller* const p, float strength)
+{ // strength is how strongly level diff is affecting the amp ratio
+ // try 0.125 for a start
+ p->strength=strength;
+ return 0;
+}
+
+int tFBleveller_setFactor(tFBleveller* const p, float factor)
+{
+ tPwrFollow_setFactor(&p->pwrFlw,factor);
+ return 0;
+}
+
+int tFBleveller_setMode(tFBleveller* const p, int mode)
+{ // 0 for decaying with upwards lev limiting, 1 for constrained absolute level (also downwards limiting)
+ p->mode=mode;
+ return 0;
+}
+
+float tFBleveller_tick(tFBleveller* const p, float input)
+{
+ float levdiff=(tPwrFollow_tick(&p->pwrFlw, input)-p->targetLevel);
+ if (p->mode==0 && levdiff<0) levdiff=0;
+ p->curr=input*(1-p->strength*levdiff);
+ return p->curr;
+}
+
+float tFBleveller_sample(tFBleveller* const p)
+{
+ return p->curr;
+}
+
+
+int tFBleveller_setTargetLevel (tFBleveller* const p, float TargetLevel)
+{
+ p->targetLevel=TargetLevel;
+}
+
+///Reed Table model
+//default values from STK are 0.6 offset and -0.8 slope
+
+void tReedTable_init (tReedTable* const p, float offset, float slope)
+{
+ p->offset = offset;
+ p->slope = slope;
+}
+
+void tReedTable_free (tReedTable* const p)
+{
+ ;
+}
+
+float tReedTable_tick (tReedTable* const p, float input)
+{
+ // The input is differential pressure across the reed.
+ float output = p->offset + (p->slope * input);
+
+ // If output is > 1, the reed has slammed shut and the
+ // reflection function value saturates at 1.0.
+ if ( output > 1.0f) output = 1.0f;
+
+ // This is nearly impossible in a physical system, but
+ // a reflection function value of -1.0 corresponds to
+ // an open end (and no discontinuity in bore profile).
+ if ( output < -1.0f) output = -1.0f;
+
+ return output;
+}
+
+float tReedTable_tanh_tick (tReedTable* const p, float input)
+{
+ // The input is differential pressure across the reed.
+ float output = p->offset + (p->slope * input);
+
+ // If output is > 1, the reed has slammed shut and the
+ // reflection function value saturates at 1.0.
+ return tanhf(output);
+}
+
+void tReedTable_setOffset (tReedTable* const p, float offset)
+{
+ p->offset = offset;
+}
+
+void tReedTable_setSlope (tReedTable* const p, float slope)
+{
+ p->slope = slope;
+}
+
+
+
+/* Simple Living String*/
+
+void tSimpleLivingString_init(tSimpleLivingString* const p, float freq, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode)
+{
+ p->curr=0.0f;
+ tExpSmooth_init(&p->wlSmooth, leaf.sampleRate/freq, 0.01); // smoother for string wavelength (not freq, to avoid expensive divisions)
+ tSimpleLivingString_setFreq(p, freq);
+ tDelayL_init(&p->delayLine,p->waveLengthInSamples, 2400);
+ tOnePole_init(&p->bridgeFilter, dampFreq);
+ tHighpass_init(&p->DCblocker,13);
+ p->decay=decay;
+ tFBleveller_init(&p->fbLev, targetLev, levSmoothFactor, levStrength, levMode);
+ p->levMode=levMode;
+}
+
+int tSimpleLivingString_setFreq(tSimpleLivingString* const p, float freq)
+{
+ if (freq<20) freq=20;
+ else if (freq>10000) freq=10000;
+ p->waveLengthInSamples = leaf.sampleRate/freq;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+ return 0;
+}
+
+int tSimpleLivingString_setWaveLength(tSimpleLivingString* const p, float waveLength)
+{
+ if (waveLength<4.8) waveLength=4.8;
+ else if (waveLength>2400) waveLength=2400;
+ p->waveLengthInSamples = waveLength;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+ return 0;
+}
+
+int tSimpleLivingString_setDampFreq(tSimpleLivingString* const p, float dampFreq)
+{
+ tOnePole_setFreq(&p->bridgeFilter, dampFreq);
+ return 0;
+}
+
+int tSimpleLivingString_setDecay(tSimpleLivingString* const p, float decay)
+{
+ p->decay=decay;
+ return 0;
+}
+
+int tSimpleLivingString_setTargetLev(tSimpleLivingString* const p, float targetLev)
+{
+ tFBleveller_setTargetLevel(&p->fbLev, targetLev);
+ return 0;
+}
+
+int tSimpleLivingString_setLevSmoothFactor(tSimpleLivingString* const p, float levSmoothFactor)
+{
+ tFBleveller_setFactor(&p->fbLev, levSmoothFactor);
+ return 0;
+}
+
+int tSimpleLivingString_setLevStrength(tSimpleLivingString* const p, float levStrength)
+{
+ tFBleveller_setStrength(&p->fbLev, levStrength);
+ return 0;
+}
+
+int tSimpleLivingString_setLevMode(tSimpleLivingString* const p, int levMode)
+{
+ tFBleveller_setMode(&p->fbLev, levMode);
+ p->levMode=levMode;
+ return 0;
+}
+
+float tSimpleLivingString_tick(tSimpleLivingString* const p, float input)
+{
+ float stringOut=tOnePole_tick(&p->bridgeFilter,tDelayL_tickOut(&p->delayLine));
+ float stringInput=tHighpass_tick(&p->DCblocker, tFBleveller_tick(&p->fbLev, (p->levMode==0?p->decay*stringOut:stringOut)+input));
+ tDelayL_tickIn(&p->delayLine, stringInput);
+ tDelayL_setDelay(&p->delayLine, tExpSmooth_tick(&p->wlSmooth));
+ p->curr = stringOut;
+ return p->curr;
+}
+
+float tSimpleLivingString_sample(tSimpleLivingString* const p)
+{
+ return p->curr;
+}
+
+/* Living String*/
+
+void tLivingString_init(tLivingString* const p, float freq, float pickPos, float prepIndex, float dampFreq, float decay, float targetLev, float levSmoothFactor, float levStrength, int levMode)
+{
+ p->curr=0.0f;
+ tExpSmooth_init(&p->wlSmooth, leaf.sampleRate/freq, 0.01); // smoother for string wavelength (not freq, to avoid expensive divisions)
+ tLivingString_setFreq(p, freq);
+ tExpSmooth_init(&p->ppSmooth, pickPos, 0.01); // smoother for pick position
+ tLivingString_setPickPos(p, pickPos);
+ p->prepIndex=prepIndex;
+ tDelayL_init(&p->delLF,p->waveLengthInSamples, 2400);
+ tDelayL_init(&p->delUF,p->waveLengthInSamples, 2400);
+ tDelayL_init(&p->delUB,p->waveLengthInSamples, 2400);
+ tDelayL_init(&p->delLB,p->waveLengthInSamples, 2400);
+ tOnePole_init(&p->bridgeFilter, dampFreq);
+ tOnePole_init(&p->nutFilter, dampFreq);
+ tOnePole_init(&p->prepFilterU, dampFreq);
+ tOnePole_init(&p->prepFilterL, dampFreq);
+ tHighpass_init(&p->DCblockerU,13);
+ tHighpass_init(&p->DCblockerL,13);
+ p->decay=decay;
+ tFBleveller_init(&p->fbLevU, targetLev, levSmoothFactor, levStrength, levMode);
+ tFBleveller_init(&p->fbLevL, targetLev, levSmoothFactor, levStrength, levMode);
+ p->levMode=levMode;
+}
+
+int tLivingString_setFreq(tLivingString* const p, float freq)
+{ // NOTE: It is faster to set wavelength in samples directly
+ if (freq<20) freq=20;
+ else if (freq>10000) freq=10000;
+ p->waveLengthInSamples = leaf.sampleRate/freq;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+ return 0;
+}
+
+int tLivingString_setWaveLength(tLivingString* const p, float waveLength)
+{
+ if (waveLength<4.8) waveLength=4.8;
+ else if (waveLength>2400) waveLength=2400;
+ p->waveLengthInSamples = waveLength;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+ return 0;
+}
+
+int tLivingString_setPickPos(tLivingString* const p, float pickPos)
+{ // between 0 and 1
+ if (pickPos<0.f) pickPos=0.f;
+ else if (pickPos>1.f) pickPos=1.f;
+ p->pickPos = pickPos;
+ tExpSmooth_setDest(&p->ppSmooth, p->pickPos);
+ return 0;
+}
+
+int tLivingString_setPrepIndex(tLivingString* const p, float prepIndex)
+{ // between 0 and 1
+ if (prepIndex<0.f) prepIndex=0.f;
+ else if (prepIndex>1.f) prepIndex=1.f;
+ p->prepIndex = prepIndex;
+ return 0;
+}
+
+int tLivingString_setDampFreq(tLivingString* const p, float dampFreq)
+{
+ tOnePole_setFreq(&p->bridgeFilter, dampFreq);
+ tOnePole_setFreq(&p->nutFilter, dampFreq);
+ tOnePole_setFreq(&p->prepFilterU, dampFreq);
+ tOnePole_setFreq(&p->prepFilterL, dampFreq);
+ return 0;
+}
+
+int tLivingString_setDecay(tLivingString* const p, float decay)
+{
+ p->decay=decay;
+ return 0;
+}
+
+int tLivingString_setTargetLev(tLivingString* const p, float targetLev)
+{
+ tFBleveller_setTargetLevel(&p->fbLevU, targetLev);
+ tFBleveller_setTargetLevel(&p->fbLevL, targetLev);
+ return 0;
+}
+
+int tLivingString_setLevSmoothFactor(tLivingString* const p, float levSmoothFactor)
+{
+ tFBleveller_setFactor(&p->fbLevU, levSmoothFactor);
+ tFBleveller_setFactor(&p->fbLevL, levSmoothFactor);
+ return 0;
+}
+
+int tLivingString_setLevStrength(tLivingString* const p, float levStrength)
+{
+ tFBleveller_setStrength(&p->fbLevU, levStrength);
+ tFBleveller_setStrength(&p->fbLevL, levStrength);
+ return 0;
+}
+
+int tLivingString_setLevMode(tLivingString* const p, int levMode)
+{
+ tFBleveller_setMode(&p->fbLevU, levMode);
+ tFBleveller_setMode(&p->fbLevL, levMode);
+ p->levMode=levMode;
+ return 0;
+}
+
+float tLivingString_tick(tLivingString* const p, float input)
+{
+ // from pickPos upwards=forwards
+ float fromLF=tDelayL_tickOut(&p->delLF);
+ float fromUF=tDelayL_tickOut(&p->delUF);
+ float fromUB=tDelayL_tickOut(&p->delUB);
+ float fromLB=tDelayL_tickOut(&p->delLB);
+ // into upper half of string, from nut, going backwards
+ float fromNut=-tFBleveller_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tOnePole_tick(&p->nutFilter, fromUF)));
+ tDelayL_tickIn(&p->delUB, fromNut);
+ // into lower half of string, from pickpoint, going backwards
+ float fromLowerPrep=-tOnePole_tick(&p->prepFilterL, fromLF);
+ float intoLower=p->prepIndex*fromLowerPrep+(1-p->prepIndex)*fromUB+input;
+ tDelayL_tickIn(&p->delLB, intoLower);
+ // into lower half of string, from bridge
+ float fromBridge=-tFBleveller_tick(&p->fbLevL, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerL, tOnePole_tick(&p->bridgeFilter, fromLB)));
+ tDelayL_tickIn(&p->delLF, fromBridge);
+ // into upper half of string, from pickpoint, going forwards/upwards
+ float fromUpperPrep=-tOnePole_tick(&p->prepFilterU, fromUB);
+ float intoUpper=p->prepIndex*fromUpperPrep+(1-p->prepIndex)*fromLF+input;
+ tDelayL_tickIn(&p->delUF, intoUpper);
+ // update all delay lengths
+ float pickP=tExpSmooth_tick(&p->ppSmooth);
+ float wLen=tExpSmooth_tick(&p->wlSmooth);
+ float lowLen=pickP*wLen;
+ float upLen=(1-pickP)*wLen;
+ tDelayL_setDelay(&p->delLF, lowLen);
+ tDelayL_setDelay(&p->delLB, lowLen);
+ tDelayL_setDelay(&p->delUF, upLen);
+ tDelayL_setDelay(&p->delUB, upLen);
+ p->curr = fromBridge;
+ return p->curr;
+}
+
+float tLivingString_sample(tLivingString* const p)
+{
+ return p->curr;
+}
+
+
+/* Stack */
+
// If stack contains note, returns index. Else returns -1;
int tStack_contains(tStack* const ns, uint16_t noteVal)
{
--- a/LEAF/Src/leaf-wavefolder.c
+++ b/LEAF/Src/leaf-wavefolder.c
@@ -16,7 +16,7 @@
#endif
-void tLockhartWavefolderInit(tLockhartWavefolder* const w)
+void tLockhartWavefolder_init(tLockhartWavefolder* const w)
{
w->Ln1 = 0.0;
w->Fn1 = 0.0;
@@ -54,7 +54,7 @@
return w;
}
-float tLockhartWavefolderTick(tLockhartWavefolder* const w, float samp)
+float tLockhartWavefolder_tick(tLockhartWavefolder* const w, float samp)
{
int l;
double u, Ln, Fn, xn;
--- a/LEAF/leaf.h
+++ b/LEAF/leaf.h
@@ -9,14 +9,14 @@
*/
#ifndef LEAF_H_INCLUDED
-#define LEAF_H_INCLUDED
-
-#define LEAF_DEBUG 0
-
-#if LEAF_DEBUG
-#include "../LEAF_JUCEPlugin/JuceLibraryCode/JuceHeader.h"
-#endif
+#define LEAF_H_INCLUDED
+#define LEAF_DEBUG 0
+
+#if LEAF_DEBUG
+#include "../LEAF_JUCEPlugin/JuceLibraryCode/JuceHeader.h"
+#endif
+
#if _WIN32 || _WIN64
#include ".\Inc\leaf-globals.h"
@@ -25,6 +25,7 @@
#include ".\Inc\leaf-delay.h"
#include ".\Inc\leaf-filter.h"
#include ".\Inc\leaf-oscillator.h"
+#include ".\Inc\leaf-oversampler.h"
#include ".\Inc\leaf-reverb.h"
#include ".\Inc\leaf-vocoder.h"
#include ".\Inc\leaf-808.h"
@@ -31,8 +32,8 @@
#include ".\Inc\leaf-string.h"
#include ".\Inc\leaf-pitch.h"
#include ".\Inc\leaf-formant.h"
-#include ".\Inc\leaf-midi.h"
-#include ".\Inc\leaf-sample.h"
+#include ".\Inc\leaf-midi.h"
+#include ".\Inc\leaf-sample.h"
#include ".\Inc\leaf-crusher.h"
#include ".\Inc\leaf-wavefolder.h"
#include ".\Inc\leaf-wavetables.h"
@@ -46,6 +47,7 @@
#include "./Inc/leaf-delay.h"
#include "./Inc/leaf-filter.h"
#include "./Inc/leaf-oscillator.h"
+#include "./Inc/leaf-oversampler.h"
#include "./Inc/leaf-reverb.h"
#include "./Inc/leaf-vocoder.h"
#include "./Inc/leaf-808.h"
@@ -52,25 +54,25 @@
#include "./Inc/leaf-string.h"
#include "./Inc/leaf-pitch.h"
#include "./Inc/leaf-formant.h"
-#include "./Inc/leaf-midi.h"
-#include "./Inc/leaf-sample.h"
+#include "./Inc/leaf-midi.h"
+#include "./Inc/leaf-sample.h"
#include "./Inc/leaf-crusher.h"
#include "./Inc/leaf-wavefolder.h"
#include "./Inc/leaf-wavetables.h"
#include "./Inc/leaf-crusher.h"
-#endif
+#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
void LEAF_init (float sampleRate, int blocksize, float(*randomNumberFunction)(void));
void LEAF_setSampleRate (float sampleRate);
-float LEAF_getSampleRate (void);
-
-#ifdef __cplusplus
-}
-#endif
+float LEAF_getSampleRate (void);
+
+#ifdef __cplusplus
+}
+#endif
#endif // LEAF_H_INCLUDED
--- a/LEAF_JUCEPlugin/Source/MyTest.cpp
+++ b/LEAF_JUCEPlugin/Source/MyTest.cpp
@@ -191,3 +191,4 @@
leaf_pool_dump();
}
+