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 "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");
addAndMakeVisible(frequency);

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

@ -13,19 +13,14 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
addAndMakeVisible(collapseButton);
collapseButton.onClick = [this] {
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
int index = audioProcessor.getCurrentFileIndex();
if (index != -1) {
int originalIndex = audioProcessor.getCurrentFileIndex();
int index = editingPerspective ? 0 : audioProcessor.getCurrentFileIndex() + 1;
if (originalIndex != -1 || editingPerspective) {
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);
}
triggerAsyncUpdate();
}
@ -34,11 +29,14 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr
path.addTriangle(0.0f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f);
collapseButton.setShape(path, false, true, true);
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
for (int i = 0; i < audioProcessor.numFiles(); i++) {
addCodeEditor(i);
{
juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock);
addCodeEditor(-1);
for (int i = 0; i < audioProcessor.numFiles(); i++) {
addCodeEditor(i);
}
fileUpdated(audioProcessor.getCurrentFileName());
}
fileUpdated(audioProcessor.getCurrentFileName());
setSize(1100, 750);
setResizable(true, true);
@ -62,18 +60,35 @@ void OscirenderAudioProcessorEditor::resized() {
volume.setBounds(volumeArea.withSizeKeepingCentre(volumeArea.getWidth(), juce::jmin(volumeArea.getHeight(), 300)));
area.removeFromLeft(3);
auto sections = 2;
int index = audioProcessor.getCurrentFileIndex();
if (index != -1) {
if (codeEditors[index]->isVisible()) {
sections++;
codeEditors[index]->setBounds(area.removeFromRight(getWidth() / sections));
} else {
codeEditors[index]->setBounds(0, 0, 0, 0);
}
collapseButton.setBounds(area.removeFromRight(20));
bool editorVisible = false;
{
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()) {
sections++;
editorVisible = true;
codeEditors[index]->setBounds(area.removeFromRight(getWidth() / sections));
} else {
codeEditors[index]->setBounds(0, 0, 0, 0);
}
collapseButton.setBounds(area.removeFromRight(20));
} else {
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 {
collapseButton.setBounds(0, 0, 0, 0);
}
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);
main.setBounds(area);
if (lua.isVisible() || obj.isVisible()) {
@ -86,25 +101,37 @@ void OscirenderAudioProcessorEditor::resized() {
}
void OscirenderAudioProcessorEditor::addCodeEditor(int index) {
std::shared_ptr<juce::CodeDocument> codeDocument = std::make_shared<juce::CodeDocument>();
int originalIndex = index;
index++;
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;
if (extension == ".lua") {
tokeniser = &luaTokeniser;
} else if (extension == ".svg") {
tokeniser = &xmlTokeniser;
}
editor = std::make_shared<juce::CodeEditorComponent>(*codeDocument, tokeniser);
}
codeDocuments.insert(codeDocuments.begin() + index, codeDocument);
juce::String extension = audioProcessor.getFileName(index).fromLastOccurrenceOf(".", true, false);
juce::CodeTokeniser* tokeniser = nullptr;
if (extension == ".lua") {
tokeniser = &luaTokeniser;
} else if (extension == ".svg") {
tokeniser = &xmlTokeniser;
}
std::shared_ptr<juce::CodeEditorComponent> editor = std::make_shared<juce::CodeEditorComponent>(*codeDocument, tokeniser);
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!
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) {
index++;
codeEditors.erase(codeEditors.begin() + index);
codeDocuments.erase(codeDocuments.begin() + index);
}
@ -116,17 +143,22 @@ void OscirenderAudioProcessorEditor::updateCodeEditor() {
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) {
visible = true;
break;
}
}
int originalIndex = audioProcessor.getCurrentFileIndex();
int index = editingPerspective ? 0 : audioProcessor.getCurrentFileIndex() + 1;
if ((originalIndex != -1 || editingPerspective) && 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());
if (index == 0) {
codeEditors[index]->loadContent(audioProcessor.perspectiveEffect->getCode());
} else {
codeEditors[index]->loadContent(juce::MemoryInputStream(*audioProcessor.getFileBlock(originalIndex), false).readEntireStreamAsString());
}
}
triggerAsyncUpdate();
}
@ -151,6 +183,14 @@ void OscirenderAudioProcessorEditor::handleAsyncUpdate() {
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
void OscirenderAudioProcessorEditor::codeDocumentTextInserted(const juce::String& newText, int insertIndex) {
updateCodeDocument();
@ -161,10 +201,18 @@ void OscirenderAudioProcessorEditor::codeDocumentTextDeleted(int startIndex, int
updateCodeDocument();
}
// parsersLock AND effectsLock must be locked before calling this function
void OscirenderAudioProcessorEditor::updateCodeDocument() {
int index = audioProcessor.getCurrentFileIndex();
juce::String file = codeDocuments[index]->getAllContent();
audioProcessor.updateFileBlock(index, std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1));
if (editingPerspective) {
juce::String file = codeDocuments[0]->getAllContent();
audioProcessor.perspectiveEffect->updateCode(file);
} else {
int originalIndex = audioProcessor.getCurrentFileIndex();
int index = audioProcessor.getCurrentFileIndex();
index++;
juce::String file = codeDocuments[index]->getAllContent();
audioProcessor.updateFileBlock(originalIndex, std::make_shared<juce::MemoryBlock>(file.toRawUTF8(), file.getNumBytesAsUTF8() + 1));
}
}
bool OscirenderAudioProcessorEditor::keyPressed(const juce::KeyPress& key) {

Wyświetl plik

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

Wyświetl plik

@ -45,16 +45,19 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<dou
auto y = input.y;
auto z = 0.0;
if (!defaultScript) {
parser.setVariable("x", x);
parser.setVariable("y", y);
parser.setVariable("z", z);
{
juce::SpinLock::ScopedLockType lock(codeLock);
if (!defaultScript) {
parser->setVariable("x", x);
parser->setVariable("y", y);
parser->setVariable("z", z);
auto result = parser.run();
if (result.size() >= 3) {
x = result[0];
y = result[1];
z = result[2];
auto result = parser->run();
if (result.size() >= 3) {
x = result[0];
y = result[1];
z = result[2];
}
}
}
@ -87,3 +90,15 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<dou
(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();
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* fixedRotateY = new BooleanParameter("Perspective Fixed Rotate Y", "perspectiveFixedRotateY", false);
BooleanParameter* fixedRotateZ = new BooleanParameter("Perspective Fixed Rotate Z", "perspectiveFixedRotateZ", false);
private:
const juce::String DEFAULT_SCRIPT = "return { x, y, z }";
juce::MemoryBlock code{DEFAULT_SCRIPT.toRawUTF8(), DEFAULT_SCRIPT.getNumBytesAsUTF8() + 1};
LuaParser parser{DEFAULT_SCRIPT};
juce::String code = DEFAULT_SCRIPT;
juce::SpinLock codeLock;
std::unique_ptr<LuaParser> parser = std::make_unique<LuaParser>(code);
bool defaultScript = true;
float currentRotateX = 0;

Wyświetl plik

@ -1,7 +1,8 @@
#include "EffectsListComponent.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;
for (int i = 0; i < parameters.size(); i++) {
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;
} 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->onClick = [this] {
button->setToggleState(editor.editingPerspective, juce::dontSendNotification);
button->onClick = [this, weakButton] {
if (auto button = weakButton.lock()) {
editor.editPerspectiveFunction(button->getToggleState());
}
};
return button;
}

Wyświetl plik

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