Hacky solution to get editing perspective function fully working

pull/170/head
James Ball 2023-07-22 22:00:59 +01:00
rodzic 26860a00e6
commit d6436aa3fa
9 zmienionych plików z 149 dodań i 65 usunięć

Wyświetl plik

@ -2,7 +2,7 @@
#include "audio/BitCrushEffect.h" #include "audio/BitCrushEffect.h"
#include "PluginEditor.h" #include "PluginEditor.h"
EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p) : audioProcessor(p), itemData(p), listBoxModel(listBox, itemData) { EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), itemData(p, editor), listBoxModel(listBox, itemData) {
setText("Audio Effects"); setText("Audio Effects");
addAndMakeVisible(frequency); addAndMakeVisible(frequency);

Wyświetl plik

@ -9,7 +9,7 @@
class OscirenderAudioProcessorEditor; class OscirenderAudioProcessorEditor;
class EffectsComponent : public juce::GroupComponent { class EffectsComponent : public juce::GroupComponent {
public: public:
EffectsComponent(OscirenderAudioProcessor&); EffectsComponent(OscirenderAudioProcessor&, OscirenderAudioProcessorEditor&);
~EffectsComponent() override; ~EffectsComponent() override;
void resized() override; void resized() override;

Wyświetl plik

@ -15,14 +15,18 @@ MainComponent::MainComponent(OscirenderAudioProcessor& p, OscirenderAudioProcess
chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) { chooser->launchAsync(flags, [this](const juce::FileChooser& chooser) {
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock); juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
bool fileAdded = false;
for (auto& url : chooser.getURLResults()) { for (auto& url : chooser.getURLResults()) {
if (url.isLocalFile()) { if (url.isLocalFile()) {
auto file = url.getLocalFile(); auto file = url.getLocalFile();
audioProcessor.addFile(file); audioProcessor.addFile(file);
fileAdded = true;
} }
} }
if (fileAdded) {
pluginEditor.addCodeEditor(audioProcessor.getCurrentFileIndex()); pluginEditor.addCodeEditor(audioProcessor.getCurrentFileIndex());
pluginEditor.fileUpdated(audioProcessor.getCurrentFileName()); pluginEditor.fileUpdated(audioProcessor.getCurrentFileName());
}
}); });
}; };

Wyświetl plik

