ref: be773ed33f71a0faa6d518c50b44eed4524c3522
parent: 65ab3d024da463b1da721f4737f7614121680ef5
author: Matthew Wang <mjw7@princeton.edu>
date: Fri Jan 8 10:39:23 EST 2021
tWaveset object, fade between multiple wavetables
--- a/TestPlugin/Source/MyTest.cpp
+++ b/TestPlugin/Source/MyTest.cpp
@@ -33,6 +33,7 @@
tWavetable wt;
tCompactWavetable cwt;
+tWaveset ws;
tBuffer samp;
tMBSampler sampler;
@@ -57,8 +58,8 @@
{
LEAF_init(&leaf, sampleRate, blockSize, memory, MSIZE, &getRandomFloat);
- tWavetable_init(&wt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
- tCompactWavetable_init(&cwt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
+// tWavetable_init(&wt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
+// tCompactWavetable_init(&cwt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
tMBTriangle_init(&btri, &leaf);
tMBPulse_init(&bpulse, &leaf);
@@ -69,6 +70,15 @@
tPhasor_init(&phasor, &leaf);
tPhasor_setFreq(&phasor, 220.f);
+
+ float const* set[4];
+ set[0] = __leaf_table_sinewave;
+ set[1] = __leaf_table_triangle[0];
+ set[2] = __leaf_table_squarewave[0];
+ set[3] = __leaf_table_sawtooth[0];
+
+ tWaveset_init(&ws, set, 4, 2048, 10000.f, &leaf);
+ tWaveset_setIndexGain(&ws, 0, -1.0f);
}
inline double getSawFall(double angle) {
@@ -86,7 +96,8 @@
// return tSimpleRetune_tick(&sretune, input);
// tMBPulse_sync(&bpulse, tPhasor_tick(&phasor) * 2.f - 1.f);
// return tMBPulse_tick(&bpulse);
- return tWavetable_tick(&wt);
+// return tWavetable_tick(&wt);
+ return tWaveset_tick(&ws);
}
int firstFrame = 1;
@@ -97,12 +108,14 @@
tMBTriangle_setFreq(&btri, val * 440.f);
tMBPulse_setFreq(&bpulse, val * 160000.f - 80000.0f);
tMBSaw_setFreq(&bsaw, val * 10000.f);
- tWavetable_setFreq(&wt, val * 160000.f - 80000.0f);
- tCompactWavetable_setFreq(&cwt, val * 10000.);
+// tWavetable_setFreq(&wt, val * 160000.f - 80000.0f);
+// tCompactWavetable_setFreq(&cwt, val * 10000.);
+ tWaveset_setFreq(&ws, val * 10000.f);
// tRetune_tuneVoice(&retune, 0, val * 3.0f + 0.5f);
// tSimpleRetune_tuneVoice(&sretune, 0, 300);
val = getSliderValue("slider2");
+ tWaveset_setIndex(&ws, val);
// tRetune_setPitchFactor(&retune, val * 3.0f + 0.5f, 1);
val = getSliderValue("slider3");
--- a/leaf/Inc/leaf-oscillators.h
+++ b/leaf/Inc/leaf-oscillators.h
@@ -29,202 +29,6 @@
//==============================================================================
/*!
- @defgroup ttable tTable
- @ingroup oscillators
- @brief Simple aliasing wavetable oscillator.
- @{
-
- @fn void tTable_init (tTable* const osc, float* table, int size, LEAF* const leaf)
- @brief Initialize a tTable to the default mempool of a LEAF instance.
- @param osc A pointer to the tTable to initialize.
- @param table A pointer to the wavetable data.
- @param size The number of samples in the wavetable.
- @param leaf A pointer to the leaf instance.
-
- @fn void tTable_initToPool (tTable* const osc, float* table, int size, tMempool* const mempool)
- @brief Initialize a tTable to a specified mempool.
- @param osc A pointer to the tTable to initialize.
- @param table A pointer to the wavetable data.
- @param size The number of samples in the wave table.
- @param mempool A pointer to the tMempool to use.
-
- @fn void tTable_free (tTable* const osc)
- @brief Free a tTable from its mempool.
- @param osc A pointer to the tTable to free.
-
- @fn float tTable_tick (tTable* const osc)
- @brief Tick a tTable oscillator.
- @param osc A pointer to the relevant tTable.
- @return The ticked sample as a float from -1 to 1.
-
- @fn void tTable_setFreq (tTable* const osc, float freq)
- @brief Set the frequency of a tTable oscillator.
- @param osc A pointer to the relevant tTable.
- @param freq The frequency to set the oscillator to.
-
- @} */
-
- typedef struct _tTable
- {
- tMempool mempool;
-
- float* waveTable;
- int size;
- float inc, freq;
- float phase;
- } _tTable;
-
- typedef _tTable* tTable;
-
- void tTable_init(tTable* const osc, float* table, int size, LEAF* const leaf);
- void tTable_initToPool(tTable* const osc, float* table, int size, tMempool* const mempool);
- void tTable_free(tTable* const osc);
-
- float tTable_tick(tTable* const osc);
- void tTable_setFreq(tTable* const osc, float freq);
-
- //==============================================================================
-
- /*!
- @defgroup twavetable tWavetable
- @ingroup oscillators
- @brief Anti-aliased wavetable oscillator.
- @{
-
- @fn void tWavetable_init (tWavetable* const osc, float* table, int size, float maxFreq, LEAF* const leaf)
- @brief Initialize a tWavetable to the default mempool of a LEAF instance.
- @param osc A pointer to the tWavetable 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 tWavetable_initToPool (tWavetable* const osc, float* table, int size, float maxFreq, tMempool* const mempool)
- @brief Initialize a tWavetable to a specified mempool.
- @param osc A pointer to the tWavetable 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 tWavetable_free (tWavetable* const osc)
- @brief Free a tWavetable from its mempool.
- @param osc A pointer to the tWavetable to free.
-
- @fn float tWavetable_tick (tWavetable* const osc)
- @brief Tick a tWavetable oscillator.
- @param osc A pointer to the relevant tWavetable.
- @return The ticked sample as a float from -1 to 1.
-
- @fn void tWavetable_setFreq (tWavetable* const osc, float freq)
- @brief Set the frequency of a tWavetable oscillator.
- @param osc A pointer to the relevant tWavetable.
- @param freq The frequency to set the oscillator to.
-
- @} */
-
- typedef struct _tWavetable
- {
- tMempool mempool;
-
- float** tables;
- int size;
- int numTables;
- float baseFreq, invBaseFreq;
- float inc, freq;
- float phase;
-
- int oct;
- float w;
- float aa;
-
- tButterworth bl;
- } _tWavetable;
-
- typedef _tWavetable* tWavetable;
-
- void tWavetable_init(tWavetable* const osc, const float* table, int size, float maxFreq, LEAF* const leaf);
- void tWavetable_initToPool(tWavetable* const osc, const float* table, int size, float maxFreq, tMempool* const mempool);
- void tWavetable_free(tWavetable* const osc);
-
- float tWavetable_tick(tWavetable* const osc);
- void tWavetable_setFreq(tWavetable* const osc, float freq);
- void tWavetable_setAntiAliasing(tWavetable* const osc, float aa);
-
- //==============================================================================
-
- /*!
- @defgroup tcompactwavetable tCompactWavetable
- @ingroup oscillators
- @brief A more space-efficient anti-aliased wavetable oscillator than tWavetable but with slightly worse fidelity.
- @{
-
- @fn void tCompactWavetable_init (tCompactWavetable* const osc, float* table, int size, float maxFreq, LEAF* const leaf)
- @brief Initialize a tCompactWavetable to the default mempool of a LEAF instance.
- @param osc A pointer to the tCompactWavetable 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 tCompactWavetable_initToPool (tCompactWavetable* const osc, float* table, int size, float maxFreq, tMempool* const mempool)
- @brief Initialize a tCompactWavetable to a specified mempool.
- @param osc A pointer to the tCompactWavetable 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 tCompactWavetable_free (tCompactWavetable* const osc)
- @brief Free a tCompactWavetable from its mempool.
- @param osc A pointer to the tCompactWavetable to free.
-
- @fn float tCompactWavetable_tick (tCompactWavetable* const osc)
- @brief Tick a tCompactWavetable oscillator.
- @param osc A pointer to the relevant tCompactWavetable.
- @return The ticked sample as a float from -1 to 1.
-
- @fn void tCompactWavetable_setFreq (tCompactWavetable* const osc, float freq)
- @brief Set the frequency of a tCompactWavetable oscillator.
- @param osc A pointer to the relevant tCompactWavetable.
- @param freq The frequency to set the oscillator to.
-
- @} */
-
- typedef struct _tCompactWavetable
- {
- tMempool mempool;
-
- float** tables;
- int numTables;
- int* sizes;
- float baseFreq, invBaseFreq;
- float inc, freq;
- float phase;
-
- int oct;
- float w;
- float aa;
-
- tButterworth bl;
-
- float dsBuffer[2];
- tOversampler ds;
- } _tCompactWavetable;
-
- typedef _tCompactWavetable* tCompactWavetable;
-
- void tCompactWavetable_init(tCompactWavetable* const osc, const float* table, int size, float maxFreq, LEAF* const leaf);
- void tCompactWavetable_initToPool(tCompactWavetable* const osc, const float* table, int size, float maxFreq, tMempool* const mempool);
- void tCompactWavetable_free(tCompactWavetable* const osc);
-
- float tCompactWavetable_tick(tCompactWavetable* const osc);
- void tCompactWavetable_setFreq(tCompactWavetable* const osc, float freq);
- void tCompactWavetable_setAntiAliasing(tCompactWavetable* const osc, float aa);
-
- //==============================================================================
-
- /*!
@defgroup tcycle tCycle
@ingroup oscillators
@brief Wavetable cycle/sine wave oscillator
@@ -1057,6 +861,270 @@
void tMBSaw_setFreq(tMBSaw* const osc, float f);
float tMBSaw_sync(tMBSaw* const osc, float sync);
void tMBSaw_setSyncMode(tMBSaw* const osc, int hardOrSoft);
+
+ //==============================================================================
+
+ /*!
+ @defgroup ttable tTable
+ @ingroup oscillators
+ @brief Simple aliasing wavetable oscillator.
+ @{
+
+ @fn void tTable_init (tTable* const osc, float* table, int size, LEAF* const leaf)
+ @brief Initialize a tTable to the default mempool of a LEAF instance.
+ @param osc A pointer to the tTable to initialize.
+ @param table A pointer to the wavetable data.
+ @param size The number of samples in the wavetable.
+ @param leaf A pointer to the leaf instance.
+
+ @fn void tTable_initToPool (tTable* const osc, float* table, int size, tMempool* const mempool)
+ @brief Initialize a tTable to a specified mempool.
+ @param osc A pointer to the tTable to initialize.
+ @param table A pointer to the wavetable data.
+ @param size The number of samples in the wave table.
+ @param mempool A pointer to the tMempool to use.
+
+ @fn void tTable_free (tTable* const osc)
+ @brief Free a tTable from its mempool.
+ @param osc A pointer to the tTable to free.
+
+ @fn float tTable_tick (tTable* const osc)
+ @brief Tick a tTable oscillator.
+ @param osc A pointer to the relevant tTable.
+ @return The ticked sample as a float from -1 to 1.
+
+ @fn void tTable_setFreq (tTable* const osc, float freq)
+ @brief Set the frequency of a tTable oscillator.
+ @param osc A pointer to the relevant tTable.
+ @param freq The frequency to set the oscillator to.
+
+ @} */
+
+ typedef struct _tTable
+ {
+ tMempool mempool;
+
+ float* waveTable;
+ int size;
+ float inc, freq;
+ float phase;
+ } _tTable;
+
+ typedef _tTable* tTable;
+
+ void tTable_init(tTable* const osc, float* table, int size, LEAF* const leaf);
+ void tTable_initToPool(tTable* const osc, float* table, int size, tMempool* const mempool);
+ void tTable_free(tTable* const osc);
+
+ float tTable_tick(tTable* const osc);
+ void tTable_setFreq(tTable* const osc, float freq);
+
+ //==============================================================================
+
+ /*!
+ @defgroup twavetable tWavetable
+ @ingroup oscillators
+ @brief Anti-aliased wavetable oscillator.
+ @{
+
+ @fn void tWavetable_init (tWavetable* const osc, float* table, int size, float maxFreq, LEAF* const leaf)
+ @brief Initialize a tWavetable to the default mempool of a LEAF instance.
+ @param osc A pointer to the tWavetable 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 tWavetable_initToPool (tWavetable* const osc, float* table, int size, float maxFreq, tMempool* const mempool)
+ @brief Initialize a tWavetable to a specified mempool.
+ @param osc A pointer to the tWavetable 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 tWavetable_free (tWavetable* const osc)
+ @brief Free a tWavetable from its mempool.
+ @param osc A pointer to the tWavetable to free.
+
+ @fn float tWavetable_tick (tWavetable* const osc)
+ @brief Tick a tWavetable oscillator.
+ @param osc A pointer to the relevant tWavetable.
+ @return The ticked sample as a float from -1 to 1.
+
+ @fn void tWavetable_setFreq (tWavetable* const osc, float freq)
+ @brief Set the frequency of a tWavetable oscillator.
+ @param osc A pointer to the relevant tWavetable.
+ @param freq The frequency to set the oscillator to.
+
+ @} */
+
+ typedef struct _tWavetable
+ {
+ tMempool mempool;
+
+ float** tables;
+ int size;
+ int numTables;
+ float baseFreq, invBaseFreq;
+ float inc, freq;
+ float phase;
+
+ int oct;
+ float w;
+ float aa;
+
+ tButterworth bl;
+ } _tWavetable;
+
+ typedef _tWavetable* tWavetable;
+
+ void tWavetable_init(tWavetable* const osc, const float* table, int size, float maxFreq, LEAF* const leaf);
+ void tWavetable_initToPool(tWavetable* const osc, const float* table, int size, float maxFreq, tMempool* const mempool);
+ void tWavetable_free(tWavetable* const osc);
+
+ float tWavetable_tick(tWavetable* const osc);
+ void tWavetable_setFreq(tWavetable* const osc, float freq);
+ void tWavetable_setAntiAliasing(tWavetable* const osc, float aa);
+
+ //==============================================================================
+
+ /*!
+ @defgroup tcompactwavetable tCompactWavetable
+ @ingroup oscillators
+ @brief A more space-efficient anti-aliased wavetable oscillator than tWavetable but with slightly worse fidelity.
+ @{
+
+ @fn void tCompactWavetable_init (tCompactWavetable* const osc, float* table, int size, float maxFreq, LEAF* const leaf)
+ @brief Initialize a tCompactWavetable to the default mempool of a LEAF instance.
+ @param osc A pointer to the tCompactWavetable 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 tCompactWavetable_initToPool (tCompactWavetable* const osc, float* table, int size, float maxFreq, tMempool* const mempool)
+ @brief Initialize a tCompactWavetable to a specified mempool.
+ @param osc A pointer to the tCompactWavetable 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 tCompactWavetable_free (tCompactWavetable* const osc)
+ @brief Free a tCompactWavetable from its mempool.
+ @param osc A pointer to the tCompactWavetable to free.
+
+ @fn float tCompactWavetable_tick (tCompactWavetable* const osc)
+ @brief Tick a tCompactWavetable oscillator.
+ @param osc A pointer to the relevant tCompactWavetable.
+ @return The ticked sample as a float from -1 to 1.
+
+ @fn void tCompactWavetable_setFreq (tCompactWavetable* const osc, float freq)
+ @brief Set the frequency of a tCompactWavetable oscillator.
+ @param osc A pointer to the relevant tCompactWavetable.
+ @param freq The frequency to set the oscillator to.
+
+ @} */
+
+ typedef struct _tCompactWavetable
+ {
+ tMempool mempool;
+
+ float** tables;
+ int numTables;
+ int* sizes;
+ float baseFreq, invBaseFreq;
+ float inc, freq;
+ float phase;
+
+ int oct;
+ float w;
+ float aa;
+
+ tButterworth bl;
+
+ float dsBuffer[2];
+ tOversampler ds;
+ } _tCompactWavetable;
+
+ typedef _tCompactWavetable* tCompactWavetable;
+
+ void tCompactWavetable_init(tCompactWavetable* const osc, const float* table, int size, float maxFreq, LEAF* const leaf);
+ void tCompactWavetable_initToPool(tCompactWavetable* const osc, const float* table, int size, float maxFreq, tMempool* const mempool);
+ void tCompactWavetable_free(tCompactWavetable* const osc);
+
+ float tCompactWavetable_tick(tCompactWavetable* const osc);
+ void tCompactWavetable_setFreq(tCompactWavetable* const osc, float freq);
+ void tCompactWavetable_setAntiAliasing(tCompactWavetable* const osc, float aa);
+
+ //==============================================================================
+
+ /*!
+ @defgroup twaveset tWaveset
+ @ingroup oscillators
+ @brief Set of anti-aliased wavetable oscillators that can be faded between.
+ @{
+
+ @fn void tWaveset_init(tWaveset* const osc, const float** tables, int n, int size, float maxFreq, LEAF* const leaf)
+ @brief Initialize a tWaveset to the default mempool of a LEAF instance.
+ @param osc A pointer to the tWaveset to initialize.
+ @param tables An array of pointers to wavetable data.
+ @param n The number of wavetables.
+ @param size The number of samples in each of the wavetables.
+ @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 tWaveset_initToPool(tWaveset* const osc, const float** tables, int n, int size, float maxFreq, tMempool* const mempool)
+ @brief Initialize a tWaveset to a specified mempool.
+ @param osc A pointer to the tWavetable to initialize.
+ @param tables An array of pointers to wavetable data.
+ @param n The number of wavetables.
+ @param size The number of samples in each of the wavetables.
+ @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 tWaveset_free (tWaveset* const osc)
+ @brief Free a tWaveset from its mempool.
+ @param osc A pointer to the tWaveset to free.
+
+ @fn float tWaveset_tick (tWaveset* const osc)
+ @brief Tick a tWaveset oscillator.
+ @param osc A pointer to the relevant tWaveset.
+ @return The ticked sample as a float from -1 to 1.
+
+ @fn void tWaveset_setFreq (tWaveset* const osc, float freq)
+ @brief Set the frequency of a tWaveset oscillator.
+ @param osc A pointer to the relevant tWaveset.
+ @param freq The frequency to set the oscillator to.
+
+ @fn void tWaveset_setIndex(tWaveset* const osc, float index)
+ @brief Set the output index of the wavetable set.
+ @param index The new index from 0.0 to 1.0 as a smooth fade from the first wavetable in the set to the last.
+
+ @} */
+
+ typedef struct _tWaveset
+ {
+ tMempool mempool;
+
+ tWavetable* wt;
+ int n;
+ float* g;
+ float index;
+ } _tWaveset;
+
+ typedef _tWaveset* tWaveset;
+
+ void tWaveset_init(tWaveset* const osc, const float** tables, int n, int size, float maxFreq, LEAF* const leaf);
+ void tWaveset_initToPool(tWaveset* const osc, const float** tables, int n, int size, float maxFreq, tMempool* const mempool);
+ void tWaveset_free(tWaveset* const osc);
+
+ float tWaveset_tick(tWaveset* const osc);
+ void tWaveset_setFreq(tWaveset* const osc, float freq);
+ void tWaveset_setAntiAliasing(tWaveset* const osc, float aa);
+ void tWaveset_setIndex(tWaveset* const osc, float index);
+ void tWaveset_setIndexGain(tWaveset* const osc, int i, float gain);
#ifdef __cplusplus
--- a/leaf/Src/leaf-oscillators.c
+++ b/leaf/Src/leaf-oscillators.c
@@ -16,376 +16,6 @@
#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)
@@ -1999,4 +1629,469 @@
{
_tMBSaw* c = *osc;
c->softsync = hardOrSoft > 0 ? 1 : 0;
+}
+
+
+// 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);
+}
+
+//================================================================================================
+//================================================================================================
+
+void tWaveset_init(tWaveset* const cy, const float** tables, int n, int size, float maxFreq, LEAF* const leaf)
+{
+ tWaveset_initToPool(cy, tables, n, size, maxFreq, &leaf->mempool);
+}
+
+void tWaveset_initToPool(tWaveset* const cy, const float** tables, int n, int size, float maxFreq, tMempool* const mp)
+{
+ _tMempool* m = *mp;
+ _tWaveset* c = *cy = (_tWaveset*) mpool_alloc(sizeof(_tWaveset), m);
+ c->mempool = m;
+
+ c->n = n;
+
+ c->g = (float*) mpool_alloc(sizeof(tWavetable) * c->n, m);
+ c->wt = (tWavetable*) mpool_alloc(sizeof(tWavetable) * c->n, m);
+ for (int i = 0; i < c->n; ++i)
+ {
+ c->g[i] = 1.0f;
+ tWavetable_initToPool(&c->wt[i], tables[i], size, maxFreq, mp);
+ }
+
+ c->index = 0.0f;
+}
+
+void tWaveset_free(tWaveset* const cy)
+{
+ _tWaveset* c = *cy;
+
+ for (int i = 0; i < c->n; ++i)
+ {
+ tWavetable_free(&c->wt[i]);
+ }
+ mpool_free((char*)c->wt, c->mempool);
+ mpool_free((char*)c, c->mempool);
+}
+
+float tWaveset_tick(tWaveset* const cy)
+{
+ _tWaveset* c = *cy;
+
+ float p = c->index * (c->n - 1);
+
+ int o1 = (int)p;
+ int o2 = o1 + 1;
+ if (c->index >= 1.0f) o2 = o1;
+ float mix = p - o1;
+
+ float s0, s1, s2;
+ for (int i = 0; i < c->n; ++i)
+ {
+ s0 = tWavetable_tick(&c->wt[i]) * c->g[i];
+ if (i == o1) s1 = s0;
+ if (i == o2) s2 = s0;
+ }
+
+ // Ideally should determine correlation to get a good equal power fade between tables
+ return s1 + (s2 - s1) * mix;
+}
+
+void tWaveset_setFreq(tWaveset* const cy, float freq)
+{
+ _tWaveset* c = *cy;
+ for (int i = 0; i < c->n; ++i)
+ {
+ tWavetable_setFreq(&c->wt[i], freq);
+ }
+}
+
+void tWaveset_setAntiAliasing(tWaveset* const cy, float aa)
+{
+ _tWaveset* c = *cy;
+ for (int i = 0; i < c->n; ++i)
+ {
+ tWavetable_setAntiAliasing(&c->wt[i], aa);
+ }
+}
+
+void tWaveset_setIndex(tWaveset* const cy, float index)
+{
+ _tWaveset* c = *cy;
+ c->index = index;
+}
+
+void tWaveset_setIndexGain(tWaveset* const cy, int i, float gain)
+{
+ _tWaveset* c = *cy;
+ c->g[i] = gain;
}