Merge pull request #49 from jameshball/perspective-effect

Implement 3D perspective effect
pull/170/head
James H Ball 2023-07-22 22:12:28 +01:00 zatwierdzone przez GitHub
commit 08c8239d39
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
28 zmienionych plików z 483 dodań i 198 usunięć

Wyświetl plik

@ -61,25 +61,6 @@ function rotate(point, rotate_x, rotate_y, rotate_z)
end
num_points = 16
-- 3D cube draw path
points = {
{x=-1.0, y=-1.0, z=1.0},
{x=1.0, y=-1.0, z=1.0},
{x=1.0, y=-1.0, z=-1.0},
{x=-1.0, y=-1.0, z=-1.0},
{x=1.0, y=-1.0, z=-1.0},
{x=1.0, y=1.0, z=-1.0},
{x=-1.0, y=1.0, z=-1.0},
{x=-1.0, y=-1.0, z=-1.0},
{x=-1.0, y=-1.0, z=1.0},
{x=1.0, y=-1.0, z=1.0},
{x=1.0, y=1.0, z=1.0},
{x=-1.0, y=1.0, z=1.0},
{x=1.0, y=1.0, z=1.0},
{x=1.0, y=1.0, z=-1.0},
{x=-1.0, y=1.0, z=-1.0},
{x=-1.0, y=1.0, z=1.0}
}
-- Percentage of the image that has currently been drawn.
-- The 'or' syntax sets 'drawing_progress' to 0 initially, or the
@ -104,6 +85,25 @@ end
-- prev_start ~= start_index == true whenever a new line has started
if prev_start ~= start_index then
rotate_speed = slider_d * step / 50000
-- 3D cube draw path
points = {
{x=-1.0, y=-1.0, z=1.0},
{x=1.0, y=-1.0, z=1.0},
{x=1.0, y=-1.0, z=-1.0},
{x=-1.0, y=-1.0, z=-1.0},
{x=1.0, y=-1.0, z=-1.0},
{x=1.0, y=1.0, z=-1.0},
{x=-1.0, y=1.0, z=-1.0},
{x=-1.0, y=-1.0, z=-1.0},
{x=-1.0, y=-1.0, z=1.0},
{x=1.0, y=-1.0, z=1.0},
{x=1.0, y=1.0, z=1.0},
{x=-1.0, y=1.0, z=1.0},
{x=1.0, y=1.0, z=1.0},
{x=1.0, y=1.0, z=-1.0},
{x=-1.0, y=1.0, z=-1.0},
{x=-1.0, y=1.0, z=1.0}
}
-- rotate and project the start and end points
proj_start = project(rotate(points[start_index], rotate_speed, rotate_speed, 0))
proj_end = project(rotate(points[end_index], rotate_speed, rotate_speed, 0))

Wyświetl plik

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
d="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" />
</svg>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 243 B

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