@ -13,19 +13,14 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
addAndMakeVisible(collapseButton); addAndMakeVisible(collapseButton);
collapseButton.onClick = [this] { collapseButton.onClick = [this] {
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock); juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
int index = audioProcessor.getCurrentFileIndex(); int originalIndex = audioProcessor.getCurrentFileIndex();
if (index != -1) { int index = editingPerspective ? 0 : audioProcessor.getCurrentFileIndex() + 1;
if (originalIndex != -1 || editingPerspective) {
if (codeEditors[index]->isVisible()) { if (codeEditors[index]->isVisible()) {
codeEditors[index]->setVisible(false); 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 { } else {
codeEditors[index]->setVisible(true); codeEditors[index]->setVisible(true);
updateCodeEditor(); updateCodeEditor();
juce::Path path;
path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
collapseButton.setShape(path, false, true, true);
} }
triggerAsyncUpdate(); triggerAsyncUpdate();
} }
@ -34,11 +29,14 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f); path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f);
collapseButton.setShape(path, false, true, true); collapseButton.setShape(path, false, true, true);
{
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock); juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
addCodeEditor(-1);
for (int i = 0; i < audioProcessor.numFiles(); i++) { for (int i = 0; i < audioProcessor.numFiles(); i++) {
addCodeEditor(i); addCodeEditor(i);
} }
fileUpdated(audioProcessor.getCurrentFileName()); fileUpdated(audioProcessor.getCurrentFileName());
}
setSize(1100, 750); setSize(1100, 750);
setResizable(true, true); setResizable(true, true);
@ -62,10 +60,15 @@ void OscirenderAudioProcessorEditor::resized() {
volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), juce::jmin(volumeArea.getHeight(), 300))); volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), juce::jmin(volumeArea.getHeight(), 300)));
area.removeFromLeft(3); area.removeFromLeft(3);
auto sections = 2; auto sections = 2;
int index = audioProcessor.getCurrentFileIndex(); bool editorVisible = false;
if (index != -1) { {
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
int originalIndex = audioProcessor.getCurrentFileIndex();
int index = editingPerspective ? 0 : audioProcessor.getCurrentFileIndex() + 1;
if (originalIndex != -1 || editingPerspective) {
if (codeEditors[index]->isVisible()) { if (codeEditors[index]->isVisible()) {
sections++; sections++;
editorVisible = true;
codeEditors[index]->setBounds(area.removeFromRight(getWidth() / sections)); codeEditors[index]->setBounds(area.removeFromRight(getWidth() / sections));
} else { } else {
codeEditors[index]->setBounds(0, 0, 0, 0); codeEditors[index]->setBounds(0, 0, 0, 0);
@ -74,6 +77,18 @@ void OscirenderAudioProcessorEditor::resized() {
} else { } else {
collapseButton.setBounds(0, 0, 0, 0); collapseButton.setBounds(0, 0, 0, 0);
} }
}
if (editorVisible) {
juce::Path path;
path.addTriangle(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
collapseButton.setShape(path, false, true, true);
} else {
juce::Path path;
path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f);
collapseButton.setShape(path, false, true, true);
}
auto effectsSection = area.removeFromRight(1.2 * getWidth() / sections); auto effectsSection = area.removeFromRight(1.2 * getWidth() / sections);
main.setBounds(area); main.setBounds(area);
if (lua.isVisible() || obj.isVisible()) { if (lua.isVisible() || obj.isVisible()) {
@ -86,25 +101,37 @@ void OscirenderAudioProcessorEditor::resized() {
} }
void OscirenderAudioProcessorEditor::addCodeEditor(int index) { void OscirenderAudioProcessorEditor::addCodeEditor(int index) {
std::shared_ptr<juce::CodeDocument> codeDocument = std::make_shared<juce::CodeDocument>(); int originalIndex = index;
codeDocuments.insert(codeDocuments.begin() + index, codeDocument); index++;
juce::String extension = audioProcessor.getFileName(index).fromLastOccurrenceOf(".", true, false); std::shared_ptr<juce::CodeDocument> codeDocument;
std::shared_ptr<juce::CodeEditorComponent> editor;
if (index == 0) {
codeDocument = perspectiveCodeDocument;
editor = perspectiveCodeEditor;
} else {
codeDocument = std::make_shared<juce::CodeDocument>();
juce::String extension = audioProcessor.getFileName(originalIndex).fromLastOccurrenceOf(".", true, false);
juce::CodeTokeniser* tokeniser = nullptr; juce::CodeTokeniser* tokeniser = nullptr;
if (extension == ".lua") { if (extension == ".lua") {
tokeniser = &luaTokeniser; tokeniser = &luaTokeniser;
} else if (extension == ".svg") { } else if (extension == ".svg") {
tokeniser = &xmlTokeniser; tokeniser = &xmlTokeniser;
} }
std::shared_ptr<juce::CodeEditorComponent> editor = std::make_shared<juce::CodeEditorComponent>(*codeDocument, tokeniser); editor = std::make_shared<juce::CodeEditorComponent>(*codeDocument, tokeniser);
}
codeDocuments.insert(codeDocuments.begin() + index, codeDocument);
codeEditors.insert(codeEditors.begin() + index, editor);
addChildComponent(*editor);
// I need to disable accessibility otherwise it doesn't work! Appears to be a JUCE issue, very annoying! // I need to disable accessibility otherwise it doesn't work! Appears to be a JUCE issue, very annoying!
editor->setAccessible(false); editor->setAccessible(false);
// listen for changes to the code editor // listen for changes to the code editor
codeDocument->addListener(this); codeDocument->addListener(this);
codeEditors.insert(codeEditors.begin() + index, editor);
addChildComponent(*editor);
} }
void OscirenderAudioProcessorEditor::removeCodeEditor(int index) { void OscirenderAudioProcessorEditor::removeCodeEditor(int index) {
index++;
codeEditors.erase(codeEditors.begin() + index); codeEditors.erase(codeEditors.begin() + index);
codeDocuments.erase(codeDocuments.begin() + index); codeDocuments.erase(codeDocuments.begin() + index);
} }
@ -120,13 +147,18 @@ void OscirenderAudioProcessorEditor::updateCodeEditor() {
break; break;
} }
} }
int index = audioProcessor.getCurrentFileIndex(); int originalIndex = audioProcessor.getCurrentFileIndex();
if (index != -1 && visible) { int index = editingPerspective ? 0 : audioProcessor.getCurrentFileIndex() + 1;
if ((originalIndex != -1 || editingPerspective) && visible) {
for (int i = 0; i < codeEditors.size(); i++) { for (int i = 0; i < codeEditors.size(); i++) {
codeEditors[i]->setVisible(false); codeEditors[i]->setVisible(false);
} }
codeEditors[index]->setVisible(true); codeEditors[index]->setVisible(true);
codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(index), false).readEntireStreamAsString()); if (index == 0) {
codeEditors[index]->loadContent(audioProcessor.perspectiveEffect->getCode());
} else {
codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString());
}
} }
triggerAsyncUpdate(); triggerAsyncUpdate();
} }
@ -151,6 +183,14 @@ void OscirenderAudioProcessorEditor::handleAsyncUpdate() {
resized(); resized();
} }
void OscirenderAudioProcessorEditor::editPerspectiveFunction(bool enable) {
editingPerspective = enable;
juce::SpinLock::ScopedLockType lock1(audioProcessor.parsersLock);
juce::SpinLock::ScopedLockType lock2(audioProcessor.effectsLock);
codeEditors[0]->setVisible(enable);
updateCodeEditor();
}
// parsersLock AND effectsLock must be locked before calling this function // parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) { void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) {
updateCodeDocument(); updateCodeDocument();
@ -161,10 +201,18 @@ void OscirenderAudioProcessorEditor::codeDocumentTextDeleted(int startIndex, int
updateCodeDocument(); updateCodeDocument();
} }
// parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessorEditor::updateCodeDocument() { void OscirenderAudioProcessorEditor::updateCodeDocument() {
if (editingPerspective) {
juce::String file = codeDocuments[0]->getAllContent();
audioProcessor.perspectiveEffect->updateCode(file);
} else {
int originalIndex = audioProcessor.getCurrentFileIndex();
int index = audioProcessor.getCurrentFileIndex(); int index = audioProcessor.getCurrentFileIndex();
index++;
juce::String file = codeDocuments[index]->getAllContent(); juce::String file = codeDocuments[index]->getAllContent();
audioProcessor.updateFileBlock(index, std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1)); audioProcessor.updateFileBlock(originalIndex, std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1));
}
} }
bool OscirenderAudioProcessorEditor::keyPressed(const juce::KeyPress& key) { bool OscirenderAudioProcessorEditor::keyPressed(const juce::KeyPress& key) {

Wyświetl plik

@ -21,19 +21,25 @@ public:
void removeCodeEditor(int index); void removeCodeEditor(int index);
void fileUpdated(juce::String fileName); void fileUpdated(juce::String fileName);
void handleAsyncUpdate() override; void handleAsyncUpdate() override;
void editPerspectiveFunction(bool enabled);
std::atomic<bool> editingPerspective = false;
private: private:
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;
MainComponent main{audioProcessor, *this}; MainComponent main{audioProcessor, *this};
LuaComponent lua{audioProcessor, *this}; LuaComponent lua{audioProcessor, *this};
ObjComponent obj{audioProcessor, *this}; ObjComponent obj{audioProcessor, *this};
EffectsComponent effects{audioProcessor}; EffectsComponent effects{audioProcessor, *this};
VolumeComponent volume{audioProcessor}; VolumeComponent volume{audioProcessor};
std::vector<std::shared_ptr<juce::CodeDocument>> codeDocuments; std::vector<std::shared_ptr<juce::CodeDocument>> codeDocuments;
std::vector<std::shared_ptr<juce::CodeEditorComponent>> codeEditors; std::vector<std::shared_ptr<juce::CodeEditorComponent>> codeEditors;
juce::LuaTokeniser luaTokeniser; juce::LuaTokeniser luaTokeniser;
juce::XmlTokeniser xmlTokeniser; juce::XmlTokeniser xmlTokeniser;
juce::ShapeButton collapseButton; juce::ShapeButton collapseButton;
std::shared_ptr<juce::CodeDocument> perspectiveCodeDocument = std::make_shared<juce::CodeDocument>();
std::shared_ptr<juce::CodeEditorComponent> perspectiveCodeEditor = std::make_shared<juce::CodeEditorComponent>(*perspectiveCodeDocument, &luaTokeniser);
void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override; void codeDocumentTextInserted(const juce::String& newText, int insertIndex) override;
void codeDocumentTextDeleted(int startIndex, int endIndex) override; void codeDocumentTextDeleted(int startIndex, int endIndex) override;

Wyświetl plik

@ -45,18 +45,21 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<dou
auto y = input.y; auto y = input.y;
auto z = 0.0; auto z = 0.0;
{
juce::SpinLock::ScopedLockType lock(codeLock);
if (!defaultScript) { if (!defaultScript) {
parser.setVariable("x", x); parser->setVariable("x", x);
parser.setVariable("y", y); parser->setVariable("y", y);
parser.setVariable("z", z); parser->setVariable("z", z);
auto result = parser.run(); auto result = parser->run();
if (result.size() >= 3) { if (result.size() >= 3) {
x = result[0]; x = result[0];
y = result[1]; y = result[1];
z = result[2]; z = result[2];
} }
} }
}
auto rotateX = baseRotateX + currentRotateX; auto rotateX = baseRotateX + currentRotateX;
auto rotateY = baseRotateY + currentRotateY; auto rotateY = baseRotateY + currentRotateY;
@ -87,3 +90,15 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<dou
(1 - effectScale) * input.y + effectScale * (y3 * focalLength / (z3 - depth)) (1 - effectScale) * input.y + effectScale * (y3 * focalLength / (z3 - depth))
); );
} }
void PerspectiveEffect::updateCode(const juce::String& newCode) {
juce::SpinLock::ScopedLockType lock(codeLock);
defaultScript = newCode == DEFAULT_SCRIPT;
code = newCode;
parser = std::make_unique<LuaParser>(code);
}
juce::String PerspectiveEffect::getCode() {
juce::SpinLock::ScopedLockType lock(codeLock);
return code;
}

