Skip to content

Commit

Permalink
Don't use juce lookuptable, it's slow as bawls
Browse files Browse the repository at this point in the history
  • Loading branch information
FigBug committed Aug 25, 2023
1 parent 96f0293 commit 07b7cc5
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 79 deletions.
1 change: 1 addition & 0 deletions modules/gin_dsp/components/gin_wavetablecomponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ void WavetableComponent::paint (juce::Graphics& g)
{
needsUpdate = false;

paths.clear();
auto numTables = std::min (32, bllt->size());
for (auto i = 0; i < numTables; i++)
paths.add (createWavetablePath (float (i) / numTables));
Expand Down
86 changes: 41 additions & 45 deletions modules/gin_dsp/dsp/gin_bandlimitedlookuptable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,86 +17,86 @@ inline int oddEven (int x)
}

//==============================================================================
double sine (double phase, double, double)
float sine (float phase, float, float)
{
return std::sin (phase * 2 * juce::MathConstants<double>::pi);
return std::sin (phase * 2 * juce::MathConstants<float>::pi);
}

double triangle (double phase, double freq, double sampleRate)
float triangle (float phase, float freq, float sampleRate)
{
double sum = 0;
float k = 1;
float sum = 0.0f;
float k = 1.0f;
while (freq * k < sampleRate / 2)
{
sum += std::pow (-1, (k - 1) / 2.0f) / (k * k) * std::sin (k * (phase * 2 * juce::MathConstants<double>::pi));
sum += std::pow (-1.0f, (k - 1) / 2.0f) / (k * k) * std::sin (k * (phase * 2 * juce::MathConstants<float>::pi));
k += 2;
}
return float (8.0f / (juce::MathConstants<float>::pi * juce::MathConstants<float>::pi) * sum);
}

double sawUp (double phase, double freq, double sampleRate)
float sawUp (float phase, float freq, float sampleRate)
{
double sum = 0;
float sum = 0;
int k = 1;
float fk = 1.0f;
while (freq * k < sampleRate / 2)
{
sum += oddEven (k) * std::sin (fk * (phase * 2 * juce::MathConstants<double>::pi)) / fk;
sum += oddEven (k) * std::sin (fk * (phase * 2 * juce::MathConstants<float>::pi)) / fk;
k++;
fk++;
}
return float (-2.0f / juce::MathConstants<float>::pi * sum);
}

double sawDown (double phase, double freq, double sampleRate)
float sawDown (float phase, float freq, float sampleRate)
{
double sum = 0;
float sum = 0;
int k = 1;
float fk = 1.0f;
while (freq * k < sampleRate / 2)
{
sum += oddEven (k) * std::sin (fk * (phase * 2 * juce::MathConstants<double>::pi)) / fk;
sum += oddEven (k) * std::sin (fk * (phase * 2 * juce::MathConstants<float>::pi)) / fk;
k++;
fk++;
}
return float (2.0f / juce::MathConstants<float>::pi * sum);
}

double pulse (double phase, double pw, double freq, double sampleRate)
float pulse (float phase, float pw, float freq, float sampleRate)
{
if (pw == 0.5)
{
double sum = 0;
float sum = 0;
float i = 1;
while (freq * (2 * i - 1) < sampleRate / 2)
{
sum += std::sin ((2 * i - 1) * (phase * 2 * juce::MathConstants<double>::pi)) / ((2 * i - 1));
sum += std::sin ((2 * i - 1) * (phase * 2 * juce::MathConstants<float>::pi)) / ((2 * i - 1));
i++;
}

return float (4.0f / juce::MathConstants<float>::pi * sum);
}
else
{
pw = juce::jlimit (0.05, 0.95, pw);
return sawUp (phase + 0.5 * pw, freq, sampleRate) - sawUp (phase - 0.5 * pw, freq, sampleRate);
pw = juce::jlimit (0.05f, 0.95f, pw);
return sawUp (phase + 0.5f * pw, freq, sampleRate) - sawUp (phase - 0.5f * pw, freq, sampleRate);
}
}

