ref: fe27383f5704ddc2d8ed4fad975fe937bca129c1
dir: /leaf/Src/leaf-oscillators.c/
/*==============================================================================
leaf-oscillators.c
Created: 20 Jan 2017 12:00:58pm
Author: Michael R Mulshine
==============================================================================*/
#if _WIN32 || _WIN64
#include "..\Inc\leaf-oscillators.h"
#include "..\leaf.h"
#else
#include "../Inc/leaf-oscillators.h"
#include "../leaf.h"
#endif
// WaveTable
void tTable_init(tTable* const cy, float* waveTable, int size, LEAF* const leaf)
{
tTable_initToPool(cy, waveTable, size, &leaf->mempool);
}
void tTable_initToPool(tTable* const cy, float* waveTable, int size, tMempool* const mp)
{
_tMempool* m = *mp;
_tTable* c = *cy = (_tTable*)mpool_alloc(sizeof(_tTable), m);
c->mempool = m;
c->waveTable = waveTable;
c->size = size;
c->inc = 0.0f;
c->phase = 0.0f;
}
void tTable_free(tTable* const cy)
{
_tTable* c = *cy;
mpool_free((char*)c, c->mempool);
}
void tTable_setFreq(tTable* const cy, float freq)
{
_tTable* c = *cy;
LEAF* leaf = c->mempool->leaf;
if (!isfinite(freq)) return;
c->freq = freq;
c->inc = freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
float tTable_tick(tTable* const cy)
{
_tTable* c = *cy;
float temp;
int intPart;
float fracPart;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = c->size * c->phase;
intPart = (int)temp;
fracPart = temp - (float)intPart;
samp0 = c->waveTable[intPart];
if (++intPart >= c->size) intPart = 0;
samp1 = c->waveTable[intPart];
return (samp0 + (samp1 - samp0) * fracPart);
}
void tTableSampleRateChanged(tTable* const cy)
{
_tTable* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
void tWavetable_init(tWavetable* const cy, const float* table, int size, float maxFreq, LEAF* const leaf)
{
tWavetable_initToPool(cy, table, size, maxFreq, &leaf->mempool);
}
void tWavetable_initToPool(tWavetable* const cy, const float* table, int size, float maxFreq, tMempool* const mp)
{
_tMempool* m = *mp;
_tWavetable* c = *cy = (_tWavetable*) mpool_alloc(sizeof(_tWavetable), m);
c->mempool = m;
// Determine base frequency
c->baseFreq = m->leaf->sampleRate / (float) size;
c->invBaseFreq = 1.0f / c->baseFreq;
// Determine how many tables we need
// Assume we need at least 2, the fundamental + one to account for setting extra anti aliasing
c->numTables = 2;
float f = c->baseFreq;
while (f < maxFreq)
{
c->numTables++;
f *= 2.0f; // pass this multiplier in to set spacing of tables? would need to change setFreq too
}
c->size = size;
// Allocate memory for the tables
c->tables = (float**) mpool_alloc(sizeof(float*) * c->numTables, m);
for (int t = 0; t < c->numTables; ++t)
{
c->tables[t] = (float*) mpool_alloc(sizeof(float) * c->size, m);
}
// Copy table
for (int i = 0; i < c->size; ++i)
{
c->tables[0][i] = table[i];
}
// Make bandlimited copies
f = m->leaf->sampleRate * 0.25; //start at half nyquist
// Not worth going over order 8 I think, and even 8 is only marginally better than 4.
tButterworth_initToPool(&c->bl, 8, -1.0f, f, mp);
for (int t = 1; t < c->numTables; ++t)
{
tButterworth_setF2(&c->bl, f);
// Do several passes here to prevent errors at the beginning of the waveform
// Not sure how many passes to do, seem to need more as the filter cutoff goes down
// 12 might be excessive but seems to work for now.
for (int p = 0; p < 12; ++p)
{
for (int i = 0; i < c->size; ++i)
{
c->tables[t][i] = tButterworth_tick(&c->bl, c->tables[t-1][i]);
}
}
f *= 0.5f; //halve the cutoff for next pass
}
tButterworth_free(&c->bl);
c->inc = 0.0f;
c->phase = 0.0f;
c->aa = 0.5f;
tWavetable_setFreq(cy, 220);
}
void tWavetable_free(tWavetable* const cy)
{
_tWavetable* c = *cy;
for (int t = 0; t < c->numTables; ++t)
{
mpool_free((char*)c->tables[t], c->mempool);
}
mpool_free((char*)c->tables, c->mempool);
mpool_free((char*)c, c->mempool);
}
float tWavetable_tick(tWavetable* const cy)
{
_tWavetable* c = *cy;
float temp;
int idx;
float frac;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = c->size * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = c->tables[c->oct][idx];
if (++idx >= c->size) idx = 0;
samp1 = c->tables[c->oct][idx];
float oct0 = (samp0 + (samp1 - samp0) * frac);
idx = (int)temp;
samp0 = c->tables[c->oct+1][idx];
if (++idx >= c->size) idx = 0;
samp1 = c->tables[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
return oct0 + (oct1 - oct0) * c->w;
}
void tWavetable_setFreq(tWavetable* const cy, float freq)
{
_tWavetable* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
// abs for negative frequencies
c->w = fabsf(c->freq * c->invBaseFreq);
// Probably ok to use a log2 approx here; won't effect tuning at all, just crossfading between octave tables
c->w = log2f_approx(c->w) + c->aa; // adding an offset here will shift our table selection upward, reducing aliasing but lower high freq fidelity. +1.0f should remove all aliasing
if (c->w < 0.0f) c->w = 0.0f; // If c->w is < 0.0f, then freq is less than our base freq
c->oct = (int)c->w;
c->w -= c->oct;
// When out of range of our tables, this will prevent a crash.
// Also causes a blip but fine since we're above maxFreq at this point.
if (c->oct >= c->numTables - 1) c->oct = c->numTables - 2;
}
void tWavetable_setAntiAliasing(tWavetable* const cy, float aa)
{
_tWavetable* c = *cy;
c->aa = aa;
tWavetable_setFreq(cy, c->freq);
}
void tCompactWavetable_init(tCompactWavetable* const cy, const float* table, int size, float maxFreq, LEAF* const leaf)
{
tCompactWavetable_initToPool(cy, table, size, maxFreq, &leaf->mempool);
}
void tCompactWavetable_initToPool(tCompactWavetable* const cy, const float* table, int size, float maxFreq, tMempool* const mp)
{
_tMempool* m = *mp;
_tCompactWavetable* c = *cy = (_tCompactWavetable*) mpool_alloc(sizeof(_tCompactWavetable), m);
c->mempool = m;
// Determine base frequency
c->baseFreq = m->leaf->sampleRate / (float) size;
c->invBaseFreq = 1.0f / c->baseFreq;
// Determine how many tables we need
c->numTables = 2;
float f = c->baseFreq;
while (f < maxFreq)
{
c->numTables++;
f *= 2.0f; // pass this multiplier in to set spacing of tables?
}
// Allocate memory for the tables
c->tables = (float**) mpool_alloc(sizeof(float*) * c->numTables, c->mempool);
c->sizes = (int*) mpool_alloc(sizeof(int) * c->numTables, c->mempool);
int n = size;
for (int t = 0; t < c->numTables; ++t)
{
c->sizes[t] = n;
c->tables[t] = (float*) mpool_alloc(sizeof(float) * c->sizes[t], m);
n = c->sizes[t] / 2;
}
// Copy table
for (int i = 0; i < c->sizes[0]; ++i)
{
c->tables[0][i] = table[i];
}
// Make bandlimited copies
// Not worth going over order 8 I think, and even 8 is only marginally better than 4.
tButterworth_initToPool(&c->bl, 8, -1.0f, m->leaf->sampleRate * 0.25f, mp);
tOversampler_initToPool(&c->ds, 2, 1, mp);
for (int t = 1; t < c->numTables; ++t)
{
// Similar to tWavetable, doing multiple passes here helps, but not sure what number is optimal
for (int p = 0; p < 12; ++p)
{
for (int i = 0; i < c->sizes[t]; ++i)
{
c->dsBuffer[0] = tButterworth_tick(&c->bl, c->tables[t-1][i*2]);
c->dsBuffer[1] = tButterworth_tick(&c->bl, c->tables[t-1][(i*2)+1]);
c->tables[t][i] = tOversampler_downsample(&c->ds, c->dsBuffer);
}
}
}
tOversampler_free(&c->ds);
tButterworth_free(&c->bl);
c->inc = 0.0f;
c->phase = 0.0f;
c->aa = 0.5f;
tCompactWavetable_setFreq(cy, 220);
}
void tCompactWavetable_free(tCompactWavetable* const cy)
{
_tCompactWavetable* c = *cy;
for (int t = 0; t < c->numTables; ++t)
{
mpool_free((char*)c->tables[t], c->mempool);
}
mpool_free((char*)c->tables, c->mempool);
mpool_free((char*)c->sizes, c->mempool);
mpool_free((char*)c, c->mempool);
}
float tCompactWavetable_tick(tCompactWavetable* const cy)
{
_tCompactWavetable* c = *cy;
float temp;
int idx;
float frac;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = c->sizes[c->oct] * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = c->tables[c->oct][idx];
if (++idx >= c->sizes[c->oct]) idx = 0;
samp1 = c->tables[c->oct][idx];
float oct0 = (samp0 + (samp1 - samp0) * frac);
temp = c->sizes[c->oct+1] * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = c->tables[c->oct+1][idx];
if (++idx >= c->sizes[c->oct+1]) idx = 0;
samp1 = c->tables[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
return oct0 + (oct1 - oct0) * c->w;
}
void tCompactWavetable_setFreq(tCompactWavetable* const cy, float freq)
{
_tCompactWavetable* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
// abs for negative frequencies
c->w = fabsf(c->freq * c->invBaseFreq);
// Probably ok to use a log2 approx here; won't effect tuning at all, just crossfading between octave tables
c->w = log2f_approx(c->w) + c->aa;//+ LEAF_SQRT2 - 1.0f; adding an offset here will shift our table selection upward, reducing aliasing but lower high freq fidelity. +1.0f should remove all aliasing
if (c->w < 0.0f) c->w = 0.0f; // If c->w is < 0.0f, then freq is less than our base freq
c->oct = (int)c->w;
c->w -= c->oct;
if (c->oct >= c->numTables - 1) c->oct = c->numTables - 2;
}
void tCompactWavetable_setAntiAliasing(tCompactWavetable* const cy, float aa)
{
_tCompactWavetable* c = *cy;
c->aa = aa;
tCompactWavetable_setFreq(cy, c->freq);
}
#if LEAF_INCLUDE_SINE_TABLE
// Cycle
void tCycle_init(tCycle* const cy, LEAF* const leaf)
{
tCycle_initToPool(cy, &leaf->mempool);
}
void tCycle_initToPool (tCycle* const cy, tMempool* const mp)
{
_tMempool* m = *mp;
_tCycle* c = *cy = (_tCycle*) mpool_alloc(sizeof(_tCycle), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
}
void tCycle_free (tCycle* const cy)
{
_tCycle* c = *cy;
mpool_free((char*)c, c->mempool);
}
void tCycle_setFreq(tCycle* const cy, float freq)
{
_tCycle* c = *cy;
LEAF* leaf = c->mempool->leaf;
if (!isfinite(freq)) return;
c->freq = freq;
c->inc = freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
//need to check bounds and wrap table properly to allow through-zero FM
float tCycle_tick(tCycle* const cy)
{
_tCycle* c = *cy;
float temp;
int idx;
float frac;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = SINE_TABLE_SIZE * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = __leaf_table_sinewave[idx];
if (++idx >= SINE_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_sinewave[idx];
return (samp0 + (samp1 - samp0) * frac);
}
void tCycleSampleRateChanged (tCycle* const cy)
{
_tCycle* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
#endif // LEAF_INCLUDE_SINE_TABLE
#if LEAF_INCLUDE_TRIANGLE_TABLE
//========================================================================
/* Triangle */
void tTriangle_init(tTriangle* const cy, LEAF* const leaf)
{
tTriangle_initToPool(cy, &leaf->mempool);
}
void tTriangle_initToPool (tTriangle* const cy, tMempool* const mp)
{
_tMempool* m = *mp;
_tTriangle* c = *cy = (_tTriangle*) mpool_alloc(sizeof(_tTriangle), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
tTriangle_setFreq(cy, 220);
}
void tTriangle_free (tTriangle* const cy)
{
_tTriangle* c = *cy;
mpool_free((char*)c, c->mempool);
}
void tTriangle_setFreq(tTriangle* const cy, float freq)
{
_tTriangle* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
// abs for negative frequencies
c->w = fabsf(c->freq * (TRI_TABLE_SIZE * leaf->invSampleRate));
c->w = log2f_approx(c->w);//+ LEAF_SQRT2 - 1.0f; adding an offset here will shift our table selection upward, reducing aliasing but lower high freq fidelity. +1.0f should remove all aliasing
if (c->w < 0.0f) c->w = 0.0f;
c->oct = (int)c->w;
c->w -= c->oct;
if (c->oct >= 10) c->oct = 9;
}
float tTriangle_tick(tTriangle* const cy)
{
_tTriangle* c = *cy;
float temp;
int idx;
float frac;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = TRI_TABLE_SIZE * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = __leaf_table_triangle[c->oct][idx];
if (++idx >= TRI_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_triangle[c->oct][idx];
float oct0 = (samp0 + (samp1 - samp0) * frac);
idx = (int)temp;
samp0 = __leaf_table_triangle[c->oct+1][idx];
if (++idx >= TRI_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_triangle[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
return oct0 + (oct1 - oct0) * c->w;
}
void tTriangleSampleRateChanged (tTriangle* const cy)
{
_tTriangle* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
#endif // LEAF_INCLUDE_TRIANGLE_TABLE
#if LEAF_INCLUDE_SQUARE_TABLE
//========================================================================
/* Square */
void tSquare_init(tSquare* const cy, LEAF* const leaf)
{
tSquare_initToPool(cy, &leaf->mempool);
}
void tSquare_initToPool (tSquare* const cy, tMempool* const mp)
{
_tMempool* m = *mp;
_tSquare* c = *cy = (_tSquare*) mpool_alloc(sizeof(_tSquare), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
tSquare_setFreq(cy, 220);
}
void tSquare_free (tSquare* const cy)
{
_tSquare* c = *cy;
mpool_free((char*)c, c->mempool);
}
void tSquare_setFreq(tSquare* const cy, float freq)
{
_tSquare* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
// abs for negative frequencies
c->w = fabsf(c->freq * (SQR_TABLE_SIZE * leaf->invSampleRate));
c->w = log2f_approx(c->w);//+ LEAF_SQRT2 - 1.0f; adding an offset here will shift our table selection upward, reducing aliasing but lower high freq fidelity. +1.0f should remove all aliasing
if (c->w < 0.0f) c->w = 0.0f;
c->oct = (int)c->w;
c->w -= c->oct;
if (c->oct >= 10) c->oct = 9;
}
float tSquare_tick(tSquare* const cy)
{
_tSquare* c = *cy;
float temp;
int idx;
float frac;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = SQR_TABLE_SIZE * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = __leaf_table_squarewave[c->oct][idx];
if (++idx >= SQR_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_squarewave[c->oct][idx];
float oct0 = (samp0 + (samp1 - samp0) * frac);
idx = (int)temp;
samp0 = __leaf_table_squarewave[c->oct+1][idx];
if (++idx >= SQR_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_squarewave[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
return oct0 + (oct1 - oct0) * c->w;
}
void tSquareSampleRateChanged (tSquare* const cy)
{
_tSquare* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
#endif // LEAF_INCLUDE_SQUARE_TABLE
#if LEAF_INCLUDE_SAWTOOTH_TABLE
//=====================================================================
// Sawtooth
void tSawtooth_init(tSawtooth* const cy, LEAF* const leaf)
{
tSawtooth_initToPool(cy, &leaf->mempool);
}
void tSawtooth_initToPool (tSawtooth* const cy, tMempool* const mp)
{
_tMempool* m = *mp;
_tSawtooth* c = *cy = (_tSawtooth*) mpool_alloc(sizeof(_tSawtooth), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
tSawtooth_setFreq(cy, 220);
}
void tSawtooth_free (tSawtooth* const cy)
{
_tSawtooth* c = *cy;
mpool_free((char*)c, c->mempool);
}
void tSawtooth_setFreq(tSawtooth* const cy, float freq)
{
_tSawtooth* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
// abs for negative frequencies
c->w = fabsf(c->freq * (SAW_TABLE_SIZE * leaf->invSampleRate));
c->w = log2f_approx(c->w);//+ LEAF_SQRT2 - 1.0f; adding an offset here will shift our table selection upward, reducing aliasing but lower high freq fidelity. +1.0f should remove all aliasing
if (c->w < 0.0f) c->w = 0.0f; // If c->w is < 0.0f, then freq is less than our base freq
c->oct = (int)c->w;
c->w -= c->oct;
if (c->oct >= 10) c->oct = 9;
}
float tSawtooth_tick(tSawtooth* const cy)
{
_tSawtooth* c = *cy;
float temp;
int idx;
float frac;
float samp0;
float samp1;
// Phasor increment
c->phase += c->inc;
while (c->phase >= 1.0f) c->phase -= 1.0f;
while (c->phase < 0.0f) c->phase += 1.0f;
// Wavetable synthesis
temp = SAW_TABLE_SIZE * c->phase;
idx = (int)temp;
frac = temp - (float)idx;
samp0 = __leaf_table_sawtooth[c->oct][idx];
if (++idx >= SAW_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_sawtooth[c->oct][idx];
float oct0 = (samp0 + (samp1 - samp0) * frac);
idx = (int)temp;
samp0 = __leaf_table_sawtooth[c->oct+1][idx];
if (++idx >= SAW_TABLE_SIZE) idx = 0;
samp1 = __leaf_table_sawtooth[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
return oct0 + (oct1 - oct0) * c->w;
}
void tSawtoothSampleRateChanged (tSawtooth* const cy)
{
_tSawtooth* c = *cy;
LEAF* leaf = c->mempool->leaf;
c->inc = c->freq * leaf->invSampleRate;
c->inc -= (int)c->inc;
}
#endif // LEAF_INCLUDE_SAWTOOTH_TABLE
//==============================================================================
/* tTri: Anti-aliased Triangle waveform. */
void tPBTriangle_init (tPBTriangle* const osc, LEAF* const leaf)
{
tPBTriangle_initToPool(osc, &leaf->mempool);
}
void tPBTriangle_initToPool (tPBTriangle* const osc, tMempool* const mp)
{
_tMempool* m = *mp;
_tPBTriangle* c = *osc = (_tPBTriangle*) mpool_alloc(sizeof(_tPBTriangle), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
c->skew = 0.5f;
c->lastOut = 0.0f;
}
void tPBTriangle_free (tPBTriangle* const cy)
{
_tPBTriangle* c = *cy;
mpool_free((char*)c, c->mempool);
}
float tPBTriangle_tick (tPBTriangle* const osc)
{
_tPBTriangle* c = *osc;
float out;
float skew;
if (c->phase < c->skew)
{
out = 1.0f;
skew = (1.0f - c->skew) * 2.0f;
}
else
{
out = -1.0f;
skew = c->skew * 2.0f;
}
out += LEAF_poly_blep(c->phase, c->inc);
out -= LEAF_poly_blep(fmodf(c->phase + (1.0f - c->skew), 1.0f), c->inc);
out = (skew * c->inc * out) + ((1 - c->inc) * c->lastOut);
c->lastOut = out;
c->phase += c->inc - (int)c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
return out;
}
void tPBTriangle_setFreq (tPBTriangle* const osc, float freq)
{
_tPBTriangle* c = *osc;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = freq * leaf->invSampleRate;
}
void tPBTriangle_setSkew (tPBTriangle* const osc, float skew)
{
_tPBTriangle* c = *osc;
c->skew = (skew + 1.0f) * 0.5f;
}
//==============================================================================
/* tPulse: Anti-aliased pulse waveform. */
void tPBPulse_init (tPBPulse* const osc, LEAF* const leaf)
{
tPBPulse_initToPool(osc, &leaf->mempool);
}
void tPBPulse_initToPool (tPBPulse* const osc, tMempool* const mp)
{
_tMempool* m = *mp;
_tPBPulse* c = *osc = (_tPBPulse*) mpool_alloc(sizeof(_tPBPulse), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
c->width = 0.5f;
}
void tPBPulse_free (tPBPulse* const osc)
{
_tPBPulse* c = *osc;
mpool_free((char*)c, c->mempool);
}
float tPBPulse_tick (tPBPulse* const osc)
{
_tPBPulse* c = *osc;
float out;
if (c->phase < c->width) out = 1.0f;
else out = -1.0f;
out += LEAF_poly_blep(c->phase, c->inc);
out -= LEAF_poly_blep(fmodf(c->phase + (1.0f - c->width), 1.0f), c->inc);
c->phase += c->inc - (int)c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
return out;
}
void tPBPulse_setFreq (tPBPulse* const osc, float freq)
{
_tPBPulse* c = *osc;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = freq * leaf->invSampleRate;
}
void tPBPulse_setWidth (tPBPulse* const osc, float width)
{
_tPBPulse* c = *osc;
c->width = width;
}
//==============================================================================
/* tSawtooth: Anti-aliased Sawtooth waveform. */
void tPBSaw_init (tPBSaw* const osc, LEAF* const leaf)
{
tPBSaw_initToPool(osc, &leaf->mempool);
}
void tPBSaw_initToPool (tPBSaw* const osc, tMempool* const mp)
{
_tMempool* m = *mp;
_tPBSaw* c = *osc = (_tPBSaw*) mpool_alloc(sizeof(_tPBSaw), m);
c->mempool = m;
c->inc = 0.0f;
c->phase = 0.0f;
}
void tPBSaw_free (tPBSaw* const osc)
{
_tPBSaw* c = *osc;
mpool_free((char*)c, c->mempool);
}
float tPBSaw_tick (tPBSaw* const osc)
{
_tPBSaw* c = *osc;
float out = (c->phase * 2.0f) - 1.0f;
out -= LEAF_poly_blep(c->phase, c->inc);
c->phase += c->inc - (int)c->inc;
if (c->phase >= 1.0f) c->phase -= 1.0f;
if (c->phase < 0.0f) c->phase += 1.0f;
return out;
}
void tPBSaw_setFreq (tPBSaw* const osc, float freq)
{
_tPBSaw* c = *osc;
LEAF* leaf = c->mempool->leaf;
c->freq = freq;
c->inc = freq * leaf->invSampleRate;
}
//========================================================================
/* Phasor */
void tPhasorSampleRateChanged (tPhasor* const ph)
{
_tPhasor* p = *ph;
LEAF* leaf = p->mempool->leaf;
p->inc = p->freq * leaf->invSampleRate;
};
void tPhasor_init(tPhasor* const ph, LEAF* const leaf)
{
tPhasor_initToPool(ph, &leaf->mempool);
}
void tPhasor_initToPool (tPhasor* const ph, tMempool* const mp)
{
_tMempool* m = *mp;
_tPhasor* p = *ph = (_tPhasor*) mpool_alloc(sizeof(_tPhasor), m);
p->mempool = m;
p->phase = 0.0f;
p->inc = 0.0f;
p->phaseDidReset = 0;
}
void tPhasor_free (tPhasor* const ph)
{
_tPhasor* p = *ph;
mpool_free((char*)p, p->mempool);
}
void tPhasor_setFreq(tPhasor* const ph, float freq)
{
_tPhasor* p = *ph;
LEAF* leaf = p->mempool->leaf;
p->freq = freq;
p->inc = freq * leaf->invSampleRate;
p->inc -= (int)p->inc;
}
float tPhasor_tick(tPhasor* const ph)
{
_tPhasor* p = *ph;
p->phase += p->inc;
p->phaseDidReset = 0;
if (p->phase >= 1.0f)
{
p->phaseDidReset = 1;
p->phase -= 1.0f;
}
else if (p->phase < 0.0f)
{
p->phaseDidReset = 1;
p->phase += 1.0f;
}
return p->phase;
}
/* Noise */
void tNoise_init(tNoise* const ns, NoiseType type, LEAF* const leaf)
{
tNoise_initToPool(ns, type, &leaf->mempool);
}
void tNoise_initToPool (tNoise* const ns, NoiseType type, tMempool* const mp)
{
_tMempool* m = *mp;
_tNoise* n = *ns = (_tNoise*) mpool_alloc(sizeof(_tNoise), m);
n->mempool = m;
LEAF* leaf = n->mempool->leaf;
n->type = type;
n->rand = leaf->random;
}
void tNoise_free (tNoise* const ns)
{
_tNoise* n = *ns;
mpool_free((char*)n, n->mempool);
}
float tNoise_tick(tNoise* const ns)
{
_tNoise* n = *ns;
float rand = (n->rand() * 2.0f) - 1.0f;
if (n->type == PinkNoise)
{
float tmp;
n->pinkb0 = 0.99765f * n->pinkb0 + rand * 0.0990460f;
n->pinkb1 = 0.96300f * n->pinkb1 + rand * 0.2965164f;
n->pinkb2 = 0.57000f * n->pinkb2 + rand * 1.0526913f;
tmp = n->pinkb0 + n->pinkb1 + n->pinkb2 + rand * 0.1848f;
return (tmp * 0.05f);
}
else // WhiteNoise
{
return rand;
}
}
//=================================================================================
/* Neuron */
void tNeuronSampleRateChanged(tNeuron* nr)
{
}
void tNeuron_init(tNeuron* const nr, LEAF* const leaf)
{
tNeuron_initToPool(nr, &leaf->mempool);
}
void tNeuron_initToPool (tNeuron* const nr, tMempool* const mp)
{
_tMempool* m = *mp;
_tNeuron* n = *nr = (_tNeuron*) mpool_alloc(sizeof(_tNeuron), m);
n->mempool = m;
tPoleZero_initToPool(&n->f, mp);
tPoleZero_setBlockZero(&n->f, 0.99f);
n->timeStep = 1.0f / 50.0f;
n->current = 0.0f; // 100.0f for sound
n->voltage = 0.0f;
n->mode = NeuronNormal;
n->P[0] = 0.0f;
n->P[1] = 0.0f;
n->P[2] = 1.0f;
n->V[0] = -12.0f;
n->V[1] = 115.0f;
n->V[2] = 10.613f;
n->gK = 36.0f;
n->gN = 120.0f;
n->gL = 0.3f;
n->C = 1.0f;
n->rate[2] = n->gL/n->C;
}
void tNeuron_free (tNeuron* const nr)
{
_tNeuron* n = *nr;
tPoleZero_free(&n->f);
mpool_free((char*)n, n->mempool);
}
void tNeuron_reset(tNeuron* const nr)
{
_tNeuron* n = *nr;
tPoleZero_setBlockZero(&n->f, 0.99f);
n->timeStep = 1.0f / 50.0f;
n->current = 0.0f; // 100.0f for sound
n->voltage = 0.0f;
n->mode = NeuronNormal;
n->P[0] = 0.0f;
n->P[1] = 0.0f;
n->P[2] = 1.0f;
n->V[0] = -12.0f;
n->V[1] = 115.0f;
n->V[2] = 10.613f;
n->gK = 36.0f;
n->gN = 120.0f;
n->gL = 0.3f;
n->C = 1.0f;
n->rate[2] = n->gL/n->C;
}
void tNeuron_setV1(tNeuron* const nr, float V1)
{
_tNeuron* n = *nr;
n->V[0] = V1;
}
void tNeuron_setV2(tNeuron* const nr, float V2)
{
_tNeuron* n = *nr;
n->V[1] = V2;
}
void tNeuron_setV3(tNeuron* const nr, float V3)
{
_tNeuron* n = *nr;
n->V[2] = V3;
}
void tNeuron_setTimeStep(tNeuron* const nr, float timeStep)
{
_tNeuron* n = *nr;
n->timeStep = timeStep;
}
void tNeuron_setK(tNeuron* const nr, float K)
{
_tNeuron* n = *nr;
n->gK = K;
}
void tNeuron_setL(tNeuron* const nr, float L)
{
_tNeuron* n = *nr;
n->gL = L;
n->rate[2] = n->gL/n->C;
}
void tNeuron_setN(tNeuron* const nr, float N)
{
_tNeuron* n = *nr;
n->gN = N;
}
void tNeuron_setC(tNeuron* const nr, float C)
{
_tNeuron* n = *nr;
n->C = C;
n->rate[2] = n->gL/n->C;
}
float tNeuron_tick(tNeuron* const nr)
{
_tNeuron* n = *nr;
float output = 0.0f;
float voltage = n->voltage;
n->alpha[0] = (0.01f * (10.0f - voltage)) / (expf((10.0f - voltage)/10.0f) - 1.0f);
n->alpha[1] = (0.1f * (25.0f-voltage)) / (expf((25.0f-voltage)/10.0f) - 1.0f);
n->alpha[2] = (0.07f * expf((-1.0f * voltage)/20.0f));
n->beta[0] = (0.125f * expf((-1.0f* voltage)/80.0f));
n->beta[1] = (4.0f * expf((-1.0f * voltage)/18.0f));
n->beta[2] = (1.0f / (expf((30.0f-voltage)/10.0f) + 1.0f));
for (int i = 0; i < 3; i++)
{
n->P[i] = (n->alpha[i] * n->timeStep) + ((1.0f - ((n->alpha[i] + n->beta[i]) * n->timeStep)) * n->P[i]);
if (n->P[i] > 1.0f) n->P[i] = 0.0f;
else if (n->P[i] < -1.0f) n->P[i] = 0.0f;
}
// rate[0]= k ; rate[1] = Na ; rate[2] = l
n->rate[0] = ((n->gK * powf(n->P[0], 4.0f)) / n->C);
n->rate[1] = ((n->gN * powf(n->P[1], 3.0f) * n->P[2]) / n->C);
//calculate the final membrane voltage based on the computed variables
n->voltage = voltage +
(n->timeStep * n->current / n->C) -
(n->timeStep * ( n->rate[0] * (voltage - n->V[0]) + n->rate[1] * (voltage - n->V[1]) + n->rate[2] * (voltage - n->V[2])));
if (n->mode == NeuronTanh)
{
n->voltage = 100.0f * tanhf(0.01f * n->voltage);
}
else if (n->mode == NeuronAaltoShaper)
{
float shapeVoltage = 0.01f * n->voltage;
float w, c, xc, xc2, xc4;
float sqrt8 = 2.82842712475f;
float wscale = 1.30612244898f;
float m_drive = 1.0f;
xc = LEAF_clip(-sqrt8, shapeVoltage, sqrt8);
xc2 = xc*xc;
c = 0.5f * shapeVoltage * (3.0f - (xc2));
xc4 = xc2 * xc2;
w = (1.0f - xc2 * 0.25f + xc4 * 0.015625f) * wscale;
shapeVoltage = w * (c + 0.05f * xc2) * (m_drive + 0.75f);
n->voltage = 100.0f * shapeVoltage;
}
if (n->voltage > 100.0f) n->voltage = 100.0f;
else if (n->voltage < -100.) n->voltage = -100.0f;
//(inputCurrent + (voltage - ((voltage * timeStep) / timeConstant)) + P[0] + P[1] + P[2]) => voltage;
// now we should have a result
//set the output voltage to the "step" ugen, which controls the DAC.
output = n->voltage * 0.01f; // volts
output = tPoleZero_tick(&n->f, output);
return output;
}
void tNeuron_setMode (tNeuron* const nr, NeuronMode mode)
{
_tNeuron* n = *nr;
n->mode = mode;
}
void tNeuron_setCurrent (tNeuron* const nr, float current)
{
_tNeuron* n = *nr;
n->current = current;
}
//----------------------------------------------------------------------------------------------------------
void tMBPulse_init(tMBPulse* const osc, LEAF* const leaf)
{
tMBPulse_initToPool(osc, &leaf->mempool);
}
void tMBPulse_initToPool(tMBPulse* const osc, tMempool* const pool)
{
_tMempool* m = *pool;
_tMBPulse* c = *osc = (_tMBPulse*) mpool_alloc(sizeof(_tMBPulse), m);
c->mempool = m;
c->_init = true;
c->amp = 1.0f;
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;
memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
}
void tMBPulse_free(tMBPulse* const osc)
{
_tMBPulse* c = *osc;
mpool_free((char*)c, c->mempool);
}
float tMBPulse_tick(tMBPulse* const osc)
{
_tMBPulse* c = *osc;
LEAF* leaf = c->mempool->leaf;
int j, k;
float freq, sync;
float a, 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) */
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 (c->_init) {
p = 0.0f;
w = freq * leaf->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 * leaf->invSampleRate;
b = 0.5f * (1.0f + c->waveform);
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;
/* 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);
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);
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);
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);
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 */
if (sw > 0)
{
if (k) {
place_step_dd(c->_f, j, p, sw, 1.0f);
k = 0;
x = 0.5f;
}
if (p >= b) {
place_step_dd(c->_f, j, p - b, sw, -1.0f);
k = 1;
x = -0.5f;
}
}
else if (sw < 0)
{
if (!k) {
place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
k = 1;
x = -0.5f;
}
if (p < b) {
place_step_dd(c->_f, j, b - p, -sw, 1.0f);
k = 0;
x = 0.5f;
}
}
} else if (!k) { /* normal operation, signal currently high */
if (sw > 0)
{
if (p >= b) {
place_step_dd(c->_f, j, p - b, sw, -1.0f);
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);
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);
k = 1;
x = -0.5f;
}
}
else if (sw < 0)
{
if (p < b) {
place_step_dd(c->_f, j, b - p, -sw, 1.0f);
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] += x;
z += a * (c->_f[j] - z);
c->out = c->amp * 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 tMBPulse_setFreq(tMBPulse* const osc, float f)
{
_tMBPulse* c = *osc;
c->freq = f;
}
void tMBPulse_setWidth(tMBPulse* const osc, float w)
{
_tMBPulse* c = *osc;
c->waveform = w;
}
float tMBPulse_sync(tMBPulse* const osc, float value)
{
_tMBPulse* 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 tMBPulse_setSyncMode(tMBPulse* const osc, int hardOrSoft)
{
_tMBPulse* c = *osc;
c->softsync = hardOrSoft > 0 ? 1 : 0;
}
//==========================================================================================================
//==========================================================================================================
void tMBTriangle_init(tMBTriangle* const osc, LEAF* const leaf)
{
tMBTriangle_initToPool(osc, &leaf->mempool);
}
void tMBTriangle_initToPool(tMBTriangle* const osc, tMempool* const pool)
{
_tMempool* m = *pool;
_tMBTriangle* c = *osc = (_tMBTriangle*) mpool_alloc(sizeof(_tMBTriangle), m);
c->mempool = m;
c->amp = 1.0f;
c->freq = 440.f;
c->lastsyncin = 0.0f;
c->sync = 0.0f;
c->syncdir = 1.0f;
c->softsync = 0;
c->waveform = 0.0f;
c->_init = true;
c->_z = 0.0f;
c->_j = 0;
memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
}
void tMBTriangle_free(tMBTriangle* const osc)
{
_tMBTriangle* c = *osc;
mpool_free((char*)c, c->mempool);
}
float tMBTriangle_tick(tMBTriangle* const osc)
{
_tMBTriangle* c = *osc;
LEAF* leaf = c->mempool->leaf;
int j, k, dir;
float freq, sync;
float a, b, b1, p, w, sw, x, z;
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) */
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 * leaf->invSampleRate;
b = 0.5f * (1.0f + c->waveform);
p = 0.5f * b;
/* 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 * leaf->invSampleRate;
b = 0.5f * (1.0f + c->waveform);
b1 = 1.0f - b;
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;
//
/* place any DDs that may have occurred in subsample before reset */
if (!k) {
x = -0.5f + p_at_reset / b;
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);
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);
k = 0;
}
}
else if (sw < 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);
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);
k = 0;
}
}
} else {
x = 0.5f - (p_at_reset - b) / b1;
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);
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);
k = 1;
}
}
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);
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);
k = 1;
}
}
}
/* now place reset DDs */
if (sw > 0)
{
if (k)
place_slope_dd(c->_f, j, p, sw, 1.0f / b + 1.0f / b1);
place_step_dd(c->_f, j, p, sw, -0.5f - x);
x = -0.5f + p / b;
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);
k = 1;
}
}
else if (sw < 0)
{
if (!k)
place_slope_dd(c->_f, j, 1.0f - p, -sw, 1.0f / b + 1.0f / b1);
place_step_dd(c->_f, j, 1.0f - p, -sw, -0.5f - x);
x = 0.5f - (p - b) / b1;
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);
k = 0;
}
}
} else if (!k) { /* normal operation, slope currently up */
x = -0.5f + p / b;
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);
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);
k = 0;
}
}
else if (sw < 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);
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);
k = 0;
}
}
} else { /* normal operation, slope currently down */
x = 0.5f - (p - b) / b1;
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);
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);
k = 1;
}
}
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);
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);
k = 1;
}
}
}
c->_f[j + DD_SAMPLE_DELAY] += x;
z += a * (c->_f[j] - z);
c->out = c->amp * 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->_z = z;
c->_j = j;
c->_k = k;
return c->out;
}
void tMBTriangle_setFreq(tMBTriangle* const osc, float f)
{
_tMBTriangle* c = *osc;
c->freq = f;
}
void tMBTriangle_setWidth(tMBTriangle* const osc, float w)
{
_tMBTriangle* c = *osc;
c->waveform = w;
}
float tMBTriangle_sync(tMBTriangle* const osc, float value)
{
_tMBTriangle* 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 tMBTriangle_setSyncMode(tMBTriangle* const osc, int hardOrSoft)
{
_tMBTriangle* c = *osc;
c->softsync = hardOrSoft > 0 ? 1 : 0;
}
//==========================================================================================================
//==========================================================================================================
void tMBSaw_init(tMBSaw* const osc, LEAF* const leaf)
{
tMBSaw_initToPool(osc, &leaf->mempool);
}
void tMBSaw_initToPool(tMBSaw* const osc, tMempool* const pool)
{
_tMempool* m = *pool;
_tMBSaw* c = *osc = (_tMBSaw*) mpool_alloc(sizeof(_tMBSaw), m);
c->mempool = m;
c->_init = true;
c->amp = 1.0f;
c->freq = 440.f;
c->lastsyncin = 0.0f;
c->sync = 0.0f;
c->syncdir = 1.0f;
c->softsync = 0;
c->_z = 0.0f;
c->_j = 0;
memset (c->_f, 0, (FILLEN + STEP_DD_PULSE_LENGTH) * sizeof (float));
}
void tMBSaw_free(tMBSaw* const osc)
{
_tMBSaw* c = *osc;
mpool_free((char*)c, c->mempool);
}
float tMBSaw_tick(tMBSaw* const osc)
{
_tMBSaw* c = *osc;
LEAF* leaf = c->mempool->leaf;
int j;
float freq, sync;
float a, 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;
w = freq * leaf->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 * leaf->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);
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;
/* place any DD that may have occurred in subsample before reset */
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);
}
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);
}
/* now place reset DD */
if (sw > 0)
place_step_dd(c->_f, j, p, sw, p_at_reset);
else if (sw < 0)
place_step_dd(c->_f, j, 1.0f - p, -sw, -p_at_reset);
} else if (p >= 1.0f) { /* normal phase reset */
p -= 1.0f;
place_step_dd(c->_f, j, p, sw, 1.0f);
} else if (p < 0.0f) {
p += 1.0f;
place_step_dd(c->_f, j, 1.0f - p, -sw, -1.0f);
}
c->_f[j + DD_SAMPLE_DELAY] += 0.5f - p;
z += a * (c->_f[j] - z); // LP filtering
c->out = c->amp * 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->_z = z;
c->_j = j;
return c->out;
}
void tMBSaw_setFreq(tMBSaw* const osc, float f)
{
_tMBSaw* c = *osc;
c->freq = f;
}
float tMBSaw_sync(tMBSaw* const osc, float value)
{
_tMBSaw* 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 tMBSaw_setSyncMode(tMBSaw* const osc, int hardOrSoft)
{
_tMBSaw* c = *osc;
c->softsync = hardOrSoft > 0 ? 1 : 0;
}