ref: 0400bc13cf30359da0fedf61cc0f24436ed1babe
parent: 32f24276cd8e22709bacbbbefabb659553c6f1e6
author: Matthew Wang <mjw7@princeton.edu>
date: Wed Jan 6 05:29:23 EST 2021
improved wavetable oscs; added tCompactWavetable
--- a/TestPlugin/LEAF.jucer
+++ b/TestPlugin/LEAF.jucer
@@ -109,8 +109,8 @@
<EXPORTFORMATS>
<XCODE_MAC targetFolder="Builds/MacOSX" microphonePermissionNeeded="1">
<CONFIGURATIONS>
- <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="OOPS" enablePluginBinaryCopyStep="1"/>
- <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="OOPS"
+ <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="LEAF" enablePluginBinaryCopyStep="1"/>
+ <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="LEAF"
enablePluginBinaryCopyStep="1"/>
</CONFIGURATIONS>
<MODULEPATHS>
@@ -136,8 +136,8 @@
iosBackgroundAudio="1" iPadScreenOrientation="UIInterfaceOrientationPortrait,UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight"
iosDeviceFamily="1,2">
<CONFIGURATIONS>
- <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="OOPS" enablePluginBinaryCopyStep="1"/>
- <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="OOPS"
+ <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="LEAF" enablePluginBinaryCopyStep="1"/>
+ <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="LEAF"
enablePluginBinaryCopyStep="1"/>
</CONFIGURATIONS>
<MODULEPATHS>
@@ -161,10 +161,10 @@
<VS2017 targetFolder="Builds/VisualStudio2017">
<CONFIGURATIONS>
<CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
- isDebug="1" optimisation="1" targetName="OOPS" debugInformationFormat="ProgramDatabase"
+ isDebug="1" optimisation="1" targetName="LEAF" debugInformationFormat="ProgramDatabase"
enablePluginBinaryCopyStep="0"/>
<CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
- isDebug="0" optimisation="3" targetName="OOPS" debugInformationFormat="ProgramDatabase"
+ isDebug="0" optimisation="3" targetName="LEAF" debugInformationFormat="ProgramDatabase"
enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/>
</CONFIGURATIONS>
<MODULEPATHS>
--- a/TestPlugin/Source/MyTest.cpp
+++ b/TestPlugin/Source/MyTest.cpp
@@ -9,7 +9,6 @@
#include "LEAFTest.h"
#include "MyTest.h"
-
static void leaf_pool_report(void);
static void leaf_pool_dump(void);
static void run_pool_test(void);
@@ -18,11 +17,6 @@
tMBTriangle btri;
tMBPulse bpulse;
-tRetune retune;
-tSimpleRetune sretune;
-
-tCompressor compressor;
-
tSVF lp, hp;
tPeriodDetection pd;
@@ -32,6 +26,12 @@
tTriangle tri;
+tNoise noise;
+tButterworth bw;
+
+tWavetable wt;
+tCompactWavetable cwt;
+
tBuffer samp;
tMBSampler sampler;
@@ -55,9 +55,8 @@
{
LEAF_init(&leaf, sampleRate, blockSize, memory, MSIZE, &getRandomFloat);
- tRetune_init(&retune, 1, mtof(48), mtof(72), 2048, &leaf);
- tSimpleRetune_init(&sretune, 1, mtof(48), mtof(72), 2048, &leaf);
- tSimpleRetune_setMode(&sretune, 1);
+ tWavetable_init(&wt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
+ tCompactWavetable_init(&cwt, __leaf_table_sawtooth[0], 2048, 10000.f, &leaf);
}
inline double getSawFall(double angle) {
@@ -72,7 +71,9 @@
float LEAFTest_tick (float input)
{
// return tRetune_tick(&retune, input)[0];
- return tSimpleRetune_tick(&sretune, input);
+// return tSimpleRetune_tick(&sretune, input);
+ return tWavetable_tick(&wt);
+ return tCompactWavetable_tick(&cwt);
}
int firstFrame = 1;
@@ -80,8 +81,10 @@
void LEAFTest_block (void)
{
float val = getSliderValue("slider1");
- tRetune_tuneVoice(&retune, 0, val * 3.0f + 0.5f);
- tSimpleRetune_tuneVoice(&sretune, 0, 300);
+ tWavetable_setFreq(&wt, val * 40000.);
+ tCompactWavetable_setFreq(&cwt, val * 10000.);
+// tRetune_tuneVoice(&retune, 0, val * 3.0f + 0.5f);
+// tSimpleRetune_tuneVoice(&sretune, 0, 300);
val = getSliderValue("slider2");
// tRetune_setPitchFactor(&retune, val * 3.0f + 0.5f, 1);
--- a/leaf/Inc/leaf-global.h
+++ b/leaf/Inc/leaf-global.h
@@ -17,6 +17,12 @@
#include "leaf-mempool.h"
+#if _WIN32 || _WIN64
+#include "..\leaf-config.h"
+#else
+#include "../leaf-config.h"
+#endif
+
/*!
* @ingroup leaf
* @brief Struct for an instance of LEAF.
--- a/leaf/Inc/leaf-math.h
+++ b/leaf/Inc/leaf-math.h
@@ -35,6 +35,7 @@
};
#define SQRT8 2.82842712475f
+#define LEAF_SQRT2 1.41421356237f
#define WSCALE 1.30612244898f
#define PI (3.14159265358979f)
#define TWO_PI (6.28318530717958f)
--- a/leaf/Inc/leaf-oscillators.h
+++ b/leaf/Inc/leaf-oscillators.h
@@ -17,8 +17,8 @@
#include "leaf-mempool.h"
#include "leaf-tables.h"
#include "leaf-filters.h"
+#include "leaf-distortion.h"
-
/*!
Header.
@include basic-oscillators.h
@@ -91,7 +91,7 @@
@brief Anti-aliased wavetable oscillator.
@{
- @fn void tWavetable_init (tTable* const osc, float* table, int size, LEAF* const leaf)
+ @fn void tWavetable_init (tWavetable* const osc, float* table, int size, 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.
@@ -98,7 +98,7 @@
@param size The number of samples in the wavetable.
@param leaf A pointer to the leaf instance.
- @fn void tWavetable_initToPool (tTable* const osc, float* table, int size, tMempool* const mempool)
+ @fn void tWavetable_initToPool (tWavetable* const osc, float* table, int size, 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.
@@ -105,16 +105,16 @@
@param size The number of samples in the wave table.
@param mempool A pointer to the tMempool to use.
- @fn void tWavetable_free (tTable* const osc)
+ @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 (tTable* const osc)
+ @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 (tTable* const osc, float freq)
+ @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.
@@ -134,6 +134,7 @@
int oct;
float w;
+ float aa;
tButterworth bl;
} _tWavetable;
@@ -140,12 +141,82 @@
typedef _tWavetable* tWavetable;
- void tWavetable_init(tWavetable* const osc, float* table, int size, LEAF* const leaf);
- void tWavetable_initToPool(tWavetable* const osc, float* table, int size, tMempool* const mempool);
+ 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, 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 leaf A pointer to the leaf instance.
+
+ @fn void tCompactWavetable_initToPool (tCompactWavetable* const osc, float* table, int size, 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 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);
//==============================================================================
--- a/leaf/Src/leaf-filters.c
+++ b/leaf/Src/leaf-filters.c
@@ -1058,9 +1058,9 @@
for(int i = 0; i < f->order; ++i)
{
if (f1 >= 0.0f)
- tSVF_initToPool(&f->svf[i], SVFTypeHighpass, f1, 0.5f/cosf((1.0f+2.0f*i)*PI/(2*f->order)), mp);
+ tSVF_initToPool(&f->svf[i], SVFTypeHighpass, f1, 0.5f/cosf((1.0f+2.0f*i)*PI/(4*f->order)), mp);
if (f2 >= 0.0f)
- tSVF_initToPool(&f->svf[i+o], SVFTypeLowpass, f2, 0.5f/cosf((1.0f+2.0f*i)*PI/(2*f->order)), mp);
+ tSVF_initToPool(&f->svf[i+o], SVFTypeLowpass, f2, 0.5f/cosf((1.0f+2.0f*i)*PI/(4*f->order)), mp);
}
}
@@ -1071,6 +1071,7 @@
for(int i = 0; i < f->numSVF; ++i)
tSVF_free(&f->svf[i]);
+ mpool_free((char*)f->svf, f->mempool);
mpool_free((char*)f, f->mempool);
}
--- a/leaf/Src/leaf-oscillators.c
+++ b/leaf/Src/leaf-oscillators.c
@@ -86,15 +86,15 @@
c->inc = c->freq * leaf->invSampleRate;
}
-void tWavetable_init(tWavetable* const cy, float* table, int size, LEAF* const leaf)
+void tWavetable_init(tWavetable* const cy, const float* table, int size, float maxFreq, LEAF* const leaf)
{
- tWavetable_initToPool(cy, table, size, &leaf->mempool);
+ tWavetable_initToPool(cy, table, size, maxFreq, &leaf->mempool);
}
-void tWavetable_initToPool(tWavetable* const cy, float* table, int size, tMempool* const mp)
+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(_tTable), m);
+ _tWavetable* c = *cy = (_tWavetable*) mpool_alloc(sizeof(_tWavetable), m);
c->mempool = m;
// Determine base frequency
@@ -102,12 +102,13 @@
c->invBaseFreq = 1.0f / c->baseFreq;
// Determine how many tables we need
- c->numTables = 1;
+ // 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 < 20000.f)
+ while (f < maxFreq)
{
c->numTables++;
- f *= 2.0f; // pass this multiplier in to set spacing of tables?
+ f *= 2.0f; // pass this multiplier in to set spacing of tables? would need to change setFreq too
}
c->size = size;
@@ -125,26 +126,36 @@
c->tables[0][i] = table[i];
}
- // Make bandlimited copies at
- f = m->leaf->sampleRate * 0.25f; //start at half nyquist
+ // 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_initToPool(&c->bl, 4, -1.0f, f, mp);
- for (int i = 0; i < c->size; ++i)
+ 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)
{
- c->tables[t][i] = tButterworth_tick(&c->bl, table[i]);
+ for (int i = 0; i < c->size; ++i)
+ {
+ c->tables[t][i] = tButterworth_tick(&c->bl, c->tables[t-1][i]);
+ }
}
- tButterworth_free(&c->bl);
- f *= 0.5f; //half the cutoff for next pass
+ 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)
+void tWavetable_free(tWavetable* const cy)
{
_tWavetable* c = *cy;
@@ -153,17 +164,16 @@
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)
+float tWavetable_tick(tWavetable* const cy)
{
_tWavetable* c = *cy;
float temp;
int idx;
- float fracPart;
+ float frac;
float samp0;
float samp1;
@@ -174,18 +184,26 @@
// Wavetable synthesis
temp = c->size * c->phase;
+
idx = (int)temp;
- fracPart = temp - (float)idx;
- samp0 = c->tables[c->oct+1][idx] +
- (c->tables[c->oct][idx] - c->tables[c->oct+1][idx]) * c->w;
+ frac = temp - (float)idx;
+ samp0 = c->tables[c->oct][idx];
if (++idx >= c->size) idx = 0;
- samp1 = c->tables[c->oct+1][idx] +
- (c->tables[c->oct][idx] - c->tables[c->oct+1][idx]) * c->w;
+ samp1 = c->tables[c->oct][idx];
- return (samp0 + (samp1 - samp0) * fracPart);
+ 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)
+void tWavetable_setFreq(tWavetable* const cy, float freq)
{
_tWavetable* c = *cy;
@@ -195,14 +213,175 @@
c->inc = c->freq * leaf->invSampleRate;
- c->w = c->freq * c->invBaseFreq;
- for (c->oct = 0; c->w > 2.0f; c->oct++)
+ // 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->w = 0.5f * c->w;
+ c->numTables++;
+ f *= 2.0f; // pass this multiplier in to set spacing of tables?
}
- c->w = 2.0f - c->w;
+
+ // 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;
+ while (c->phase >= 1.0f) c->phase -= 1.0f;
+ while (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;
+
+ // 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)
@@ -243,9 +422,9 @@
float tCycle_tick(tCycle* const cy)
{
_tCycle* c = *cy;
- float temp;;
- int intPart;;
- float fracPart;
+ float temp;
+ int idx;
+ float frac;
float samp0;
float samp1;
@@ -257,13 +436,13 @@
// Wavetable synthesis
temp = SINE_TABLE_SIZE * c->phase;
- intPart = (int)temp;
- fracPart = temp - (float)intPart;
- samp0 = __leaf_table_sinewave[intPart];
- if (++intPart >= SINE_TABLE_SIZE) intPart = 0;
- samp1 = __leaf_table_sinewave[intPart];
+ 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) * fracPart);
+ return (samp0 + (samp1 - samp0) * frac);
}
void tCycleSampleRateChanged (tCycle* const cy)
@@ -310,12 +489,14 @@
c->inc = c->freq * leaf->invSampleRate;
- c->w = c->freq * INV_20;
- for (c->oct = 0; c->w > 2.0f; c->oct++)
- {
- c->w = 0.5f * c->w;
- }
- c->w = 2.0f - c->w;
+ // 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;
}
@@ -323,20 +504,36 @@
{
_tTriangle* 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 = TRI_TABLE_SIZE * c->phase;
- float out = 0.0f;
+ 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];
- int idx = (int)(c->phase * TRI_TABLE_SIZE);
+ float oct0 = (samp0 + (samp1 - samp0) * frac);
- // Wavetable synthesis
- out = __leaf_table_triangle[c->oct+1][idx] +
- (__leaf_table_triangle[c->oct][idx] - __leaf_table_triangle[c->oct+1][idx]) * c->w;
+ 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];
- return out;
+ float oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ return oct0 + (oct1 - oct0) * c->w;
}
void tTriangleSampleRateChanged (tTriangle* const cy)
@@ -383,12 +580,14 @@
c->inc = c->freq * leaf->invSampleRate;
- c->w = c->freq * INV_20;
- for (c->oct = 0; c->w > 2.0f; c->oct++)
- {
- c->w = 0.5f * c->w;
- }
- c->w = 2.0f - c->w;
+ // 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)
@@ -395,20 +594,36 @@
{
_tSquare* 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 = SQR_TABLE_SIZE * c->phase;
- float out = 0.0f;
+ 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];
- int idx = (int)(c->phase * SQR_TABLE_SIZE);
+ float oct0 = (samp0 + (samp1 - samp0) * frac);
- // Wavetable synthesis
- out = __leaf_table_squarewave[c->oct+1][idx] +
- (__leaf_table_squarewave[c->oct][idx] - __leaf_table_squarewave[c->oct+1][idx]) * c->w;
+ 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];
- return out;
+ float oct1 = (samp0 + (samp1 - samp0) * frac);
+
+ return oct0 + (oct1 - oct0) * c->w;
}
void tSquareSampleRateChanged (tSquare* const cy)
@@ -455,13 +670,14 @@
c->inc = c->freq * leaf->invSampleRate;
- // base freq 20
- c->w = c->freq * INV_20;
- for (c->oct = 0; c->w > 2.0f; c->oct++)
- {
- c->w = 0.5f * c->w;
- }
- c->w = 2.0f - c->w;
+ // 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)
@@ -468,20 +684,36 @@
{
_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;
- float out = 0.0f;
+ // Wavetable synthesis
+ temp = SAW_TABLE_SIZE * c->phase;
- int idx = (int)(c->phase * SAW_TABLE_SIZE);
+ 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];
- // Wavetable synthesis
- out = __leaf_table_sawtooth[c->oct+1][idx] +
- (__leaf_table_sawtooth[c->oct][idx] - __leaf_table_sawtooth[c->oct+1][idx]) * c->w;
+ float oct0 = (samp0 + (samp1 - samp0) * frac);
- return out;
+ 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)
--- a/leaf/leaf-config.h
+++ b/leaf/leaf-config.h
@@ -18,7 +18,7 @@
//==============================================================================
-//! Include FIR tables required to use tOversampler.
+//! Include FIR tables required to use tOversampler and tCompactWavetable which uses tOversampler.
#define LEAF_INCLUDE_OVERSAMPLER_TABLES 1
// Unused