double squareWave (double phase, double freq, double sampleRate)
float squareWave (float phase, float freq, float sampleRate)
{
double sum = 0;
float sum = 0;
float i = 1;
while (freq * (2 * i - 1) < sampleRate / 2)
{
sum += std::sin ((2 * i - 1) * (phase * 2 * juce::MathConstants<double>::pi)) / ((2 * i - 1));
sum += std::sin ((2 * i - 1) * (phase * 2 * juce::MathConstants<float>::pi)) / ((2 * i - 1));
i++;
}

return float (4.0f / juce::MathConstants<float>::pi * sum);
}

double noise()
float noise()
{
const float mean = 0.0f;
const float stddev = 0.1f;
Expand All @@ -108,31 +108,28 @@ double noise()
}

//==============================================================================
void BandLimitedLookupTable::loadFromBuffer (juce::AudioSampleBuffer& buffer, double sampleRate, int notesPerTable_)
void BandLimitedLookupTable::loadFromBuffer (juce::AudioSampleBuffer& buffer, float sampleRate, int notesPerTable_)
{
tables.clear();

double duration = buffer.getNumSamples() / sampleRate;
double baseFreq = 1.0 / duration;
float duration = buffer.getNumSamples() / sampleRate;
float baseFreq = 1.0f / duration;
int sz = buffer.getNumSamples();

tableSize = sz;
notesPerTable = notesPerTable_;

juce::dsp::FFT fft (juce::roundToInt (std::log2 (sz)));
jassert (fft.getSize() == sz);

for (double note = notesPerTable + 0.5; note < 127.0; note += notesPerTable)
for (float note = notesPerTable + 0.5f; note < 127.0f; note += notesPerTable)
{
auto noteFreq = getMidiNoteInHertz (note);
if (noteFreq < baseFreq)
{
auto func = [&] (float phase)
{
auto data = buffer.getReadPointer (0);
return data[int (phase * float (sz)) % sz];
};

tables.add (new juce::dsp::LookupTableTransform<float> (func, 0.0f, 1.0f, (size_t) sz + 1));
auto& t = tables.emplace_back (std::vector<float>());
t.resize (sz);
std::memcpy (t.data(), buffer.getReadPointer (0), sz * sizeof (float));
}
else
{
Expand Down Expand Up @@ -161,12 +158,11 @@ void BandLimitedLookupTable::loadFromBuffer (juce::AudioSampleBuffer& buffer, do

fft.perform (freq.data(), time.data(), true);

auto func = [&] (float phase)
{
return time[int (phase * float(sz)) % sz].real();
};
auto& t = tables.emplace_back (std::vector<float>());
t.resize (sz);

tables.add (new juce::dsp::LookupTableTransform<float> (func, 0.0f, 1.0f, (size_t) sz + 1));
for (auto i = 0; i < sz; i++)
t[i] = time[i].real();
}
}
}
Expand All @@ -176,10 +172,10 @@ BandLimitedLookupTables::BandLimitedLookupTables (double sampleRate_, int notesP
: sampleRate (sampleRate_),
notesPerTable (notesPerTable_),
tableSize (tableSize_),
sineTable (sine, sampleRate, 64, tableSize),
sawUpTable (sawUp, sampleRate, notesPerTable, tableSize),
sawDownTable (sawDown, sampleRate, notesPerTable, tableSize),
triangleTable (triangle, sampleRate, notesPerTable, tableSize)
sineTable (sine, float (sampleRate), 64, tableSize),
sawUpTable (sawUp, float (sampleRate), notesPerTable, tableSize),
sawDownTable (sawDown, float (sampleRate), notesPerTable, tableSize),
triangleTable (triangle, float (sampleRate), notesPerTable, tableSize)
{

}
Expand All @@ -189,9 +185,9 @@ void BandLimitedLookupTables::setSampleRate (double sampleRate_)
if (! juce::approximatelyEqual (sampleRate, sampleRate_))
{
sampleRate = sampleRate_;
sineTable.reset (sine, sampleRate, 64, tableSize);
sawUpTable.reset (sawUp, sampleRate, notesPerTable, tableSize);
sawDownTable.reset (sawDown, sampleRate, notesPerTable, tableSize);
triangleTable.reset (triangle, sampleRate, notesPerTable, tableSize);
sineTable.reset (sine, float (sampleRate), 64, tableSize);
sawUpTable.reset (sawUp, float (sampleRate), notesPerTable, tableSize);
sawDownTable.reset (sawDown, float (sampleRate), notesPerTable, tableSize);
triangleTable.reset (triangle, float (sampleRate), notesPerTable, tableSize);
}
}
83 changes: 50 additions & 33 deletions modules/gin_dsp/dsp/gin_bandlimitedlookuptable.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
#pragma once

//==============================================================================
double sine (double phase, double unused1 = 0, double unused2 = 0);
double triangle (double phase, double freq, double sampleRate);
double sawUp (double phase, double freq, double sampleRate);
double sawDown (double phase, double freq, double sampleRate);
double pulse (double phase, double pw, double freq, double sampleRate);
double squareWave (double phase, double freq, double sampleRate);
double noise();
float sine (float phase, float unused1 = 0, float unused2 = 0);
float triangle (float phase, float freq, float sampleRate);
float sawUp (float phase, float freq, float sampleRate);
float sawDown (float phase, float freq, float sampleRate);
float pulse (float phase, float pw, float freq, float sampleRate);
float squareWave (float phase, float freq, float sampleRate);
float noise();

//==============================================================================
/** Lookup tables for holding bandlimited waveforms. Holds one waveform for every N number notes
Expand All @@ -27,42 +27,59 @@ class BandLimitedLookupTable
public:
BandLimitedLookupTable() = default;

BandLimitedLookupTable (std::function<double (double, double, double)> function, double sampleRate,
BandLimitedLookupTable (std::function<float (float, float, float)> function, float sampleRate,
int notesPerTable_ = 6, int tableSize_ = 2048)
{
reset (function, sampleRate, notesPerTable_, tableSize_);
}

void reset (std::function<double (double, double, double)> function, double sampleRate,
int notesPerTable_ = 6, int tableSize = 2048)
void reset (std::function<float (float, float, float)> function, float sampleRate,
int notesPerTable_ = 6, int tableSize_ = 2048)
{
tables.clear();

tableSize = tableSize_;
notesPerTable = notesPerTable_;

for (double note = notesPerTable + 0.5; note < 127.0; note += notesPerTable)
for (float note = notesPerTable + 0.5f; note < 127.0f; note += notesPerTable)
{
auto freq = getMidiNoteInHertz (note);

auto func = [function, freq, sampleRate] (float phase) -> float
{
return float (function (phase, freq, sampleRate));
};
auto& t = tables.emplace_back (std::vector<float>());
t.resize (tableSize);

tables.add (new juce::dsp::LookupTableTransform<float> (func, 0.0f, 1.0f, (size_t) tableSize));
for (auto i = 0; i < tableSize; i++)
{
auto v = juce::jmap (float (i), 0.0f, tableSize - 1.0f, 0.0f, 1.0f);
t[i] = function (v, freq, sampleRate);
}
}
}

inline float process (float note, float phase)
{
int tableIndex = juce::jlimit (0, tables.size() - 1, int ((note - 0.5) / notesPerTable));
return tables[tableIndex]->processSampleUnchecked (phase);
auto tableIndex = juce::jlimit (0, int (tables.size() - 1), int ((note - 0.5) / notesPerTable));
auto pos = int (phase * tableSize);

jassert (pos >= 0 && pos < tableSize);

return tables[tableIndex][pos];
}

inline float get (int tableIndex, float phase)
{
auto pos = int (phase * tableSize);

jassert (pos >= 0 && pos < tableSize);

return tables[tableIndex][pos];
}

void loadFromBuffer (juce::AudioSampleBuffer& buffer, double sampleRate, int notesPerTable);
void loadFromBuffer (juce::AudioSampleBuffer& buffer, float sampleRate, int notesPerTable);

juce::OwnedArray<juce::dsp::LookupTableTransform<float>> tables;
std::vector<std::vector<float>> tables;

int tableSize = 0;
int notesPerTable = 0;
};

Expand Down Expand Up @@ -92,25 +109,25 @@ class BandLimitedLookupTables

inline float processSine (float phase)
{
return sineTable.tables[0]->processSampleUnchecked (phase);
return sineTable.get (0, phase);
}

inline float processTriangle (float note, float phase)
{
int tableIndex = juce::jlimit (0, triangleTable.tables.size() - 1, int ((note - 0.5) / triangleTable.notesPerTable));
return triangleTable.tables[tableIndex]->processSampleUnchecked (phase);
int tableIndex = juce::jlimit (0, int (triangleTable.tables.size() - 1), int ((note - 0.5) / triangleTable.notesPerTable));
return triangleTable.get (tableIndex, phase);
}

inline float processSawUp (float note, float phase)
{
int tableIndex = juce::jlimit (0, sawUpTable.tables.size() - 1, int ((note - 0.5) / sawUpTable.notesPerTable));
return sawUpTable.tables[tableIndex]->processSampleUnchecked (phase);
int tableIndex = juce::jlimit (0, int (sawUpTable.tables.size() - 1), int ((note - 0.5) / sawUpTable.notesPerTable));
return sawUpTable.get (tableIndex, phase);
}

inline float processSawDown (float note, float phase)
{
int tableIndex = juce::jlimit (0, sawDownTable.tables.size() - 1, int ((note - 0.5) / sawDownTable.notesPerTable));
return sawDownTable.tables[tableIndex]->processSampleUnchecked (phase);
int tableIndex = juce::jlimit (0, int (sawDownTable.tables.size() - 1), int ((note - 0.5) / sawDownTable.notesPerTable));
return sawDownTable.get (tableIndex, phase);
}

inline float processSquare (float note, float phase)
Expand All @@ -122,10 +139,10 @@ class BandLimitedLookupTables
if (phaseDown < 0.0f) phaseDown += 1.0f;

auto count = std::min (sawDownTable.tables.size(), sawDownTable.tables.size());
int tableIndex = juce::jlimit (0, count - 1, int ((note - 0.5) / count));
int tableIndex = juce::jlimit (0, int (count - 1), int ((note - 0.5) / count));

auto s1 = sawDownTable.tables[tableIndex]->processSampleUnchecked (phaseDown);
auto s2 = sawUpTable.tables[tableIndex]->processSampleUnchecked (phaseUp);
auto s1 = sawDownTable.get (tableIndex, phase);
auto s2 = sawUpTable.get (tableIndex, phase);

return s1 + s2;
}
Expand All @@ -139,10 +156,10 @@ class BandLimitedLookupTables
if (phaseDown < 0.0f) phaseDown += 1.0f;

auto count = std::min (sawDownTable.tables.size(), sawDownTable.tables.size());
int tableIndex = juce::jlimit (0, count - 1, int ((note - 0.5) / count));
int tableIndex = juce::jlimit (0, int (count - 1), int ((note - 0.5) / count));

auto s1 = sawDownTable.tables[tableIndex]->processSampleUnchecked (phaseDown);
auto s2 = sawUpTable.tables[tableIndex]->processSampleUnchecked (phaseUp);
auto s1 = sawDownTable.get (tableIndex, phase);
auto s2 = sawUpTable.get (tableIndex, phase);

return s1 + s2;
}
Expand Down
2 changes: 1 addition & 1 deletion modules/gin_dsp/dsp/gin_wtoscillators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ bool loadWavetables (juce::OwnedArray<BandLimitedLookupTable>& bllt, juce::Audio
auto slice = sliceBuffer (buffer, i * tableSize, tableSize);

auto table = new BandLimitedLookupTable();
table->loadFromBuffer (slice, sampleRate, 6);
table->loadFromBuffer (slice, float (sampleRate), 6);
bllt.add (table);
}
return true;
Expand Down

0 comments on commit 07b7cc5

Please sign in to comment.