Wyświetl plik

@ -9,14 +9,17 @@ public:
PerspectiveEffect(); PerspectiveEffect();
Vector2 apply(int index, Vector2 input, const std::vector<double>& values, double sampleRate) override; Vector2 apply(int index, Vector2 input, const std::vector<double>& values, double sampleRate) override;
void updateCode(const juce::String& newCode);
juce::String getCode();
BooleanParameter* fixedRotateX = new BooleanParameter("Perspective Fixed Rotate X", "perspectiveFixedRotateX", false); BooleanParameter* fixedRotateX = new BooleanParameter("Perspective Fixed Rotate X", "perspectiveFixedRotateX", false);
BooleanParameter* fixedRotateY = new BooleanParameter("Perspective Fixed Rotate Y", "perspectiveFixedRotateY", false); BooleanParameter* fixedRotateY = new BooleanParameter("Perspective Fixed Rotate Y", "perspectiveFixedRotateY", false);
BooleanParameter* fixedRotateZ = new BooleanParameter("Perspective Fixed Rotate Z", "perspectiveFixedRotateZ", false); BooleanParameter* fixedRotateZ = new BooleanParameter("Perspective Fixed Rotate Z", "perspectiveFixedRotateZ", false);
private: private:
const juce::String DEFAULT_SCRIPT = "return { x, y, z }"; const juce::String DEFAULT_SCRIPT = "return { x, y, z }";
juce::MemoryBlock code{DEFAULT_SCRIPT.toRawUTF8(), DEFAULT_SCRIPT.getNumBytesAsUTF8() + 1}; juce::String code = DEFAULT_SCRIPT;
LuaParser parser{DEFAULT_SCRIPT}; juce::SpinLock codeLock;
std::unique_ptr<LuaParser> parser = std::make_unique<LuaParser>(code);
bool defaultScript = true; bool defaultScript = true;
float currentRotateX = 0; float currentRotateX = 0;