@ -1,7 +1,6 @@
#include "ObjComponent.h"
#include "PluginEditor.h"
#include <numbers>
#include "Util.h"
ObjComponent::ObjComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessorEditor& editor) : audioProcessor(p), pluginEditor(editor) {
setText("3D .obj File Settings");
@ -26,9 +25,9 @@ ObjComponent::ObjComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessor
audioProcessor.rotateY->setValue(rotateY.slider.getValue());
audioProcessor.rotateZ->setValue(rotateZ.slider.getValue());
audioProcessor.fixedRotateX = fixedRotateX->getToggleState();
audioProcessor.fixedRotateY = fixedRotateY->getToggleState();
audioProcessor.fixedRotateZ = fixedRotateZ->getToggleState();
audioProcessor.fixedRotateX->setBoolValueNotifyingHost(fixedRotateX->getToggleState());
audioProcessor.fixedRotateY->setBoolValueNotifyingHost(fixedRotateY->getToggleState());
audioProcessor.fixedRotateZ->setBoolValueNotifyingHost(fixedRotateZ->getToggleState());
};
rotateX.slider.onValueChange = onRotationChange;
@ -67,21 +66,6 @@ ObjComponent::ObjComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessor
}
};
auto doc = juce::XmlDocument::parse(BinaryData::fixed_rotate_svg);
Util::changeSvgColour(doc.get(), "white");
fixedRotateWhite = juce::Drawable::createFromSVG(*doc);
Util::changeSvgColour(doc.get(), "red");
fixedRotateRed = juce::Drawable::createFromSVG(*doc);
// TODO: any way of removing this duplication?
getLookAndFeel().setColour(juce::DrawableButton::backgroundOnColourId, juce::Colours::transparentWhite);
fixedRotateX->setClickingTogglesState(true);
fixedRotateY->setClickingTogglesState(true);
fixedRotateZ->setClickingTogglesState(true);
fixedRotateX->setImages(fixedRotateWhite.get(), nullptr, nullptr, nullptr, fixedRotateRed.get());
fixedRotateY->setImages(fixedRotateWhite.get(), nullptr, nullptr, nullptr, fixedRotateRed.get());
fixedRotateZ->setImages(fixedRotateWhite.get(), nullptr, nullptr, nullptr, fixedRotateRed.get());
fixedRotateX->onClick = onRotationChange;
fixedRotateY->onClick = onRotationChange;
fixedRotateZ->onClick = onRotationChange;
@ -89,10 +73,6 @@ ObjComponent::ObjComponent(OscirenderAudioProcessor& p, OscirenderAudioProcessor
rotateX.setComponent(fixedRotateX);
rotateY.setComponent(fixedRotateY);
rotateZ.setComponent(fixedRotateZ);
fixedRotateX->setToggleState(audioProcessor.fixedRotateX, juce::NotificationType::dontSendNotification);
fixedRotateY->setToggleState(audioProcessor.fixedRotateY, juce::NotificationType::dontSendNotification);
fixedRotateZ->setToggleState(audioProcessor.fixedRotateZ, juce::NotificationType::dontSendNotification);
}
ObjComponent::~ObjComponent() {

Wyświetl plik

@ -3,6 +3,7 @@
#include <JuceHeader.h>
#include "PluginProcessor.h"
#include "components/EffectComponent.h"
#include "components/SvgButton.h"
class OscirenderAudioProcessorEditor;
class ObjComponent : public juce::GroupComponent, public juce::MouseListener {
@ -26,11 +27,9 @@ private:
juce::TextButton resetRotation{"Reset Rotation"};
juce::ToggleButton mouseRotate{"Rotate with Mouse (Esc to disable)"};
std::unique_ptr<juce::Drawable> fixedRotateWhite;
std::unique_ptr<juce::Drawable> fixedRotateRed;
std::shared_ptr<juce::DrawableButton> fixedRotateX = std::make_shared<juce::DrawableButton>("fixedRotateX", juce::DrawableButton::ButtonStyle::ImageFitted);
std::shared_ptr<juce::DrawableButton> fixedRotateY = std::make_shared<juce::DrawableButton>("fixedRotateY", juce::DrawableButton::ButtonStyle::ImageFitted);
std::shared_ptr<juce::DrawableButton> fixedRotateZ = std::make_shared<juce::DrawableButton>("fixedRotateZ", juce::DrawableButton::ButtonStyle::ImageFitted);
std::shared_ptr<SvgButton> fixedRotateX = std::make_shared<SvgButton>("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateX);
std::shared_ptr<SvgButton> fixedRotateY = std::make_shared<SvgButton>("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateY);
std::shared_ptr<SvgButton> fixedRotateZ = std::make_shared<SvgButton>("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateZ);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ObjComponent)
};

Wyświetl plik

@ -13,32 +13,30 @@ 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);
}
resized();
triggerAsyncUpdate();
}
};
juce::Path path;
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,19 +143,24 @@ 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());
}
}
resized();
triggerAsyncUpdate();
}
// parsersLock MUST be locked before calling this function
@ -147,6 +179,18 @@ void OscirenderAudioProcessorEditor::fileUpdated(juce::String fileName) {
updateCodeEditor();
}
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();
@ -157,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

