diff --git a/Source/ObjComponent.cpp b/Source/ObjComponent.cpp index 577cc6f..92dc9f8 100644 --- a/Source/ObjComponent.cpp +++ b/Source/ObjComponent.cpp @@ -25,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; @@ -73,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() { diff --git a/Source/ObjComponent.h b/Source/ObjComponent.h index c4c875b..a178381 100644 --- a/Source/ObjComponent.h +++ b/Source/ObjComponent.h @@ -27,9 +27,9 @@ private: juce::TextButton resetRotation{"Reset Rotation"}; juce::ToggleButton mouseRotate{"Rotate with Mouse (Esc to disable)"}; - std::shared_ptr fixedRotateX = std::make_shared("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), "white", "red"); - std::shared_ptr fixedRotateY = std::make_shared("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), "white", "red"); - std::shared_ptr fixedRotateZ = std::make_shared("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), "white", "red"); + std::shared_ptr fixedRotateX = std::make_shared("fixedRotateX", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateX); + std::shared_ptr fixedRotateY = std::make_shared("fixedRotateY", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateY); + std::shared_ptr fixedRotateZ = std::make_shared("fixedRotateZ", juce::String(BinaryData::fixed_rotate_svg), "white", "red", audioProcessor.fixedRotateZ); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ObjComponent) }; \ No newline at end of file diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 2fda0f5..d02c354 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -124,6 +124,13 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() } } } + + addParameter(fixedRotateX); + addParameter(fixedRotateY); + addParameter(fixedRotateZ); + addParameter(perspectiveEffect->fixedRotateX); + addParameter(perspectiveEffect->fixedRotateY); + addParameter(perspectiveEffect->fixedRotateZ); } OscirenderAudioProcessor::~OscirenderAudioProcessor() {} diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 1540ac2..a4ed403 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -107,16 +107,16 @@ public: }, new EffectParameter("Focal length", "focalLength", 1.0, 0.0, 2.0) ); - std::atomic fixedRotateX = false; - std::atomic fixedRotateY = false; - std::atomic 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 rotateX = std::make_shared( [this](int index, Vector2 input, const std::vector& 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); @@ -131,7 +131,7 @@ 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); @@ -146,7 +146,7 @@ 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); diff --git a/Source/audio/BooleanParameter.h b/Source/audio/BooleanParameter.h index 108d0b7..abe07ee 100644 --- a/Source/audio/BooleanParameter.h +++ b/Source/audio/BooleanParameter.h @@ -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; } diff --git a/Source/audio/PerspectiveEffect.cpp b/Source/audio/PerspectiveEffect.cpp index e32e0a7..a36cdda 100644 --- a/Source/audio/PerspectiveEffect.cpp +++ b/Source/audio/PerspectiveEffect.cpp @@ -7,37 +7,55 @@ Vector2 PerspectiveEffect::apply(int index, Vector2 input, const std::vectorgetBoolValue()) { + 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 * 50) { - currentRotateX -= std::numbers::pi * 50; + if (currentRotateX > std::numbers::pi * 8) { + currentRotateX -= std::numbers::pi * 8; } - if (currentRotateY > std::numbers::pi * 50) { - currentRotateY -= std::numbers::pi * 50; + if (currentRotateY > std::numbers::pi * 8) { + currentRotateY -= std::numbers::pi * 8; } - if (currentRotateZ > std::numbers::pi * 50) { - currentRotateZ -= std::numbers::pi * 50; + if (currentRotateZ > std::numbers::pi * 8) { + currentRotateZ -= std::numbers::pi * 8; } auto x = input.x; auto y = input.y; auto z = 0.0; - parser.setVariable("x", x); - parser.setVariable("y", y); - parser.setVariable("z", z); + 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]; + } } auto rotateX = baseRotateX + currentRotateX; diff --git a/Source/audio/PerspectiveEffect.h b/Source/audio/PerspectiveEffect.h index f6995d5..2c16b80 100644 --- a/Source/audio/PerspectiveEffect.h +++ b/Source/audio/PerspectiveEffect.h @@ -9,10 +9,15 @@ public: PerspectiveEffect(); Vector2 apply(int index, Vector2 input, const std::vector& values, double sampleRate) override; + + 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}; + bool defaultScript = true; float currentRotateX = 0; float currentRotateY = 0; diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp index df69b41..1e2dc9f 100644 --- a/Source/components/EffectsListComponent.cpp +++ b/Source/components/EffectsListComponent.cpp @@ -1,15 +1,16 @@ #include "EffectsListComponent.h" +#include "SvgButton.h" -EffectsListComponent::EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr 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) { + auto parameters = effect.parameters; for (int i = 0; i < parameters.size(); i++) { - std::shared_ptr effectComponent = std::make_shared(data.audioProcessor, *effect, i, i == 0); + std::shared_ptr effectComponent = std::make_shared(audioProcessor, effect, i, i == 0); // using weak_ptr to avoid circular reference and memory leak std::weak_ptr 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 +18,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 +72,25 @@ void EffectsListComponent::resized() { list.setBounds(area); } +std::shared_ptr 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 button = std::make_shared(parameter->name, BinaryData::fixed_rotate_svg, "white", "red", toggle); + button->onClick = [this, toggle] { + toggle->setBoolValueNotifyingHost(!toggle->getBoolValue()); + }; + return button; + } + return nullptr; +} + int EffectsListBoxModel::getRowHeight(int row) { auto data = (AudioEffectListBoxItemData&)modelData; return data.getEffect(row)->parameters.size() * 30; @@ -79,7 +104,7 @@ juce::Component* EffectsListBoxModel::refreshComponentForRow(int rowNumber, bool std::unique_ptr item(dynamic_cast(existingComponentToUpdate)); if (juce::isPositiveAndBelow(rowNumber, modelData.getNumItems())) { auto data = (AudioEffectListBoxItemData&)modelData; - item = std::make_unique(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, data.getEffect(rowNumber)); + item = std::make_unique(listBox, (AudioEffectListBoxItemData&)modelData, rowNumber, *data.getEffect(rowNumber)); } return item.release(); } diff --git a/Source/components/EffectsListComponent.h b/Source/components/EffectsListComponent.h index 67cd556..705db95 100644 --- a/Source/components/EffectsListComponent.h +++ b/Source/components/EffectsListComponent.h @@ -89,7 +89,7 @@ struct AudioEffectListBoxItemData : public DraggableListBoxItemData class EffectsListComponent : public DraggableListBoxItem { public: - EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, std::shared_ptr effect); + EffectsListComponent(DraggableListBox& lb, AudioEffectListBoxItemData& data, int rn, Effect& effect); ~EffectsListComponent(); void paint(juce::Graphics& g) override; @@ -97,10 +97,14 @@ public: void resized() override; protected: - std::shared_ptr effect; + Effect& effect; ComponentListModel listModel; juce::ListBox list; private: + OscirenderAudioProcessor& audioProcessor; + + std::shared_ptr createComponent(EffectParameter* parameter); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectsListComponent) }; diff --git a/Source/components/SvgButton.h b/Source/components/SvgButton.h index 1a965d9..9a654c1 100644 --- a/Source/components/SvgButton.h +++ b/Source/components/SvgButton.h @@ -1,9 +1,9 @@ #pragma once #include -class SvgButton : public juce::DrawableButton { +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) : juce::DrawableButton(name, juce::DrawableButton::ButtonStyle::ImageFitted) { + 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); @@ -16,14 +16,34 @@ class SvgButton : public juce::DrawableButton { 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 {} + ~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 normalImage; std::unique_ptr normalImageOn; + BooleanParameter* toggle; void changeSvgColour(juce::XmlElement* xml, juce::String colour) { forEachXmlChildElement(*xml, xmlnode) { diff --git a/Source/components/VisualiserComponent.cpp b/Source/components/VisualiserComponent.cpp index 3385306..67cc223 100644 --- a/Source/components/VisualiserComponent.cpp +++ b/Source/components/VisualiserComponent.cpp @@ -86,6 +86,6 @@ void VisualiserComponent::paintXY(juce::Graphics& g, juce::Rectangle 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); } }