diff --git a/Changelist.txt b/Changelist.txt index 976dfc3..56b0778 100644 --- a/Changelist.txt +++ b/Changelist.txt @@ -7,6 +7,7 @@ - Draw all phase dots for LFO and Step LFO - Try and make delay less zippery (WIP) - Scroll wheel can adjust combo boxes +- Fixed presets getting deleted when renaming 1.0.3: diff --git a/plugin/Resources/layout.json b/plugin/Resources/layout.json index 23e4dbe..7fdf7d3 100644 --- a/plugin/Resources/layout.json +++ b/plugin/Resources/layout.json @@ -13,7 +13,8 @@ { "id": "Spread", "x": "prevR()", "y": "prevY()", "w": 56, "h": 70 }, { "id": "Formant", "x": "prevR()", "y": "prevY()", "w": 56, "h": 70 }, { "id": "Bend", "x": "prevR()", "y": "prevY()", "w": 56, "h": 70 }, - { "id": "wt", "x": "prevR()", "y": 23, "r": "parW()", "b": "parH()" } + { "id": "wt", "x": "prevR()", "y": 23, "r": "parW()", "b": "parH()" }, + { "id": "add", "r": "parW() - 8", "b": "parH() - 8", "w": 16, "h": 16 } ] }, { "id": "noise", "x": "0", "y": "prevB()+1", "w": 120, "h": 163, "children": diff --git a/plugin/Source/Panels.h b/plugin/Source/Panels.h index fef200a..af65802 100644 --- a/plugin/Source/Panels.h +++ b/plugin/Source/Panels.h @@ -24,25 +24,41 @@ public: addEnable (osc.enable); - addControl (new gin::Knob (osc.pos), 0, 0); - addControl (new gin::Knob (osc.tune, true), 1, 0); - addControl (new gin::Knob (osc.finetune, true), 2, 0); - addControl (new gin::Knob (osc.level), 3, 0); - addControl (new gin::Knob (osc.pan, true), 4, 0); + addControl (new gin::Knob (osc.pos)); + addControl (new gin::Knob (osc.tune, true)); + addControl (new gin::Knob (osc.finetune, true)); + addControl (new gin::Knob (osc.level)); + addControl (new gin::Knob (osc.pan, true)); - addControl (new gin::Select (osc.voices), 0, 1); - addControl (detune = new gin::Knob (osc.detune), 1, 1); - addControl (spread = new gin::Knob (osc.spread), 2, 1); - addControl (new gin::Knob (osc.formant, true), 3, 1); - addControl (new gin::Knob (osc.bend, true), 4, 1); + addControl (new gin::Select (osc.voices)); + addControl (detune = new gin::Knob (osc.detune)); + addControl (spread = new gin::Knob (osc.spread)); + addControl (new gin::Knob (osc.formant, true)); + addControl (new gin::Knob (osc.bend, true)); watchParam (osc.voices); wt = new gin::WavetableComponent(); wt->setName ("wt"); wt->setWavetables (idx == 0 ? &proc.osc1Tables : &proc.osc2Tables); - wt->onFileDrop = [this] (const juce::File& f) { proc.loadUserWavetable (idx, f); }; - addControl (wt, 5, 0, 3, 2); + wt->onFileDrop = [this] (const juce::File& f) { loadUserWavetable (f); }; + addControl (wt); + + addAndMakeVisible (addButton); + addButton.onClick = [this] + { + auto chooser = std::make_shared (juce::String ("Load Wavetable"), juce::File(), juce::String ("*.wav")); + + auto chooserFlags = juce::FileBrowserComponent::canSelectFiles | juce::FileBrowserComponent::openMode; + chooser->launchAsync (chooserFlags, [this, chooser] (const juce::FileChooser&) + { + auto f = chooser->getResult(); + if (! f.existsAsFile()) + return; + + loadUserWavetable (f); + } ); + }; timer.startTimerHz (60); timer.onTimer = [this] @@ -66,6 +82,34 @@ public: proc.osc2Table.removeListener (this); } + void loadUserWavetable (const juce::File& f) + { + auto sz = gin::getWavetableSize (f); + if (sz <= 0) + { + auto w = std::make_shared ("Import Wavetable", "Wav file does not contain Wavetable metadata. What is the wavetable size?", juce::AlertWindow::NoIcon, getParentComponent()); + w->setLookAndFeel (proc.lf.get()); + w->addComboBox ("size", { "256", "512", "1024", "2048" }, "Samples per table:"); + w->getComboBoxComponent ("size")->setSelectedItemIndex (3); + + w->addButton ("OK", 1, juce::KeyPress (juce::KeyPress::returnKey)); + w->addButton ("Cancel", 0, juce::KeyPress (juce::KeyPress::escapeKey)); + + w->runAsync (*getParentComponent(), [this, w, f] (int ret) + { + auto userSize = w->getComboBoxComponent ("size")->getText().getIntValue(); + + w->setVisible (false); + if (ret == 1) + proc.loadUserWavetable (idx, f, userSize); + }); + } + else + { + proc.loadUserWavetable (idx, f, -1); + } + } + void mouseUp (const juce::MouseEvent& e) override { auto& h = getHeader(); @@ -145,6 +189,7 @@ public: gin::SVGButton nextButton { "next", gin::Assets::next }; gin::SVGButton prevButton { "prev", gin::Assets::prev }; + gin::SVGButton addButton { "add", gin::Assets::add }; }; //============================================================================== diff --git a/plugin/Source/PluginProcessor.cpp b/plugin/Source/PluginProcessor.cpp index e460ae8..bedf7b1 100644 --- a/plugin/Source/PluginProcessor.cpp +++ b/plugin/Source/PluginProcessor.cpp @@ -543,23 +543,23 @@ void WavetableAudioProcessor::reloadWavetables() if (userTable1.getSize() > 0) { if (shouldLoad (0, osc1Table.toString(), sr)) - loadWaveTable (osc1Tables, sr, userTable1, "wav"); + loadWaveTable (osc1Tables, sr, userTable1, "wav", osc1Size); } else if (auto mb = loadMemory (osc1Table.toString()); mb.getSize() > 0) { if (shouldLoad (0, osc1Table.toString(), sr)) - loadWaveTable (osc1Tables, sr, mb, "flac"); + loadWaveTable (osc1Tables, sr, mb, "flac", osc1Size); } if (userTable2.getSize() > 0) { if (shouldLoad (0, osc2Table.toString(), sr)) - loadWaveTable (osc2Tables, sr, userTable2, "wav"); + loadWaveTable (osc2Tables, sr, userTable2, "wav", osc2Size); } else if (auto mb = loadMemory (osc2Table.toString()); mb.getSize() > 0) { if (shouldLoad (1, osc2Table.toString(), sr)) - loadWaveTable (osc2Tables, sr, mb, "flac"); + loadWaveTable (osc2Tables, sr, mb, "flac", osc2Size); } } @@ -590,20 +590,24 @@ void WavetableAudioProcessor::incWavetable (int osc, int delta) reloadWavetables(); } -void WavetableAudioProcessor::loadUserWavetable (int osc, const juce::File f) +bool WavetableAudioProcessor::loadUserWavetable (int osc, const juce::File& f, int sz) { auto& table = osc == 0 ? osc1Tables : osc2Tables; auto& mb = osc == 0 ? userTable1 : userTable2; auto& name = osc == 0 ? osc1Table : osc2Table; + auto& size = osc == 0 ? osc1Size : osc2Size; juce::MemoryBlock raw; f.loadFileAsData (raw); - if (loadWaveTable (table, gin::Processor::getSampleRate(), raw, "wav")) + if (loadWaveTable (table, gin::Processor::getSampleRate(), raw, "wav", sz)) { mb = raw; name = f.getFileNameWithoutExtension(); + size = sz; + return true; } + return false; } //============================================================================== @@ -614,6 +618,9 @@ void WavetableAudioProcessor::stateUpdated() osc1Table = state.getProperty ("wt1"); osc2Table = state.getProperty ("wt2"); + osc1Size = state.getProperty ("wt1Size", -1); + osc2Size = state.getProperty ("wt2Size", -1); + userTable1.reset(); userTable2.reset(); @@ -632,6 +639,9 @@ void WavetableAudioProcessor::updateState() state.setProperty ("wt1", osc1Table, nullptr); state.setProperty ("wt2", osc2Table, nullptr); + state.setProperty ("wt1Size", osc1Size, nullptr); + state.setProperty ("wt2Size", osc2Size, nullptr); + state.setProperty ("wt1Data", userTable1.toBase64Encoding(), nullptr); state.setProperty ("wt2Data", userTable2.toBase64Encoding(), nullptr); } @@ -979,7 +989,7 @@ void WavetableAudioProcessor::updateParams (int newBlockSize) outputGain.setGain (modMatrix.getValue (globalParams.level)); } -bool WavetableAudioProcessor::loadWaveTable (juce::OwnedArray& table, double sr, const juce::MemoryBlock& wav, const juce::String& format) +bool WavetableAudioProcessor::loadWaveTable (juce::OwnedArray& table, double sr, const juce::MemoryBlock& wav, const juce::String& format, int size) { auto is = new juce::MemoryInputStream (wav, false); @@ -987,14 +997,22 @@ bool WavetableAudioProcessor::loadWaveTable (juce::OwnedArray (juce::WavAudioFormat().createReaderFor (is, true))) { - if (auto sz = gin::getWavetableSize (wav); sz > 0) + if (size <= 0) + size = gin::getWavetableSize (wav); + + if (size > 0) { - juce::AudioSampleBuffer buf (1, int (reader->lengthInSamples)); - reader->read (&buf, 0, int (reader->lengthInSamples), 0, true, false); + int samplesToUse = int (reader->lengthInSamples); + int frames = samplesToUse / size; + + samplesToUse = frames * size; + + juce::AudioSampleBuffer buf (1, samplesToUse); + reader->read (&buf, 0, samplesToUse, 0, true, false); juce::OwnedArray t; - loadWavetables (t, sr, buf, reader->sampleRate, sz); - + loadWavetables (t, sr, buf, reader->sampleRate, size); + juce::ScopedLock sl (dspLock); std::swap (t, table); diff --git a/plugin/Source/PluginProcessor.h b/plugin/Source/PluginProcessor.h index f1c79fa..ce81c9c 100644 --- a/plugin/Source/PluginProcessor.h +++ b/plugin/Source/PluginProcessor.h @@ -41,10 +41,10 @@ public: void reloadWavetables(); void incWavetable (int osc, int delta); - void loadUserWavetable (int osc, const juce::File f); + bool loadUserWavetable (int osc, const juce::File& f, int sz); void applyEffects (juce::AudioSampleBuffer& buffer); - bool loadWaveTable (juce::OwnedArray& table, double sr, const juce::MemoryBlock& wav, const juce::String& format); + bool loadWaveTable (juce::OwnedArray& table, double sr, const juce::MemoryBlock& wav, const juce::String& format, int size); // Voice Params struct OSCParams @@ -261,6 +261,7 @@ public: juce::Value osc1Table, osc2Table; juce::MemoryBlock userTable1, userTable2; + int osc1Size = -1, osc2Size = -1; //============================================================================== gin::ModMatrix modMatrix;