@ -9,7 +9,7 @@
#include "components/VolumeComponent.h"
class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener {
class OscirenderAudioProcessorEditor : public juce::AudioProcessorEditor, private juce::CodeDocument::Listener, public juce::AsyncUpdater {
public:
OscirenderAudioProcessorEditor(OscirenderAudioProcessor&);
~OscirenderAudioProcessorEditor() override;
@ -20,19 +20,26 @@ public:
void addCodeEditor(int index);
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

@ -79,6 +79,17 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
delayEffect,
std::vector<EffectParameter*>{new EffectParameter("Delay Decay", "delayDecay", 0.0, 0.0, 1.0), new EffectParameter("Delay Length", "delayEchoLength", 0.5, 0.0, 1.0)}
));
toggleableEffects.push_back(std::make_shared<Effect>(
perspectiveEffect,
std::vector<EffectParameter*>{
new EffectParameter("3D Perspective", "depthScale", 0.0, 0.0, 1.0),
new EffectParameter("3D Depth (z)", "zPos", 0.1, 0.0, 1.0),
new EffectParameter("3D Rotate Speed", "rotateSpeed3D", 0.0, -1.0, 1.0),
new EffectParameter("Rotate X", "rotateX", 1.0, -1.0, 1.0),
new EffectParameter("Rotate Y", "rotateY", 1.0, -1.0, 1.0),
new EffectParameter("Rotate Z", "rotateZ", 0.0, -1.0, 1.0),
}
));
toggleableEffects.push_back(traceMax);
toggleableEffects.push_back(traceMin);
@ -113,6 +124,13 @@ OscirenderAudioProcessor::OscirenderAudioProcessor()
}
}
}
addParameter(fixedRotateX);
addParameter(fixedRotateY);
addParameter(fixedRotateZ);
addParameter(perspectiveEffect->fixedRotateX);
addParameter(perspectiveEffect->fixedRotateY);
addParameter(perspectiveEffect->fixedRotateZ);
}
OscirenderAudioProcessor::~OscirenderAudioProcessor() {}
@ -453,7 +471,8 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
}
{
juce::SpinLock::ScopedLockType lock(effectsLock);
juce::SpinLock::ScopedLockType lock1(parsersLock);
juce::SpinLock::ScopedLockType lock2(effectsLock);
for (auto& effect : toggleableEffects) {
if (effect->enabled->getValue()) {
channels = effect->apply(sample, channels);
@ -494,6 +513,11 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
if (!renderingSample && frameDrawn >= drawnFrameLength) {
updateFrame();
// TODO: updateFrame already iterates over all the shapes,
// so we can improve performance by calculating frameDrawn
// and shapeDrawn directly. frameDrawn is simply actualTraceMin * frameLength
// but shapeDrawn is the amount of the current shape that has been drawn so
// we need to iterate over all the shapes to calculate it.
if (traceMinEnabled) {
while (frameDrawn < actualTraceMin * frameLength) {
incrementShapeDrawing();
@ -503,12 +527,14 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju
}
}
// TODO this is the slowest part of the program - any way to improve this would help!
void OscirenderAudioProcessor::incrementShapeDrawing() {
double length = currentShape < frame.size() ? frame[currentShape]->len : 0.0;
// hard cap on how many times it can be over the length to
// prevent audio stuttering
frameDrawn += juce::jmin(lengthIncrement, 20 * length);
shapeDrawn += juce::jmin(lengthIncrement, 20 * length);
auto increment = juce::jmin(lengthIncrement, 20 * length);
frameDrawn += increment;
shapeDrawn += increment;
// Need to skip all shapes that the lengthIncrement draws over.
// This is especially an issue when there are lots of small lines being

Wyświetl plik

@ -20,6 +20,7 @@
#include "audio/DelayEffect.h"
#include "audio/PitchDetector.h"
#include "audio/WobbleEffect.h"
#include "audio/PerspectiveEffect.h"
//==============================================================================
/**
@ -106,23 +107,23 @@ public:
}, new EffectParameter("Focal length", "focalLength", 1.0, 0.0, 2.0)
);
std::atomic<bool> fixedRotateX = false;
std::atomic<bool> fixedRotateY = false;
std::atomic<bool> fixedRotateZ = false;
BooleanParameter* fixedRotateX = new BooleanParameter("Object Fixed Rotate X", "objFixedRotateX", false);
BooleanParameter* fixedRotateY = new BooleanParameter("Object Fixed Rotate Y", "objFixedRotateY", false);
BooleanParameter* fixedRotateZ = new BooleanParameter("Object Fixed Rotate Z", "objFixedRotateZ", false);
std::shared_ptr<Effect> rotateX = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
if (getCurrentFileIndex() != -1) {
auto obj = getCurrentFileParser()->getObject();
if (obj == nullptr) return input;
auto rotation = values[0] * std::numbers::pi;
if (fixedRotateX) {
if (fixedRotateX->getBoolValue()) {
obj->setCurrentRotationX(rotation);
} else {
obj->setBaseRotationX(rotation);
}
}
return input;
}, new EffectParameter("Rotate X", "rotateX", 1.0, -1.0, 1.0)
}, new EffectParameter("Rotate X", "objRotateX", 1.0, -1.0, 1.0)
);
std::shared_ptr<Effect> rotateY = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
@ -130,14 +131,14 @@ public:
auto obj = getCurrentFileParser()->getObject();
if (obj == nullptr) return input;
auto rotation = values[0] * std::numbers::pi;
if (fixedRotateY) {
if (fixedRotateY->getBoolValue()) {
obj->setCurrentRotationY(rotation);
} else {
obj->setBaseRotationY(rotation);
}
}
return input;
}, new EffectParameter("Rotate Y", "rotateY", 1.0, -1.0, 1.0)
}, new EffectParameter("Rotate Y", "objRotateY", 1.0, -1.0, 1.0)
);
std::shared_ptr<Effect> rotateZ = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
@ -145,14 +146,14 @@ public:
auto obj = getCurrentFileParser()->getObject();
if (obj == nullptr) return input;
auto rotation = values[0] * std::numbers::pi;
if (fixedRotateZ) {
if (fixedRotateZ->getBoolValue()) {
obj->setCurrentRotationZ(rotation);
} else {
obj->setBaseRotationZ(rotation);
}
}
return input;
}, new EffectParameter("Rotate Z", "rotateZ", 0.0, -1.0, 1.0)
}, new EffectParameter("Rotate Z", "objRotateZ", 0.0, -1.0, 1.0)
);
std::shared_ptr<Effect> rotateSpeed = std::make_shared<Effect>(
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
@ -162,10 +163,11 @@ public:
obj->setRotationSpeed(values[0]);
}
return input;
}, new EffectParameter("Rotate Speed", "rotateSpeed3D", 0.0, -1.0, 1.0)
}, new EffectParameter("Rotate Speed", "objRotateSpeed", 0.0, -1.0, 1.0)
);
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>();
juce::SpinLock parsersLock;
std::vector<std::shared_ptr<FileParser>> parsers;

Wyświetl plik

@ -1,10 +0,0 @@
#pragma once
#include <JuceHeader.h>
namespace Util {
void changeSvgColour(juce::XmlElement* xml, juce::String colour) {
forEachXmlChildElement(*xml, xmlnode) {
xmlnode->setAttribute("fill", colour);
}
}
}

Wyświetl plik

@ -29,10 +29,22 @@ public:
return value.load();
}
bool getBoolValue() const {
return value.load();
}
void setValue(float newValue) override {
value.store(newValue >= 0.5f);
}
void setBoolValue(bool newValue) {
value.store(newValue);
}
void setBoolValueNotifyingHost(bool newValue) {
setValueNotifyingHost(newValue ? 1.0f : 0.0f);
}
float getDefaultValue() const override {
return false;
}

Wyświetl plik

@ -28,9 +28,10 @@ void Effect::animateValues() {
auto parameter = parameters[i];
float minValue = parameter->min;
float maxValue = parameter->max;
float phase = parameter->lfo != nullptr ? nextPhase(parameter) : 0.0;
bool lfoEnabled = parameter->lfo != nullptr && parameter->lfo->getValueUnnormalised() != (int)LfoType::Static;
float phase = lfoEnabled ? nextPhase(parameter) : 0.0;
float percentage = phase / (2 * std::numbers::pi);
LfoType type = parameter->lfo != nullptr ? (LfoType)(int)parameter->lfo->getValueUnnormalised() : LfoType::Static;
LfoType type = lfoEnabled ? (LfoType)(int)parameter->lfo->getValueUnnormalised() : LfoType::Static;
switch (type) {
case LfoType::Sine:

Wyświetl plik

@ -0,0 +1,107 @@
#include "PerspectiveEffect.h"
#include <numbers>
PerspectiveEffect::PerspectiveEffect() {}
Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
auto effectScale = values[0];
auto depth = 1.0 + (values[1] - 0.1) * 3;
auto rotateSpeed = linearSpeedToActualSpeed(values[2]);
double baseRotateX, baseRotateY, baseRotateZ;
if (fixedRotateX->getBoolValue()) {
baseRotateX = 0;
currentRotateX = values[3] * std::numbers::pi;
} else {
baseRotateX = values[3] * std::numbers::pi;
}
if (fixedRotateY->getBoolValue()) {
baseRotateY = 0;
currentRotateY = values[4] * std::numbers::pi;
} else {
baseRotateY = values[4] * std::numbers::pi;
}
if (fixedRotateZ->getBoolValue()) {
baseRotateZ = 0;
currentRotateZ = values[5] * std::numbers::pi;
} else {
baseRotateZ = values[5] * std::numbers::pi;
}
currentRotateX += baseRotateX * rotateSpeed;
currentRotateY += baseRotateY * rotateSpeed;
currentRotateZ += baseRotateZ * rotateSpeed;
if (currentRotateX > std::numbers::pi * 8) {
currentRotateX -= std::numbers::pi * 8;
}
if (currentRotateY > std::numbers::pi * 8) {
currentRotateY -= std::numbers::pi * 8;
}
if (currentRotateZ > std::numbers::pi * 8) {
currentRotateZ -= std::numbers::pi * 8;
}
auto x = input.x;
auto y = input.y;
auto z = 0.0;
{
// TODO: Instead of evaluating the script every time, we could evaluate it
// once at the start for all values of x and y and then interpolate between
// the results.
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 rotateX = baseRotateX + currentRotateX;
auto rotateY = baseRotateY + currentRotateY;
auto rotateZ = baseRotateZ + currentRotateZ;
// rotate around x-axis
double cosValue = std::cos(rotateX);
double sinValue = std::sin(rotateX);
double y2 = cosValue * y - sinValue * z;
double z2 = sinValue * y + cosValue * z;
// rotate around y-axis
cosValue = std::cos(rotateY);
sinValue = std::sin(rotateY);
double x2 = cosValue * x + sinValue * z2;
double z3 = -sinValue * x + cosValue * z2;
// rotate around z-axis
cosValue = cos(rotateZ);
sinValue = sin(rotateZ);
double x3 = cosValue * x2 - sinValue * y2;
double y3 = sinValue * x2 + cosValue * y2;
// perspective projection
auto focalLength = 1.0;
return Vector2(
(1 - effectScale) * input.x + effectScale * (x3 * 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

@ -0,0 +1,32 @@
#pragma once
#include "EffectApplication.h"
#include "../shape/Vector2.h"
#include "../audio/Effect.h"
#include "../lua/LuaParser.h"
class PerspectiveEffect : public EffectApplication {
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::String code = DEFAULT_SCRIPT;
juce::SpinLock codeLock;
std::unique_ptr<LuaParser> parser = std::make_unique<LuaParser>(code);
bool defaultScript = true;
float currentRotateX = 0;
float currentRotateY = 0;
float currentRotateZ = 0;
float linearSpeedToActualSpeed(float rotateSpeed) {
return (std::exp(3 * juce::jmin(10.0f, std::abs(rotateSpeed))) - 1) / 50000.0;
}
};

Wyświetl plik

@ -2,7 +2,7 @@
EffectComponent::EffectComponent(OscirenderAudioProcessor& p, Effect& effect, int index) : effect(effect), index(index), audioProcessor(p) {
addAndMakeVisible(slider);
addAndMakeVisible(lfoSlider);
addChildComponent(lfoSlider);
addAndMakeVisible(selected);
addAndMakeVisible(lfo);
@ -128,7 +128,7 @@ void EffectComponent::resized() {
lfo.setBounds(bounds.removeFromRight(100).reduced(5));
}
auto checkboxLabel = bounds.removeFromLeft(110);
auto checkboxLabel = bounds.removeFromLeft(120);
if (checkboxVisible) {
checkboxLabel.removeFromLeft(2);

Wyświetl plik

@ -1,15 +1,17 @@
#include "EffectsListComponent.h"
#include "SvgButton.h"
#include "../PluginEditor.h"
EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr<Effect> effect) : DraggableListBoxItem(lb, data, rn), effect(effect) {
auto parameters = effect->parameters;
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>(data.audioProcessor, *effect, i, i == 0);
std::shared_ptr<EffectComponent> effectComponent = std::make_shared<EffectComponent>(audioProcessor, effect, i, i == 0);
// using weak_ptr to avoid circular reference and memory leak
std::weak_ptr<EffectComponent> weakEffectComponent = effectComponent;
effectComponent->slider.setValue(parameters[i]->getValueUnnormalised(), juce::dontSendNotification);
effectComponent->slider.onValueChange = [this, i, weakEffectComponent] {
if (auto effectComponent = weakEffectComponent.lock()) {
this->effect->setValue(i, effectComponent->slider.getValue());
this->effect.setValue(i, effectComponent->slider.getValue());
}
};
@ -17,12 +19,17 @@ EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectList
effectComponent->selected.onClick = [this, weakEffectComponent] {
if (auto effectComponent = weakEffectComponent.lock()) {
auto data = (AudioEffectListBoxItemData&)modelData;
juce::SpinLock::ScopedLockType lock(data.audioProcessor.effectsLock);
juce::SpinLock::ScopedLockType lock(audioProcessor.effectsLock);
data.setSelected(rowNum, effectComponent->selected.getToggleState());
}
};
}
auto component = createComponent(parameters[i]);
if (component != nullptr) {
effectComponent->setComponent(component);
}
listModel.addComponent(effectComponent);
}
@ -66,6 +73,36 @@ void EffectsListComponent::resized() {
list.setBounds(area);
}
std::shared_ptr<juce::Component> EffectsListComponent::createComponent(EffectParameter* parameter) {
if (parameter->paramID == "rotateX" || parameter->paramID == "rotateY" || parameter->paramID == "rotateZ") {
BooleanParameter* toggle;
if (parameter->paramID == "rotateX") {
toggle = audioProcessor.perspectiveEffect->fixedRotateX;
} else if (parameter->paramID == "rotateY") {
toggle = audioProcessor.perspectiveEffect->fixedRotateY;
} else if (parameter->paramID == "rotateZ") {
toggle = audioProcessor.perspectiveEffect->fixedRotateZ;
}
std::shared_ptr<SvgButton> button = std::make_shared<SvgButton>(parameter->name, BinaryData::fixed_rotate_svg, "white", "red", toggle);
button->onClick = [this, toggle] {
toggle->setBoolValueNotifyingHost(!toggle->getBoolValue());
};
return button;
} else if (parameter->paramID == "depthScale") {
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->setToggleState(editor.editingPerspective, juce::dontSendNotification);
button->onClick = [this, weakButton] {
if (auto button = weakButton.lock()) {
editor.editPerspectiveFunction(button->getToggleState());
}
};
return button;
}
return nullptr;
}
int EffectsListBoxModel::getRowHeight(int row) {
auto data = (AudioEffectListBoxItemData&)modelData;
return data.getEffect(row)->parameters.size() * 30;
@ -79,7 +116,7 @@ juce::Component* EffectsListBoxModel::refreshComponentForRow(int rowNumber, bool
std::unique_ptr<EffectsListComponent> item(dynamic_cast<EffectsListComponent*>(existingComponentToUpdate));
if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) {
auto data = (AudioEffectListBoxItemData&)modelData;
item = std::make_unique<EffectsListComponent>(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, data.getEffect(rowNumber));
item = std::make_unique<EffectsListComponent>(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, *data.getEffect(rowNumber));
}
return item.release();
}

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();
@ -89,7 +91,7 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData
class EffectsListComponent : public DraggableListBoxItem
{
public:
EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr<Effect> effect);
EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect);
~EffectsListComponent();
void paint(juce::Graphics& g) override;
@ -97,10 +99,15 @@ public:
void resized() override;
protected:
std::shared_ptr<Effect> effect;
Effect& effect;
ComponentListModel listModel;
juce::ListBox list;
private:
OscirenderAudioProcessor& audioProcessor;
OscirenderAudioProcessorEditor& editor;
std::shared_ptr<juce::Component> createComponent(EffectParameter* parameter);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectsListComponent)
};

Wyświetl plik

@ -0,0 +1,53 @@
#pragma once
#include <JuceHeader.h>
class SvgButton : public juce::DrawableButton, public juce::AudioProcessorParameter::Listener, public juce::AsyncUpdater {
public:
SvgButton(juce::String name, juce::String svg, juce::String colour, juce::String colourOn, BooleanParameter* toggle = nullptr) : juce::DrawableButton(name, juce::DrawableButton::ButtonStyle::ImageFitted), toggle(toggle) {
auto doc = juce::XmlDocument::parse(svg);
changeSvgColour(doc.get(), colour);
normalImage = juce::Drawable::createFromSVG(*doc);
changeSvgColour(doc.get(), colourOn);
normalImageOn = juce::Drawable::createFromSVG(*doc);
getLookAndFeel().setColour(juce::DrawableButton::backgroundOnColourId, juce::Colours::transparentWhite);
if (colour != colourOn) {
setClickingTogglesState(true);
}
setImages(normalImage.get(), nullptr, nullptr, nullptr, normalImageOn.get());
if (toggle != nullptr) {
toggle->addListener(this);
setToggleState(toggle->getBoolValue(), juce::NotificationType::dontSendNotification);
}
}
SvgButton(juce::String name, juce::String svg, juce::String colour) : SvgButton(name, svg, colour, colour) {}
~SvgButton() override {
if (toggle != nullptr) {
toggle->removeListener(this);
}
}
void parameterValueChanged(int parameterIndex, float newValue) override {
triggerAsyncUpdate();
}
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override {}
void handleAsyncUpdate() override {
setToggleState(toggle->getBoolValue(), juce::NotificationType::dontSendNotification);
}
private:
std::unique_ptr<juce::Drawable> normalImage;
std::unique_ptr<juce::Drawable> normalImageOn;
BooleanParameter* toggle;
void changeSvgColour(juce::XmlElement* xml, juce::String colour) {
forEachXmlChildElement(*xml, xmlnode) {
xmlnode->setAttribute("fill", colour);
}
}
};

Wyświetl plik

@ -12,7 +12,7 @@ VisualiserComponent::~VisualiserComponent() {
}
void VisualiserComponent::setBuffer(std::vector<float>& newBuffer) {
juce::SpinLock::ScopedLockType scope(lock);
juce::CriticalSection::ScopedLockType scope(lock);
buffer.clear();
for (int i = 0; i < newBuffer.size(); i += precision * numChannels) {
buffer.push_back(newBuffer[i]);
@ -31,7 +31,7 @@ void VisualiserComponent::paint(juce::Graphics& g) {
auto r = getLocalBounds().toFloat();
auto minDim = juce::jmin(r.getWidth(), r.getHeight());
juce::SpinLock::ScopedLockType scope(lock);
juce::CriticalSection::ScopedLockType scope(lock);
if (buffer.size() > 0) {
g.setColour(waveformColour);
paintXY(g, r.withSizeKeepingCentre(minDim, minDim));
@ -86,6 +86,6 @@ void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle<float> area
double strength = 10;
lengthScale = std::log(strength * lengthScale + 1) / std::log(strength + 1);
g.setColour(waveformColour.withAlpha(lengthScale));
g.drawLine(line, 2.0f);
g.drawLine(line, area.getWidth() / 150.0f);
}
}

Wyświetl plik

@ -18,7 +18,7 @@ public:
void run() override;
private:
juce::SpinLock lock;
juce::CriticalSection lock;
std::vector<float> buffer;
int numChannels = 2;
juce::Colour backgroundColour, waveformColour;

Wyświetl plik

@ -89,8 +89,8 @@ public:
std::shared_ptr<std::vector<float>> firstBuffer = std::make_shared<std::vector<float>>();
std::shared_ptr<std::vector<float>> secondBuffer = std::make_shared<std::vector<float>>();
std::shared_ptr<juce::SpinLock> firstBufferLock = std::make_shared<juce::SpinLock>();
std::shared_ptr<juce::SpinLock> secondBufferLock = std::make_shared<juce::SpinLock>();
std::shared_ptr<juce::CriticalSection> firstBufferLock = std::make_shared<juce::CriticalSection>();
std::shared_ptr<juce::CriticalSection> secondBufferLock = std::make_shared<juce::CriticalSection>();
private:
// Indirectly used by the producer to signal whether it holds the lock on the buffer.
// This is accurate if the global producer lock is held as the buffer lock is acquired

Wyświetl plik

@ -3,35 +3,6 @@
#include <JuceHeader.h>
#include "BufferConsumer.h"
// This is needed over juce::SpinLock because juce::SpinLock yeilds, which
// leads to some consumers never holding the lock.
// TODO: verify that this is a legitimate solution.
struct crude_spinlock {
std::atomic<bool> lock_ = {0};
void lock() noexcept {
for (;;) {
// Optimistically assume the lock is free on the first try
if (!lock_.exchange(true, std::memory_order_acquire)) {
return;
}
// Wait for lock to be released without generating cache misses
while (lock_.load(std::memory_order_relaxed)) {}
}
}
bool try_lock() noexcept {
// First do a relaxed load to check if lock is free in order to prevent
// unnecessary cache misses if someone does while(!try_lock())
return !lock_.load(std::memory_order_relaxed) &&
!lock_.exchange(true, std::memory_order_acquire);
}
void unlock() noexcept {
lock_.store(false, std::memory_order_release);
}
};
class BufferProducer {
public:
BufferProducer() {}
@ -42,17 +13,16 @@ public:
// being written to.
// This is only called by the thread that owns the consumer thread.
void registerConsumer(std::shared_ptr<BufferConsumer> consumer) {
lock.lock();
juce::CriticalSection::ScopedLockType l(lock);
consumers.push_back(consumer);
bufferPositions.push_back(0);
consumer->getBuffer(true);
lock.unlock();
}
// This is only called by the thread that owns the consumer thread.
// This can't happen at the same time as write() it locks the producer lock.
void unregisterConsumer(std::shared_ptr<BufferConsumer> consumer) {
lock.lock();
juce::CriticalSection::ScopedLockType l(lock);
for (int i = 0; i < consumers.size(); i++) {
if (consumers[i] == consumer) {
consumer->releaseLock();
@ -61,12 +31,11 @@ public:
break;
}
}
lock.unlock();
}
// Writes a sample to the current buffer for all consumers.
void write(float left, float right) {
lock.lock();
juce::CriticalSection::ScopedLockType l(lock);
for (int i = 0; i < consumers.size(); i++) {
std::shared_ptr<std::vector<float>> buffer = consumers[i]->getBuffer(false);
if (buffer == nullptr) {
@ -85,11 +54,10 @@ public:
consumers[i]->finishedWriting();
}
}
lock.unlock();
}
private:
crude_spinlock lock;
juce::CriticalSection lock;
std::vector<std::shared_ptr<BufferConsumer>> consumers;
std::vector<int> bufferPositions;
};

Wyświetl plik

@ -28,11 +28,11 @@ void LuaParser::parse() {
}
// only the audio thread runs this fuction
Vector2 LuaParser::draw() {
Vector2 sample;
std::vector<float> LuaParser::run() {
std::vector<float> values;
if (functionRef == -1) {
return sample;
return values;
}
lua_pushnumber(L, step);
@ -60,29 +60,25 @@ Vector2 LuaParser::draw() {
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);
auto length = lua_rawlen(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);
for (int i = 1; i <= length; i++) {
lua_pushinteger(L, i);
lua_gettable(L, -2);
float value = lua_tonumber(L, -1);
lua_pop(L, 1);
values.push_back(value);
}
}
lua_pop(L, 1);
step++;
return sample;
return values;
}
// this CANNOT run at the same time as draw()
// this CANNOT run at the same time as run()
// many threads can run this function
void LuaParser::setVariable(juce::String variableName, double value) {
juce::SpinLock::ScopedLockType lock(variableLock);

Wyświetl plik

@ -1,5 +1,4 @@
#pragma once
#include "../shape/Vector2.h"
#include <JuceHeader.h>
#include "../shape/Shape.h"
@ -9,7 +8,7 @@ public:
LuaParser(juce::String script);
~LuaParser();
Vector2 draw();
std::vector<float> run();
void setVariable(juce::String variableName, double value);
private:

Wyświetl plik

@ -48,7 +48,11 @@ Vector2 FileParser::nextSample() {
juce::SpinLock::ScopedLockType scope(lock);
if (lua != nullptr) {
return lua->draw();
auto values = lua->run();
if (values.size() < 2) {
return Vector2();
}
return Vector2(values[0], values[1]);
}
}

Wyświetl plik

@ -17,6 +17,7 @@
<FILE id="IqXIZW" name="demo.svg" compile="0" resource="1" file="Resources/svg/demo.svg"/>
<FILE id="YwkQpy" name="fixed_rotate.svg" compile="0" resource="1"
file="Resources/svg/fixed_rotate.svg"/>
<FILE id="D2AI1b" name="pencil.svg" compile="0" resource="1" file="Resources/svg/pencil.svg"/>
<FILE id="rXjNlx" name="threshold.svg" compile="0" resource="1" file="Resources/svg/threshold.svg"/>
<FILE id="qC6QiP" name="volume.svg" compile="0" resource="1" file="Resources/svg/volume.svg"/>
</GROUP>
@ -54,6 +55,10 @@
file="Source/audio/EffectParameter.h"/>
<FILE id="uhyh7T" name="LuaEffect.cpp" compile="1" resource="0" file="Source/audio/LuaEffect.cpp"/>
<FILE id="jqDcZq" name="LuaEffect.h" compile="0" resource="0" file="Source/audio/LuaEffect.h"/>
<FILE id="QBWW9w" name="PerspectiveEffect.cpp" compile="1" resource="0"
file="Source/audio/PerspectiveEffect.cpp"/>
<FILE id="h0dMim" name="PerspectiveEffect.h" compile="0" resource="0"
file="Source/audio/PerspectiveEffect.h"/>
<FILE id="t2bsR8" name="PitchDetector.cpp" compile="1" resource="0"
file="Source/audio/PitchDetector.cpp"/>
<FILE id="rQC2gX" name="PitchDetector.h" compile="0" resource="0" file="Source/audio/PitchDetector.h"/>
@ -108,6 +113,7 @@
<FILE id="x0Syav" name="LuaListComponent.h" compile="0" resource="0"
file="Source/components/LuaListComponent.h"/>
<FILE id="QQzSwh" name="SliderTextBox.h" compile="0" resource="0" file="Source/components/SliderTextBox.h"/>
<FILE id="QrDKRZ" name="SvgButton.h" compile="0" resource="0" file="Source/components/SvgButton.h"/>
<FILE id="y3UiR0" name="VisualiserComponent.cpp" compile="1" resource="0"
file="Source/components/VisualiserComponent.cpp"/>
<FILE id="ZueyNl" name="VisualiserComponent.h" compile="0" resource="0"
@ -434,7 +440,6 @@
<FILE id="vIYWRG" name="TextParser.cpp" compile="1" resource="0" file="Source/txt/TextParser.cpp"/>
<FILE id="LlefOK" name="TextParser.h" compile="0" resource="0" file="Source/txt/TextParser.h"/>
</GROUP>
<FILE id="JceyXh" name="Util.h" compile="0" resource="0" file="Source/Util.h"/>
<GROUP id="{022CB910-9A16-C4AE-4C3B-9CB57BE87FC2}" name="xml">
<FILE id="pW7WRh" name="pugiconfig.hpp" compile="0" resource="0" file="Source/xml/pugiconfig.hpp"/>
<FILE id="CnkgyF" name="pugixml.cpp" compile="1" resource="0" file="Source/xml/pugixml.cpp"/>