Wyświetl plik

@ -1,7 +1,8 @@
#include "EffectsListComponent.h" #include "EffectsListComponent.h"
#include "SvgButton.h" #include "SvgButton.h"
#include "../PluginEditor.h"
EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect) : DraggableListBoxItem(lb, data, rn), effect(effect), audioProcessor(data.audioProcessor) { EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect) : DraggableListBoxItem(lb, data, rn), effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) {
auto parameters = effect.parameters; auto parameters = effect.parameters;
for (int i = 0; i < parameters.size(); i++) { for (int i = 0; i < parameters.size(); i++) {
std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(audioProcessor, effect, i, i == 0); std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(audioProcessor, effect, i, i == 0);
@ -88,10 +89,14 @@ std::shared_ptr<juce::Component> EffectsListComponent::createComponent(EffectPar
}; };
return button; return button;
} else if (parameter->paramID == "depthScale") { } else if (parameter->paramID == "depthScale") {
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, "white"); std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::pencil_svg, "white", "red");
std::weak_ptr<SvgButton> weakButton = button;
button->setEdgeIndent(5); button->setEdgeIndent(5);
button->onClick = [this] { button->setToggleState(editor.editingPerspective, juce::dontSendNotification);
button->onClick = [this, weakButton] {
if (auto button = weakButton.lock()) {
editor.editPerspectiveFunction(button->getToggleState());
}
}; };
return button; return button;
} }

Wyświetl plik

@ -7,12 +7,14 @@
#include "ComponentList.h" #include "ComponentList.h"
// Application-specific data container // Application-specific data container
class OscirenderAudioProcessorEditor;
struct AudioEffectListBoxItemData : public DraggableListBoxItemData struct AudioEffectListBoxItemData : public DraggableListBoxItemData
{ {
std::vector<std::shared_ptr<Effect>> data; std::vector<std::shared_ptr<Effect>> data;
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& editor;
AudioEffectListBoxItemData(OscirenderAudioProcessor& p) : audioProcessor(p) {} AudioEffectListBoxItemData(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), editor(editor) {}
int getNumItems() override { int getNumItems() override {
return data.size(); return data.size();
@ -102,6 +104,7 @@ protected:
juce::ListBox list; juce::ListBox list;
private: private:
OscirenderAudioProcessor& audioProcessor; OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& editor;
std::shared_ptr<juce::Component> createComponent(EffectParameter* parameter); std::shared_ptr<juce::Component> createComponent(EffectParameter* parameter);