From 07b7cc59960212fe8f4257212927697e0d1b3ce6 Mon Sep 17 00:00:00 2001 From: Roland Rabien Date: Fri, 25 Aug 2023 14:31:02 -0700 Subject: [PATCH] Don't use juce lookuptable, it's slow as bawls --- .../components/gin_wavetablecomponent.cpp | 1 + .../dsp/gin_bandlimitedlookuptable.cpp | 86 +++++++++---------- .../gin_dsp/dsp/gin_bandlimitedlookuptable.h | 83 +++++++++++------- modules/gin_dsp/dsp/gin_wtoscillators.cpp | 2 +- 4 files changed, 93 insertions(+), 79 deletions(-) diff --git a/modules/gin_dsp/components/gin_wavetablecomponent.cpp b/modules/gin_dsp/components/gin_wavetablecomponent.cpp index 5de5d1bff1..8a204bcaed 100644 --- a/modules/gin_dsp/components/gin_wavetablecomponent.cpp +++ b/modules/gin_dsp/components/gin_wavetablecomponent.cpp @@ -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)); diff --git a/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.cpp b/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.cpp index bb0c567065..27744deada 100644 --- a/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.cpp +++ b/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.cpp @@ -17,60 +17,60 @@ inline int oddEven (int x) } //============================================================================== -double sine (double phase, double, double) +float sine (float phase, float, float) { - return std::sin (phase * 2 * juce::MathConstants::pi); + return std::sin (phase * 2 * juce::MathConstants::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::pi)); + sum += std::pow (-1.0f, (k - 1) / 2.0f) / (k * k) * std::sin (k * (phase * 2 * juce::MathConstants::pi)); k += 2; } return float (8.0f / (juce::MathConstants::pi * juce::MathConstants::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::pi)) / fk; + sum += oddEven (k) * std::sin (fk * (phase * 2 * juce::MathConstants::pi)) / fk; k++; fk++; } return float (-2.0f / juce::MathConstants::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::pi)) / fk; + sum += oddEven (k) * std::sin (fk * (phase * 2 * juce::MathConstants::pi)) / fk; k++; fk++; } return float (2.0f / juce::MathConstants::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::pi)) / ((2 * i - 1)); + sum += std::sin ((2 * i - 1) * (phase * 2 * juce::MathConstants::pi)) / ((2 * i - 1)); i++; } @@ -78,25 +78,25 @@ double pulse (double phase, double pw, double freq, double sampleRate) } 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::pi)) / ((2 * i - 1)); + sum += std::sin ((2 * i - 1) * (phase * 2 * juce::MathConstants::pi)) / ((2 * i - 1)); i++; } return float (4.0f / juce::MathConstants::pi * sum); } -double noise() +float noise() { const float mean = 0.0f; const float stddev = 0.1f; @@ -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 (func, 0.0f, 1.0f, (size_t) sz + 1)); + auto& t = tables.emplace_back (std::vector()); + t.resize (sz); + std::memcpy (t.data(), buffer.getReadPointer (0), sz * sizeof (float)); } else { @@ -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()); + t.resize (sz); - tables.add (new juce::dsp::LookupTableTransform (func, 0.0f, 1.0f, (size_t) sz + 1)); + for (auto i = 0; i < sz; i++) + t[i] = time[i].real(); } } } @@ -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) { } @@ -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); } } diff --git a/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.h b/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.h index f07d2cc0c2..baf15f13ea 100644 --- a/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.h +++ b/modules/gin_dsp/dsp/gin_bandlimitedlookuptable.h @@ -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 @@ -27,42 +27,59 @@ class BandLimitedLookupTable public: BandLimitedLookupTable() = default; - BandLimitedLookupTable (std::function function, double sampleRate, + BandLimitedLookupTable (std::function function, float sampleRate, int notesPerTable_ = 6, int tableSize_ = 2048) { reset (function, sampleRate, notesPerTable_, tableSize_); } - void reset (std::function function, double sampleRate, - int notesPerTable_ = 6, int tableSize = 2048) + void reset (std::function 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()); + t.resize (tableSize); - tables.add (new juce::dsp::LookupTableTransform (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> tables; + std::vector> tables; + int tableSize = 0; int notesPerTable = 0; }; @@ -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) @@ -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; } @@ -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; } diff --git a/modules/gin_dsp/dsp/gin_wtoscillators.cpp b/modules/gin_dsp/dsp/gin_wtoscillators.cpp index 997081fd87..0d8ad307ac 100644 --- a/modules/gin_dsp/dsp/gin_wtoscillators.cpp +++ b/modules/gin_dsp/dsp/gin_wtoscillators.cpp @@ -139,7 +139,7 @@ bool loadWavetables (juce::OwnedArray& 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;