From 91aa11ddcc096b5fef9b2d90cde0be5be3885b04 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sat, 1 Jul 2023 15:29:53 +0100 Subject: [PATCH 1/6] Lua support fully working and efficient except for sliders --- Source/PluginProcessor.cpp | 55 +++++++++++++---------- Source/lua/LuaParser.cpp | 84 +++++++++++++++++++++--------------- Source/lua/LuaParser.h | 4 +- Source/parser/FileParser.cpp | 16 +++++-- Source/parser/FileParser.h | 3 ++ Source/parser/FrameSource.h | 2 + 6 files changed, 103 insertions(+), 61 deletions(-) diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 0b583b0..a15aab7 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -300,7 +300,9 @@ void OscirenderAudioProcessor::updateFrame() { frameLength = Shape::totalLength(frame); } - } + } else { + DBG("frame not ready!"); + } } void OscirenderAudioProcessor::updateLengthIncrement() { @@ -345,7 +347,11 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer& buffer, j double y = 0.0; double length = 0.0; - if (currentShape < frame.size()) { + bool renderingSample = currentFile >= 0 && parsers[currentFile]->isSample(); + + if (renderingSample) { + channels = parsers[currentFile]->nextSample(); + } else if (currentShape < frame.size()) { auto& shape = frame[currentShape]; length = shape->length(); double drawingProgress = length == 0.0 ? 1 : shapeDrawn / length; @@ -360,6 +366,7 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer& buffer, j x = channels.x; y = channels.y; + if (totalNumOutputChannels >= 2) { channelData[0][sample] = x; @@ -368,29 +375,31 @@ void OscirenderAudioProcessor::processBlock (juce::AudioBuffer& buffer, j channelData[0][sample] = x; } - // hard cap on how many times it can be over the length to - // prevent audio stuttering - frameDrawn += std::min(lengthIncrement, 20 * length); - shapeDrawn += std::min(lengthIncrement, 20 * length); + if (!renderingSample) { + // hard cap on how many times it can be over the length to + // prevent audio stuttering + frameDrawn += std::min(lengthIncrement, 20 * length); + shapeDrawn += std::min(lengthIncrement, 20 * length); - // Need to skip all shapes that the lengthIncrement draws over. - // This is especially an issue when there are lots of small lines being - // drawn. - while (shapeDrawn > length) { - shapeDrawn -= length; - currentShape++; - if (currentShape >= frame.size()) { - currentShape = 0; - break; - } - // POTENTIAL TODO: Think of a way to make this more efficient when iterating - // this loop many times - length = frame[currentShape]->len; - } + // Need to skip all shapes that the lengthIncrement draws over. + // This is especially an issue when there are lots of small lines being + // drawn. + while (shapeDrawn > length) { + shapeDrawn -= length; + currentShape++; + if (currentShape >= frame.size()) { + currentShape = 0; + break; + } + // POTENTIAL TODO: Think of a way to make this more efficient when iterating + // this loop many times + length = frame[currentShape]->len; + } + } - if (frameDrawn >= frameLength) { - updateFrame(); - } + if (!renderingSample && frameDrawn >= frameLength) { + updateFrame(); + } } juce::MidiBuffer processedMidi; diff --git a/Source/lua/LuaParser.cpp b/Source/lua/LuaParser.cpp index f697b25..4ddef0e 100644 --- a/Source/lua/LuaParser.cpp +++ b/Source/lua/LuaParser.cpp @@ -8,45 +8,61 @@ LuaParser::LuaParser(juce::String script) { luaL_openlibs(L); this->script = script; + parse(); } LuaParser::~LuaParser() { lua_close(L); } -std::vector> LuaParser::draw() { - std::vector> shapes; - - for (int i = 0; i < 100; i++) { - lua_pushnumber(L, step); - lua_setglobal(L, "step"); - - const int ret = luaL_dostring(L, script.toUTF8()); - if (ret != 0) { - const char* error = lua_tostring(L, -1); - DBG(error); - lua_pop(L, 1); - } else if (lua_istable(L, -1)) { - // get the first element of the table - lua_pushinteger(L, 1); - lua_gettable(L, -2); - float x = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - - // get the second element of the table - lua_pushinteger(L, 2); - lua_gettable(L, -2); - float y = (int)lua_tonumber(L, -1); - lua_pop(L, 1); - - shapes.push_back(std::make_unique(x, y)); - } - - // pop the table - lua_pop(L, 1); - - step++; +void LuaParser::parse() { + const int ret = luaL_loadstring(L, script.toUTF8()); + if (ret != 0) { + const char* error = lua_tostring(L, -1); + DBG(error); + lua_pop(L, 1); + functionRef = -1; + } else { + functionRef = luaL_ref(L, LUA_REGISTRYINDEX); } - - return shapes; +} + +Vector2 LuaParser::draw() { + Vector2 sample; + + if (functionRef == -1) { + return sample; + } + + lua_pushnumber(L, step); + lua_setglobal(L, "step"); + + lua_geti(L, LUA_REGISTRYINDEX, functionRef); + + const int ret = lua_pcall(L, 0, LUA_MULTRET, 0); + if (ret != 0) { + const char* error = lua_tostring(L, -1); + DBG(error); + functionRef = -1; + } else if (lua_istable(L, -1)) { + // get the first element of the table + lua_pushinteger(L, 1); + lua_gettable(L, -2); + float x = lua_tonumber(L, -1); + lua_pop(L, 1); + + // get the second element of the table + lua_pushinteger(L, 2); + lua_gettable(L, -2); + float y = lua_tonumber(L, -1); + lua_pop(L, 1); + + sample = Vector2(x, y); + } + + lua_pop(L, 1); + + step++; + + return sample; } diff --git a/Source/lua/LuaParser.h b/Source/lua/LuaParser.h index 1596168..295a152 100644 --- a/Source/lua/LuaParser.h +++ b/Source/lua/LuaParser.h @@ -9,8 +9,10 @@ public: LuaParser(juce::String script); ~LuaParser(); - std::vector> draw(); + Vector2 draw(); private: + void parse(); + int functionRef = -1; long step = 1; lua_State* L; juce::String script; diff --git a/Source/parser/FileParser.cpp b/Source/parser/FileParser.cpp index 85a51cf..c0178e7 100644 --- a/Source/parser/FileParser.cpp +++ b/Source/parser/FileParser.cpp @@ -23,6 +23,8 @@ void FileParser::parse(juce::String extension, std::unique_ptr(stream->readEntireStreamAsString()); } + + sampleSource = lua != nullptr; } std::vector> FileParser::nextFrame() { @@ -30,7 +32,6 @@ std::vector> FileParser::nextFrame() { auto tempCamera = camera; auto tempSvg = svg; auto tempText = text; - auto tempLua = lua; if (tempObject != nullptr && tempCamera != nullptr) { return tempCamera->draw(*tempObject); @@ -38,14 +39,23 @@ std::vector> FileParser::nextFrame() { return tempSvg->draw(); } else if (tempText != nullptr) { return tempText->draw(); - } else if (tempLua != nullptr) { - return tempLua->draw(); } auto tempShapes = std::vector>(); tempShapes.push_back(std::make_unique(0, 0, 0.5, 0.5, std::numbers::pi / 4.0, 2 * std::numbers::pi)); return tempShapes; } +Vector2 FileParser::nextSample() { + auto tempLua = lua; + if (tempLua != nullptr) { + return tempLua->draw(); + } +} + +bool FileParser::isSample() { + return sampleSource; +} + bool FileParser::isActive() { return active; } diff --git a/Source/parser/FileParser.h b/Source/parser/FileParser.h index 6214358..2d9e822 100644 --- a/Source/parser/FileParser.h +++ b/Source/parser/FileParser.h @@ -14,12 +14,15 @@ public: void parse(juce::String extension, std::unique_ptr) override; std::vector> nextFrame() override; + Vector2 nextSample() override; + bool isSample() override; bool isActive() override; void disable() override; void enable() override; private: bool active = true; + bool sampleSource = false; std::shared_ptr object; std::shared_ptr camera; diff --git a/Source/parser/FrameSource.h b/Source/parser/FrameSource.h index efb4927..a15ff04 100644 --- a/Source/parser/FrameSource.h +++ b/Source/parser/FrameSource.h @@ -9,6 +9,8 @@ class FrameSource { public: virtual void parse(juce::String extension, std::unique_ptr) = 0; virtual std::vector> nextFrame() = 0; + virtual Vector2 nextSample() = 0; + virtual bool isSample() = 0; virtual bool isActive() = 0; virtual void disable() = 0; virtual void enable() = 0; From bb2a03ec2dabe9c38d84998795e8c2efeeba6d9b Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 2 Jul 2023 11:22:01 +0100 Subject: [PATCH 2/6] Correctly show code editor by default --- Source/PluginEditor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 2656fed..ccf1d98 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -64,6 +64,7 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr juce::Path path; path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); collapseButton.setShape(path, false, true, true); + resized(); } OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() {} From 902286a086e72c536963c8ae9b013f2a7c29df56 Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 2 Jul 2023 13:09:24 +0100 Subject: [PATCH 3/6] All files have their own code editors, fix various bugs --- Source/MainComponent.cpp | 6 ++ Source/MainComponent.h | 3 +- Source/PluginEditor.cpp | 134 ++++++++++++++++++++-------------- Source/PluginEditor.h | 9 ++- Source/PluginProcessor.cpp | 8 ++ Source/PluginProcessor.h | 1 + Source/parser/FrameProducer.h | 2 +- 7 files changed, 103 insertions(+), 60 deletions(-) diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp index a357d28..cd7ceb2 100644 --- a/Source/MainComponent.cpp +++ b/Source/MainComponent.cpp @@ -20,6 +20,7 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess audioProcessor.addFile(file); } } + pluginEditor.addCodeEditor(audioProcessor.getCurrentFileIndex()); pluginEditor.updateCodeEditor(); updateFileLabel(); }); @@ -29,6 +30,11 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess closeFileButton.setButtonText("Close File"); closeFileButton.onClick = [this] { + int index = audioProcessor.getCurrentFileIndex(); + if (index == -1) { + return; + } + pluginEditor.removeCodeEditor(audioProcessor.getCurrentFileIndex()); audioProcessor.removeFile(audioProcessor.getCurrentFileIndex()); pluginEditor.updateCodeEditor(); updateFileLabel(); diff --git a/Source/MainComponent.h b/Source/MainComponent.h index 1a483ce..6c47037 100644 --- a/Source/MainComponent.h +++ b/Source/MainComponent.h @@ -13,6 +13,7 @@ public: ~MainComponent() override; void resized() override; + void updateFileLabel(); private: OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessorEditor& pluginEditor; @@ -22,7 +23,5 @@ private: juce::TextButton closeFileButton; juce::Label fileLabel; - void updateFileLabel(); - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainComponent) }; \ No newline at end of file diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index ccf1d98..2e7da2e 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -21,48 +21,27 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr addAndMakeVisible(effects); addAndMakeVisible(main); - codeEditor = std::make_unique(codeDocument, &luaTokeniser); - addAndMakeVisible(*codeEditor); - - codeEditor->loadContent (R"LUA( - -- defines a factorial function - function fact (n) - if n == 0 then - return 1 - else - return n * fact(n-1) - end - end - - print("enter a number:") - a = io.read("*number") -- read a number - print(fact(a)) -)LUA"); - - // I need to disable accessibility otherwise it doesn't work! Appears to be a JUCE issue, very annoying! - codeEditor->setAccessible(false); - - // listen for changes to the code editor - codeDocument.addListener(this); - addAndMakeVisible(collapseButton); collapseButton.onClick = [this] { - if (codeEditor->isVisible()) { - codeEditor->setVisible(false); - juce::Path path; - path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f); - collapseButton.setShape(path, false, true, true); - } else { - codeEditor->setVisible(true); - updateCodeEditor(); - juce::Path path; - path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); - collapseButton.setShape(path, false, true, true); - } - resized(); + int index = audioProcessor.getCurrentFileIndex(); + if (index != -1) { + if (codeEditors[index]->isVisible()) { + codeEditors[index]->setVisible(false); + juce::Path path; + path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f); + collapseButton.setShape(path, false, true, true); + } else { + codeEditors[index]->setVisible(true); + updateCodeEditor(); + juce::Path path; + path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); + collapseButton.setShape(path, false, true, true); + } + resized(); + } }; juce::Path path; - path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f); + path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f); collapseButton.setShape(path, false, true, true); resized(); } @@ -81,54 +60,101 @@ void OscirenderAudioProcessorEditor::paint (juce::Graphics& g) void OscirenderAudioProcessorEditor::resized() { auto area = getLocalBounds(); auto sections = 2; - if (codeEditor != nullptr) { - if (codeEditor->isVisible()) { + int index = audioProcessor.getCurrentFileIndex(); + if (index != -1) { + if (codeEditors[index]->isVisible()) { sections++; - codeEditor->setBounds(area.removeFromRight(getWidth() / sections)); + codeEditors[index]->setBounds(area.removeFromRight(getWidth() / sections)); } else { - codeEditor->setBounds(0, 0, 0, 0); + codeEditors[index]->setBounds(0, 0, 0, 0); } collapseButton.setBounds(area.removeFromRight(20)); - } + } else { + collapseButton.setBounds(0, 0, 0, 0); + } effects.setBounds(area.removeFromRight(getWidth() / sections)); main.setBounds(area.removeFromTop(getHeight() / 2)); } +void OscirenderAudioProcessorEditor::addCodeEditor(int index) { + std::shared_ptr codeDocument = std::make_shared(); + codeDocuments.insert(codeDocuments.begin() + index, codeDocument); + juce::String extension = audioProcessor.getFile(index).getFileExtension(); + juce::CodeTokeniser* tokeniser = nullptr; + if (extension == ".lua") { + tokeniser = &luaTokeniser; + } else if (extension == ".svg") { + tokeniser = &xmlTokeniser; + } + std::shared_ptr editor = std::make_shared(*codeDocument, tokeniser); + // I need to disable accessibility otherwise it doesn't work! Appears to be a JUCE issue, very annoying! + editor->setAccessible(false); + // listen for changes to the code editor + codeDocument->addListener(this); + codeEditors.insert(codeEditors.begin() + index, editor); + addChildComponent(*editor); +} + +void OscirenderAudioProcessorEditor::removeCodeEditor(int index) { + codeEditors.erase(codeEditors.begin() + index); + codeDocuments.erase(codeDocuments.begin() + index); +} + void OscirenderAudioProcessorEditor::updateCodeEditor() { - if (codeEditor->isVisible() && audioProcessor.getCurrentFileIndex() != -1) { - codeEditor->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(audioProcessor.getCurrentFileIndex()), false).readEntireStreamAsString()); + // check if any code editors are visible + bool visible = false; + for (int i = 0; i < codeEditors.size(); i++) { + if (codeEditors[i]->isVisible()) { + visible = true; + break; + } + } + int index = audioProcessor.getCurrentFileIndex(); + if (index != -1 && visible) { + for (int i = 0; i < codeEditors.size(); i++) { + codeEditors[i]->setVisible(false); + } + codeEditors[index]->setVisible(true); + codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(index), false).readEntireStreamAsString()); } + resized(); } void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) { - juce::String file = codeDocument.getAllContent(); - audioProcessor.updateFileBlock(audioProcessor.getCurrentFileIndex(), std::make_shared(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); + int index = audioProcessor.getCurrentFileIndex(); + juce::String file = codeDocuments[index]->getAllContent(); + audioProcessor.updateFileBlock(index, std::make_shared(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); } void OscirenderAudioProcessorEditor::codeDocumentTextDeleted(int startIndex, int endIndex) { - juce::String file = codeDocument.getAllContent(); - audioProcessor.updateFileBlock(audioProcessor.getCurrentFileIndex(), std::make_shared(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); + int index = audioProcessor.getCurrentFileIndex(); + juce::String file = codeDocuments[index]->getAllContent(); + audioProcessor.updateFileBlock(index, std::make_shared(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); } bool OscirenderAudioProcessorEditor::keyPressed(const juce::KeyPress& key) { int numFiles = audioProcessor.numFiles(); int currentFile = audioProcessor.getCurrentFileIndex(); + bool updated = false; if (key.getTextCharacter() == 'j') { currentFile++; if (currentFile == numFiles) { currentFile = 0; } - audioProcessor.changeCurrentFile(currentFile); - updateCodeEditor(); - return true; + updated = true; } else if (key.getTextCharacter() == 'k') { currentFile--; if (currentFile < 0) { currentFile = numFiles - 1; } + updated = true; + } + + if (updated) { audioProcessor.changeCurrentFile(currentFile); updateCodeEditor(); - return true; + main.updateFileLabel(); } - return false; + + return updated; } diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 8035de7..d559687 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -26,16 +26,19 @@ public: //============================================================================== void paint (juce::Graphics&) override; void resized() override; - + + void addCodeEditor(int index); + void removeCodeEditor(int index); void updateCodeEditor(); private: OscirenderAudioProcessor& audioProcessor; MainComponent main; EffectsComponent effects; - juce::CodeDocument codeDocument; + std::vector> codeDocuments; + std::vector> codeEditors; juce::LuaTokeniser luaTokeniser; - std::unique_ptr codeEditor; + juce::XmlTokeniser xmlTokeniser; juce::ShapeButton collapseButton; void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index a15aab7..c4352df 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -241,6 +241,10 @@ void OscirenderAudioProcessor::openFile(int index) { } void OscirenderAudioProcessor::changeCurrentFile(int index) { + if (index == -1) { + currentFile = -1; + producer->setSource(std::make_shared(), -1); + } if (index < 0 || index >= fileBlocks.size()) { return; } @@ -257,6 +261,10 @@ juce::File OscirenderAudioProcessor::getCurrentFile() { return files[currentFile]; } +juce::File OscirenderAudioProcessor::getFile(int index) { + return files[index]; +} + std::shared_ptr OscirenderAudioProcessor::getFileBlock(int index) { return fileBlocks[index]; } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index d98e0b6..5edbc69 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -95,6 +95,7 @@ public: void changeCurrentFile(int index); int getCurrentFileIndex(); juce::File getCurrentFile(); + juce::File getFile(int index); std::shared_ptr getFileBlock(int index); private: double theta = 0.0; diff --git a/Source/parser/FrameProducer.h b/Source/parser/FrameProducer.h index 65b3991..4ae980f 100644 --- a/Source/parser/FrameProducer.h +++ b/Source/parser/FrameProducer.h @@ -14,5 +14,5 @@ public: private: FrameConsumer& frameConsumer; std::shared_ptr frameSource; - int sourceFileIndex = 0; + int sourceFileIndex = -1; }; \ No newline at end of file From e4b55457233f6be5c7742a5c1a87047f9482098c Mon Sep 17 00:00:00 2001 From: James Ball Date: Sun, 2 Jul 2023 18:47:04 +0100 Subject: [PATCH 4/6] Add common EffectComponent across all sliders incl. frequency and remove EffectComponentGroup --- Source/EffectComponentGroup.cpp | 20 ---------- Source/EffectComponentGroup.h | 19 --------- Source/EffectsComponent.cpp | 9 +---- Source/EffectsComponent.h | 5 +-- Source/LuaComponent.cpp | 12 ++++++ Source/LuaComponent.h | 19 +++++++++ Source/MainComponent.h | 1 - Source/PluginEditor.h | 1 - Source/components/DraggableListBox.cpp | 2 - Source/components/DraggableListBox.h | 1 - Source/components/EffectComponent.cpp | 41 ++++++++++++++++++++ Source/components/EffectComponent.h | 27 +++++++++++++ Source/components/MyListComponent.cpp | 53 +++++++++++++++----------- Source/components/MyListComponent.h | 20 ++-------- osci-render.jucer | 11 ++++-- 15 files changed, 145 insertions(+), 96 deletions(-) delete mode 100644 Source/EffectComponentGroup.cpp delete mode 100644 Source/EffectComponentGroup.h create mode 100644 Source/LuaComponent.cpp create mode 100644 Source/LuaComponent.h create mode 100644 Source/components/EffectComponent.cpp create mode 100644 Source/components/EffectComponent.h diff --git a/Source/EffectComponentGroup.cpp b/Source/EffectComponentGroup.cpp deleted file mode 100644 index 0e41322..0000000 --- a/Source/EffectComponentGroup.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "EffectComponentGroup.h" - -EffectComponentGroup::EffectComponentGroup(const juce::String& id, const juce::String& name) : id(id) { - addAndMakeVisible(slider); - addAndMakeVisible(label); - - label.setText(name, juce::dontSendNotification); - label.attachToComponent(&slider, true); - - slider.setSliderStyle(juce::Slider::LinearHorizontal); - slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 90, slider.getTextBoxHeight()); -} - -EffectComponentGroup::~EffectComponentGroup() { -} - -void EffectComponentGroup::resized() { - auto sliderLeft = 100; - slider.setBounds(sliderLeft, 0, getWidth() - 200, 20); -} diff --git a/Source/EffectComponentGroup.h b/Source/EffectComponentGroup.h deleted file mode 100644 index 634c42c..0000000 --- a/Source/EffectComponentGroup.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include - -class EffectComponentGroup : public juce::Component { -public: - EffectComponentGroup(const juce::String& id, const juce::String& name); - ~EffectComponentGroup() override; - - void resized() override; - - juce::Slider slider; - juce::Label label; - juce::String id; - -private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectComponentGroup) -}; - diff --git a/Source/EffectsComponent.cpp b/Source/EffectsComponent.cpp index 1415913..5d73c49 100644 --- a/Source/EffectsComponent.cpp +++ b/Source/EffectsComponent.cpp @@ -5,8 +5,8 @@ EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p) : audioProcessor setText("Audio Effects"); addAndMakeVisible(frequency); + frequency.setHideCheckbox(true); - frequency.slider.setRange(0.0, 12000.0); frequency.slider.setSkewFactorFromMidPoint(500.0); frequency.slider.setTextValueSuffix("Hz"); frequency.slider.setValue(audioProcessor.frequency, juce::dontSendNotification); @@ -43,13 +43,8 @@ EffectsComponent::~EffectsComponent() { } void EffectsComponent::resized() { - auto xPadding = 10; - auto yPadding = 20; - frequency.setBounds(xPadding, yPadding, getWidth() - xPadding, 40); - auto area = getLocalBounds().reduced(20); - auto row = area.removeFromTop(24); - addBtn.setBounds(row.removeFromRight(100)); + frequency.setBounds(area.removeFromTop(30)); area.removeFromTop(6); listBox.setBounds(area); diff --git a/Source/EffectsComponent.h b/Source/EffectsComponent.h index dfabc9d..a3654a8 100644 --- a/Source/EffectsComponent.h +++ b/Source/EffectsComponent.h @@ -2,7 +2,6 @@ #include #include "audio/BitCrushEffect.h" -#include "EffectComponentGroup.h" #include "PluginProcessor.h" #include "components/DraggableListBox.h" #include "components/MyListComponent.h" @@ -17,13 +16,13 @@ public: private: OscirenderAudioProcessor& audioProcessor; - juce::TextButton addBtn; + // juce::TextButton addBtn; MyListBoxItemData itemData; MyListBoxModel listBoxModel; DraggableListBox listBox; - EffectComponentGroup frequency = EffectComponentGroup("frequency", "Frequency"); + EffectComponent frequency = EffectComponent(0.0, 12000.0, 0.1, 400, "Frequency", "frequency"); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectsComponent) }; \ No newline at end of file diff --git a/Source/LuaComponent.cpp b/Source/LuaComponent.cpp new file mode 100644 index 0000000..fa6af2c --- /dev/null +++ b/Source/LuaComponent.cpp @@ -0,0 +1,12 @@ +#include "LuaComponent.h" +#include "PluginEditor.h" + +LuaComponent::LuaComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor) { + setText(".lua File Settings"); +} + +LuaComponent::~LuaComponent() { +} + +void LuaComponent::resized() { +} diff --git a/Source/LuaComponent.h b/Source/LuaComponent.h new file mode 100644 index 0000000..da17505 --- /dev/null +++ b/Source/LuaComponent.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "PluginProcessor.h" + +class OscirenderAudioProcessorEditor; +class LuaComponent : public juce::GroupComponent { +public: + LuaComponent(OscirenderAudioProcessor&, OscirenderAudioProcessorEditor&); + ~LuaComponent() override; + + void resized() override; +private: + OscirenderAudioProcessor& audioProcessor; + OscirenderAudioProcessorEditor& pluginEditor; + + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LuaComponent) +}; \ No newline at end of file diff --git a/Source/MainComponent.h b/Source/MainComponent.h index 6c47037..6d35db2 100644 --- a/Source/MainComponent.h +++ b/Source/MainComponent.h @@ -1,7 +1,6 @@ #pragma once #include -#include "EffectComponentGroup.h" #include "PluginProcessor.h" #include "parser/FileParser.h" #include "parser/FrameProducer.h" diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index d559687..fd0c884 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -10,7 +10,6 @@ #include #include "PluginProcessor.h" -#include "EffectComponentGroup.h" #include "EffectsComponent.h" #include "MainComponent.h" diff --git a/Source/components/DraggableListBox.cpp b/Source/components/DraggableListBox.cpp index 51eaa0d..c904759 100644 --- a/Source/components/DraggableListBox.cpp +++ b/Source/components/DraggableListBox.cpp @@ -4,8 +4,6 @@ DraggableListBoxItemData::~DraggableListBoxItemData() {}; void DraggableListBoxItem::paint(juce::Graphics& g) { - modelData.paintContents(rowNum, g, getLocalBounds()); - if (insertAfter) { g.setColour(juce::Colours::red); diff --git a/Source/components/DraggableListBox.h b/Source/components/DraggableListBox.h index b00e708..17f907f 100644 --- a/Source/components/DraggableListBox.h +++ b/Source/components/DraggableListBox.h @@ -8,7 +8,6 @@ struct DraggableListBoxItemData virtual ~DraggableListBoxItemData() = 0; virtual int getNumItems() = 0; - virtual void paintContents(int, juce::Graphics&, juce::Rectangle) = 0; virtual void moveBefore(int indexOfItemToMove, int indexOfItemToPlaceBefore) = 0; virtual void moveAfter(int indexOfItemToMove, int indexOfItemToPlaceAfter) = 0; diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp new file mode 100644 index 0000000..71c7e0b --- /dev/null +++ b/Source/components/EffectComponent.cpp @@ -0,0 +1,41 @@ +#include "EffectComponent.h" + +EffectComponent::EffectComponent(double min, double max, double step, double value, juce::String name, juce::String id) : name(name), id(id) { + addAndMakeVisible(slider); + addAndMakeVisible(selected); + + slider.setSliderStyle(juce::Slider::LinearHorizontal); + slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 90, slider.getTextBoxHeight()); + slider.setRange(min, max, step); + slider.setValue(value, juce::dontSendNotification); + + selected.setToggleState(false, juce::dontSendNotification); +} + +EffectComponent::~EffectComponent() {} + +void EffectComponent::resized() { + auto sliderRight = getWidth() - 140; + auto bounds = getLocalBounds(); + bounds.removeFromRight(10); + slider.setBounds(bounds.removeFromRight(sliderRight)); + if (hideCheckbox) { + bounds.removeFromLeft(5); + } else { + bounds.removeFromLeft(2); + selected.setBounds(bounds.removeFromLeft(25)); + } + textBounds = bounds; +} + +void EffectComponent::paint(juce::Graphics& g) { + g.fillAll(juce::Colours::lightgrey); + g.setColour(juce::Colours::black); + auto bounds = getLocalBounds(); + g.drawRect(bounds); + g.drawText(name, textBounds, juce::Justification::left); +} + +void EffectComponent::setHideCheckbox(bool hide) { + hideCheckbox = hide; +} diff --git a/Source/components/EffectComponent.h b/Source/components/EffectComponent.h new file mode 100644 index 0000000..7139793 --- /dev/null +++ b/Source/components/EffectComponent.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include "../PluginProcessor.h" +#include "../audio/Effect.h" + + +class EffectComponent : public juce::Component { +public: + EffectComponent(double min, double max, double step, double value, juce::String name, juce::String id); + ~EffectComponent(); + + void resized() override; + void paint(juce::Graphics& g) override; + + void setHideCheckbox(bool hide); + + juce::Slider slider; + juce::String id; + juce::String name; + juce::ToggleButton selected; + +private: + bool hideCheckbox = false; + juce::Rectangle textBounds; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectComponent) +}; diff --git a/Source/components/MyListComponent.cpp b/Source/components/MyListComponent.cpp index 7ca398a..f1b585c 100644 --- a/Source/components/MyListComponent.cpp +++ b/Source/components/MyListComponent.cpp @@ -1,15 +1,11 @@ #include "MyListComponent.h" -MyListComponent::MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, int rn) : DraggableListBoxItem(lb, data, rn) { - addAndMakeVisible(slider); - addAndMakeVisible(selected); +MyListComponent::MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, int rn, std::shared_ptr effectComponent) : DraggableListBoxItem(lb, data, rn), effectComponent(effectComponent) { + addAndMakeVisible(*effectComponent); - slider.setSliderStyle(juce::Slider::LinearHorizontal); - slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 90, slider.getTextBoxHeight()); - slider.setRange(0.0, 1.0, 0.01); - slider.setValue(data.getValue(rn), juce::dontSendNotification); - slider.onValueChange = [this] { - ((MyListBoxItemData&)modelData).setValue(rowNum, slider.getValue()); + effectComponent->slider.setValue(data.getValue(rn), juce::dontSendNotification); + effectComponent->slider.onValueChange = [this] { + ((MyListBoxItemData&)modelData).setValue(rowNum, this->effectComponent->slider.getValue()); }; // check if effect is in audioProcessor enabled effects @@ -20,31 +16,44 @@ MyListComponent::MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, break; } } - selected.setToggleState(isSelected, juce::dontSendNotification); - selected.onClick = [this] { - ((MyListBoxItemData&)modelData).setSelected(rowNum, selected.getToggleState()); + effectComponent->selected.setToggleState(isSelected, juce::dontSendNotification); + effectComponent->selected.onClick = [this] { + ((MyListBoxItemData&)modelData).setSelected(rowNum, this->effectComponent->selected.getToggleState()); }; } MyListComponent::~MyListComponent() {} -void MyListComponent::paint (juce::Graphics& g) { - modelData.paintContents(rowNum, g, dataArea); - DraggableListBoxItem::paint(g); +void MyListComponent::paint(juce::Graphics& g) { + DraggableListBoxItem::paint(g); + auto bounds = getLocalBounds(); + bounds.removeFromLeft(20); + // draw drag and drop handle using circles + g.setColour(juce::Colours::white); + double size = 4; + double leftPad = 4; + double spacing = 7; + double topPad = 7; + g.fillEllipse(leftPad, topPad, size, size); + g.fillEllipse(leftPad, topPad + spacing, size, size); + g.fillEllipse(leftPad, topPad + 2 * spacing, size, size); + g.fillEllipse(leftPad + spacing, topPad, size, size); + g.fillEllipse(leftPad + spacing, topPad + spacing, size, size); + g.fillEllipse(leftPad + spacing, topPad + 2 * spacing, size, size); } void MyListComponent::resized() { - auto sliderLeft = 150; - slider.setBounds(sliderLeft, 0, getWidth() - sliderLeft - 10, getHeight()); - selected.setBounds(2, 0, 25, getHeight()); + auto area = getLocalBounds(); + area.removeFromLeft(20); + effectComponent->setBounds(area); } - juce::Component* MyListBoxModel::refreshComponentForRow(int rowNumber, bool isRowSelected, juce::Component *existingComponentToUpdate) { std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); - if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) - { - item = std::make_unique(listBox, (MyListBoxItemData&)modelData, rowNumber); + if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) { + auto data = (MyListBoxItemData&)modelData; + std::shared_ptr effectComponent = std::make_shared(0, 1, 0.01, 0, data.getText(rowNumber), data.getId(rowNumber)); + item = std::make_unique(listBox, (MyListBoxItemData&)modelData, rowNumber, effectComponent); } return item.release(); } diff --git a/Source/components/MyListComponent.h b/Source/components/MyListComponent.h index 141203d..8fa4947 100644 --- a/Source/components/MyListComponent.h +++ b/Source/components/MyListComponent.h @@ -3,6 +3,7 @@ #include #include "../PluginProcessor.h" #include "../audio/Effect.h" +#include "EffectComponent.h" // Application-specific data container struct MyListBoxItemData : public DraggableListBoxItemData @@ -24,14 +25,6 @@ struct MyListBoxItemData : public DraggableListBoxItemData // data.push_back(juce::String("Yahoo")); } - void paintContents(int rowNum, juce::Graphics& g, juce::Rectangle bounds) override { - g.fillAll(juce::Colours::lightgrey); - g.setColour(juce::Colours::black); - g.drawRect(bounds); - bounds.removeFromLeft(30); - g.drawText(data[rowNum]->getName(), bounds, juce::Justification::left); - } - void moveBefore(int indexOfItemToMove, int indexOfItemToPlaceBefore) override { auto effect = data[indexOfItemToMove]; @@ -101,19 +94,14 @@ struct MyListBoxItemData : public DraggableListBoxItemData class MyListComponent : public DraggableListBoxItem { public: - MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, int rn); + MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, int rn, std::shared_ptr effectComponent); ~MyListComponent(); - void paint(juce::Graphics&) override; + void paint(juce::Graphics& g) override; void resized() override; protected: - juce::Rectangle dataArea; - - juce::Slider slider; - juce::String id; - juce::ToggleButton selected; - + std::shared_ptr effectComponent; private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MyListComponent) }; diff --git a/osci-render.jucer b/osci-render.jucer index 7dc6732..f5be6b6 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -7,6 +7,9 @@ cppLanguageStandard="20" projectLineFeed=" " headerPath="./include"> + + @@ -81,6 +84,10 @@ file="Source/components/DraggableListBox.cpp"/> + + - - Date: Sun, 2 Jul 2023 21:00:14 +0100 Subject: [PATCH 5/6] Add .lua file settings with infinite (non-working) slider support --- Source/EffectsComponent.h | 6 +-- Source/LuaComponent.cpp | 8 +++- Source/LuaComponent.h | 3 ++ Source/PluginEditor.cpp | 4 +- Source/PluginEditor.h | 2 + ...Component.cpp => EffectsListComponent.cpp} | 22 ++++----- ...ListComponent.h => EffectsListComponent.h} | 18 +++---- Source/components/LuaListComponent.cpp | 48 +++++++++++++++++++ Source/components/LuaListComponent.h | 33 +++++++++++++ osci-render.jucer | 12 +++-- 10 files changed, 127 insertions(+), 29 deletions(-) rename Source/components/{MyListComponent.cpp => EffectsListComponent.cpp} (58%) rename Source/components/{MyListComponent.h => EffectsListComponent.h} (81%) create mode 100644 Source/components/LuaListComponent.cpp create mode 100644 Source/components/LuaListComponent.h diff --git a/Source/EffectsComponent.h b/Source/EffectsComponent.h index a3654a8..76917d9 100644 --- a/Source/EffectsComponent.h +++ b/Source/EffectsComponent.h @@ -4,7 +4,7 @@ #include "audio/BitCrushEffect.h" #include "PluginProcessor.h" #include "components/DraggableListBox.h" -#include "components/MyListComponent.h" +#include "components/EffectsListComponent.h" class EffectsComponent : public juce::GroupComponent { public: @@ -18,8 +18,8 @@ private: // juce::TextButton addBtn; - MyListBoxItemData itemData; - MyListBoxModel listBoxModel; + AudioEffectListBoxItemData itemData; + EffectsListBoxModel listBoxModel; DraggableListBox listBox; EffectComponent frequency = EffectComponent(0.0, 12000.0, 0.1, 400, "Frequency", "frequency"); diff --git a/Source/LuaComponent.cpp b/Source/LuaComponent.cpp index fa6af2c..a04bb1a 100644 --- a/Source/LuaComponent.cpp +++ b/Source/LuaComponent.cpp @@ -1,12 +1,18 @@ #include "LuaComponent.h" #include "PluginEditor.h" -LuaComponent::LuaComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor) { +LuaComponent::LuaComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor), slidersModel(sliders) { setText(".lua File Settings"); + + sliders.setModel(&slidersModel); + sliders.setRowHeight(30); + addAndMakeVisible(sliders); } LuaComponent::~LuaComponent() { } void LuaComponent::resized() { + auto area = getLocalBounds().reduced(20); + sliders.setBounds(area); } diff --git a/Source/LuaComponent.h b/Source/LuaComponent.h index da17505..7f5b4ad 100644 --- a/Source/LuaComponent.h +++ b/Source/LuaComponent.h @@ -2,6 +2,7 @@ #include #include "PluginProcessor.h" +#include "components/LuaListComponent.h" class OscirenderAudioProcessorEditor; class LuaComponent : public juce::GroupComponent { @@ -14,6 +15,8 @@ private: OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessorEditor& pluginEditor; + LuaListBoxModel slidersModel; + juce::ListBox sliders; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LuaComponent) }; \ No newline at end of file diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 2e7da2e..558b363 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -11,7 +11,7 @@ //============================================================================== OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioProcessor& p) - : AudioProcessorEditor(&p), audioProcessor(p), effects(p), main(p, *this), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white) + : AudioProcessorEditor(&p), audioProcessor(p), effects(p), main(p, *this), collapseButton("Collapse", juce::Colours::white, juce::Colours::white, juce::Colours::white), lua(p, *this) { // Make sure that before the constructor has finished, you've set the // editor's size to whatever you need it to be. @@ -20,6 +20,7 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr addAndMakeVisible(effects); addAndMakeVisible(main); + addAndMakeVisible(lua); addAndMakeVisible(collapseButton); collapseButton.onClick = [this] { @@ -74,6 +75,7 @@ void OscirenderAudioProcessorEditor::resized() { } effects.setBounds(area.removeFromRight(getWidth() / sections)); main.setBounds(area.removeFromTop(getHeight() / 2)); + lua.setBounds(area); } void OscirenderAudioProcessorEditor::addCodeEditor(int index) { diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index fd0c884..d92818c 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -12,6 +12,7 @@ #include "PluginProcessor.h" #include "EffectsComponent.h" #include "MainComponent.h" +#include "LuaComponent.h" //============================================================================== /** @@ -33,6 +34,7 @@ private: OscirenderAudioProcessor& audioProcessor; MainComponent main; + LuaComponent lua; EffectsComponent effects; std::vector> codeDocuments; std::vector> codeEditors; diff --git a/Source/components/MyListComponent.cpp b/Source/components/EffectsListComponent.cpp similarity index 58% rename from Source/components/MyListComponent.cpp rename to Source/components/EffectsListComponent.cpp index f1b585c..06b369c 100644 --- a/Source/components/MyListComponent.cpp +++ b/Source/components/EffectsListComponent.cpp @@ -1,11 +1,11 @@ -#include "MyListComponent.h" +#include "EffectsListComponent.h" -MyListComponent::MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, int rn, std::shared_ptr effectComponent) : DraggableListBoxItem(lb, data, rn), effectComponent(effectComponent) { +EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr effectComponent) : DraggableListBoxItem(lb, data, rn), effectComponent(effectComponent) { addAndMakeVisible(*effectComponent); effectComponent->slider.setValue(data.getValue(rn), juce::dontSendNotification); effectComponent->slider.onValueChange = [this] { - ((MyListBoxItemData&)modelData).setValue(rowNum, this->effectComponent->slider.getValue()); + ((AudioEffectListBoxItemData&)modelData).setValue(rowNum, this->effectComponent->slider.getValue()); }; // check if effect is in audioProcessor enabled effects @@ -18,13 +18,13 @@ MyListComponent::MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, } effectComponent->selected.setToggleState(isSelected, juce::dontSendNotification); effectComponent->selected.onClick = [this] { - ((MyListBoxItemData&)modelData).setSelected(rowNum, this->effectComponent->selected.getToggleState()); + ((AudioEffectListBoxItemData&)modelData).setSelected(rowNum, this->effectComponent->selected.getToggleState()); }; } -MyListComponent::~MyListComponent() {} +EffectsListComponent::~EffectsListComponent() {} -void MyListComponent::paint(juce::Graphics& g) { +void EffectsListComponent::paint(juce::Graphics& g) { DraggableListBoxItem::paint(g); auto bounds = getLocalBounds(); bounds.removeFromLeft(20); @@ -42,18 +42,18 @@ void MyListComponent::paint(juce::Graphics& g) { g.fillEllipse(leftPad + spacing, topPad + 2 * spacing, size, size); } -void MyListComponent::resized() { +void EffectsListComponent::resized() { auto area = getLocalBounds(); area.removeFromLeft(20); effectComponent->setBounds(area); } -juce::Component* MyListBoxModel::refreshComponentForRow(int rowNumber, bool isRowSelected, juce::Component *existingComponentToUpdate) { - std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); +juce::Component* EffectsListBoxModel::refreshComponentForRow(int rowNumber, bool isRowSelected, juce::Component *existingComponentToUpdate) { + std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) { - auto data = (MyListBoxItemData&)modelData; + auto data = (AudioEffectListBoxItemData&)modelData; std::shared_ptr effectComponent = std::make_shared(0, 1, 0.01, 0, data.getText(rowNumber), data.getId(rowNumber)); - item = std::make_unique(listBox, (MyListBoxItemData&)modelData, rowNumber, effectComponent); + item = std::make_unique(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, effectComponent); } return item.release(); } diff --git a/Source/components/MyListComponent.h b/Source/components/EffectsListComponent.h similarity index 81% rename from Source/components/MyListComponent.h rename to Source/components/EffectsListComponent.h index 8fa4947..9f6a746 100644 --- a/Source/components/MyListComponent.h +++ b/Source/components/EffectsListComponent.h @@ -6,12 +6,12 @@ #include "EffectComponent.h" // Application-specific data container -struct MyListBoxItemData : public DraggableListBoxItemData +struct AudioEffectListBoxItemData : public DraggableListBoxItemData { std::vector> data; OscirenderAudioProcessor& audioProcessor; - MyListBoxItemData(OscirenderAudioProcessor& p) : audioProcessor(p) {} + AudioEffectListBoxItemData(OscirenderAudioProcessor& p) : audioProcessor(p) {} int getNumItems() override { return data.size(); @@ -91,11 +91,11 @@ struct MyListBoxItemData : public DraggableListBoxItemData }; // Custom list-item Component (which includes item-delete button) -class MyListComponent : public DraggableListBoxItem +class EffectsListComponent : public DraggableListBoxItem { public: - MyListComponent(DraggableListBox& lb, MyListBoxItemData& data, int rn, std::shared_ptr effectComponent); - ~MyListComponent(); + EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr effectComponent); + ~EffectsListComponent(); void paint(juce::Graphics& g) override; void resized() override; @@ -103,15 +103,15 @@ public: protected: std::shared_ptr effectComponent; private: - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MyListComponent) + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectsListComponent) }; // Customized DraggableListBoxModel overrides refreshComponentForRow() to ensure that every -// list-item Component is a MyListComponent. -class MyListBoxModel : public DraggableListBoxModel +// list-item Component is a EffectsListComponent. +class EffectsListBoxModel : public DraggableListBoxModel { public: - MyListBoxModel(DraggableListBox& lb, DraggableListBoxItemData& md) + EffectsListBoxModel(DraggableListBox& lb, DraggableListBoxItemData& md) : DraggableListBoxModel(lb, md) {} juce::Component* refreshComponentForRow(int, bool, juce::Component*) override; diff --git a/Source/components/LuaListComponent.cpp b/Source/components/LuaListComponent.cpp new file mode 100644 index 0000000..1926a91 --- /dev/null +++ b/Source/components/LuaListComponent.cpp @@ -0,0 +1,48 @@ +#include "LuaListComponent.h" + +LuaListComponent::LuaListComponent(int sliderNum) { + juce::String sliderName = ""; + + sliderNum++; + while (sliderNum > 0) { + int mod = (sliderNum - 1) % 26; + sliderName = (char)(mod + 'A') + sliderName; + sliderNum = (sliderNum - mod) / 26; + } + + effectComponent = std::make_shared(0.0, 1.0, 0.01, 0, "Lua " + sliderName, "lua" + sliderName); + effectComponent->setHideCheckbox(true); + addAndMakeVisible(*effectComponent); +} + +LuaListComponent::~LuaListComponent() {} + +void LuaListComponent::resized() { + effectComponent->setBounds(getLocalBounds()); +} + +void paintListBoxItem(int sliderNum, juce::Graphics& g, int width, int height, bool rowIsSelected) {} + +int LuaListBoxModel::getNumRows() { + return numSliders + 1; +} + +void LuaListBoxModel::paintListBoxItem(int rowNumber, juce::Graphics& g, int width, int height, bool rowIsSelected) {} + +juce::Component* LuaListBoxModel::refreshComponentForRow(int sliderNum, bool isRowSelected, juce::Component *existingComponentToUpdate) { + if (sliderNum < getNumRows() - 1) { + std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); + if (juce::isPositiveAndBelow(sliderNum, getNumRows())) { + item = std::make_unique(sliderNum); + } + return item.release(); + } else { + std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); + item = std::make_unique("Add"); + item->onClick = [this]() { + numSliders++; + listBox.updateContent(); + }; + return item.release(); + } +} diff --git a/Source/components/LuaListComponent.h b/Source/components/LuaListComponent.h new file mode 100644 index 0000000..1d0d63e --- /dev/null +++ b/Source/components/LuaListComponent.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include "../PluginProcessor.h" +#include "../audio/Effect.h" +#include "EffectComponent.h" + +class LuaListComponent : public juce::Component +{ +public: + LuaListComponent(int sliderNum); + ~LuaListComponent(); + + void resized() override; + +protected: + std::shared_ptr effectComponent; +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LuaListComponent) +}; + +class LuaListBoxModel : public juce::ListBoxModel +{ +public: + LuaListBoxModel(juce::ListBox& lb) : listBox(lb) {} + + int getNumRows() override; + void paintListBoxItem(int rowNumber, juce::Graphics& g, int width, int height, bool rowIsSelected) override; + juce::Component* refreshComponentForRow(int sliderNum, bool isRowSelected, juce::Component *existingComponentToUpdate) override; + +private: + int numSliders = 5; + juce::ListBox& listBox; +}; diff --git a/osci-render.jucer b/osci-render.jucer index f5be6b6..072aa95 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -88,10 +88,14 @@ file="Source/components/EffectComponent.cpp"/> - - + + + + Date: Tue, 4 Jul 2023 14:58:36 +0100 Subject: [PATCH 6/6] Complete infinite lua slider support --- Source/LuaComponent.cpp | 2 +- Source/PluginProcessor.cpp | 32 +++++++++++++++++++++++++ Source/PluginProcessor.h | 9 +++---- Source/audio/LuaEffect.cpp | 14 +++++++++++ Source/audio/LuaEffect.h | 15 ++++++++++++ Source/components/EffectComponent.cpp | 17 ++++++++++--- Source/components/EffectComponent.h | 2 ++ Source/components/LuaListComponent.cpp | 33 ++++++++++++-------------- Source/components/LuaListComponent.h | 5 ++-- Source/lua/LuaParser.cpp | 32 +++++++++++++++++++++++++ Source/lua/LuaParser.h | 7 ++++++ Source/parser/FileParser.cpp | 20 ++++++++++++++++ Source/parser/FileParser.h | 6 +++++ osci-render.jucer | 2 ++ 14 files changed, 168 insertions(+), 28 deletions(-) create mode 100644 Source/audio/LuaEffect.cpp create mode 100644 Source/audio/LuaEffect.h diff --git a/Source/LuaComponent.cpp b/Source/LuaComponent.cpp index a04bb1a..a42fe2a 100644 --- a/Source/LuaComponent.cpp +++ b/Source/LuaComponent.cpp @@ -1,7 +1,7 @@ #include "LuaComponent.h" #include "PluginEditor.h" -LuaComponent::LuaComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor), slidersModel(sliders) { +LuaComponent::LuaComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor), slidersModel(sliders, p) { setText(".lua File Settings"); sliders.setModel(&slidersModel); diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index c4352df..896b52d 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -14,6 +14,9 @@ #include "audio/VectorCancellingEffect.h" #include "audio/DistortEffect.h" #include "audio/SmoothEffect.h" +#include "audio/BitCrushEffect.h" +#include "audio/BulgeEffect.h" +#include "audio/LuaEffect.h" //============================================================================== OscirenderAudioProcessor::OscirenderAudioProcessor() @@ -38,6 +41,10 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() allEffects.push_back(std::make_shared(std::make_unique(true), "Vertical shift", "verticalDistort")); allEffects.push_back(std::make_shared(std::make_unique(false), "Horizontal shift", "horizontalDistort")); allEffects.push_back(std::make_shared(std::make_unique(), "Smoothing", "smoothing")); + + for (int i = 0; i < 5; i++) { + addLuaSlider(); + } } OscirenderAudioProcessor::~OscirenderAudioProcessor() @@ -145,6 +152,26 @@ bool OscirenderAudioProcessor::isBusesLayoutSupported (const BusesLayout& layout } #endif +void OscirenderAudioProcessor::addLuaSlider() { + juce::String sliderName = ""; + + int sliderNum = luaEffects.size() + 1; + while (sliderNum > 0) { + int mod = (sliderNum - 1) % 26; + sliderName = (char)(mod + 'A') + sliderName; + sliderNum = (sliderNum - mod) / 26; + } + + luaEffects.push_back(std::make_shared(std::make_unique(sliderName, *this), "Lua " + sliderName, "lua" + sliderName)); +} + +void OscirenderAudioProcessor::updateLuaValues() { + Vector2 vector; + for (auto& effect : luaEffects) { + effect->apply(0, vector); + } +} + void OscirenderAudioProcessor::updateAngleDelta() { auto cyclesPerSample = frequency / currentSampleRate; thetaDelta = cyclesPerSample * 2.0 * juce::MathConstants::pi; @@ -238,6 +265,7 @@ void OscirenderAudioProcessor::openFile(int index) { producer->setSource(parsers[index], index); currentFile = index; invalidateFrameBuffer = true; + updateLuaValues(); } void OscirenderAudioProcessor::changeCurrentFile(int index) { @@ -257,6 +285,10 @@ int OscirenderAudioProcessor::getCurrentFileIndex() { return currentFile; } +std::shared_ptr OscirenderAudioProcessor::getCurrentFileParser() { + return parsers[currentFile]; +} + juce::File OscirenderAudioProcessor::getCurrentFile() { return files[currentFile]; } diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 5edbc69..d2feea0 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -14,8 +14,6 @@ #include "parser/FrameProducer.h" #include "parser/FrameConsumer.h" #include "audio/Effect.h" -#include "audio/BitCrushEffect.h" -#include "audio/BulgeEffect.h" //============================================================================== /** @@ -72,8 +70,7 @@ public: std::vector> allEffects; std::shared_ptr>> enabledEffects = std::make_shared>>(); - BitCrushEffect bitCrushEffect = BitCrushEffect(); - BulgeEffect bulgeEffect = BulgeEffect(); + std::vector> luaEffects; std::vector> parsers; std::vector> fileBlocks; @@ -82,6 +79,8 @@ public: std::unique_ptr producer; + void addLuaSlider(); + void updateLuaValues(); void updateAngleDelta(); void addFrame(std::vector> frame, int fileIndex) override; void enableEffect(std::shared_ptr effect); @@ -94,6 +93,7 @@ public: void openFile(int index); void changeCurrentFile(int index); int getCurrentFileIndex(); + std::shared_ptr getCurrentFileParser(); juce::File getCurrentFile(); juce::File getFile(int index); std::shared_ptr getFileBlock(int index); @@ -116,6 +116,7 @@ private: void updateFrame(); void updateLengthIncrement(); + void syncLuaSliders(); const double MIN_LENGTH_INCREMENT = 0.000001; diff --git a/Source/audio/LuaEffect.cpp b/Source/audio/LuaEffect.cpp new file mode 100644 index 0000000..eeeb721 --- /dev/null +++ b/Source/audio/LuaEffect.cpp @@ -0,0 +1,14 @@ +#include "LuaEffect.h" +#include "../lua/LuaParser.h" + +Vector2 LuaEffect::apply(int index, Vector2 input, double value, double frequency, double sampleRate) { + int fileIndex = audioProcessor.getCurrentFileIndex(); + if (fileIndex == -1) { + return input; + } + std::shared_ptr parser = audioProcessor.getCurrentFileParser()->getLua(); + if (parser != nullptr) { + parser->setVariable("slider_" + name.toLowerCase(), value); + } + return input; +} diff --git a/Source/audio/LuaEffect.h b/Source/audio/LuaEffect.h new file mode 100644 index 0000000..cf97666 --- /dev/null +++ b/Source/audio/LuaEffect.h @@ -0,0 +1,15 @@ +#pragma once +#include "EffectApplication.h" +#include "../shape/Vector2.h" +#include "../audio/Effect.h" +#include "../PluginProcessor.h" + +class LuaEffect : public EffectApplication { +public: + LuaEffect(juce::String name, OscirenderAudioProcessor& p) : audioProcessor(p), name(name) {}; + + Vector2 apply(int index, Vector2 input, double value, double frequency, double sampleRate) override; +private: + OscirenderAudioProcessor& audioProcessor; + juce::String name; +}; \ No newline at end of file diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp index 71c7e0b..e52c156 100644 --- a/Source/components/EffectComponent.cpp +++ b/Source/components/EffectComponent.cpp @@ -1,17 +1,28 @@ #include "EffectComponent.h" EffectComponent::EffectComponent(double min, double max, double step, double value, juce::String name, juce::String id) : name(name), id(id) { + componentSetup(); + slider.setRange(min, max, step); + slider.setValue(value, juce::dontSendNotification); +} + +EffectComponent::EffectComponent(double min, double max, double step, Effect& effect) : name(effect.getName()), id(effect.getId()) { + componentSetup(); + slider.setRange(min, max, step); + slider.setValue(effect.getValue(), juce::dontSendNotification); +} + +void EffectComponent::componentSetup() { addAndMakeVisible(slider); addAndMakeVisible(selected); slider.setSliderStyle(juce::Slider::LinearHorizontal); slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, 90, slider.getTextBoxHeight()); - slider.setRange(min, max, step); - slider.setValue(value, juce::dontSendNotification); - selected.setToggleState(false, juce::dontSendNotification); + selected.setToggleState(false, juce::dontSendNotification); } + EffectComponent::~EffectComponent() {} void EffectComponent::resized() { diff --git a/Source/components/EffectComponent.h b/Source/components/EffectComponent.h index 7139793..89bdc2f 100644 --- a/Source/components/EffectComponent.h +++ b/Source/components/EffectComponent.h @@ -7,6 +7,7 @@ class EffectComponent : public juce::Component { public: EffectComponent(double min, double max, double step, double value, juce::String name, juce::String id); + EffectComponent(double min, double max, double step, Effect& effect); ~EffectComponent(); void resized() override; @@ -20,6 +21,7 @@ public: juce::ToggleButton selected; private: + void componentSetup(); bool hideCheckbox = false; juce::Rectangle textBounds; diff --git a/Source/components/LuaListComponent.cpp b/Source/components/LuaListComponent.cpp index 1926a91..a048c71 100644 --- a/Source/components/LuaListComponent.cpp +++ b/Source/components/LuaListComponent.cpp @@ -1,17 +1,14 @@ #include "LuaListComponent.h" -LuaListComponent::LuaListComponent(int sliderNum) { - juce::String sliderName = ""; - - sliderNum++; - while (sliderNum > 0) { - int mod = (sliderNum - 1) % 26; - sliderName = (char)(mod + 'A') + sliderName; - sliderNum = (sliderNum - mod) / 26; - } - - effectComponent = std::make_shared(0.0, 1.0, 0.01, 0, "Lua " + sliderName, "lua" + sliderName); +LuaListComponent::LuaListComponent(OscirenderAudioProcessor& p, Effect& effect) { + effectComponent = std::make_shared(0.0, 1.0, 0.01, effect); effectComponent->setHideCheckbox(true); + + effectComponent->slider.onValueChange = [this, &effect, &p] { + effect.setValue(effectComponent->slider.getValue()); + effect.apply(0, Vector2()); + }; + addAndMakeVisible(*effectComponent); } @@ -24,23 +21,23 @@ void LuaListComponent::resized() { void paintListBoxItem(int sliderNum, juce::Graphics& g, int width, int height, bool rowIsSelected) {} int LuaListBoxModel::getNumRows() { - return numSliders + 1; + return audioProcessor.luaEffects.size() + 1; } void LuaListBoxModel::paintListBoxItem(int rowNumber, juce::Graphics& g, int width, int height, bool rowIsSelected) {} -juce::Component* LuaListBoxModel::refreshComponentForRow(int sliderNum, bool isRowSelected, juce::Component *existingComponentToUpdate) { - if (sliderNum < getNumRows() - 1) { +juce::Component* LuaListBoxModel::refreshComponentForRow(int rowNum, bool isRowSelected, juce::Component *existingComponentToUpdate) { + if (rowNum < getNumRows() - 1) { std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); - if (juce::isPositiveAndBelow(sliderNum, getNumRows())) { - item = std::make_unique(sliderNum); + if (juce::isPositiveAndBelow(rowNum, getNumRows())) { + item = std::make_unique(audioProcessor, *audioProcessor.luaEffects[rowNum]); } return item.release(); } else { std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); - item = std::make_unique("Add"); + item = std::make_unique("+"); item->onClick = [this]() { - numSliders++; + audioProcessor.addLuaSlider(); listBox.updateContent(); }; return item.release(); diff --git a/Source/components/LuaListComponent.h b/Source/components/LuaListComponent.h index 1d0d63e..8a1a7e2 100644 --- a/Source/components/LuaListComponent.h +++ b/Source/components/LuaListComponent.h @@ -7,7 +7,7 @@ class LuaListComponent : public juce::Component { public: - LuaListComponent(int sliderNum); + LuaListComponent(OscirenderAudioProcessor& p, Effect& effect); ~LuaListComponent(); void resized() override; @@ -21,7 +21,7 @@ private: class LuaListBoxModel : public juce::ListBoxModel { public: - LuaListBoxModel(juce::ListBox& lb) : listBox(lb) {} + LuaListBoxModel(juce::ListBox& lb, OscirenderAudioProcessor& p) : listBox(lb), audioProcessor(p) {} int getNumRows() override; void paintListBoxItem(int rowNumber, juce::Graphics& g, int width, int height, bool rowIsSelected) override; @@ -30,4 +30,5 @@ public: private: int numSliders = 5; juce::ListBox& listBox; + OscirenderAudioProcessor& audioProcessor; }; diff --git a/Source/lua/LuaParser.cpp b/Source/lua/LuaParser.cpp index 4ddef0e..c03c16c 100644 --- a/Source/lua/LuaParser.cpp +++ b/Source/lua/LuaParser.cpp @@ -27,6 +27,7 @@ void LuaParser::parse() { } } +// only the audio thread runs this fuction Vector2 LuaParser::draw() { Vector2 sample; @@ -36,6 +37,22 @@ Vector2 LuaParser::draw() { lua_pushnumber(L, step); lua_setglobal(L, "step"); + + // this CANNOT run at the same time as setVariable + if (updateVariables) { + bool expected = false; + if (accessingVariables.compare_exchange_strong(expected, true)) { + for (int i = 0; i < variableNames.size(); i++) { + lua_pushnumber(L, variables[i]); + lua_setglobal(L, variableNames[i].toUTF8()); + DBG("set " + variableNames[i] + " to " + juce::String(variables[i])); + } + variableNames.clear(); + variables.clear(); + accessingVariables = false; + updateVariables = false; + } + } lua_geti(L, LUA_REGISTRYINDEX, functionRef); @@ -66,3 +83,18 @@ Vector2 LuaParser::draw() { return sample; } + +// this CANNOT run at the same time as draw() +// many threads can run this function +bool LuaParser::setVariable(juce::String variableName, double value) { + bool expected = false; + // this is very unlikely to fail, and if it does, it's not a big deal + if (accessingVariables.compare_exchange_strong(expected, true)) { + variableNames.push_back(variableName); + variables.push_back(value); + accessingVariables = false; + updateVariables = true; + return true; + } + return false; +} diff --git a/Source/lua/LuaParser.h b/Source/lua/LuaParser.h index 295a152..1840009 100644 --- a/Source/lua/LuaParser.h +++ b/Source/lua/LuaParser.h @@ -10,10 +10,17 @@ public: ~LuaParser(); Vector2 draw(); + bool setVariable(juce::String variableName, double value); + private: void parse(); + int functionRef = -1; long step = 1; lua_State* L; juce::String script; + std::atomic updateVariables = false; + std::atomic accessingVariables = false; + std::vector variableNames; + std::vector variables; }; \ No newline at end of file diff --git a/Source/parser/FileParser.cpp b/Source/parser/FileParser.cpp index c0178e7..386346b 100644 --- a/Source/parser/FileParser.cpp +++ b/Source/parser/FileParser.cpp @@ -67,3 +67,23 @@ void FileParser::disable() { void FileParser::enable() { active = true; } + +std::shared_ptr FileParser::getObject() { + return object; +} + +std::shared_ptr FileParser::getCamera() { + return camera; +} + +std::shared_ptr FileParser::getSvg() { + return svg; +} + +std::shared_ptr FileParser::getText() { + return text; +} + +std::shared_ptr FileParser::getLua() { + return lua; +} diff --git a/Source/parser/FileParser.h b/Source/parser/FileParser.h index 2d9e822..1764786 100644 --- a/Source/parser/FileParser.h +++ b/Source/parser/FileParser.h @@ -20,6 +20,12 @@ public: void disable() override; void enable() override; + std::shared_ptr getObject(); + std::shared_ptr getCamera(); + std::shared_ptr getSvg(); + std::shared_ptr getText(); + std::shared_ptr getLua(); + private: bool active = true; bool sampleSource = false; diff --git a/osci-render.jucer b/osci-render.jucer index 072aa95..0968341 100644 --- a/osci-render.jucer +++ b/osci-render.jucer @@ -113,6 +113,8 @@ file="Source/audio/EffectApplication.cpp"/> + +