ref: 30ed191b459ec3101ccd4fbc3fe5bf6ed97e5f8f
parent: c353a6bf6608f6c238562592ad210f62eae87ce6
author: Jeffrey Snyder <jeffsnyder@jeffreys-mbp.mynetworksettings.com>
date: Wed Aug 24 11:21:27 EDT 2022
keep up with the Joneses
--- a/leaf/Inc/leaf-math.h
+++ b/leaf/Inc/leaf-math.h
@@ -18,7 +18,6 @@
#include "stdint.h"
#include "stdlib.h"
#include "limits.h"
-
//==============================================================================
//==============================================================================
@@ -234,11 +233,7 @@
float median3f(float a, float b, float c);
void place_step_dd(float *buffer, int index, float phase, float w, float scale);
-
void place_slope_dd(float *buffer, int index, float phase, float w, float slope_delta);
-
- /*! @} */
-
//==============================================================================
#ifdef __cplusplus
--- a/leaf/Inc/leaf-oscillators.h
+++ b/leaf/Inc/leaf-oscillators.h
@@ -721,8 +721,6 @@
tMempool mempool;
float out;
- float amp;
- float last_amp;
float freq;
float waveform; // duty cycle, must be in [-1, 1]
float lastsyncin;
@@ -732,7 +730,6 @@
float _p, _w, _b, _x, _z;
int _j, _k;
float _f [FILLEN + STEP_DD_PULSE_LENGTH];
- bool _init;
float invSampleRate;
} _tMBPulse;
@@ -748,7 +745,7 @@
float tMBPulse_sync(tMBPulse* const osc, float sync);
void tMBPulse_setPhase(tMBPulse* const osc, float phase);
void tMBPulse_setSyncMode(tMBPulse* const osc, int hardOrSoft);
- void tMBPulse_setBufferOffset(tMBPulse* const osc, int offset);
+ void tMBPulse_setBufferOffset(tMBPulse* const osc, uint32_t offset);
void tMBPulse_setSampleRate (tMBPulse* const osc, float sr);
/*!
@@ -796,8 +793,6 @@
tMempool mempool;
float out;
- float amp;
- float last_amp;
float freq;
float waveform; // duty cycle, must be in [-1, 1]
float lastsyncin;
@@ -807,7 +802,6 @@
float _p, _w, _b, _z;
int _j, _k;
float _f [FILLEN + LONGEST_DD_PULSE_LENGTH];
- bool _init;
float invSampleRate;
} _tMBTriangle;
@@ -823,7 +817,7 @@
float tMBTriangle_sync(tMBTriangle* const osc, float sync);
void tMBTriangle_setPhase(tMBTriangle* const osc, float phase);
void tMBTriangle_setSyncMode(tMBTriangle* const osc, int hardOrSoft);
- void tMBTriangle_setBufferOffset(tMBTriangle* const osc, int offset);
+ void tMBTriangle_setBufferOffset(tMBTriangle* const osc, uint32_t offset);
void tMBTriangle_setSampleRate (tMBTriangle* const osc, float sr);
@@ -871,8 +865,6 @@
{
tMempool mempool;
float out;
- float amp;
- float last_amp;
float freq;
float lastsyncin;
float sync;
@@ -881,7 +873,6 @@
float _p, _w, _z;
int _j;
float _f [FILLEN + STEP_DD_PULSE_LENGTH];
- bool _init;
float invSampleRate;
} _tMBSaw;
@@ -896,11 +887,83 @@
float tMBSaw_sync(tMBSaw* const osc, float sync);
void tMBSaw_setPhase(tMBSaw* const osc, float phase);
void tMBSaw_setSyncMode(tMBSaw* const osc, int hardOrSoft);
- void tMBSaw_setBufferOffset(tMBSaw* const osc, int offset);
+ void tMBSaw_setBufferOffset(tMBSaw* const osc, uint32_t offset);
void tMBSaw_setSampleRate (tMBSaw* const osc, float sr);
//==============================================================================
+ /*!
+ @defgroup tmbsaw tMBSawPulse
+ @ingroup oscillators
+ @brief Saw wave mixed with Pulse wave oscillator with minBLEP anti-aliasing.
+ @{
+
+ @fn void tMBSaw_init(tMBSaw* const osc, LEAF* const leaf)
+ @brief Initialize a tMBSaw to the default mempool of a LEAF instance.
+ @param osc A pointer to the tMBSaw to initialize.
+
+ @fn void tMBSaw_initToPool(tMBSaw* const osc, tMempool* const mempool)
+ @brief Initialize a tMBSaw to a specified mempool.
+ @param osc A pointer to the tMBSaw to initialize.
+
+ @fn void tMBSaw_free(tMBSaw* const osc)
+ @brief Free a tMBSaw from its mempool.
+ @param osc A pointer to the tMBSaw to free.
+
+ @fn float tMBSaw_tick(tMBSaw* const osc)
+ @brief Tick the oscillator.
+ @param osc A pointer to the relevant tMBSaw.
+ @return The ticked sample.
+
+ @fn void tMBSaw_setFreq(tMBSaw* const osc, float f)
+ @brief Set the frequency of the oscillator.
+ @param osc A pointer to the relevant tMBSaw.
+ @param freq The new frequency.
+
+ @fn float tMBSaw_sync(tMBSaw* const osc, float sync)
+ @brief Sync this oscillator to another signal.
+ @param osc A pointer to the relevant tMBSaw.
+ @param sync A sample of the signal to sync to.
+ @return The passed in sample.
+
+ @fn void tMBSaw_setSyncMode(tMBSaw* const osc, int hardOrSoft)
+ @brief Set the sync behavior of the oscillator.
+ @param hardOrSoft 0 for hard sync, 1 for soft sync
+ 
+ @} */
+
+ typedef struct _tMBSawPulse
+ {
+ tMempool mempool;
+ float out;
+ float freq;
+ float lastsyncin;
+ float sync;
+ float syncdir;
+ int softsync;
+ float waveform;
+ float _p, _w, _b, _x, _z, _k;
+ int _j;
+ float _f [FILLEN + STEP_DD_PULSE_LENGTH];
+ float invSampleRate;
+ float shape;
+ } _tMBSawPulse;
+ typedef _tMBSawPulse* tMBSawPulse;
+
+ void tMBSawPulse_init(tMBSawPulse* const osc, LEAF* const leaf);
+ void tMBSawPulse_initToPool(tMBSawPulse* const osc, tMempool* const mempool);
+ void tMBSawPulse_free(tMBSawPulse* const osc);
+
+ float tMBSawPulse_tick(tMBSawPulse* const osc);
+ void tMBSawPulse_setFreq(tMBSawPulse* const osc, float f);
+ float tMBSawPulse_sync(tMBSawPulse* const osc, float sync);
+ void tMBSawPulse_setPhase(tMBSawPulse* const osc, float phase);
+ void tMBSawPulse_setShape(tMBSawPulse* const osc, float shape);
+ void tMBSawPulse_setSyncMode(tMBSawPulse* const osc, int hardOrSoft);
+ void tMBSawPulse_setBufferOffset(tMBSawPulse* const osc, uint32_t offset);
+ void tMBSawPulse_setSampleRate (tMBSawPulse* const osc, float sr);
+
+ //==============================================================================
/*!
@defgroup ttable tTable
@ingroup oscillators
--- a/leaf/Src/leaf-filters.c
+++ b/leaf/Src/leaf-filters.c
@@ -17,9 +17,12 @@
#include "../Inc/leaf-filters.h"
#include "../Inc/leaf-tables.h"
#include "../leaf.h"
-//#include "tim.h"
#endif
+#ifdef ARM_MATH_CM7
+#include "arm_math.h"
+#endif
+
// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ OnePole Filter ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ //
void tAllpass_init(tAllpass* const ft, float initDelay, uint32_t maxDelay, LEAF* const leaf)
{
@@ -1821,12 +1824,15 @@
}
return ((a + 105.0f)*a + 945.0f) / output;
}
+
volatile int errorCheckCheck = 0;
+//#define SAFE_FILTER
float tDiodeFilter_tick (tDiodeFilter* const vf, float in)
{
_tDiodeFilter* f = *vf;
-
+#ifdef SAFE_FILTER
int errorCheck = 0;
+#endif
// 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);
@@ -1840,24 +1846,30 @@
// 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);
+#ifdef SAFE_FILTER
if (isnan(y3))
{
errorCheck = 1;
}
+#endif
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;
+#ifdef SAFE_FILTER
if (isnan(tempy3denom))
{
errorCheck = 2;
}
+#endif
if (tempy3denom == 0.0f)
{
tempy3denom = 0.000001f;
}
y3 = y3 / tempy3denom;
+#ifdef SAFE_FILTER
if (isnan(y3))
{
errorCheck = 3;
}
+#endif
if (t1 == 0.0f)
{
t1 = 0.000001f;
@@ -1878,19 +1890,22 @@
// update state
f->s0 += 2.0f * (t0*xx + t1*(y1-y0));
+#ifdef SAFE_FILTER
if (isnan(f->s0))
{
errorCheck = 4;
}
-
+
if (isinf(f->s0))
{
errorCheck = 5;
}
+
if (errorCheck != 0)
{
- errorCheckCheck = 1;
+ errorCheckCheck = errorCheck;
}
+#endif
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));
@@ -1975,7 +1990,16 @@
float smoothABS ( float x, const float y) // y controls 'smoothness' usually between 0.002 -> 0.04
{
- return (sqrtf((x * x) + y)) - sqrtf(y);
+ //possible speedup with sqrt CMSIS-DSP approximation? seems to resolve to just a normal call to sqrt. Maybe the vector version since there are two square roots to determine? -JS
+// #ifdef ARM_MATH_CM7
+// float output1;
+// float output2;
+// arm_sqrt_f32((x * x) + y, &output1);
+// arm_sqrt_f32(y, &output2);
+// return output1 - output2;
+// #else
+ return (sqrtf((x * x) + y)) - sqrtf(y);
+// #endif
}
float smoothclip (float x, const float a, const float b) // assuming symmetrical clipping
--- a/leaf/Src/leaf-math.c
+++ b/leaf/Src/leaf-math.c
@@ -847,39 +847,39 @@
/// MINBLEPS
// https://github.com/MrBlueXav/Dekrispator_v2 blepvco.c
void place_step_dd(float *buffer, int index, float phase, float w, float scale)
-{
- float r;
- long i;
-
- r = MINBLEP_PHASES * phase / w;
- i = lrintf(r - 0.5f);
- r -= (float)i;
- i &= MINBLEP_PHASE_MASK; /* extreme modulation can cause i to be out-of-range */
-
- while (i < MINBLEP_PHASES * STEP_DD_PULSE_LENGTH) {
- buffer[index] += scale * (step_dd_table[i].value + r * step_dd_table[i].delta);
- i += MINBLEP_PHASES;
- index++;
- }
-}
-//----------------------------------------------------------------------------------------------------------
+ {
+ float r;
+ long i;
-void place_slope_dd(float *buffer, int index, float phase, float w, float slope_delta)
-{
- float r;
- long i;
-
- r = MINBLEP_PHASES * phase / w;
- i = lrintf(r - 0.5f);
- r -= (float)i;
- i &= MINBLEP_PHASE_MASK; /* extreme modulation can cause i to be out-of-range */
-
- slope_delta *= w;
-
- while (i < MINBLEP_PHASES * SLOPE_DD_PULSE_LENGTH) {
- buffer[index] += slope_delta * (slope_dd_table[i] + r * (slope_dd_table[i + 1] - slope_dd_table[i]));
- i += MINBLEP_PHASES;
- index++;
+ r = MINBLEP_PHASES * phase / w;
+ i = lrintf(r - 0.5f);
+ r -= (float)i;
+ i &= MINBLEP_PHASE_MASK; /* extreme modulation can cause i to be out-of-range */
+
+ while (i < MINBLEP_PHASES * STEP_DD_PULSE_LENGTH) {
+ buffer[index] += scale * (step_dd_table[i].value + r * step_dd_table[i].delta);
+ i += MINBLEP_PHASES;
+ index++;
+ }
}
-}
+void place_slope_dd(float *buffer, int index, float phase, float w, float slope_delta)
+ {
+ float r;
+ long i;
+
+ r = MINBLEP_PHASES * phase / w;
+ i = lrintf(r - 0.5f);
+ r -= (float)i;
+ i &= MINBLEP_PHASE_MASK; /* extreme modulation can cause i to be out-of-range */
+
+ slope_delta *= w;
+
+ while (i < MINBLEP_PHASES * SLOPE_DD_PULSE_LENGTH) {
+ buffer[index] += slope_delta * (slope_dd_table[i] + r * (slope_dd_table[i + 1] - slope_dd_table[i]));
+ i += MINBLEP_PHASES;
+ index++;
+ }
+ }
#endif // LEAF_INCLUDE_MINBLEP_TABLES
+ /*! @} */
+
--- a/leaf/Src/leaf-oscillators.c
+++ b/leaf/Src/leaf-oscillators.c
@@ -938,8 +938,7 @@
c->invSampleRate = leaf->invSampleRate;
- c->_init = true;
- c->amp = 1.0f;
+
c->freq = 440.f;
c->lastsyncin = 0.0f;
c->sync = 0.0f;
@@ -948,6 +947,12 @@
c->waveform = 0.0f;
c->_z = 0.0f;
c->_j = 0;
+ c->_p = 0.0f; /* phase [0, 1) */
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
+ c->_b = 0.5f * (1.0f + c->waveform); /* duty cycle (0, 1) */
+ c->_x = 0.5f; /* temporary output variable */
+ c->_k = 0.0f; /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
+
memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
}
@@ -962,11 +967,11 @@
_tMBPulse* c = *osc;
int j, k;
- float freq, sync;
- float a, b, p, w, x, z, sw;
+ float sync;
+ float b, p, w, x, z, sw;
sync = c->sync;
- freq = c->freq;
+
p = c->_p; /* phase [0, 1) */
w = c->_w; /* phase increment */
b = c->_b; /* duty cycle (0, 1) */
@@ -974,31 +979,8 @@
z = c->_z; /* low pass filter state */
j = c->_j; /* index into buffer _f */
k = c->_k; /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
- //
- if (c->_init) {
- p = 0.0f;
-
- w = freq * c->invSampleRate;
- b = 0.5f * (1.0f + c->waveform );
-
- /* for variable-width rectangular wave, we could do DC compensation with:
- * x = 1.0f - b;
- * but that doesn't work well with highly modulated hard sync. Instead,
- * we keep things in the range [-0.5f, 0.5f]. */
- x = 0.5f;
- /* if we valued alias-free startup over low startup time, we could do:
- * p -= w;
- * place_step_dd(_f, j, 0.0f, w, 0.5f); */
- k = 0;
- c->_init = false;
- }
- //
- // a = 0.2 + 0.8 * vco->_port [FILT];
- a = 0.5f; // when a = 1, LPfilter is disabled
-
- w = freq * c->invSampleRate;
- b = 0.5f * (1.0f + c->waveform);
+
if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
sw = w * c->syncdir;
@@ -1163,8 +1145,8 @@
}
c->_f[j + DD_SAMPLE_DELAY] += x;
- z += a * (c->_f[j] - z);
- c->out = c->amp * z;
+ z += 0.5f * (c->_f[j] - z);
+ c->out = z;
if (++j == FILLEN)
{
@@ -1188,6 +1170,7 @@
{
_tMBPulse* c = *osc;
c->freq = f;
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
}
void tMBPulse_setWidth(tMBPulse* const osc, float w)
@@ -1219,10 +1202,10 @@
}
//useful if you have several oscillators so the buffer refill is not synchronized
-void tMBPulse_setBufferOffset(tMBPulse* const osc, int offset)
+void tMBPulse_setBufferOffset(tMBPulse* const osc, uint32_t offset)
{
_tMBPulse* c = *osc;
- offset = LEAF_clip(0,offset, FILLEN-1);
+ offset = offset & (FILLEN-1);
c->_j = offset;
}
@@ -1254,7 +1237,6 @@
LEAF* leaf = c->mempool->leaf;
c->invSampleRate = leaf->invSampleRate;
- c->amp = 1.0f;
c->freq = 440.f;
c->lastsyncin = 0.0f;
c->sync = 0.0f;
@@ -1261,9 +1243,12 @@
c->syncdir = 1.0f;
c->softsync = 0;
c->waveform = 0.0f;
- c->_init = true;
c->_z = 0.0f;
c->_j = 0;
+ c->_p = 0.0f; /* phase [0, 1) */
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
+ c->_b = 0.5f * (1.0f + c->waveform); /* duty cycle (0, 1) */
+ c->_k = 0.0f; /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
}
@@ -1277,41 +1262,27 @@
{
_tMBTriangle* c = *osc;
- int j, k, dir;
- float freq, sync;
- float a, b, b1, p, w, sw, x, z;
+ int j, k;
+ float sync;
+ float b, b1, invB, invB1, p, w, sw, z;
+ float x = 0.5f;
sync = c->sync;
- dir = c->syncdir;
- freq = c->freq;
+
+
p = c->_p; /* phase [0, 1) */
w = c->_w; /* phase increment */
b = c->_b; /* duty cycle (0, 1) */
+ invB = 1.0f / b;
z = c->_z; /* low pass filter state */
j = c->_j; /* index into buffer _f */
k = c->_k; /* output state, 0 = positive slope, 1 = negative slope */
- if (c->_init) {
- // w = (exp2ap (freq[1] + vco->_port[OCTN] + vco->_port[TUNE] + expm[1] * vco->_port[EXPG] + 8.03136)
- // + 1e3 * linm[1] * vco->_port[LING]) / SAMPLERATE;
- w = freq * c->invSampleRate;
- b = 0.5f * (1.0f + c->waveform);
-// p = 0.5f * b;
- p = 0.f;
- /* if we valued alias-free startup over low startup time, we could do:
- * p -= w;
- * place_slope_dd(_f, j, 0.0f, w, 1.0f / b); */
- k = 0;
- c->_init = false;
- }
- // a = 0.2 + 0.8 * vco->_port [FILT];
- a = 0.5f; // when a = 1, LPfilter is disabled
-
- w = freq * c->invSampleRate;
+
b = 0.5f * (1.0f + c->waveform);
b1 = 1.0f - b;
-
+ invB1 = 1.0f / b1;
if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
sw = w * c->syncdir;
@@ -1327,18 +1298,18 @@
/* place any DDs that may have occurred in subsample before reset */
if (!k) {
- x = -0.5f + p_at_reset / b;
+ x = -0.5f + p_at_reset * invB;
if (sw > 0)
{
if (p_at_reset >= b) {
- x = 0.5f - (p_at_reset - b) / b1;
- place_slope_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f / b1 - 1.0f / b);
+ x = 0.5f - (p_at_reset - b) * invB1;
+ place_slope_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -invB1 - invB);
k = 1;
}
if (p_at_reset >= 1.0f) {
p_at_reset -= 1.0f;
- x = -0.5f + p_at_reset / b;
- place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, 1.0f / b + 1.0f / b1);
+ x = -0.5f + p_at_reset * invB;
+ place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, invB + invB1);
k = 0;
}
}
@@ -1346,29 +1317,29 @@
{
if (p_at_reset < 0.0f) {
p_at_reset += 1.0f;
- x = 0.5f - (p_at_reset - b) / b1;
- place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, 1.0f / b + 1.0f / b1);
+ x = 0.5f - (p_at_reset - b) * invB1;
+ place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, invB + invB1);
k = 1;
}
if (k && p_at_reset < b) {
- x = -0.5f + p_at_reset / b;
- place_slope_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, -1.0f / b1 - 1.0f / b);
+ x = -0.5f + p_at_reset * invB;
+ place_slope_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, -invB1 - invB);
k = 0;
}
}
} else {
- x = 0.5f - (p_at_reset - b) / b1;
+ x = 0.5f - (p_at_reset - b) * invB1;
if (sw > 0)
{
if (p_at_reset >= 1.0f) {
p_at_reset -= 1.0f;
- x = -0.5f + p_at_reset / b;
- place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, 1.0f / b + 1.0f / b1);
+ x = -0.5f + p_at_reset * invB;
+ place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, invB + invB1);
k = 0;
}
if (!k && p_at_reset >= b) {
- x = 0.5f - (p_at_reset - b) / b1;
- place_slope_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f / b1 - 1.0f / b);
+ x = 0.5f - (p_at_reset - b) * invB1;
+ place_slope_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -invB1 - invB);
k = 1;
}
}
@@ -1375,14 +1346,14 @@
else if (sw < 0)
{
if (p_at_reset < b) {
- x = -0.5f + p_at_reset / b;
- place_slope_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, -1.0f / b1 - 1.0f / b);
+ x = -0.5f + p_at_reset * invB;
+ place_slope_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, -invB1 - invB);
k = 0;
}
if (p_at_reset < 0.0f) {
p_at_reset += 1.0f;
- x = 0.5f - (p_at_reset - b) / b1;
- place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, 1.0f / b + 1.0f / b1);
+ x = 0.5f - (p_at_reset - b) * invB1;
+ place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, invB + invB1);
k = 1;
}
}
@@ -1392,13 +1363,13 @@
if (sw > 0)
{
if (k)
- place_slope_dd(c->_f, j, p, sw, 1.0f / b + 1.0f / b1);
+ place_slope_dd(c->_f, j, p, sw, invB + invB1);
place_step_dd(c->_f, j, p, sw, -0.5f - x);
- x = -0.5f + p / b;
+ x = -0.5f + p * invB;
k = 0;
if (p >= b) {
- x = 0.5f - (p - b) / b1;
- place_slope_dd(c->_f, j, p - b, sw, -1.0f / b1 - 1.0f / b);
+ x = 0.5f - (p - b) * invB1;
+ place_slope_dd(c->_f, j, p - b, sw, -invB1 - invB);
k = 1;
}
}
@@ -1405,30 +1376,30 @@
else if (sw < 0)
{
if (!k)
- place_slope_dd(c->_f, j, 1.0f - p, -sw, 1.0f / b + 1.0f / b1);
+ place_slope_dd(c->_f, j, 1.0f - p, -sw, invB + invB1);
place_step_dd(c->_f, j, 1.0f - p, -sw, -0.5f - x);
- x = 0.5f - (p - b) / b1;
+ x = 0.5f - (p - b) * invB1;
k = 1;
if (p < b) {
- x = -0.5f + p / b;
- place_slope_dd(c->_f, j, b - p, -sw, -1.0f / b1 - 1.0f / b);
+ x = -0.5f + p * invB;
+ place_slope_dd(c->_f, j, b - p, -sw, -invB1 - invB);
k = 0;
}
}
} else if (!k) { /* normal operation, slope currently up */
- x = -0.5f + p / b;
+ x = -0.5f + p * invB;
if (sw > 0)
{
if (p >= b) {
- x = 0.5f - (p - b) / b1;
- place_slope_dd(c->_f, j, p - b, sw, -1.0f / b1 - 1.0f / b);
+ x = 0.5f - (p - b) * invB1;;
+ place_slope_dd(c->_f, j, p - b, sw, -invB1 - invB);
k = 1;
}
if (p >= 1.0f) {
p -= 1.0f;
- x = -0.5f + p / b;
- place_slope_dd(c->_f, j, p, sw, 1.0f / b + 1.0f / b1);
+ x = -0.5f + p * invB;
+ place_slope_dd(c->_f, j, p, sw, invB + invB1);
k = 0;
}
}
@@ -1436,13 +1407,13 @@
{
if (p < 0.0f) {
p += 1.0f;
- x = 0.5f - (p - b) / b1;
- place_slope_dd(c->_f, j, 1.0f - p, -sw, 1.0f / b + 1.0f / b1);
+ x = 0.5f - (p - b) * invB1;
+ place_slope_dd(c->_f, j, 1.0f - p, -sw, invB + invB1);
k = 1;
}
if (k && p < b) {
- x = -0.5f + p / b;
- place_slope_dd(c->_f, j, b - p, -sw, -1.0f / b1 - 1.0f / b);
+ x = -0.5f + p * invB;
+ place_slope_dd(c->_f, j, b - p, -sw, -invB1 - invB);
k = 0;
}
}
@@ -1449,18 +1420,18 @@
} else { /* normal operation, slope currently down */
- x = 0.5f - (p - b) / b1;
+ x = 0.5f - (p - b) * invB1;
if (sw > 0)
{
if (p >= 1.0f) {
p -= 1.0f;
- x = -0.5f + p / b;
- place_slope_dd(c->_f, j, p, sw, 1.0f / b + 1.0f / b1);
+ x = -0.5f + p * invB;
+ place_slope_dd(c->_f, j, p, sw, invB + invB1);
k = 0;
}
if (!k && p >= b) {
- x = 0.5f - (p - b) / b1;
- place_slope_dd(c->_f, j, p - b, sw, -1.0f / b1 - 1.0f / b);
+ x = 0.5f - (p - b) * invB1;
+ place_slope_dd(c->_f, j, p - b, sw, -invB1 - invB);
k = 1;
}
}
@@ -1467,14 +1438,14 @@
else if (sw < 0)
{
if (p < b) {
- x = -0.5f + p / b;
- place_slope_dd(c->_f, j, b - p, -sw, -1.0f / b1 - 1.0f / b);
+ x = -0.5f + p * invB;
+ place_slope_dd(c->_f, j, b - p, -sw, -invB1 - invB);
k = 0;
}
if (p < 0.0f) {
p += 1.0f;
- x = 0.5f - (p - b) / b1;
- place_slope_dd(c->_f, j, 1.0f - p, -sw, 1.0f / b + 1.0f / b1);
+ x = 0.5f - (p - b) * invB1;
+ place_slope_dd(c->_f, j, 1.0f - p, -sw, invB + invB1);
k = 1;
}
}
@@ -1481,8 +1452,8 @@
}
c->_f[j + DD_SAMPLE_DELAY] += x;
- z += a * (c->_f[j] - z);
- c->out = c->amp * z;
+ z += 0.5f * (c->_f[j] - z);
+ c->out = z;
if (++j == FILLEN)
{
@@ -1505,6 +1476,7 @@
{
_tMBTriangle* c = *osc;
c->freq = f;
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
}
void tMBTriangle_setWidth(tMBTriangle* const osc, float w)
@@ -1542,10 +1514,10 @@
}
//useful if you have several oscillators so the buffer refill is not synchronized
-void tMBTriangle_setBufferOffset(tMBTriangle* const osc, int offset)
+void tMBTriangle_setBufferOffset(tMBTriangle* const osc, uint32_t offset)
{
_tMBTriangle* c = *osc;
- offset = LEAF_clip(0,offset, FILLEN-1);
+ offset = offset & (FILLEN-1);
c->_j = offset;
}
@@ -1571,8 +1543,6 @@
LEAF* leaf = c->mempool->leaf;
c->invSampleRate = leaf->invSampleRate;
- c->_init = true;
- c->amp = 1.0f;
c->freq = 440.f;
c->lastsyncin = 0.0f;
c->sync = 0.0f;
@@ -1580,6 +1550,9 @@
c->softsync = 0;
c->_z = 0.0f;
c->_j = 0;
+ c->_p = 0.0f; /* phase [0, 1) */
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
+
memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
}
@@ -1594,45 +1567,30 @@
_tMBSaw* c = *osc;
int j;
- float freq, sync;
- float a, p, w, sw, z;
+ float sync;
+ float p, w, sw, z;
sync = c->sync;
- freq = c->freq;
+
p = c->_p; /* phase [0, 1) */
w = c->_w; /* phase increment */
z = c->_z; /* low pass filter state */
j = c->_j; /* index into buffer _f */
-
- if (c->_init) {
-// p = 0.5f;
- p = 0.f;
- w = freq * c->invSampleRate;
-
- /* if we valued alias-free startup over low startup time, we could do:
- * p -= w;
- * place_slope_dd(_f, j, 0.0f, w, -1.0f); */
- c->_init = false;
- }
-
- //a = 0.2 + 0.8 * vco->_port [FILT];
- a = 0.5f; // when a = 1, LPfilter is disabled
-
- w = freq * c->invSampleRate;
+
if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
// Should insert minblep for softsync?
- // if (p_at_reset >= 1.0f) {
- // p_at_reset -= (int)p_at_reset;
- // place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, 2.0f);
- // }
- // if (p_at_reset < 0.0f) {
- // p_at_reset += 1.0f - (int)p_at_reset;
- // place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, -2.0f);
- // }
- // if (sw > 0) place_slope_dd(c->_f, j, p, sw, 2.0f);
- // else if (sw < 0) place_slope_dd(c->_f, j, 1.0f - p, -sw, -2.0f);
+ // if (p_at_reset >= 1.0f) {
+ // p_at_reset -= (int)p_at_reset;
+ // place_slope_dd(c->_f, j, p_at_reset + eof_offset, sw, 2.0f);
+ // }
+ // if (p_at_reset < 0.0f) {
+ // p_at_reset += 1.0f - (int)p_at_reset;
+ // place_slope_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, -2.0f);
+ // }
+ // if (sw > 0) place_slope_dd(c->_f, j, p, sw, 2.0f);
+ // else if (sw < 0) place_slope_dd(c->_f, j, 1.0f - p, -sw, -2.0f);
sw = w * c->syncdir;
p += sw - (int)sw;
@@ -1670,8 +1628,8 @@
}
c->_f[j + DD_SAMPLE_DELAY] += 0.5f - p;
- z += a * (c->_f[j] - z); // LP filtering
- c->out = c->amp * z;
+ z += 0.5f * (c->_f[j] - z); // LP filtering
+ c->out = z;
if (++j == FILLEN)
{
@@ -1692,6 +1650,8 @@
{
_tMBSaw* c = *osc;
c->freq = f;
+
+ c->_w = c->freq * c->invSampleRate;
}
float tMBSaw_sync(tMBSaw* const osc, float value)
@@ -1723,10 +1683,10 @@
}
//useful if you have several oscillators so the buffer refill is not synchronized
-void tMBSaw_setBufferOffset(tMBSaw* const osc, int offset)
+void tMBSaw_setBufferOffset(tMBSaw* const osc, uint32_t offset)
{
_tMBSaw* c = *osc;
- offset = LEAF_clip(0,offset, FILLEN-1);
+ offset = offset & (FILLEN-1);
c->_j = offset;
}
@@ -1733,6 +1693,336 @@
void tMBSaw_setSampleRate(tMBSaw* const osc, float sr)
{
_tMBSaw* c = *osc;
+ c->invSampleRate = 1.0f/sr;
+}
+
+
+//==================================================================================================
+
+void tMBSawPulse_init(tMBSawPulse* const osc, LEAF* const leaf)
+{
+ tMBSawPulse_initToPool(osc, &leaf->mempool);
+}
+
+void tMBSawPulse_initToPool(tMBSawPulse* const osc, tMempool* const pool)
+{
+ _tMempool* m = *pool;
+ _tMBSawPulse* c = *osc = (_tMBSawPulse*) mpool_alloc(sizeof(_tMBSawPulse), m);
+ c->mempool = m;
+ LEAF* leaf = c->mempool->leaf;
+
+ c->invSampleRate = leaf->invSampleRate;
+ c->freq = 440.f;
+ c->lastsyncin = 0.0f;
+ c->sync = 0.0f;
+ c->syncdir = 1.0f;
+ c->softsync = 0;
+ c->waveform = 0.0f;
+ c->_z = 0.0f;
+ c->_j = 0;
+ c->_p = 0.0f; /* phase [0, 1) */
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
+ c->_b = 0.5f * (1.0f + c->waveform); /* duty cycle (0, 1) */
+ c->_x = 0.5f; /* temporary output variable */
+ c->_k = 0.0f; /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
+
+ memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
+}
+
+void tMBSawPulse_free(tMBSawPulse* const osc)
+{
+ _tMBSawPulse* c = *osc;
+ mpool_free((char*)c, c->mempool);
+}
+
+
+
+
+
+
+
+float tMBSawPulse_tick(tMBSawPulse* const osc)
+{
+ _tMBSawPulse* c = *osc;
+
+ int j, k;
+ float sync;
+ float b, p, w, x, z, sw;
+ float shape = c->shape;
+ float sawShape = 1.0f - c->shape;
+ sync = c->sync;
+ p = c->_p; /* phase [0, 1) */
+ w = c->_w; /* phase increment */
+ b = c->_b; /* duty cycle (0, 1) */
+ x = c->_x; /* temporary output variable */
+ z = c->_z; /* low pass filter state */
+ j = c->_j; /* index into buffer _f */
+ k = c->_k; /* output state, 0 = high (0.5f), 1 = low (-0.5f) */
+
+ if (sync > 0.0f && c->softsync > 0) c->syncdir = -c->syncdir;
+
+ sw = w * c->syncdir;
+ p += sw - (int)sw;
+
+ if (sync > 0.0f && c->softsync == 0)
+ { /* sync to master */
+ float eof_offset = sync * sw;
+ float p_at_reset = p - eof_offset;
+
+ if (sw > 0) p = eof_offset;
+ else if (sw < 0) p = 1.0f - eof_offset;
+
+ //pulse stuff
+ /* place any DDs that may have occurred in subsample before reset */
+ if (!k)
+ {
+ if (sw > 0)
+ {
+ if (p_at_reset >= b)
+ {
+ place_step_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f * shape);
+ k = 1;
+ x = -0.5f;
+ }
+ if (p_at_reset >= 1.0f)
+ {
+ p_at_reset -= 1.0f;
+ place_step_dd(c->_f, j, p_at_reset + eof_offset, sw, 1.0f);
+ k = 0;
+ x = 0.5f;
+ }
+ }
+ else if (sw < 0)
+ {
+ if (p_at_reset < 0.0f)
+ {
+ p_at_reset += 1.0f;
+ place_step_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, -1.0f);
+ k = 1;
+ x = -0.5f;
+ }
+ if (k && p_at_reset < b)
+ {
+ place_step_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, 1.0f * shape);
+ k = 0;
+ x = 0.5f;
+ }
+ }
+ }
+ else
+ {
+ if (sw > 0)
+ {
+ if (p_at_reset >= 1.0f)
+ {
+ p_at_reset -= 1.0f;
+ place_step_dd(c->_f, j, p_at_reset + eof_offset, sw, 1.0f);
+ k = 0;
+ x = 0.5f;
+ }
+ if (!k && p_at_reset >= b)
+ {
+ place_step_dd(c->_f, j, p_at_reset - b + eof_offset, sw, -1.0f * shape);
+ k = 1;
+ x = -0.5f;
+ }
+ }
+ else if (sw < 0)
+ {
+ if (p_at_reset < b)
+ {
+ place_step_dd(c->_f, j, b - p_at_reset - eof_offset, -sw, 1.0f * shape);
+ k = 0;
+ x = 0.5f;
+ }
+ if (p_at_reset < 0.0f)
+ {
+ p_at_reset += 1.0f;
+ place_step_dd(c->_f, j, 1.0f - p_at_reset - eof_offset, -sw, -1.0f);
+ k = 1;
+ x = -0.5f;
+ }
+ }
+ }
+
+
+ /* now place reset DD for pulse */
+ if (sw > 0)
+ {
+ if (k) {
+ place_step_dd(c->_f, j, p, sw, 1.0f * shape);
+ k = 0;
+ x = 0.5f;
+ }
+ if (p >= b) {
+ place_step_dd(c->_f, j, p - b, sw, -1.0f * shape);
+ k = 1;
+ x = -0.5f;
+ }
+ }
+ else if (sw < 0)
+ {
+ if (!k) {
+ place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f * shape);
+ k = 1;
+ x = -0.5f;
+ }
+ if (p < b) {
+ place_step_dd(c->_f, j, b - p, -sw, 1.0f * shape);
+ k = 0;
+ x = 0.5f;
+ }
+ }
+ /* now place reset DD for saw*/
+ if (sw > 0)
+ place_step_dd(c->_f, j, p, sw, p_at_reset * sawShape);
+ else if (sw < 0)
+ place_step_dd(c->_f, j, 1.0f - p, -sw, -p_at_reset * sawShape);
+
+ }
+
+
+ else if (!k)
+ { /* normal operation for pulse, signal currently high */
+
+ if (sw > 0)
+ {
+ if (p >= b) {
+ place_step_dd(c->_f, j, p - b, sw, -1.0f * shape);
+ k = 1;
+ x = -0.5f;
+ }
+ if (p >= 1.0f) {
+ p -= 1.0f;
+ place_step_dd(c->_f, j, p, sw, 1.0f);
+ k = 0;
+ x = 0.5f;
+ }
+ }
+ else if (sw < 0)
+ {
+ if (p < 0.0f) {
+ p += 1.0f;
+ place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+ k = 1;
+ x = -0.5f;
+ }
+ if (k && p < b) {
+ place_step_dd(c->_f, j, b - p, -sw, 1.0f * shape);
+ k = 0;
+ x = 0.5f;
+ }
+ }
+
+ } else { /* normal operation, signal currently low */
+
+ if (sw > 0)
+ {
+ if (p >= 1.0f) {
+ p -= 1.0f;
+ place_step_dd(c->_f, j, p, sw, 1.0f);
+ k = 0;
+ x = 0.5f;
+ }
+ if (!k && p >= b) {
+ place_step_dd(c->_f, j, p - b, sw, -1.0f * shape);
+ k = 1;
+ x = -0.5f;
+ }
+ }
+ else if (sw < 0)
+ {
+ if (p < b) {
+ place_step_dd(c->_f, j, b - p, -sw, 1.0f * shape);
+ k = 0;
+ x = 0.5f;
+ }
+ if (p < 0.0f) {
+ p += 1.0f;
+ place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
+ k = 1;
+ x = -0.5f;
+ }
+ }
+ }
+
+ c->_f[j + DD_SAMPLE_DELAY] += ((0.5f - p) * sawShape); //saw
+
+ c->_f[j + DD_SAMPLE_DELAY] += (x * shape);//pulse
+
+ z += 0.5f * (c->_f[j] - z); // LP filtering
+ c->out = z;
+
+ if (++j == FILLEN)
+ {
+ j = 0;
+ memcpy (c->_f, c->_f + FILLEN, STEP_DD_PULSE_LENGTH * sizeof (float));
+ memset (c->_f + STEP_DD_PULSE_LENGTH, 0, FILLEN * sizeof (float));
+ }
+
+ c->_p = p;
+ c->_w = w;
+ c->_b = b;
+ c->_x = x;
+ c->_z = z;
+ c->_j = j;
+ c->_k = k;
+
+ return -c->out;
+}
+
+void tMBSawPulse_setFreq(tMBSawPulse* const osc, float f)
+{
+ _tMBSawPulse* c = *osc;
+ c->freq = f;
+ c->_w = c->freq * c->invSampleRate; /* phase increment */
+}
+
+float tMBSawPulse_sync(tMBSawPulse* const osc, float value)
+{
+ _tMBSawPulse* c = *osc;
+
+ //based on https://github.com/VCVRack/Fundamental/blob/5799ee2a9b21492b42ebcb9b65d5395ef5c1cbe2/src/VCO.cpp#L123
+ float last = c->lastsyncin;
+ float delta = value - last;
+ float crossing = -last / delta;
+ c->lastsyncin = value;
+ if ((0.f < crossing) && (crossing <= 1.f) && (value >= 0.f))
+ c->sync = (1.f - crossing) * delta;
+ else c->sync = 0.f;
+
+ return value;
+}
+
+void tMBSawPulse_setPhase(tMBSawPulse* const osc, float phase)
+{
+ _tMBSawPulse* c = *osc;
+ c->_p = phase;
+}
+
+void tMBSawPulse_setShape(tMBSawPulse* const osc, float shape)
+{
+ _tMBSawPulse* c = *osc;
+ c->shape = shape;
+}
+
+void tMBSawPulse_setSyncMode(tMBSawPulse* const osc, int hardOrSoft)
+{
+ _tMBSawPulse* c = *osc;
+ c->softsync = hardOrSoft > 0 ? 1 : 0;
+}
+
+//useful if you have several oscillators so the buffer refill is not synchronized
+void tMBSawPulse_setBufferOffset(tMBSawPulse* const osc, uint32_t offset)
+{
+ _tMBSawPulse* c = *osc;
+ offset = offset & (FILLEN-1);
+ c->_j = offset;
+}
+
+void tMBSawPulse_setSampleRate(tMBSawPulse* const osc, float sr)
+{
+ _tMBSawPulse* c = *osc;
c->invSampleRate = 1.0f/sr;
}