diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 0c71d957..6ab3adc2 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -2,7 +2,6 @@ name: Build
on:
push:
branches:
- - main
- develop
workflow_dispatch:
jobs:
diff --git a/Resources/svg/link.svg b/Resources/svg/link.svg
new file mode 100644
index 00000000..38ec0372
--- /dev/null
+++ b/Resources/svg/link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/Source/CommonPluginEditor.cpp b/Source/CommonPluginEditor.cpp
index 8209474d..828d3f2b 100644
--- a/Source/CommonPluginEditor.cpp
+++ b/Source/CommonPluginEditor.cpp
@@ -45,7 +45,7 @@ CommonPluginEditor::CommonPluginEditor(CommonAudioProcessor& p, juce::String app
visualiserSettings.setColour(juce::ResizableWindow::backgroundColourId, Colours::dark);
recordingSettings.setLookAndFeel(&getLookAndFeel());
- recordingSettings.setSize(300, 280);
+ recordingSettings.setSize(300, 330);
#if JUCE_WINDOWS
// if not standalone, use native title bar for compatibility with DAWs
recordingSettingsWindow.setUsingNativeTitleBar(processor.wrapperType == juce::AudioProcessor::WrapperType::wrapperType_Standalone);
diff --git a/Source/CommonPluginEditor.h b/Source/CommonPluginEditor.h
index adff0775..d973ffde 100644
--- a/Source/CommonPluginEditor.h
+++ b/Source/CommonPluginEditor.h
@@ -44,14 +44,14 @@ public:
#endif
#if OSCI_PREMIUM
- int VISUALISER_SETTINGS_HEIGHT = 1200;
+ int VISUALISER_SETTINGS_HEIGHT = 1230;
#else
int VISUALISER_SETTINGS_HEIGHT = 700;
#endif
VisualiserSettings visualiserSettings = VisualiserSettings(audioProcessor.visualiserParameters, 3);
RecordingSettings recordingSettings = RecordingSettings(audioProcessor.recordingParameters);
- SettingsWindow recordingSettingsWindow = SettingsWindow("Recording Settings", recordingSettings, 330, 350, 330, 350);
+ SettingsWindow recordingSettingsWindow = SettingsWindow("Recording Settings", recordingSettings, 330, 360, 330, 360);
VisualiserComponent visualiser{
audioProcessor,
*this,
diff --git a/Source/EffectPluginEditor.cpp b/Source/EffectPluginEditor.cpp
index 9dfe1824..d5aada0f 100644
--- a/Source/EffectPluginEditor.cpp
+++ b/Source/EffectPluginEditor.cpp
@@ -17,19 +17,30 @@ EffectPluginEditor::EffectPluginEditor(EffectAudioProcessor& p)
addAndMakeVisible(visualiser);
addAndMakeVisible(titleVisualiser);
- addAndMakeVisible(bitCrush);
+ addAndMakeVisible(sliderVisualiser);
titleVisualiser.setCropRectangle(juce::Rectangle(-0.1f, 0.35f, 1.2f, 0.3f));
+ // Configure the slider visualiser component
+ sliderVisualiser.onValueChange([this]() {
+ audioProcessor.bitCrush->parameters[0]->setUnnormalisedValueNotifyingHost(sliderVisualiser.getValue());
+ });
- bitCrush.slider.onValueChange = [this] {
- audioProcessor.bitCrush->parameters[0]->setUnnormalisedValueNotifyingHost(bitCrush.slider.getValue());
- };
+ // Set the label for the slider
+ sliderVisualiser.setLabel("bit crush");
setSize(600, 200);
setResizable(false, false);
tooltipDropShadow.setOwner(&tooltipWindow.get());
tooltipWindow->setMillisecondsBeforeTipAppears(0);
+
+ audioProcessor.bitCrush->addListener(0, this);
+}
+
+EffectPluginEditor::~EffectPluginEditor() {
+ audioProcessor.bitCrush->removeListener(0, this);
+ setLookAndFeel(nullptr);
+ juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr);
}
void EffectPluginEditor::resized() {
@@ -40,10 +51,18 @@ void EffectPluginEditor::resized() {
auto titleBounds = bounds.removeFromTop(100);
titleVisualiser.setBounds(titleBounds);
- bitCrush.setBounds(bounds);
+ // Set bounds for sliderVisualiser
+ sliderVisualiser.setBounds(bounds);
}
-EffectPluginEditor::~EffectPluginEditor() {
- setLookAndFeel(nullptr);
- juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr);
+void EffectPluginEditor::parameterValueChanged(int parameterIndex, float newValue) {
+ if (parameterIndex == 0) {
+ juce::MessageManager::getInstance()->callAsync([this, newValue]() {
+ sliderVisualiser.setValue(newValue);
+ // Update visualizer directly as setValue doesn't always trigger onChange
+ sliderVisualiser.updateVisualiser();
+ });
+ }
}
+
+void EffectPluginEditor::parameterGestureChanged(int parameterIndex, bool gestureIsStarting) {}
diff --git a/Source/EffectPluginEditor.h b/Source/EffectPluginEditor.h
index 2b1868b8..3b6f869b 100644
--- a/Source/EffectPluginEditor.h
+++ b/Source/EffectPluginEditor.h
@@ -5,13 +5,16 @@
#include "visualiser/VisualiserRenderer.h"
#include "LookAndFeel.h"
#include "components/EffectComponent.h"
+#include "components/SliderVisualiserComponent.h"
-class EffectPluginEditor : public juce::AudioProcessorEditor {
+class EffectPluginEditor : public juce::AudioProcessorEditor, public juce::AudioProcessorParameter::Listener {
public:
EffectPluginEditor(EffectAudioProcessor&);
~EffectPluginEditor() override;
void resized() override;
+ void parameterValueChanged(int parameterIndex, float newValue) override;
+ void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
private:
EffectAudioProcessor& audioProcessor;
@@ -35,11 +38,13 @@ public:
"Title"
};
+ SliderVisualiserComponent sliderVisualiser{
+ audioProcessor
+ };
+
juce::SharedResourcePointer tooltipWindow;
juce::DropShadower tooltipDropShadow{juce::DropShadow(juce::Colours::black.withAlpha(0.5f), 6, {0,0})};
- EffectComponent bitCrush{*audioProcessor.bitCrush};
-
#if JUCE_LINUX
juce::OpenGLContext openGlContext;
#endif
diff --git a/Source/EffectPluginProcessor.cpp b/Source/EffectPluginProcessor.cpp
index 87fdf5c6..20170a5b 100644
--- a/Source/EffectPluginProcessor.cpp
+++ b/Source/EffectPluginProcessor.cpp
@@ -26,8 +26,11 @@ EffectAudioProcessor::EffectAudioProcessor()
}
}
- titleShapes = titleParser.draw();
- titleShapesLength = osci::Shape::totalLength(titleShapes);
+ // Initialize title shapes
+ juce::Font titleFont = juce::Font(1.0f, juce::Font::bold);
+ TextParser titleParser{"bit crush", titleFont};
+ auto titleShapes = titleParser.draw();
+ titleRenderer.setShapes(std::move(titleShapes));
}
const juce::String EffectAudioProcessor::getName() const {
@@ -86,6 +89,7 @@ void EffectAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
}
currentSampleRate = sampleRate;
+ titleRenderer.setSampleRate(sampleRate);
threadManager.prepare(sampleRate, samplesPerBlock);
}
@@ -126,56 +130,25 @@ void EffectAudioProcessor::processBlock(juce::AudioBuffer& buffer, juce::
if (output.getNumChannels() > 1) {
outputArray[1][sample] = point.y;
}
+ // handle the title drawing
+ osci::Point titlePoint = titleRenderer.nextVector();
- // handle the title drawing
-
- osci::Point titlePoint = { 0.0, 0.0, 1.0f };
- if (currentTitleShape < titleShapes.size()) {
- auto& shape = titleShapes[currentTitleShape];
- double length = shape->length();
- double drawingProgress = length == 0.0 ? 1 : titleShapeDrawn / length;
- titlePoint = shape->nextVector(drawingProgress);
- titlePoint.z = 1.0f;
-
- // apply bit crush without animating values, as this has already been done
- titlePoint = bitCrush->apply(sample, titlePoint, 0.0, false);
- }
-
+ // apply bit crush without animating values, as this has already been done
+ titlePoint = bitCrush->apply(sample, titlePoint, 0.0, false);
+
threadManager.write(titlePoint, "VisualiserRendererTitle");
-
- incrementTitleShapeDrawing();
-
- if (titleFrameDrawn >= titleShapesLength) {
- double currentShapeLength = 0;
- if (currentTitleShape < titleShapes.size()) {
- currentShapeLength = titleShapes[currentTitleShape]->len;
- }
- titleFrameDrawn -= titleShapesLength;
- currentTitleShape = 0;
+
+ osci::Point sliderPoint;
+ {
+ juce::SpinLock::ScopedLockType lock(sliderLock);
+ sliderPoint = sliderRenderer.nextVector();
}
+
+ threadManager.write(sliderPoint, "VisualiserRendererSlider");
}
}
-void EffectAudioProcessor::incrementTitleShapeDrawing() {
- if (titleShapes.size() <= 0) return;
- double length = currentTitleShape < titleShapes.size() ? titleShapes[currentTitleShape]->len : 0.0;
- double FREQUENCY = 60.0;
- double lengthIncrement = titleShapesLength / (currentSampleRate / FREQUENCY);
- titleFrameDrawn += lengthIncrement;
- titleShapeDrawn += lengthIncrement;
- // Need to skip all shapes that the lengthIncrement draws over.
- // This is especially an issue when there are lots of small lines being
- // drawn.
- while (titleShapeDrawn > length) {
- titleShapeDrawn -= length;
- currentTitleShape++;
- if (currentTitleShape >= titleShapes.size()) {
- currentTitleShape = 0;
- }
- length = titleShapes[currentTitleShape]->len;
- }
-}
void EffectAudioProcessor::getStateInformation(juce::MemoryBlock& destData) {
std::unique_ptr xml = std::make_unique("project");
diff --git a/Source/EffectPluginProcessor.h b/Source/EffectPluginProcessor.h
index 132a60eb..c1f6e6df 100644
--- a/Source/EffectPluginProcessor.h
+++ b/Source/EffectPluginProcessor.h
@@ -6,6 +6,7 @@
#include "audio/BitCrushEffect.h"
#include "audio/AutoGainControlEffect.h"
#include "txt/TextParser.h"
+#include "audio/ShapeVectorRenderer.h"
class EffectAudioProcessor : public juce::AudioProcessor
#if JucePlugin_Enable_ARA
@@ -56,6 +57,9 @@ public:
VisualiserParameters visualiserParameters;
osci::AudioBackgroundThreadManager threadManager;
+
+ juce::SpinLock sliderLock;
+ ShapeVectorRenderer sliderRenderer;
protected:
@@ -64,16 +68,7 @@ protected:
private:
double currentSampleRate = 44100.0;
- // variables for the title
- juce::Font titleFont = juce::Font(1.0f, juce::Font::bold);
- TextParser titleParser{"bit crush", titleFont};
- std::vector> titleShapes;
- double titleShapesLength = 0.0;
- int currentTitleShape = 0;
- double titleShapeDrawn = 0.0;
- double titleFrameDrawn = 0.0;
-
- void incrementTitleShapeDrawing();
+ ShapeVectorRenderer titleRenderer;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EffectAudioProcessor)
diff --git a/Source/EffectsComponent.cpp b/Source/EffectsComponent.cpp
index d39a4525..69b9148b 100644
--- a/Source/EffectsComponent.cpp
+++ b/Source/EffectsComponent.cpp
@@ -11,10 +11,6 @@ EffectsComponent::EffectsComponent(OscirenderAudioProcessor& p, OscirenderAudioP
frequency.slider.setTextValueSuffix("Hz");
frequency.slider.setValue(audioProcessor.frequencyEffect->getValue(), juce::dontSendNotification);
- frequency.slider.onValueChange = [this] {
- audioProcessor.frequencyEffect->parameters[0]->setUnnormalisedValueNotifyingHost(frequency.slider.getValue());
- };
-
/*addBtn.setButtonText("Add Item...");
addBtn.onClick = [this]()
{
diff --git a/Source/FrameSettingsComponent.cpp b/Source/FrameSettingsComponent.cpp
index e0e298b1..77d119bd 100644
--- a/Source/FrameSettingsComponent.cpp
+++ b/Source/FrameSettingsComponent.cpp
@@ -36,15 +36,15 @@ FrameSettingsComponent::FrameSettingsComponent(OscirenderAudioProcessor& p, Osci
audioProcessor.animationOffset->setUnnormalisedValueNotifyingHost(offsetBox.getValue());
};
+ rateBox.onReturnKey = updateAnimation;
rateBox.onFocusLost = updateAnimation;
+
offsetBox.onFocusLost = updateAnimation;
+ offsetBox.onReturnKey = updateAnimation;
- threshold.slider.onValueChange = [this]() {
- audioProcessor.imageThreshold->setValue(threshold.slider.getValue());
- };
-
- stride.slider.onValueChange = [this]() {
- audioProcessor.imageStride->setValue(stride.slider.getValue());
+ animate.setClickingTogglesState(true);
+ animate.onClick = [this]() {
+ audioProcessor.animateFrames->setValue(animate.getToggleState());
};
audioProcessor.animationRate->addListener(this);
diff --git a/Source/PerspectiveComponent.cpp b/Source/PerspectiveComponent.cpp
index f418c6d7..c2660661 100644
--- a/Source/PerspectiveComponent.cpp
+++ b/Source/PerspectiveComponent.cpp
@@ -7,9 +7,6 @@ PerspectiveComponent::PerspectiveComponent(OscirenderAudioProcessor& p, Oscirend
addAndMakeVisible(perspective);
addAndMakeVisible(focalLength);
-
- perspective.setSliderOnValueChange();
- focalLength.setSliderOnValueChange();
}
PerspectiveComponent::~PerspectiveComponent() {}
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index 5d6379be..53cf8f66 100644
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -54,7 +54,7 @@ public:
juce::ComponentAnimator codeEditorAnimator;
LuaComponent lua{audioProcessor, *this};
- SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings", visualiserSettings, 550, 500, 550, VISUALISER_SETTINGS_HEIGHT);
+ SettingsWindow visualiserSettingsWindow = SettingsWindow("Visualiser Settings", visualiserSettings, 550, 500, 1500, VISUALISER_SETTINGS_HEIGHT);
LuaConsole console;
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 9b8c13f7..2600630c 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -34,16 +34,19 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
toggleableEffects.push_back(std::make_shared(
std::make_shared(),
new osci::EffectParameter("Vector Cancelling", "Inverts the audio and image every few samples to 'cancel out' the audio, making the audio quiet, and distorting the image.", "vectorCancelling", VERSION_HINT, 0.1111111, 0.0, 1.0)));
- toggleableEffects.push_back(std::make_shared(
+ auto scaleEffect = std::make_shared(
[this](int index, osci::Point input, const std::vector>& values, double sampleRate) {
return input * osci::Point(values[0], values[1], values[2]);
},
std::vector{
- new osci::EffectParameter("Scale X", "Scales the object in the horizontal direction.", "scaleX", VERSION_HINT, 1.0, -5.0, 5.0),
- new osci::EffectParameter("Scale Y", "Scales the object in the vertical direction.", "scaleY", VERSION_HINT, 1.0, -5.0, 5.0),
- new osci::EffectParameter("Scale Z", "Scales the depth of the object.", "scaleZ", VERSION_HINT, 1.0, -5.0, 5.0),
- }));
- toggleableEffects.push_back(std::make_shared(
+ new osci::EffectParameter("Scale X", "Scales the object in the horizontal direction.", "scaleX", VERSION_HINT, 1.0, -3.0, 3.0),
+ new osci::EffectParameter("Scale Y", "Scales the object in the vertical direction.", "scaleY", VERSION_HINT, 1.0, -3.0, 3.0),
+ new osci::EffectParameter("Scale Z", "Scales the depth of the object.", "scaleZ", VERSION_HINT, 1.0, -3.0, 3.0),
+ });
+ scaleEffect->markLockable(true);
+ booleanParameters.push_back(scaleEffect->linked);
+ toggleableEffects.push_back(scaleEffect);
+ auto distortEffect = std::make_shared(
[this](int index, osci::Point input, const std::vector>& values, double sampleRate) {
int flip = index % 2 == 0 ? 1 : -1;
osci::Point jitter = osci::Point(flip * values[0], flip * values[1], flip * values[2]);
@@ -53,7 +56,10 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
new osci::EffectParameter("Distort X", "Distorts the image in the horizontal direction by jittering the audio sample being drawn.", "distortX", VERSION_HINT, 0.0, 0.0, 1.0),
new osci::EffectParameter("Distort Y", "Distorts the image in the vertical direction by jittering the audio sample being drawn.", "distortY", VERSION_HINT, 0.0, 0.0, 1.0),
new osci::EffectParameter("Distort Z", "Distorts the depth of the image by jittering the audio sample being drawn.", "distortZ", VERSION_HINT, 0.1, 0.0, 1.0),
- }));
+ });
+ distortEffect->markLockable(false);
+ booleanParameters.push_back(distortEffect->linked);
+ toggleableEffects.push_back(distortEffect);
auto rippleEffect = std::make_shared(
[this](int index, osci::Point input, const std::vector>& values, double sampleRate) {
double phase = values[1] * std::numbers::pi;
diff --git a/Source/audio/AudioRecorder.h b/Source/audio/AudioRecorder.h
index 98fe9ae8..4ab640cc 100644
--- a/Source/audio/AudioRecorder.h
+++ b/Source/audio/AudioRecorder.h
@@ -43,6 +43,17 @@ public:
activeWriter = threadedWriter.get();
}
}
+ } else {
+ // Error: Invalid sample rate
+ juce::AlertWindow::showMessageBoxAsync(
+ juce::AlertWindow::WarningIcon,
+ "Recording Error",
+ "Cannot start recording: Invalid sample rate (" + juce::String(sampleRate) + "). Sample rate must be greater than 0.",
+ "OK"
+ );
+ stop();
+ stopCallback();
+ return;
}
}
@@ -91,7 +102,7 @@ private:
juce::int64 nextSampleNum = 0;
double recordingLength = 99999999999.0;
- double sampleRate = 192000;
+ double sampleRate = -1;
juce::CriticalSection writerLock;
std::atomic activeWriter { nullptr };
diff --git a/Source/audio/ShapeVectorRenderer.cpp b/Source/audio/ShapeVectorRenderer.cpp
new file mode 100644
index 00000000..477c790d
--- /dev/null
+++ b/Source/audio/ShapeVectorRenderer.cpp
@@ -0,0 +1,77 @@
+#include "ShapeVectorRenderer.h"
+
+ShapeVectorRenderer::ShapeVectorRenderer(double sampleRate, double frequency)
+ : currentSampleRate(sampleRate), frequency(frequency) {
+}
+
+void ShapeVectorRenderer::setShapes(std::vector> newShapes) {
+ shapes.clear();
+
+ // Move the shapes from the input vector to our internal vector
+ for (auto& shape : newShapes) {
+ shapes.push_back(std::move(shape));
+ }
+
+ // Calculate the total length of all shapes
+ shapesLength = osci::Shape::totalLength(shapes);
+
+ // Reset the drawing state
+ currentShape = 0;
+ shapeDrawn = 0.0;
+ frameDrawn = 0.0;
+}
+
+osci::Point ShapeVectorRenderer::nextVector() {
+ osci::Point point = { 0.0, 0.0, 1.0f };
+
+ if (shapes.size() <= 0) {
+ return point;
+ }
+
+ if (currentShape < shapes.size()) {
+ auto& shape = shapes[currentShape];
+ double length = shape->length();
+ double drawingProgress = length == 0.0 ? 1.0 : shapeDrawn / length;
+ point = shape->nextVector(drawingProgress);
+ point.z = 1.0f;
+ }
+
+ incrementShapeDrawing();
+
+ if (frameDrawn >= shapesLength) {
+ frameDrawn -= shapesLength;
+ currentShape = 0;
+ }
+
+ return point;
+}
+
+void ShapeVectorRenderer::setSampleRate(double newSampleRate) {
+ currentSampleRate = newSampleRate;
+}
+
+void ShapeVectorRenderer::setFrequency(double newFrequency) {
+ frequency = newFrequency;
+}
+
+void ShapeVectorRenderer::incrementShapeDrawing() {
+ if (shapes.size() <= 0) return;
+
+ double length = currentShape < shapes.size() ? shapes[currentShape]->len : 0.0;
+ double lengthIncrement = shapesLength / (currentSampleRate / frequency);
+
+ frameDrawn += lengthIncrement;
+ shapeDrawn += lengthIncrement;
+
+ // Need to skip all shapes that the lengthIncrement draws over.
+ // This is especially an issue when there are lots of small lines being
+ // drawn.
+ while (shapeDrawn > length) {
+ shapeDrawn -= length;
+ currentShape++;
+ if (currentShape >= shapes.size()) {
+ currentShape = 0;
+ }
+ length = shapes[currentShape]->len;
+ }
+}
diff --git a/Source/audio/ShapeVectorRenderer.h b/Source/audio/ShapeVectorRenderer.h
new file mode 100644
index 00000000..510558f8
--- /dev/null
+++ b/Source/audio/ShapeVectorRenderer.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include
+#include
+#include
+#include "../visualiser/VisualiserParameters.h"
+
+class ShapeVectorRenderer {
+public:
+ ShapeVectorRenderer(double sampleRate = 44100.0, double frequency = 60.0);
+
+ // Set new shapes to render
+ void setShapes(std::vector> newShapes);
+
+ // Get the next point in the shape vector sequence
+ osci::Point nextVector();
+
+ // Update sample rate if it changes
+ void setSampleRate(double newSampleRate);
+
+ // Set the frequency at which shapes should be drawn
+ void setFrequency(double newFrequency);
+
+private:
+ double currentSampleRate = 44100.0;
+ double frequency = 60.0;
+
+ std::vector> shapes;
+ double shapesLength = 0.0;
+ int currentShape = 0;
+ double shapeDrawn = 0.0;
+ double frameDrawn = 0.0;
+
+ void incrementShapeDrawing();
+};
diff --git a/Source/components/DoubleTextBox.h b/Source/components/DoubleTextBox.h
index cc451e43..9ffc0420 100644
--- a/Source/components/DoubleTextBox.h
+++ b/Source/components/DoubleTextBox.h
@@ -8,6 +8,7 @@ public:
setMultiLine(false);
setJustification(juce::Justification::centred);
setFont(juce::Font(15.0f, juce::Font::plain));
+ setSelectAllWhenFocused(true);
onTextChange = [this]() {
setText(getText(), false);
};
diff --git a/Source/components/EffectComponent.cpp b/Source/components/EffectComponent.cpp
index cb8a6dd5..ba5bd375 100644
--- a/Source/components/EffectComponent.cpp
+++ b/Source/components/EffectComponent.cpp
@@ -1,4 +1,5 @@
#include "EffectComponent.h"
+
#include "../LookAndFeel.h"
EffectComponent::EffectComponent(osci::Effect& effect, int index) : effect(effect), index(index) {
@@ -15,6 +16,16 @@ EffectComponent::EffectComponent(osci::Effect& effect, int index) : effect(effec
addAndMakeVisible(*sidechainButton);
}
+ if (effect.linked != nullptr && index == 0) {
+ linkButton.setTooltip("When enabled, parameters are linked and changes to one parameter will affect all parameters.");
+ linkButton.onClick = [this]() {
+ if (this->effect.linked != nullptr) {
+ this->effect.linked->setBoolValueNotifyingHost(!this->effect.linked->getBoolValue());
+ }
+ };
+ addAndMakeVisible(linkButton);
+ }
+
slider.setSliderStyle(juce::Slider::LinearHorizontal);
slider.setTextBoxStyle(juce::Slider::TextBoxRight, false, TEXT_BOX_WIDTH, slider.getTextBoxHeight());
if (effect.parameters[index]->step == 1.0) {
@@ -57,8 +68,8 @@ EffectComponent::EffectComponent(osci::Effect& effect) : EffectComponent(effect,
void EffectComponent::setSliderValueIfChanged(osci::FloatParameter* parameter, juce::Slider& slider) {
juce::String newSliderValue = juce::String(parameter->getValueUnnormalised(), 3);
- juce::String oldSliderValue = juce::String((float) slider.getValue(), 3);
-
+ juce::String oldSliderValue = juce::String((float)slider.getValue(), 3);
+
// only set the slider value if the parameter value is different so that we prefer the more
// precise slider value.
if (newSliderValue != oldSliderValue) {
@@ -70,7 +81,17 @@ void EffectComponent::setupComponent() {
osci::EffectParameter* parameter = effect.parameters[index];
setEnabled(effect.enabled == nullptr || effect.enabled->getBoolValue());
-
+
+ if (effect.linked != nullptr && index == 0) {
+ linkButton.setToggleState(effect.linked->getBoolValue(), juce::dontSendNotification);
+
+ if (effect.linked->getBoolValue()) {
+ for (int i = 1; i < effect.parameters.size(); i++) {
+ effect.setValue(i, effect.parameters[0]->getValueUnnormalised());
+ }
+ }
+ }
+
if (updateToggleState != nullptr) {
updateToggleState();
}
@@ -83,6 +104,20 @@ void EffectComponent::setupComponent() {
setSliderValueIfChanged(parameter, slider);
slider.setDoubleClickReturnValue(true, parameter->defaultValue);
+ // Set the new slider value change handler
+ slider.onValueChange = [this] {
+ // Update the effect parameter with this slider's value
+ effect.setValue(index, slider.getValue());
+
+ if (effect.linked != nullptr && effect.linked->getBoolValue()) {
+ for (int i = 0; i < effect.parameters.size(); i++) {
+ if (i != index) {
+ effect.setValue(i, slider.getValue());
+ }
+ }
+ }
+ };
+
lfoEnabled = parameter->lfo != nullptr && parameter->lfoRate != nullptr;
if (lfoEnabled) {
lfo.setSelectedId(parameter->lfo->getValueUnnormalised(), juce::dontSendNotification);
@@ -125,7 +160,6 @@ void EffectComponent::setupComponent() {
effect.parameters[index]->sidechain->setBoolValueNotifyingHost(!effect.parameters[index]->sidechain->getBoolValue());
};
}
-
if (sidechainEnabled && effect.parameters[index]->sidechain->getBoolValue()) {
slider.setEnabled(false);
@@ -138,7 +172,6 @@ void EffectComponent::setupComponent() {
}
}
-
EffectComponent::~EffectComponent() {
effect.removeListener(index, this);
}
@@ -146,14 +179,17 @@ EffectComponent::~EffectComponent() {
void EffectComponent::resized() {
auto bounds = getLocalBounds();
auto componentBounds = bounds.removeFromRight(25);
- if (component != nullptr) {
+
+ if (effect.linked != nullptr) {
+ linkButton.setBounds(componentBounds);
+ } else if (component != nullptr) {
component->setBounds(componentBounds);
}
if (sidechainEnabled) {
sidechainButton->setBounds(bounds.removeFromRight(20));
}
-
+
if (settingsButton.isVisible()) {
settingsButton.setBounds(bounds.removeFromRight(20));
}
@@ -165,7 +201,7 @@ void EffectComponent::resized() {
}
bounds.removeFromRight(2);
-
+
bounds.removeFromLeft(5);
label.setBounds(bounds.removeFromLeft(drawingSmall ? SMALL_TEXT_WIDTH : TEXT_WIDTH));
@@ -205,12 +241,6 @@ void EffectComponent::setRangeEnabled(bool enabled) {
}
void EffectComponent::setComponent(std::shared_ptr component) {
- this->component = component;
+ this->component = component;
addAndMakeVisible(component.get());
}
-
-void EffectComponent::setSliderOnValueChange() {
- slider.onValueChange = [this] {
- effect.setValue(index, slider.getValue());
- };
-}
diff --git a/Source/components/EffectComponent.h b/Source/components/EffectComponent.h
index dfb72f99..0031c2aa 100644
--- a/Source/components/EffectComponent.h
+++ b/Source/components/EffectComponent.h
@@ -1,5 +1,6 @@
#pragma once
#include
+
#include "LabelledTextBox.h"
#include "SvgButton.h"
@@ -18,14 +19,13 @@ public:
void setRangeEnabled(bool enabled);
void setComponent(std::shared_ptr component);
- void setSliderOnValueChange();
juce::Slider slider;
juce::Slider lfoSlider;
osci::Effect& effect;
int index = 0;
juce::ComboBox lfo;
-
+
class EffectSettingsComponent : public juce::Component {
public:
EffectSettingsComponent(EffectComponent* parent) {
@@ -38,9 +38,9 @@ public:
addAndMakeVisible(lfoEndSlider);
addAndMakeVisible(smoothValueChangeLabel);
addAndMakeVisible(smoothValueChangeSlider);
-
+
osci::EffectParameter* parameter = parent->effect.parameters[parent->index];
-
+
min.textBox.setValue(parameter->min, juce::dontSendNotification);
max.textBox.setValue(parameter->max, juce::dontSendNotification);
@@ -87,7 +87,7 @@ public:
lfoEndSlider.onValueChange = [this, parameter]() {
parameter->lfoEndPercent->setUnnormalisedValueNotifyingHost(lfoEndSlider.getValue());
};
-
+
smoothValueChangeLabel.setText("Smooth Value Change Speed", juce::dontSendNotification);
smoothValueChangeLabel.setJustificationType(juce::Justification::centred);
smoothValueChangeLabel.setFont(juce::Font(14.0f, juce::Font::bold));
@@ -102,7 +102,7 @@ public:
popupLabel.setJustificationType(juce::Justification::centred);
popupLabel.setFont(juce::Font(14.0f, juce::Font::bold));
}
-
+
void resized() override {
auto bounds = getLocalBounds();
popupLabel.setBounds(bounds.removeFromTop(30));
@@ -115,7 +115,7 @@ public:
smoothValueChangeLabel.setBounds(bounds.removeFromTop(20));
smoothValueChangeSlider.setBounds(bounds.removeFromTop(40));
}
-
+
private:
juce::Label popupLabel;
LabelledTextBox min{"Min"};
@@ -127,7 +127,7 @@ public:
juce::Label smoothValueChangeLabel;
juce::Slider smoothValueChangeSlider;
};
-
+
std::function updateToggleState;
private:
@@ -145,9 +145,17 @@ private:
std::unique_ptr sidechainButton;
+ SvgButton linkButton = SvgButton(effect.parameters[index]->name + " Link",
+ BinaryData::link_svg,
+ juce::Colours::white,
+ juce::Colours::red,
+ nullptr,
+ BinaryData::link_svg
+ );
+
juce::Label label;
- SvgButton settingsButton = { "settingsButton", BinaryData::cog_svg, juce::Colours::white };
+ SvgButton settingsButton = {"settingsButton", BinaryData::cog_svg, juce::Colours::white};
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(EffectComponent)
};
diff --git a/Source/components/EffectsListComponent.cpp b/Source/components/EffectsListComponent.cpp
index 06c71afe..7c48aaa9 100644
--- a/Source/components/EffectsListComponent.cpp
+++ b/Source/components/EffectsListComponent.cpp
@@ -12,11 +12,6 @@ effect(effect), audioProcessor(data.audioProcessor), editor(data.editor) {
// 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());
- }
- };
list.setEnabled(selected.getToggleState());
selected.onClick = [this, weakEffectComponent] {
diff --git a/Source/components/LuaListComponent.cpp b/Source/components/LuaListComponent.cpp
index 9b13c2c0..afa9cded 100644
--- a/Source/components/LuaListComponent.cpp
+++ b/Source/components/LuaListComponent.cpp
@@ -3,10 +3,6 @@
LuaListComponent::LuaListComponent(OscirenderAudioProcessor& p, osci::Effect& effect) {
effectComponent = std::make_shared(effect);
- effectComponent->slider.onValueChange = [this, &effect, &p] {
- effect.setValue(effectComponent->slider.getValue());
- };
-
addAndMakeVisible(*effectComponent);
}
diff --git a/Source/components/SliderVisualiserComponent.cpp b/Source/components/SliderVisualiserComponent.cpp
new file mode 100644
index 00000000..d9008cda
--- /dev/null
+++ b/Source/components/SliderVisualiserComponent.cpp
@@ -0,0 +1,149 @@
+#include "SliderVisualiserComponent.h"
+
+SliderVisualiserComponent::SliderVisualiserComponent(EffectAudioProcessor& processor)
+ : audioProcessor(processor)
+{
+ sliderVisualiser.setCropRectangle(juce::Rectangle(0.0f, 0.35f, 1.0f, 0.25f));
+ sliderVisualiser.setInterceptsMouseClicks(false, false);
+
+ // Configure slider to be invisible but functional
+ slider.setLookAndFeel(nullptr); // Remove any look and feel
+ slider.setColour(juce::Slider::trackColourId, juce::Colours::transparentBlack);
+ slider.setColour(juce::Slider::backgroundColourId, juce::Colours::transparentBlack);
+ slider.setColour(juce::Slider::thumbColourId, juce::Colours::transparentBlack);
+ slider.setColour(juce::Slider::textBoxOutlineColourId, juce::Colours::transparentBlack);
+ slider.setColour(juce::Slider::textBoxTextColourId, juce::Colours::transparentBlack);
+ slider.setColour(juce::Slider::textBoxBackgroundColourId, juce::Colours::transparentBlack);
+ slider.setTextBoxStyle(juce::Slider::NoTextBox, true, 0, 0);
+ slider.setRange(0.0, 1.0, 0.001);
+
+ // Setup default callback
+ slider.onValueChange = [this] { updateVisualiser(); };
+
+ // Set default label
+ setLabel("Param");
+
+ addAndMakeVisible(sliderVisualiser);
+ addAndMakeVisible(slider);
+}
+
+SliderVisualiserComponent::~SliderVisualiserComponent()
+{
+}
+
+void SliderVisualiserComponent::resized()
+{
+ auto bounds = getLocalBounds();
+ sliderVisualiser.setBounds(bounds);
+
+ // Scale down the slider to match the visual representation
+ // It should only occupy the portion where the slider visuals appear (not the label area)
+ int labelWidth = bounds.getWidth() * labelWidthProportion;
+ auto sliderBounds = bounds.withTrimmedLeft(labelWidth - 5);
+ slider.setBounds(sliderBounds.withTrimmedRight(10)); // Add some padding on the right
+}
+
+void SliderVisualiserComponent::setValue(double newValue)
+{
+ slider.setValue(newValue);
+}
+
+double SliderVisualiserComponent::getValue() const
+{
+ return slider.getValue();
+}
+
+void SliderVisualiserComponent::setRange(double newMinimum, double newMaximum, double newInterval)
+{
+ slider.setRange(newMinimum, newMaximum, newInterval);
+}
+
+void SliderVisualiserComponent::addListener(juce::Slider::Listener* listener)
+{
+ slider.addListener(listener);
+}
+
+void SliderVisualiserComponent::removeListener(juce::Slider::Listener* listener)
+{
+ slider.removeListener(listener);
+}
+
+void SliderVisualiserComponent::onValueChange(std::function callback)
+{
+ slider.onValueChange = [this, callback]() {
+ updateVisualiser();
+ callback();
+ };
+}
+
+void SliderVisualiserComponent::setLabel(const juce::String& labelText)
+{
+ // Only update if the label has changed
+ if (label != labelText) {
+ label = labelText;
+ updateLabelShapes();
+ updateVisualiser();
+ }
+}
+
+void SliderVisualiserComponent::updateLabelShapes()
+{
+ // Create text shapes using TextParser
+ if (label.isNotEmpty()) {
+ TextParser labelParser{label, labelFont};
+ labelShapes = labelParser.draw();
+
+ // Calculate the center of the label area
+ double labelCenter = -1.0 + (labelWidthProportion * 1.0); // Center of the label area in oscilloscope space
+
+ // Scale and position the label shapes to appear on the left side
+ for (auto& shape : labelShapes) {
+ // Scale down the text to fit nicely
+ shape->scale(0.3, 0.3, 1.0);
+ // Position centered in the label area
+ shape->translate(labelCenter, 0.0, 0.0);
+ }
+ } else {
+ labelShapes.clear();
+ }
+}
+
+void SliderVisualiserComponent::updateVisualiser()
+{
+ // Create shapes for the slider indicator
+ std::vector> combinedShapes;
+
+ // First add the label shapes
+ for (auto& shape : labelShapes) {
+ combinedShapes.push_back(shape->clone());
+ }
+
+ // Calculate the visualizer coordinates that exactly match our component layout
+ // Convert component coordinates to oscilloscope coordinates (-1.0 to 1.0)
+ double sliderStart = -1.0 + (labelWidthProportion * 2.0); // Scaled from component space to oscilloscope space
+ double sliderEnd = 1.0;
+ double sliderRange = sliderEnd - sliderStart;
+
+ double value = slider.getValue();
+ double circleRadius = 0.07;
+
+ // Map the normalized slider value (0-1) to our visual slider range
+ double xPos = sliderStart + (value * sliderRange);
+
+ // Add the left line only if there's space for it (circle not at left edge)
+ if (xPos - circleRadius > sliderStart) {
+ combinedShapes.push_back(std::make_unique(sliderStart, 0.0, xPos - circleRadius, 0.0)); // Left line
+ }
+
+ // Add the right line only if there's space for it (circle not at right edge)
+ if (xPos + circleRadius < sliderEnd) {
+ combinedShapes.push_back(std::make_unique(xPos + circleRadius, 0.0, sliderEnd, 0.0)); // Right line
+ }
+
+ // Add circle
+ combinedShapes.push_back(std::make_unique(xPos, 0.0, circleRadius, circleRadius, 0.0, 2.0 * M_PI));
+
+ juce::SpinLock::ScopedLockType lock(audioProcessor.sliderLock);
+ // Set the shapes in the renderer
+ audioProcessor.sliderRenderer.setShapes(std::move(combinedShapes));
+}
diff --git a/Source/components/SliderVisualiserComponent.h b/Source/components/SliderVisualiserComponent.h
new file mode 100644
index 00000000..208421d5
--- /dev/null
+++ b/Source/components/SliderVisualiserComponent.h
@@ -0,0 +1,52 @@
+#pragma once
+
+#include
+#include "../visualiser/VisualiserRenderer.h"
+#include "../EffectPluginProcessor.h"
+#include "../txt/TextParser.h"
+
+class SliderVisualiserComponent : public juce::Component
+{
+public:
+ SliderVisualiserComponent(EffectAudioProcessor& processor);
+ ~SliderVisualiserComponent() override;
+
+ void resized() override;
+ void setValue(double newValue);
+ double getValue() const;
+
+ // Forwards to underlying slider
+ void setRange(double newMinimum, double newMaximum, double newInterval = 0.0);
+ void addListener(juce::Slider::Listener* listener);
+ void removeListener(juce::Slider::Listener* listener);
+ void onValueChange(std::function callback);
+
+ // Set the label text to be displayed next to the slider
+ void setLabel(const juce::String& labelText);
+
+ // Update the visualiser display
+ void updateVisualiser();
+
+private:
+ EffectAudioProcessor& audioProcessor;
+
+ VisualiserRenderer sliderVisualiser{
+ audioProcessor.visualiserParameters,
+ audioProcessor.threadManager,
+ 512,
+ 60.0,
+ "Slider"
+ };
+
+ juce::Slider slider;
+ juce::String label;
+ std::vector> labelShapes;
+ juce::Font labelFont{0.7f};
+
+ // Proportion of the width allocated for the label
+ const double labelWidthProportion = 0.35;
+
+ void updateLabelShapes();
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SliderVisualiserComponent)
+};
diff --git a/Source/img/ImageParser.cpp b/Source/img/ImageParser.cpp
index b44e6957..1c4a120c 100644
--- a/Source/img/ImageParser.cpp
+++ b/Source/img/ImageParser.cpp
@@ -338,7 +338,12 @@ float ImageParser::getPixelValue(int x, int y, bool invert) {
if (usingLiveImage) {
if (liveImage.isValid()) {
if (x < 0 || x >= width || y < 0 || y >= height) return 0;
+#if JUCE_MAC
+ juce::Colour pixel = liveImage.getPixelAt(x, y);
+#else
juce::Colour pixel = liveImage.getPixelAt(x, height - y - 1);
+#endif
+
float value = pixel.getBrightness();
if (invert && value > 0) value = 1.0f - value;
return value;
diff --git a/Source/parser/FileParser.cpp b/Source/parser/FileParser.cpp
index 18547ee5..c146921b 100644
--- a/Source/parser/FileParser.cpp
+++ b/Source/parser/FileParser.cpp
@@ -15,9 +15,7 @@ void FileParser::showFileSizeWarning(juce::String fileName, int64_t totalBytes,
}
const double fileSizeMB = totalBytes / (1024.0 * 1024.0);
- juce::String message = juce::String::formatted(
- "The %s file '%s' you're trying to open is %.2f MB in size, and may time a long time to open. "
- "Would you like to continue loading it?", fileType.toRawUTF8(), fileName.toRawUTF8(), fileSizeMB);
+ juce::String message = "The " + fileType + " file '" + fileName + "' you're trying to open is " + juce::String(fileSizeMB, 2) + " MB in size, and may time a long time to open.\n\nWould you like to continue loading it?";
juce::MessageManager::callAsync([this, message, callback]() {
juce::AlertWindow::showOkCancelBox(
diff --git a/Source/visualiser/RecordingSettings.cpp b/Source/visualiser/RecordingSettings.cpp
index 2bcb6055..e11cd0dd 100644
--- a/Source/visualiser/RecordingSettings.cpp
+++ b/Source/visualiser/RecordingSettings.cpp
@@ -18,11 +18,8 @@ RecordingSettings::RecordingSettings(RecordingParameters& ps) : parameters(ps) {
addAndMakeVisible(customSharedTextureOutputLabel);
addAndMakeVisible(customSharedTextureOutputEditor);
- quality.setSliderOnValueChange();
quality.setRangeEnabled(false);
- resolution.setSliderOnValueChange();
resolution.setRangeEnabled(false);
- frameRate.setSliderOnValueChange();
frameRate.setRangeEnabled(false);
recordAudio.onClick = [this] {
diff --git a/Source/visualiser/VisualiserComponent.cpp b/Source/visualiser/VisualiserComponent.cpp
index 3e9dc242..91c98757 100644
--- a/Source/visualiser/VisualiserComponent.cpp
+++ b/Source/visualiser/VisualiserComponent.cpp
@@ -26,6 +26,8 @@ VisualiserComponent::VisualiserComponent(
visualiserOnly(visualiserOnly),
parent(parent),
editor(pluginEditor) {
+ setShouldBeRunning(true);
+
#if OSCI_PREMIUM
addAndMakeVisible(editor.ffmpegDownloader);
#endif
diff --git a/Source/visualiser/VisualiserParameters.h b/Source/visualiser/VisualiserParameters.h
index 99f2f0f4..8da10c57 100644
--- a/Source/visualiser/VisualiserParameters.h
+++ b/Source/visualiser/VisualiserParameters.h
@@ -86,6 +86,12 @@ public:
class VisualiserParameters {
public:
+ VisualiserParameters() {
+#if OSCI_PREMIUM
+ scaleEffect->markLockable(true);
+ booleans.push_back(scaleEffect->linked);
+#endif
+ }
double getIntensity() {
return intensityEffect->getActualValue() / 100;
diff --git a/Source/visualiser/VisualiserRenderer.cpp b/Source/visualiser/VisualiserRenderer.cpp
index 521f9f58..e42380f9 100644
--- a/Source/visualiser/VisualiserRenderer.cpp
+++ b/Source/visualiser/VisualiserRenderer.cpp
@@ -30,7 +30,6 @@ VisualiserRenderer::VisualiserRenderer(
{
openGLContext.setRenderer(this);
openGLContext.attachTo(*this);
- setShouldBeRunning(true);
}
VisualiserRenderer::~VisualiserRenderer() {
@@ -203,6 +202,10 @@ void VisualiserRenderer::newOpenGLContextCreated() {
juce::CriticalSection::ScopedLockType lock(samplesLock);
+#if JUCE_WINDOWS && JUCE_DEBUG
+ glDisable(GL_DEBUG_OUTPUT);
+#endif
+
glColorMask(true, true, true, true);
viewportChanged(viewportArea);
diff --git a/Source/visualiser/VisualiserSettings.cpp b/Source/visualiser/VisualiserSettings.cpp
index c734a394..24ca6398 100644
--- a/Source/visualiser/VisualiserSettings.cpp
+++ b/Source/visualiser/VisualiserSettings.cpp
@@ -15,7 +15,8 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
addAndMakeVisible(screenOverlayLabel);
addAndMakeVisible(screenOverlay);
#if OSCI_PREMIUM
- addAndMakeVisible(positionSize);
+ addAndMakeVisible(scale);
+ addAndMakeVisible(position);
addAndMakeVisible(screenColour);
addAndMakeVisible(flipVerticalToggle);
addAndMakeVisible(flipHorizontalToggle);
@@ -31,9 +32,6 @@ VisualiserSettings::VisualiserSettings(VisualiserParameters& p, int numChannels)
parameters.screenOverlay->setUnnormalisedValueNotifyingHost(screenOverlay.getSelectedId());
};
- sweepMs.setSliderOnValueChange();
- triggerValue.setSliderOnValueChange();
-
sweepMs.setEnabled(sweepToggle.getToggleState());
triggerValue.setEnabled(sweepToggle.getToggleState());
@@ -79,7 +77,9 @@ void VisualiserSettings::resized() {
#if OSCI_PREMIUM
area.removeFromTop(10);
- positionSize.setBounds(area.removeFromTop(positionSize.getHeight()));
+ scale.setBounds(area.removeFromTop(scale.getHeight()));
+ area.removeFromTop(10);
+ position.setBounds(area.removeFromTop(position.getHeight()));
area.removeFromTop(10);
flipVerticalToggle.setBounds(area.removeFromTop(rowHeight));
flipHorizontalToggle.setBounds(area.removeFromTop(rowHeight));
diff --git a/Source/visualiser/VisualiserSettings.h b/Source/visualiser/VisualiserSettings.h
index ca4fc871..dc0f6305 100644
--- a/Source/visualiser/VisualiserSettings.h
+++ b/Source/visualiser/VisualiserSettings.h
@@ -3,12 +3,13 @@
#define VERSION_HINT 2
#include
-#include "../components/EffectComponent.h"
-#include "../components/SvgButton.h"
+
#include "../LookAndFeel.h"
-#include "../components/SwitchButton.h"
#include "../audio/SmoothEffect.h"
#include "../audio/StereoEffect.h"
+#include "../components/EffectComponent.h"
+#include "../components/SvgButton.h"
+#include "../components/SwitchButton.h"
#include "VisualiserParameters.h"
class GroupedSettings : public juce::GroupComponent {
@@ -16,29 +17,28 @@ public:
GroupedSettings(std::vector> effects, juce::String label) : effects(effects), juce::GroupComponent(label, label) {
for (auto effect : effects) {
addAndMakeVisible(effect.get());
- effect->setSliderOnValueChange();
}
-
+
setColour(groupComponentBackgroundColourId, Colours::veryDark.withMultipliedBrightness(3.0));
}
-
+
void resized() override {
auto area = getLocalBounds();
area.removeFromTop(35);
double rowHeight = 30;
-
+
for (auto effect : effects) {
effect->setBounds(area.removeFromTop(rowHeight));
}
}
-
+
int getHeight() {
return 40 + effects.size() * 30;
}
private:
std::vector> effects;
-
+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GroupedSettings)
};
@@ -62,9 +62,8 @@ private:
std::make_shared(*parameters.lineSaturationEffect),
std::make_shared(*parameters.intensityEffect),
},
- "Line Colour"
- };
-
+ "Line Colour"};
+
#if OSCI_PREMIUM
GroupedSettings screenColour{
std::vector>{
@@ -72,10 +71,9 @@ private:
std::make_shared(*parameters.screenSaturationEffect),
std::make_shared(*parameters.ambientEffect),
},
- "Screen Colour"
- };
+ "Screen Colour"};
#endif
-
+
GroupedSettings lightEffects{
std::vector>{
std::make_shared(*parameters.persistenceEffect),
@@ -88,16 +86,14 @@ private:
std::make_shared(*parameters.ambientEffect),
#endif
},
- "Light Effects"
- };
-
+ "Light Effects"};
+
GroupedSettings videoEffects{
std::vector>{
std::make_shared(*parameters.noiseEffect),
},
- "Video Effects"
- };
-
+ "Video Effects"};
+
GroupedSettings lineEffects{
std::vector>{
std::make_shared(*parameters.smoothEffect),
@@ -105,28 +101,31 @@ private:
std::make_shared(*parameters.stereoEffect),
#endif
},
- "Line Effects"
- };
-
+ "Line Effects"};
+
EffectComponent sweepMs{*parameters.sweepMsEffect};
EffectComponent triggerValue{*parameters.triggerValueEffect};
-
+
juce::Label screenOverlayLabel{"Screen Overlay", "Screen Overlay"};
juce::ComboBox screenOverlay;
-
+
jux::SwitchButton upsamplingToggle{parameters.upsamplingEnabled};
jux::SwitchButton sweepToggle{parameters.sweepEnabled};
#if OSCI_PREMIUM
- GroupedSettings positionSize{
+ GroupedSettings scale{
std::vector>{
std::make_shared(*parameters.scaleEffect, 0),
std::make_shared(*parameters.scaleEffect, 1),
+ },
+ "Image Scale"};
+
+ GroupedSettings position{
+ std::vector>{
std::make_shared(*parameters.offsetEffect, 0),
std::make_shared(*parameters.offsetEffect, 1),
},
- "Line Position & Scale"
- };
+ "Image Position"};
jux::SwitchButton flipVerticalToggle{parameters.flipVertical};
jux::SwitchButton flipHorizontalToggle{parameters.flipHorizontal};
@@ -160,7 +159,7 @@ private:
class SettingsWindow : public juce::DialogWindow {
public:
- SettingsWindow(juce::String name, juce::Component& component, int windowWidth, int windowHeight, int componentWidth, int componentHeight) : juce::DialogWindow(name, Colours::darker, true, true), component(component) {
+ SettingsWindow(juce::String name, juce::Component& component, int windowWidth, int windowHeight, int componentWidth, int componentHeight) : juce::DialogWindow(name, Colours::darker, true, true), component(component), componentHeight(componentHeight) {
setContentComponent(&viewport);
centreWithSize(windowWidth, windowHeight);
setResizeLimits(windowWidth, windowHeight, componentWidth, componentHeight);
@@ -175,7 +174,14 @@ public:
setVisible(false);
}
+ void resized() override {
+ DialogWindow::resized();
+ // Update the component width to match the viewport width while maintaining its height
+ component.setSize(viewport.getWidth(), componentHeight);
+ }
+
private:
juce::Viewport viewport;
juce::Component& component;
+ int componentHeight;
};
diff --git a/modules/osci_render_core b/modules/osci_render_core
index 3e2d2daa..4ddac1f6 160000
--- a/modules/osci_render_core
+++ b/modules/osci_render_core
@@ -1 +1 @@
-Subproject commit 3e2d2daa7e07e4317bdd096bcdb3dbb721241efa
+Subproject commit 4ddac1f6ff0853c989bf3ab619e0f1e0264f5a45
diff --git a/osci-bitcrush.jucer b/osci-bitcrush.jucer
index 7dfc7e41..4cb31bc0 100644
--- a/osci-bitcrush.jucer
+++ b/osci-bitcrush.jucer
@@ -81,6 +81,10 @@
+
+
@@ -93,6 +97,10 @@
file="Source/components/MainMenuBarModel.cpp"/>
+
+
diff --git a/osci-render.jucer b/osci-render.jucer
index 5e1d4d25..7e42d3ba 100644
--- a/osci-render.jucer
+++ b/osci-render.jucer
@@ -4,7 +4,7 @@
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginWantsMidiIn"
pluginManufacturer="jameshball" aaxIdentifier="sh.ball.oscirender"
cppLanguageStandard="20" projectLineFeed="
" headerPath="./include"
- version="2.5.0.7" companyName="James H Ball" companyWebsite="https://osci-render.com"
+ version="2.5.1.1" companyName="James H Ball" companyWebsite="https://osci-render.com"
companyEmail="james@ball.sh" defines="NOMINMAX=1
INTERNET_FLAG_NO_AUTO_REDIRECT=0
OSCI_PREMIUM=1
JUCE_USE_CUSTOM_PLUGIN_STANDALONE_APP=1
JUCE_MODAL_LOOPS_PERMITTED=1"
pluginAUMainType="'aumf'">
@@ -53,6 +53,7 @@
file="Resources/svg/fixed_rotate.svg"/>
+
diff --git a/sosci.jucer b/sosci.jucer
index fe10bf2f..f46a3792 100644
--- a/sosci.jucer
+++ b/sosci.jucer
@@ -3,7 +3,7 @@
@@ -57,6 +57,7 @@
file="Resources/svg/fixed_rotate.svg"/>
+