ref: c39e469c76573e0fd129e7f65c7286af82d7702b
parent: ab2f66165913c6ea18e105ce5da7431fb21a2fbd
author: spiricom <jeff@snyderphonics.com>
date: Tue May 25 15:19:11 EDT 2021
more efficient wavetable stuff
--- a/Examples/basic-oscillators.c
+++ b/Examples/basic-oscillators.c
@@ -14,7 +14,7 @@
{
LEAF leaf;
- LEAF_init(&leaf, 44100, 128, mempool, 1000, &exampleRandom);
+ LEAF_init(&leaf, 44100, mempool, 1000, &exampleRandom);
tCycle_init(&cycle, &leaf);
tCycle_setFreq(&cycle, 220);
--- a/leaf/Inc/leaf-oscillators.h
+++ b/leaf/Inc/leaf-oscillators.h
@@ -65,9 +65,11 @@
{
tMempool mempool;
// Underlying phasor
- float phase;
- float inc,freq;
- float invSampleRate;
+ uint32_t phase;
+ uint32_t inc;
+ float freq;
+ float invSampleRateTimesTwoTo32;
+ uint32_t mask;
} _tCycle;
typedef _tCycle* tCycle;
@@ -977,6 +979,7 @@
float* baseTable;
float** tables;
int size;
+ int sizeMask;
int numTables;
float maxFreq;
float baseFreq, invBaseFreq;
@@ -1086,34 +1089,47 @@
@} */
typedef struct _tWaveSynth
- {
- tMempool mempool;
-
- tWaveTable* tables;
- tWaveOsc** oscs;
- int numTables;
- int numVoices;
- float* g;
- float index;
- float maxFreq;
- } _tWaveSynth;
+ {
+ tMempool mempool;
+ tWaveTable* tables;
+ int numTables;
+ float index;
+ float maxFreq;
+ int o1;
+ int o2;
+ float mix;
+ uint32_t phase;
+ int32_t inc;
+ float freq;
+ float invSampleRateTimesTwoTo32;
+ int oct;
+ int size;
+
+ // Determine base frequency
+ float baseFreq;
+ float invBaseFreq;
+ float sampleRate;
+ float w;
+ float aa;
+ int numSubTables;
+
+ } _tWaveSynth;
typedef _tWaveSynth* tWaveSynth;
- void tWaveSynth_init(tWaveSynth* const osc, int numVoices, float** tables, int* sizes,
- int numTables, float maxFreq, LEAF* const leaf);
- void tWaveSynth_initToPool(tWaveSynth* const osc, int numVoices, float** tables, int* sizes,
- int numTables, float maxFreq, tMempool* const mempool);
+ void tWaveSynth_init(tWaveSynth* const cy, tWaveTable* tables, int size,
+ int numTables, float maxFreq, LEAF* const leaf);
+
+ void tWaveSynth_initToPool(tWaveSynth* const cy, tWaveTable* tables, int size,
+ int numTables, float maxFreq, tMempool* const mp);
void tWaveSynth_free(tWaveSynth* const osc);
float tWaveSynth_tick(tWaveSynth* const osc);
- float tWaveSynth_tickVoice(tWaveSynth* const cy, int voice);
- void tWaveSynth_setFreq(tWaveSynth* const osc, int voice, float freq);
+
+ void tWaveSynth_setFreq(tWaveSynth* const cy, float freq);
void tWaveSynth_setAntiAliasing(tWaveSynth* const osc, float aa);
void tWaveSynth_setIndex(tWaveSynth* const osc, float index);
- void tWaveSynth_setIndexGain(tWaveSynth* const osc, int i, float gain);
- void tWaveSynth_setIndexPhase(tWaveSynth* const osc, int i, float phase);
-// void tWaveSynth_setIndexTable(tWaveSynth* const osc, int i, float* table, int size);
+ void tWaveSynth_setTables(tWaveSynth* const cy, tWaveTable* tables, int numTables, int size);
void tWaveSynth_setSampleRate (tWaveSynth* const osc, float sr);
//==============================================================================
@@ -1154,6 +1170,7 @@
float** tables;
int numTables;
int* sizes;
+ int* sizeMasks;
float maxFreq;
float baseFreq, invBaseFreq;
tButterworth bl;
@@ -1233,7 +1250,59 @@
void tWaveOscS_setSampleRate (tWaveOscS* const osc, float sr);
//==============================================================================
+ /*!
+ @defgroup twaveoscs tWaveOscS
+ @ingroup oscillators
+ @brief A more space-efficient anti-aliased wavetable oscillator than tWaveOsc but with slightly worse fidelity.
+ @{
+
+ @fn void tWaveOscS_init (tWaveOscS* const osc, float* table, int size, float maxFreq, LEAF* const leaf)
+ @brief Initialize a tWaveOscS to the default mempool of a LEAF instance.
+ @param osc A pointer to the tWaveOscS to initialize.
+ @param table A pointer to the wavetable data.
+ @param size The number of samples in the wavetable.
+ @param maxFreq The maximum expected frequency of the oscillator. The higher this is, the more memory will be needed.
+ @param leaf A pointer to the leaf instance.
+
+ @fn void tWaveOscS_initToPool (tWaveOscS* const osc, float* table, int size, float maxFreq, tMempool* const mempool)
+ @brief Initialize a tWaveOscS to a specified mempool.
+ @param osc A pointer to the tWaveOscS to initialize.
+ @param table A pointer to the wavetable data.
+ @param size The number of samples in the wave table.
+ @param maxFreq The maximum expected frequency of the oscillator. The higher this is, the more memory will be needed.
+ @param mempool A pointer to the tMempool to use.
+
+ @fn void tWaveOscS_free (tWaveOscS* const osc)
+ @brief Free a tWaveOscS from its mempool.
+ @param osc A pointer to the tWaveOscS to free.
+
+ @fn float tWaveOscS_tick (tWaveOscS* const osc)
+ @brief Tick a tWaveOscS oscillator.
+ @param osc A pointer to the relevant tWaveOscS.
+ @return The ticked sample as a float from -1 to 1.
+
+ @fn void tWaveOscS_setFreq (tWaveOscS* const osc, float freq)
+ @brief Set the frequency of a tWaveOscS oscillator.
+ @param osc A pointer to the relevant tWaveOscS.
+ @param freq The frequency to set the oscillator to.
+
+ @} */
+ typedef struct _tWaveSubOscS
+ {
+ tMempool mempool;
+ tWaveTableS table;
+
+ } _tWaveSubOscS;
+
+ typedef _tWaveSubOscS* tWaveSubOscS;
+
+ void tWaveSubOscS_init(tWaveSubOscS* const osc, tWaveTableS* const table, LEAF* const leaf);
+ void tWaveSubOscS_initToPool(tWaveSubOscS* const osc, tWaveTableS* const table, tMempool* const mempool);
+ void tWaveSubOscS_free(tWaveSubOscS* const osc);
+ float tWaveSubOscS_tick(tWaveSubOscS* const cy, float phase, int oct, float w);
+
+ //==============================================================================
/*!
@defgroup twavesynths tWaveSynthS
@ingroup oscillators
@@ -1282,30 +1351,46 @@
{
tMempool mempool;
+ //tWaveTableS* tables;
+
tWaveTableS* tables;
- tWaveOscS** oscs;
+
+ tWaveSubOscS* oscs;
int numTables;
- int numVoices;
- float* g;
float index;
float maxFreq;
+ int o1;
+ int o2;
+ float mix;
+ uint32_t phase;
+ int32_t inc;
+ float freq;
+ float invSampleRateTimesTwoTo32;
+ int oct;
+ int size;
+
+ // Determine base frequency
+ float baseFreq;
+ float invBaseFreq;
+ float sampleRate;
+ float w;
+ float aa;
+ int numSubTables;
+
} _tWaveSynthS;
typedef _tWaveSynthS* tWaveSynthS;
- void tWaveSynthS_init(tWaveSynthS* const osc, int numVoices, float** tables, int* sizes,
- int numTables, float maxFreq, LEAF* const leaf);
- void tWaveSynthS_initToPool(tWaveSynthS* const osc, int numVoices, float** tables, int* sizes,
+ void tWaveSynthS_init(tWaveSynthS* const cy, tWaveTableS* tables, int size,
+ int numTables, float maxFreq, LEAF* const leaf);
+ void tWaveSynthS_initToPool(tWaveSynthS* const osc, tWaveTableS* tables, int size,
int numTables, float maxFreq, tMempool* const mempool);
void tWaveSynthS_free(tWaveSynthS* const osc);
float tWaveSynthS_tick(tWaveSynthS* const osc);
- float tWaveSynthS_tickVoice(tWaveSynthS* const cy, int voice);
- void tWaveSynthS_setFreq(tWaveSynthS* const osc, int voice, float freq);
+ void tWaveSynthS_setFreq(tWaveSynthS* const osc, float freq);
void tWaveSynthS_setAntiAliasing(tWaveSynthS* const osc, float aa);
void tWaveSynthS_setIndex(tWaveSynthS* const osc, float index);
- void tWaveSynthS_setIndexGain(tWaveSynthS* const osc, int i, float gain);
- void tWaveSynthS_setIndexPhase(tWaveSynthS* const osc, int i, float phase);
// void tWaveSynthS_setIndexTable(tWaveSynthS* const osc, int i, float* table, int size);
void tWaveSynthS_setSampleRate (tWaveSynthS* const osc, float sr);
--- a/leaf/Inc/leaf-physical.h
+++ b/leaf/Inc/leaf-physical.h
@@ -337,6 +337,49 @@
// ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
+
+ typedef struct _tSimpleLivingString2
+ {
+
+ tMempool mempool;
+ float freq, waveLengthInSamples; // the frequency of the string, determining delay length
+ float brightness; // frequency for the bridge LP filter, in Hz
+ float decay; // amplitude damping factor for the string (only active in mode 0)
+ int levMode;
+ float curr;
+ tHermiteDelay delayLine;
+ tTwoZero bridgeFilter;
+ tHighpass DCblocker;
+ tFeedbackLeveler fbLev;
+ tExpSmooth wlSmooth;
+ float sampleRate;
+ } _tSimpleLivingString2;
+
+ typedef _tSimpleLivingString2* tSimpleLivingString2;
+
+ void tSimpleLivingString2_init (tSimpleLivingString2* const, float freq, float brightness,
+ float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode, LEAF* const leaf);
+ void tSimpleLivingString2_initToPool (tSimpleLivingString2* const, float freq, float brightness,
+ float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode, tMempool* const);
+ void tSimpleLivingString2_free (tSimpleLivingString2* const);
+
+ float tSimpleLivingString2_tick (tSimpleLivingString2* const, float input);
+ float tSimpleLivingString2_sample (tSimpleLivingString2* const);
+ void tSimpleLivingString2_setFreq (tSimpleLivingString2* const, float freq);
+ void tSimpleLivingString2_setWaveLength (tSimpleLivingString2* const, float waveLength); // in samples
+ void tSimpleLivingString2_setBrightness (tSimpleLivingString2* const, float brightness);
+ void tSimpleLivingString2_setDecay (tSimpleLivingString2* const, float decay); // should be near 1.0
+ void tSimpleLivingString2_setTargetLev (tSimpleLivingString2* const, float targetLev);
+ void tSimpleLivingString2_setLevSmoothFactor (tSimpleLivingString2* const, float levSmoothFactor);
+ void tSimpleLivingString2_setLevStrength (tSimpleLivingString2* const, float levStrength);
+ void tSimpleLivingString2_setLevMode (tSimpleLivingString2* const, int levMode);
+ void tSimpleLivingString2_setSampleRate (tSimpleLivingString2* const, float sr);
+
+ // ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
+
/*!
@defgroup tlivingstring tLivingString
@ingroup physical
--- a/leaf/Src/leaf-oscillators.c
+++ b/leaf/Src/leaf-oscillators.c
@@ -30,9 +30,10 @@
c->mempool = m;
LEAF* leaf = c->mempool->leaf;
- c->inc = 0.0f;
- c->phase = 0.0f;
- c->invSampleRate = leaf->invSampleRate;
+ c->inc = 0;
+ c->phase = 0;
+ c->invSampleRateTimesTwoTo32 = (leaf->invSampleRate * TWO_TO_32);
+ c->mask = SINE_TABLE_SIZE - 1;
}
void tCycle_free (tCycle* const cy)
@@ -46,12 +47,11 @@
{
_tCycle* c = *cy;
- if (!isfinite(freq)) return;
+ //if (!isfinite(freq)) return;
c->freq = freq;
- c->inc = freq * c->invSampleRate;
- c->inc -= (int)c->inc;
+ c->inc = freq * c->invSampleRateTimesTwoTo32;
}
//need to check bounds and wrap table properly to allow through-zero FM
@@ -66,16 +66,14 @@
// 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;
+ temp = ((float)c->phase * 0.000000476837158f);
+ idx = ((int)temp) & c->mask;
frac = temp - (float)idx;
samp0 = __leaf_table_sinewave[idx];
- if (++idx >= SINE_TABLE_SIZE) idx = 0;
+ idx = (idx + 1) & c->mask;
samp1 = __leaf_table_sinewave[idx];
return (samp0 + (samp1 - samp0) * frac);
@@ -85,7 +83,7 @@
{
_tCycle* c = *cy;
- c->invSampleRate = 1.0f/sr;
+ c->invSampleRateTimesTwoTo32 = (1.0f/sr) * TWO_TO_32;
tCycle_setFreq(cy, c->freq);
}
#endif // LEAF_INCLUDE_SINE_TABLE
@@ -1785,7 +1783,7 @@
}
c->size = size;
-
+ c->sizeMask = size-1;
// Allocate memory for the tables
c->tables = (float**) mpool_alloc(sizeof(float*) * c->numTables, c->mempool);
c->baseTable = (float*) mpool_alloc(sizeof(float) * c->size, c->mempool);
@@ -1937,6 +1935,7 @@
float samp1;
int size = c->table->size;
+ int sizeMask = c->table->sizeMask;
float** tables = c->table->tables;
// Phasor increment
@@ -1950,6 +1949,7 @@
idx = (int)temp;
frac = temp - (float)idx;
samp0 = tables[c->oct][idx];
+ idx = (idx + 1) & sizeMask;
if (++idx >= size) idx = 0;
samp1 = tables[c->oct][idx];
@@ -1957,7 +1957,7 @@
idx = (int)temp;
samp0 = tables[c->oct+1][idx];
- if (++idx >= size) idx = 0;
+ idx = (idx + 1) & sizeMask;
samp1 = tables[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
@@ -2011,132 +2011,149 @@
//================================================================================================
//================================================================================================
-void tWaveSynth_init(tWaveSynth* const cy, int numVoices, float** tables, int* sizes,
- int numTables, float maxFreq, LEAF* const leaf)
+void tWaveSynth_init(tWaveSynth* const cy, tWaveTable* tables, int size,
+ int numTables, float maxFreq, LEAF* const leaf)
{
- tWaveSynth_initToPool(cy, numVoices, tables, sizes, numTables, maxFreq, &leaf->mempool);
+ tWaveSynth_initToPool(cy, tables, size, numTables, maxFreq, &leaf->mempool);
}
-void tWaveSynth_initToPool(tWaveSynth* const cy, int numVoices, float** tables, int* sizes,
- int numTables, float maxFreq, tMempool* const mp)
+void tWaveSynth_initToPool(tWaveSynth* const cy, tWaveTable* tables, int size,
+ int numTables, float maxFreq, tMempool* const mp)
{
_tMempool* m = *mp;
_tWaveSynth* c = *cy = (_tWaveSynth*) mpool_alloc(sizeof(_tWaveSynth), m);
+
c->mempool = m;
-
- c->numTables = 0;
- for (int t = 0; t < numTables; ++t)
- {
- if (sizes[t] > 0) c->numTables++;
- }
-
- c->tables = (tWaveTable*) mpool_alloc(sizeof(tWaveTable) * c->numTables, m);
- c->oscs = (tWaveOsc**) mpool_alloc(sizeof(tWaveOsc*) * c->numTables, m);
- c->numVoices = numVoices;
-
- int i = 0;
- for (int t = 0; t < numTables; ++t)
- {
- if (sizes[t] > 0)
- {
- tWaveTable_initToPool(&c->tables[i], tables[t], sizes[t], maxFreq, mp);
- c->oscs[i] = (tWaveOsc*) mpool_alloc(sizeof(tWaveOsc) * c->numVoices, m);
- for (int v = 0; v < c->numVoices; ++v) tWaveOsc_initToPool(&c->oscs[i][v], &c->tables[i], mp);
- i++;
- }
- }
-
- c->g = (float*) mpool_alloc(sizeof(float) * c->numTables, m);
- for (int i = 0; i < c->numTables; ++i) c->g[i] = 1.0f;
-
+ LEAF* leaf = c->mempool->leaf;
+ c->tables = tables;
+ c->numTables = numTables;
+
c->index = 0.0f;
+ c->o1 = 0;
+ c->o2 = 1;
+ c->mix = 0.0f;
+ c->phase = 0;
+ c->inc = 0;
+ c->oct = 0;
+ c->size = size;
+ c->w = 0.0f;
+ c->aa = 0.5f;
+ c->sampleRate = leaf->sampleRate;
+ // Determine base frequency
+ c->baseFreq = c->sampleRate / (float) size;
+ c->invBaseFreq = 1.0f / c->baseFreq;
+ c->numSubTables = c->tables[0]->numTables;
+
+ c->invSampleRateTimesTwoTo32 = leaf->invSampleRate * TWO_TO_32;
c->maxFreq = maxFreq;
}
-void tWaveSynth_free(tWaveSynth* const cy)
+void tWaveSynth_free(tWaveSynth* const cy)
{
_tWaveSynth* c = *cy;
-
- for (int i = 0; i < c->numTables; ++i)
- {
- tWaveTable_free(&c->tables[i]);
- for (int v = 0; v < c->numVoices; ++v) tWaveOsc_free(&c->oscs[i][v]);
- mpool_free((char*)c->oscs[i], c->mempool);
- }
- mpool_free((char*)c->g, c->mempool);
- mpool_free((char*)c->oscs, c->mempool);
- mpool_free((char*)c->tables, c->mempool);
mpool_free((char*)c, c->mempool);
}
-float tWaveSynth_tick(tWaveSynth* const cy)
-{
- _tWaveSynth* c = *cy;
-
- float f = c->index * (c->numTables - 1);
-
- int o1 = (int)f;
- int o2 = o1 + 1;
- if (c->index >= 1.0f) o2 = o1;
- float mix = f - o1;
-
- float s1 = 0.f, s2 = 0.f;
- for (int t = 0; t < c->numTables; ++t)
- {
- float s0 = 0.f;
- for (int v = 0; v < c->numVoices; ++v) s0 += tWaveOsc_tick(&c->oscs[t][v]);
- if (t == o1) s1 = s0 * c->g[t];
- if (t == o2) s2 = s0 * c->g[t];
- }
-
- // Ideally should determine correlation to get a good equal power fade between tables
- return s1 + (s2 - s1) * mix;
-}
-float tWaveSynth_tickVoice(tWaveSynth* const cy, int voice)
+float tWaveSynth_tick(tWaveSynth* const cy)
{
_tWaveSynth* c = *cy;
- float f = c->index * (c->numTables - 1);
-
- int o1 = (int)f;
- int o2 = o1 + 1;
- if (c->index >= 1.0f) o2 = o1;
- float mix = f - o1;
-
+ // Phasor increment (unsigned 32bit int wraps automatically with overflow so no need for if branch checks, as you need with float)
+ c->phase += c->inc;
+ float floatPhase = (double)c->phase * 2.32830643654e-10;
float s1 = 0.f, s2 = 0.f;
- for (int t = 0; t < c->numTables; ++t)
- {
- // Should we tick every voice anyway to preserve relative phases?
- float s0 = tWaveOsc_tick(&c->oscs[t][voice]);
- if (t == o1) s1 = s0 * c->g[t];
- if (t == o2) s2 = s0 * c->g[t];
- }
-
+
+
+ float temp;
+ int idx;
+ float frac;
+ float samp0;
+ float samp1;
+
+ int oct = c->oct;
+
+
+ int sizeMask = c->tables[c->o1]->sizeMask;
+ float** tables = c->tables[c->o1]->tables;
+
+ // Wavetable synthesis
+ temp = sizeMask * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct][idx];
+ idx = (idx + 1) & sizeMask;
+ samp1 = tables[oct][idx];
+
+ float oct0 = (samp0 + (samp1 - samp0) * frac);
+
+
+
+ temp = sizeMask * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct+1][idx];
+ idx = (idx + 1) & sizeMask;
+ samp1 = tables[oct+1][idx];
+
+ float oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ s1 = oct0 + (oct1 - oct0) * c->w;
+
+
+ sizeMask = c->tables[c->o2]->sizeMask;
+ tables = c->tables[c->o2]->tables;
+
+ // Wavetable synthesis
+ temp = sizeMask * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct][idx];
+ idx = (idx + 1) & sizeMask;
+ samp1 = tables[oct][idx];
+
+ oct0 = (samp0 + (samp1 - samp0) * frac);
+
+ temp = sizeMask * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct+1][idx];
+ idx = (idx + 1) & sizeMask;
+ samp1 = tables[oct+1][idx];
+
+ oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ s2 = oct0 + (oct1 - oct0) * c->w;
+
// Ideally should determine correlation to get a good equal power fade between tables
- return s1 + (s2 - s1) * mix;
+ return s1 + (s2 - s1) * c->mix;
}
-void tWaveSynth_setFreq(tWaveSynth* const cy, int voice, float freq)
+void tWaveSynth_setFreq(tWaveSynth* const cy, float freq)
{
_tWaveSynth* c = *cy;
- for (int t = 0; t < c->numTables; ++t)
- {
- tWaveOsc_setFreq(&c->oscs[t][voice], freq);
- }
+
+ c->freq = freq;
+
+ c->inc = c->freq * c->invSampleRateTimesTwoTo32;
+
+ // 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
+ // I bet we could turn this into a lookup and save a lot of processing
+ 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->numSubTables - 1) c->oct = c->numSubTables - 2;
}
void tWaveSynth_setAntiAliasing(tWaveSynth* const cy, float aa)
{
_tWaveSynth* c = *cy;
- for (int t = 0; t < c->numTables; ++t)
- {
- for (int v = 0; v < c->numVoices; ++v)
- {
- tWaveOsc_setAntiAliasing(&c->oscs[t][v], aa);
- }
- }
+ c->aa = aa;
}
void tWaveSynth_setIndex(tWaveSynth* const cy, float index)
@@ -2143,33 +2160,54 @@
{
_tWaveSynth* c = *cy;
c->index = index;
+ float f = c->index * (c->numTables - 1);
+
+ c->o1 = (int)f;
+ c->o2 = c->o1 + 1;
+ if (c->index >= 1.0f) c->o2 = c->o1;
+ c->mix = f - c->o1;
}
-void tWaveSynth_setIndexGain(tWaveSynth* const cy, int i, float gain)
+void tWaveSynth_setTables(tWaveSynth* const cy, tWaveTable* tables, int numTables, int size)
{
_tWaveSynth* c = *cy;
- if (i >= c->numTables) return;
- c->g[i] = gain;
+ LEAF* leaf = c->mempool->leaf;
+ c->tables = tables;
+ c->numTables = numTables;
+ c->size = size;
+
+ c->sampleRate = leaf->sampleRate;
+ // Determine base frequency
+ c->baseFreq = c->sampleRate / (float) size;
+ c->invBaseFreq = 1.0f / c->baseFreq;
+ c->numSubTables = c->tables[0]->numTables;
+
}
-void tWaveSynth_setIndexPhase(tWaveSynth* const cy, int i, float phase)
+/*//// eventually gotta finish this so you can X/Y control the indices and fade between non-adjacent tables
+void tWaveSynthS_setIndexXY(tWaveSynthS* const cy, float indexX, float indexY)
{
- _tWaveSynth* c = *cy;
- if (i >= c->numTables) return;
- for (int v = 0; v < c->numVoices; ++v)
- {
- tWaveOsc_setPhaseOffset(&c->oscs[i][v], phase);
- }
+ _tWaveSynthS* c = *cy;
+ c->index = index;
+ float f1 = c->index * (c->numTables - 1);
+
+ c->o1 = (int)f1;
+ c->o2 = c->o1 + 1;
+ if (c->index >= 1.0f) c->o2 = c->o1;
+ c->mix = f1 - c->o1;
+
+ float f2 = c->index * (c->numTables - 1);
}
+*/
void tWaveSynth_setSampleRate(tWaveSynth* const cy, float sr)
{
_tWaveSynth* c = *cy;
-
+ //TODO: need to fix this -JS
for (int i = 0; i < c->numTables; ++i)
{
tWaveTable_setSampleRate(&c->tables[i], sr);
- for (int v = 0; v < c->numVoices; ++v) tWaveOsc_setSampleRate(&c->oscs[i][v], sr);
+ //tWaveSubOscS_setSampleRate(&c->oscs[i], sr);
}
}
@@ -2209,11 +2247,13 @@
c->tables = (float**) mpool_alloc(sizeof(float*) * c->numTables, c->mempool);
c->sizes = (int*) mpool_alloc(sizeof(int) * c->numTables, c->mempool);
c->sizes[0] = size;
+ c->sizeMasks[0] = (size - 1);
c->baseTable = (float*) mpool_alloc(sizeof(float) * c->sizes[0], c->mempool);
c->tables[0] = c->baseTable;
for (int t = 1; t < c->numTables; ++t)
{
c->sizes[t] = c->sizes[t-1] / 2;
+ c->sizeMasks[t] = (c->sizes[t] - 1);
c->tables[t] = (float*) mpool_alloc(sizeof(float) * c->sizes[t], c->mempool);
}
@@ -2333,6 +2373,8 @@
c->mempool = m;
LEAF* leaf = c->mempool->leaf;
+ c->table = *table;
+
c->invSampleRate = leaf->invSampleRate;
c->inc = 0.0f;
c->phase = 0.0f;
@@ -2364,6 +2406,7 @@
if (c->phase < 0.0f) c->phase += 1.0f;
int* sizes = c->table->sizes;
+ int* sizeMasks = c->table->sizeMasks;
float** tables = c->table->tables;
// Wavetable synthesis
@@ -2371,16 +2414,16 @@
idx = (int)temp;
frac = temp - (float)idx;
samp0 = tables[c->oct][idx];
- if (++idx >= sizes[c->oct]) idx = 0;
+ idx = (idx + 1) & sizeMasks[c->oct];
samp1 = tables[c->oct][idx];
float oct0 = (samp0 + (samp1 - samp0) * frac);
- temp = sizes[c->oct+1] * c->phase;
+ temp = sizes[c->oct+1] * (c->phase + c->phaseOffset);
idx = (int)temp;
frac = temp - (float)idx;
samp0 = tables[c->oct+1][idx];
- if (++idx >= sizes[c->oct+1]) idx = 0;
+ idx = (idx + 1) & sizeMasks[c->oct + 1];
samp1 = tables[c->oct+1][idx];
float oct1 = (samp0 + (samp1 - samp0) * frac);
@@ -2401,6 +2444,7 @@
c->w = fabsf(c->freq * c->table->invBaseFreq);
// Probably ok to use a log2 approx here; won't effect tuning at all, just crossfading between octave tables
+ // I bet we could turn this into a lookup and save a lot of processing
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;
@@ -2431,46 +2475,119 @@
//================================================================================================
//================================================================================================
-void tWaveSynthS_init(tWaveSynthS* const cy, int numVoices, float** tables, int* sizes,
+
+
+
+
+void tWaveSubOscS_init(tWaveSubOscS* const cy, tWaveTableS* const table, LEAF* const leaf)
+{
+ tWaveSubOscS_initToPool(cy, table, &leaf->mempool);
+}
+
+void tWaveSubOscS_initToPool(tWaveSubOscS* const cy, tWaveTableS* const table, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tWaveSubOscS* c = *cy = (_tWaveSubOscS*) mpool_alloc(sizeof(_tWaveSubOscS), m);
+ c->mempool = m;
+
+ c->table = *table;
+}
+
+void tWaveSubOscS_free(tWaveSubOscS* const cy)
+{
+ _tWaveSubOscS* c = *cy;
+ mpool_free((char*)c, c->mempool);
+}
+
+float tWaveSubOscS_tick(tWaveSubOscS* const cy, float phase, int oct, float w)
+{
+ _tWaveSubOscS* c = *cy;
+
+ float temp;
+ int idx;
+ float frac;
+ float samp0;
+ float samp1;
+
+ int* sizes = c->table->sizes;
+ int* sizeMasks = c->table->sizeMasks;
+ float** tables = c->table->tables;
+
+ // Wavetable synthesis
+ temp = sizes[oct] * phase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct][idx];
+ idx = (idx + 1) & sizeMasks[oct];
+ samp1 = tables[oct][idx];
+
+ float oct0 = (samp0 + (samp1 - samp0) * frac);
+
+ temp = sizes[oct+1] * phase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct+1][idx];
+ idx = (idx + 1) & sizeMasks[oct+1];
+ samp1 = tables[oct+1][idx];
+
+ float oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ return oct0 + (oct1 - oct0) * w;
+}
+
+
+
+
+
+//================================================================================================
+//================================================================================================
+
+
+void tWaveSynthS_init(tWaveSynthS* const cy, tWaveTableS* tables, int size,
int numTables, float maxFreq, LEAF* const leaf)
{
- tWaveSynthS_initToPool(cy, numVoices, tables, sizes, numTables, maxFreq, &leaf->mempool);
+ tWaveSynthS_initToPool(cy, tables, size, numTables, maxFreq, &leaf->mempool);
}
-void tWaveSynthS_initToPool(tWaveSynthS* const cy, int numVoices, float** tables, int* sizes,
+void tWaveSynthS_initToPool(tWaveSynthS* const cy, tWaveTableS* tables, int size,
int numTables, float maxFreq, tMempool* const mp)
{
_tMempool* m = *mp;
_tWaveSynthS* c = *cy = (_tWaveSynthS*) mpool_alloc(sizeof(_tWaveSynthS), m);
+
c->mempool = m;
+
+ LEAF* leaf = c->mempool->leaf;
+ c->tables = tables;
+ c->numTables = numTables;
- c->numTables = 0;
- for (int t = 0; t < numTables; ++t)
- {
- if (sizes[t] > 0) c->numTables++;
- }
-
- c->tables = (tWaveTableS*) mpool_alloc(sizeof(tWaveTableS) * c->numTables, m);
- c->oscs = (tWaveOscS**) mpool_alloc(sizeof(tWaveOscS*) * c->numTables, m);
-
- c->numVoices = numVoices;
-
+ //c->oscs = (tWaveSubOscS*) mpool_alloc(sizeof(tWaveSubOscS*) * c->numTables, m);
+
int i = 0;
- for (int t = 0; t < numTables; ++t)
- {
- if (sizes[t] > 0)
- {
- tWaveTableS_initToPool(&c->tables[i], tables[t], sizes[t], maxFreq, mp);
- c->oscs[i] = (tWaveOscS*) mpool_alloc(sizeof(tWaveOscS) * c->numVoices, m);
- for (int v = 0; v < c->numVoices; ++v) tWaveOscS_initToPool(&c->oscs[i][v], &c->tables[i], mp);
- i++;
- }
- }
+ //for (int t = 0; t < numTables; ++t)
+ //{
+ //tWaveTableS_initToPool(&c->tables[i], table + (size*t), size, maxFreq, mp); //is the sizeoffloat necessary? is the pointer location in bytes or 32-bit words?
+ //tWaveSubOscS_initToPool(&c->oscs[i], tables[i], mp);
+ //i++;
+ //}
- c->g = (float*) mpool_alloc(sizeof(float) * c->numTables, m);
- for (int i = 0; i < c->numTables; ++i) c->g[i] = 1.0f;
-
c->index = 0.0f;
+ c->o1 = 0;
+ c->o2 = 1;
+ c->mix = 0.0f;
+ c->phase = 0;
+ c->inc = 0;
+ c->oct = 0;
+ c->size = size;
+ c->w = 0.0f;
+ c->aa = 0.5f;
+ c->sampleRate = leaf->sampleRate;
+ // Determine base frequency
+ c->baseFreq = c->sampleRate / (float) size;
+ c->invBaseFreq = 1.0f / c->baseFreq;
+ c->numSubTables = c->tables[0]->numTables;
+
+ c->invSampleRateTimesTwoTo32 = leaf->invSampleRate * TWO_TO_32;
c->maxFreq = maxFreq;
}
@@ -2480,83 +2597,115 @@
for (int i = 0; i < c->numTables; ++i)
{
- tWaveTableS_free(&c->tables[i]);
- for (int v = 0; v < c->numVoices; ++v) tWaveOscS_free(&c->oscs[i][v]);
- mpool_free((char*)c->oscs[i], c->mempool);
+ //tWaveSubOscS_free(&c->oscs[i]);
+ //mpool_free((char*)c->oscs[i], c->mempool);
}
- mpool_free((char*)c->g, c->mempool);
- mpool_free((char*)c->oscs, c->mempool);
- mpool_free((char*)c->tables, c->mempool);
+ //mpool_free((char*)c->oscs, c->mempool);
mpool_free((char*)c, c->mempool);
}
+volatile int errorCounter = 0;
float tWaveSynthS_tick(tWaveSynthS* const cy)
{
_tWaveSynthS* c = *cy;
- float f = c->index * (c->numTables - 1);
-
- int o1 = (int)f;
- int o2 = o1 + 1;
- if (c->index >= 1.0f) o2 = o1;
- float mix = f - o1;
-
+ // Phasor increment (unsigned 32bit int wraps automatically with overflow so no need for if branch checks, as you need with float)
+ c->phase += c->inc;
+ float floatPhase = (double)c->phase * 2.32830643654e-10;
float s1 = 0.f, s2 = 0.f;
- for (int t = 0; t < c->numTables; ++t)
- {
- float s0 = 0.f;
- for (int v = 0; v < c->numVoices; ++v) s0 += tWaveOscS_tick(&c->oscs[t][v]);
- if (t == o1) s1 = s0 * c->g[t];
- if (t == o2) s2 = s0 * c->g[t];
- }
-
- // Ideally should determine correlation to get a good equal power fade between tables
- return s1 + (s2 - s1) * mix;
-}
-float tWaveSynthS_tickVoice(tWaveSynthS* const cy, int voice)
-{
- _tWaveSynthS* c = *cy;
-
- float f = c->index * (c->numTables - 1);
-
- int o1 = (int)f;
- int o2 = o1 + 1;
- if (c->index >= 1.0f) o2 = o1;
- float mix = f - o1;
-
- float s1 = 0.f, s2 = 0.f;
- for (int t = 0; t < c->numTables; ++t)
- {
- // Should we tick every voice anyway to preserve relative phases?
- float s0 = tWaveOscS_tick(&c->oscs[t][voice]);
- if (t == o1) s1 = s0 * c->g[t];
- if (t == o2) s2 = s0 * c->g[t];
- }
-
+ //s1 = tWaveSubOscS_tick(&c->oscs[c->o1], floatPhase, c->oct, c->w);
+ //s2 = tWaveSubOscS_tick(&c->oscs[c->o2], floatPhase, c->oct, c->w);
+
+
+ float temp;
+ int idx;
+ float frac;
+ float samp0;
+ float samp1;
+
+ int oct = c->oct;
+
+
+ int* sizeMasks = c->tables[c->o1]->sizeMasks;
+ float** tables = c->tables[c->o1]->tables;
+
+ // Wavetable synthesis
+ temp = sizeMasks[oct] * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct][idx];
+ idx = (idx + 1) & sizeMasks[oct];
+ samp1 = tables[oct][idx];
+
+ float oct0 = (samp0 + (samp1 - samp0) * frac);
+
+
+
+ temp = sizeMasks[oct+1] * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct+1][idx];
+ idx = (idx + 1) & sizeMasks[oct+1];
+ samp1 = tables[oct+1][idx];
+
+ float oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ s1 = oct0 + (oct1 - oct0) * c->w;
+
+
+ sizeMasks = c->tables[c->o2]->sizeMasks;
+ tables = c->tables[c->o2]->tables;
+
+ // Wavetable synthesis
+ temp = sizeMasks[oct] * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct][idx];
+ idx = (idx + 1) & sizeMasks[oct];
+ samp1 = tables[oct][idx];
+
+ oct0 = (samp0 + (samp1 - samp0) * frac);
+
+ temp = sizeMasks[oct+1] * floatPhase;
+ idx = (int)temp;
+ frac = temp - (float)idx;
+ samp0 = tables[oct+1][idx];
+ idx = (idx + 1) & sizeMasks[oct+1];
+ samp1 = tables[oct+1][idx];
+
+ oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ s2 = oct0 + (oct1 - oct0) * c->w;
+
// Ideally should determine correlation to get a good equal power fade between tables
- return s1 + (s2 - s1) * mix;
+ return s1 + (s2 - s1) * c->mix;
}
-void tWaveSynthS_setFreq(tWaveSynthS* const cy, int voice, float freq)
+void tWaveSynthS_setFreq(tWaveSynthS* const cy, float freq)
{
_tWaveSynthS* c = *cy;
- for (int t = 0; t < c->numTables; ++t)
- {
- tWaveOscS_setFreq(&c->oscs[t][voice], freq);
- }
+
+ c->freq = freq;
+
+ c->inc = c->freq * c->invSampleRateTimesTwoTo32;
+
+ // 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
+ // I bet we could turn this into a lookup and save a lot of processing
+ 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->numSubTables - 1) c->oct = c->numSubTables - 2;
}
void tWaveSynthS_setAntiAliasing(tWaveSynthS* const cy, float aa)
{
_tWaveSynthS* c = *cy;
- for (int t = 0; t < c->numTables; ++t)
- {
- for (int v = 0; v < c->numVoices; ++v)
- {
- tWaveOscS_setAntiAliasing(&c->oscs[t][v], aa);
- }
- }
+ c->aa = aa;
}
void tWaveSynthS_setIndex(tWaveSynthS* const cy, float index)
@@ -2563,32 +2712,38 @@
{
_tWaveSynthS* c = *cy;
c->index = index;
-}
+ float f = c->index * (c->numTables - 1);
-void tWaveSynthS_setIndexGain(tWaveSynthS* const cy, int i, float gain)
-{
- _tWaveSynthS* c = *cy;
- if (i >= c->numTables) return;
- c->g[i] = gain;
+ c->o1 = (int)f;
+ c->o2 = c->o1 + 1;
+ if (c->index >= 1.0f) c->o2 = c->o1;
+ c->mix = f - c->o1;
}
-void tWaveSynthS_setIndexPhase(tWaveSynthS* const cy, int i, float phase)
+/*//// eventually gotta finish this so you can X/Y control the indices and fade between non-adjacent tables
+void tWaveSynthS_setIndexXY(tWaveSynthS* const cy, float indexX, float indexY)
{
_tWaveSynthS* c = *cy;
- if (i >= c->numTables) return;
- for (int v = 0; v < c->numVoices; ++v)
- {
- tWaveOscS_setPhaseOffset(&c->oscs[i][v], phase);
- }
+ c->index = index;
+ float f1 = c->index * (c->numTables - 1);
+
+ c->o1 = (int)f1;
+ c->o2 = c->o1 + 1;
+ if (c->index >= 1.0f) c->o2 = c->o1;
+ c->mix = f1 - c->o1;
+
+ float f2 = c->index * (c->numTables - 1);
}
+*/
void tWaveSynthS_setSampleRate(tWaveSynthS* const cy, float sr)
{
_tWaveSynthS* c = *cy;
+ //TODO: need to fix this -JS
for (int i = 0; i < c->numTables; ++i)
{
tWaveTableS_setSampleRate(&c->tables[i], sr);
- for (int v = 0; v < c->numVoices; ++v) tWaveOscS_setSampleRate(&c->oscs[i][v], sr);
+ //tWaveSubOscS_setSampleRate(&c->oscs[i], sr);
}
}
//
--- a/leaf/Src/leaf-physical.c
+++ b/leaf/Src/leaf-physical.c
@@ -498,6 +498,141 @@
tHighpass_setSampleRate(&p->DCblocker, p->sampleRate);
}
+
+
+/* Simple Living String*/
+
+void tSimpleLivingString2_init(tSimpleLivingString2* const pl, float freq, float brightness,
+ float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode, LEAF* const leaf)
+{
+ tSimpleLivingString2_initToPool(pl, freq, brightness, decay, targetLev, levSmoothFactor, levStrength, levMode, &leaf->mempool);
+}
+
+void tSimpleLivingString2_initToPool (tSimpleLivingString2* const pl, float freq, float brightness,
+ float decay, float targetLev, float levSmoothFactor,
+ float levStrength, int levMode, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tSimpleLivingString2* p = *pl = (_tSimpleLivingString2*) mpool_alloc(sizeof(_tSimpleLivingString2), m);
+ p->mempool = m;
+ LEAF* leaf = p->mempool->leaf;
+
+ p->sampleRate = leaf->sampleRate;
+ p->curr=0.0f;
+ tExpSmooth_initToPool(&p->wlSmooth, p->sampleRate/freq, 0.01f, mp); // smoother for string wavelength (not freq, to avoid expensive divisions)
+ tSimpleLivingString2_setFreq(pl, freq);
+ tHermiteDelay_initToPool(&p->delayLine,p->waveLengthInSamples, 2400, mp);
+ tHermiteDelay_clear(&p->delayLine);
+ tTwoZero_initToPool(&p->bridgeFilter, mp);
+ tSimpleLivingString2_setBrightness(pl, brightness);
+ tHighpass_initToPool(&p->DCblocker,13, mp);
+ p->decay=decay;
+ tFeedbackLeveler_initToPool(&p->fbLev, targetLev, levSmoothFactor, levStrength, levMode, mp);
+ p->levMode=levMode;
+}
+
+void tSimpleLivingString2_free (tSimpleLivingString2* const pl)
+{
+ _tSimpleLivingString2* p = *pl;
+
+ tExpSmooth_free(&p->wlSmooth);
+ tHermiteDelay_free(&p->delayLine);
+ tTwoZero_free(&p->bridgeFilter);
+ tHighpass_free(&p->DCblocker);
+ tFeedbackLeveler_free(&p->fbLev);
+
+ mpool_free((char*)p, p->mempool);
+}
+
+void tSimpleLivingString2_setFreq(tSimpleLivingString2* const pl, float freq)
+{
+ _tSimpleLivingString2* p = *pl;
+
+ if (freq<20) freq=20;
+ else if (freq>10000) freq=10000;
+ p->waveLengthInSamples = p->sampleRate/freq -1;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void tSimpleLivingString2_setWaveLength(tSimpleLivingString2* const pl, float waveLength)
+{
+ _tSimpleLivingString2* p = *pl;
+
+ if (waveLength<4.8) waveLength=4.8f;
+ else if (waveLength>2400) waveLength=2400;
+ p->waveLengthInSamples = waveLength-1;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+}
+
+void tSimpleLivingString2_setBrightness(tSimpleLivingString2* const pl, float brightness)
+{
+ _tSimpleLivingString2* p = *pl;
+ float h0=(1.0 + brightness) * 0.5f;
+ float h1=(1.0 - brightness) * 0.25f;
+ tTwoZero_setCoefficients(&p->bridgeFilter, h1, h0, h1);
+}
+
+void tSimpleLivingString2_setDecay(tSimpleLivingString2* const pl, float decay)
+{
+ _tSimpleLivingString2* p = *pl;
+ p->decay=decay;
+}
+
+void tSimpleLivingString2_setTargetLev(tSimpleLivingString2* const pl, float targetLev)
+{
+ _tSimpleLivingString2* p = *pl;
+ tFeedbackLeveler_setTargetLevel(&p->fbLev, targetLev);
+}
+
+void tSimpleLivingString2_setLevSmoothFactor(tSimpleLivingString2* const pl, float levSmoothFactor)
+{
+ _tSimpleLivingString2* p = *pl;
+ tFeedbackLeveler_setFactor(&p->fbLev, levSmoothFactor);
+}
+
+void tSimpleLivingString2_setLevStrength(tSimpleLivingString2* const pl, float levStrength)
+{
+ _tSimpleLivingString2* p = *pl;
+ tFeedbackLeveler_setStrength(&p->fbLev, levStrength);
+}
+
+void tSimpleLivingString2_setLevMode(tSimpleLivingString2* const pl, int levMode)
+{
+ _tSimpleLivingString2* p = *pl;
+ tFeedbackLeveler_setMode(&p->fbLev, levMode);
+ p->levMode=levMode;
+}
+
+float tSimpleLivingString2_tick(tSimpleLivingString2* const pl, float input)
+{
+ _tSimpleLivingString2* p = *pl;
+
+ float stringOut=tTwoZero_tick(&p->bridgeFilter,tHermiteDelay_tickOut(&p->delayLine));
+ float stringInput=tHighpass_tick(&p->DCblocker,(tFeedbackLeveler_tick(&p->fbLev, (p->levMode==0?p->decay*stringOut:stringOut)+input)));
+ tHermiteDelay_tickIn(&p->delayLine, stringInput);
+ tHermiteDelay_setDelay(&p->delayLine, tExpSmooth_tick(&p->wlSmooth));
+ p->curr = stringOut;
+ return p->curr;
+}
+
+
+float tSimpleLivingString2_sample(tSimpleLivingString2* const pl)
+{
+ _tSimpleLivingString2* p = *pl;
+ return p->curr;
+}
+
+void tSimpleLivingString2_setSampleRate(tSimpleLivingString2* const pl, float sr)
+{
+ _tSimpleLivingString2* p = *pl;
+ float freq = p->sampleRate/p->waveLengthInSamples;
+ p->sampleRate = sr;
+ p->waveLengthInSamples = p->sampleRate/freq;
+ tExpSmooth_setDest(&p->wlSmooth, p->waveLengthInSamples);
+ tTwoZero_setSampleRate(&p->bridgeFilter, p->sampleRate);
+ tHighpass_setSampleRate(&p->DCblocker, p->sampleRate);
+}
/* Living String*/
void tLivingString_init(tLivingString* const pl, float freq, float pickPos, float prepIndex,
@@ -754,8 +889,9 @@
tTwoZero_initToPool(&p->nutFilter, mp);
tTwoZero_initToPool(&p->prepFilterU, mp);
tTwoZero_initToPool(&p->prepFilterL, mp);
- tHighpass_initToPool(&p->DCblockerU,13, mp);
- tHighpass_initToPool(&p->DCblockerL,13, mp);
+ tLivingString2_setBrightness(pl, brightness);
+ tHighpass_initToPool(&p->DCblockerU,8, mp);
+ tHighpass_initToPool(&p->DCblockerL,8, mp);
p->decay=decay;
p->prepIndex = prepIndex;
tFeedbackLeveler_initToPool(&p->fbLevU, targetLev, levSmoothFactor, levStrength, levMode, mp);
@@ -910,7 +1046,7 @@
float lowLen=prepP*wLen;
float upLen=(1.0f-prepP)*wLen;
uint32_t pickPInt;
-/*
+
if (pickP > prepP)
{
float fullPickPoint = ((pickP*wLen) - lowLen);
@@ -933,7 +1069,7 @@
tHermiteDelay_addTo(&p->delLB, input * (1.0f - pickPFloat), (uint) (lowLen - pickPInt));
tHermiteDelay_addTo(&p->delLB, input * pickPFloat, (uint) (lowLen - pickPInt - 1));
}
-*/
+/*
if (pickP > prepP)
{
float fullPickPoint = ((pickP*wLen) - lowLen);
@@ -950,13 +1086,13 @@
tHermiteDelay_addTo(&p->delLF, input, pickPInt);
tHermiteDelay_addTo(&p->delLB, input, (uint32_t) (lowLen - pickPInt));
}
-
+*/
float fromLF=tHermiteDelay_tickOut(&p->delLF);
float fromUF=tHermiteDelay_tickOut(&p->delUF);
float fromUB=tHermiteDelay_tickOut(&p->delUB);
float fromLB=tHermiteDelay_tickOut(&p->delLB);
// into upper half of string, from bridge, going backwards
- float fromBridge=-tFeedbackLeveler_tick(&p->fbLevU, (p->levMode==0?p->decay:1)*tHighpass_tick(&p->DCblockerU, tTwoZero_tick(&p->bridgeFilter, fromUF)));
+ float fromBridge=-tFeedbackLeveler_tick(&p->fbLevU, (p->levMode==0?p->decay:1.0f)*tHighpass_tick(&p->DCblockerU, tTwoZero_tick(&p->bridgeFilter, fromUF)));
tHermiteDelay_tickIn(&p->delUB, fromBridge);
// into lower half of string, from prepPoint, going backwards
float fromLowerPrep=-tTwoZero_tick(&p->prepFilterL, fromLF);
@@ -976,11 +1112,41 @@
tHermiteDelay_setDelay(&p->delUF, upLen);
tHermiteDelay_setDelay(&p->delUB, upLen);
- uint32_t pickupPosInt;
+ uint32_t PUPInt;
float pickupOut = 0.0f;
- if (p->pickupPos < 0.98f)
+ float pupos = tExpSmooth_tick(&p->puSmooth);
+ if (pupos < 0.9999f)
{
- float fullPickupPos = (p->pickupPos*upLen);
+ if (pupos > prepP)
+ {
+ float fullPUPoint = ((pupos*wLen) - lowLen);
+ PUPInt = (uint) fullPUPoint; // where does the input go? that's the pick point
+ float PUPFloat = fullPUPoint - PUPInt;
+
+ pickupOut = tHermiteDelay_tapOut(&p->delUF, PUPInt) * (1.0f - PUPFloat);
+ pickupOut += tHermiteDelay_tapOut(&p->delUF, PUPInt + 1) * PUPFloat;
+ pickupOut += tHermiteDelay_tapOut(&p->delUB, (uint) (upLen - PUPInt)) * (1.0f - PUPFloat);
+ pickupOut += tHermiteDelay_tapOut(&p->delUB, (uint) (upLen - PUPInt - 1)) * PUPFloat;
+ }
+ else
+ {
+ float fullPUPoint = pupos * wLen;
+ PUPInt = (uint) fullPUPoint; // where does the input go? that's the pick point
+ float PUPFloat = fullPUPoint - PUPInt;
+
+ pickupOut = tHermiteDelay_tapOut(&p->delLF, PUPInt) * (1.0f - PUPFloat);
+ pickupOut += tHermiteDelay_tapOut(&p->delLF, PUPInt + 1) * PUPFloat;
+ pickupOut += tHermiteDelay_tapOut(&p->delLB, (uint) (lowLen - PUPInt)) * (1.0f - PUPFloat);
+ pickupOut += tHermiteDelay_tapOut(&p->delLB, (uint) (lowLen - PUPInt - 1)) * PUPFloat;
+ }
+
+ p->curr = pickupOut;
+
+
+
+
+/*
+ float fullPickupPos = (pupos*upLen);
pickupPosInt = (uint32_t) fullPickupPos;
float pickupPosFloat = fullPickupPos - pickupPosInt;
if (pickupPosInt == 0)
@@ -990,6 +1156,7 @@
pickupOut = tHermiteDelay_tapOutInterpolated(&p->delUF, pickupPosInt, pickupPosFloat);
pickupOut += tHermiteDelay_tapOutInterpolated(&p->delUB, (uint32_t) (upLen - pickupPosInt), pickupPosFloat);
p->curr = pickupOut;
+ */
}
else
--- a/leaf/leaf-config.h
+++ b/leaf/leaf-config.h
@@ -58,9 +58,12 @@
#define LEAF_USE_CMSIS 0
+#ifdef __cplusplus
//! Use stdlib malloc() and free() internally instead of LEAF's normal mempool behavior for when you want to avoid being limited to and managing mempool a fixed mempool size. Usage of all object remains essentially the same.
#define LEAF_USE_DYNAMIC_ALLOCATION 1
-
+#else
+#define LEAF_USE_DYNAMIC_ALLOCATION 0
+#endif
//==============================================================================
#endif // LEAF_CONFIG_H_INCLUDED