kopia lustrzana https://github.com/jameshball/osci-render
Merge branch 'jameshball:main' into lua-voice-external-input
commit
f40e19647b
|
@ -2,7 +2,6 @@ name: Build
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.59,13.41C11,13.8 11,14.44 10.59,14.83C10.2,15.22 9.56,15.22 9.17,14.83C7.22,12.88 7.22,9.71 9.17,7.76V7.76L12.71,4.22C14.66,2.27 17.83,2.27 19.78,4.22C21.73,6.17 21.73,9.34 19.78,11.29L18.29,12.78C18.3,11.96 18.17,11.14 17.89,10.36L18.36,9.88C19.54,8.71 19.54,6.81 18.36,5.64C17.19,4.46 15.29,4.46 14.12,5.64L10.59,9.17C9.41,10.34 9.41,12.24 10.59,13.41M13.41,9.17C13.8,8.78 14.44,8.78 14.83,9.17C16.78,11.12 16.78,14.29 14.83,16.24V16.24L11.29,19.78C9.34,21.73 6.17,21.73 4.22,19.78C2.27,17.83 2.27,14.66 4.22,12.71L5.71,11.22C5.7,12.04 5.83,12.86 6.11,13.65L5.64,14.12C4.46,15.29 4.46,17.19 5.64,18.36C6.81,19.54 8.71,19.54 9.88,18.36L13.41,14.83C14.59,13.66 14.59,11.76 13.41,10.59C13,10.2 13,9.56 13.41,9.17Z" /></svg>
|
Po Szerokość: | Wysokość: | Rozmiar: 795 B |
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -17,19 +17,30 @@ EffectPluginEditor::EffectPluginEditor(EffectAudioProcessor& p)
|
|||
|
||||
addAndMakeVisible(visualiser);
|
||||
addAndMakeVisible(titleVisualiser);
|
||||
addAndMakeVisible(bitCrush);
|
||||
addAndMakeVisible(sliderVisualiser);
|
||||
|
||||
titleVisualiser.setCropRectangle(juce::Rectangle<float>(-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) {}
|
||||
|
|
|
@ -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<juce::TooltipWindow> 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
|
||||
|
|
|
@ -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<float>& 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<juce::XmlElement> xml = std::make_unique<juce::XmlElement>("project");
|
||||
|
|
|
@ -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<std::unique_ptr<osci::Shape>> 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)
|
||||
|
|
|
@ -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]()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -7,9 +7,6 @@ PerspectiveComponent::PerspectiveComponent(OscirenderAudioProcessor& p, Oscirend
|
|||
|
||||
addAndMakeVisible(perspective);
|
||||
addAndMakeVisible(focalLength);
|
||||
|
||||
perspective.setSliderOnValueChange();
|
||||
focalLength.setSliderOnValueChange();
|
||||
}
|
||||
|
||||
PerspectiveComponent::~PerspectiveComponent() {}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -34,16 +34,19 @@ OscirenderAudioProcessor::OscirenderAudioProcessor() : CommonAudioProcessor(Buse
|
|||
toggleableEffects.push_back(std::make_shared<osci::Effect>(
|
||||
std::make_shared<VectorCancellingEffect>(),
|
||||
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<osci::Effect>(
|
||||
auto scaleEffect = std::make_shared<osci::Effect>(
|
||||
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
return input * osci::Point(values[0], values[1], values[2]);
|
||||
},
|
||||
std::vector<osci::EffectParameter*>{
|
||||
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<osci::Effect>(
|
||||
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<osci::Effect>(
|
||||
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& 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<osci::Effect>(
|
||||
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
|
||||
double phase = values[1] * std::numbers::pi;
|
||||
|
|
|
@ -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<juce::AudioFormatWriter::ThreadedWriter*> activeWriter { nullptr };
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#include "ShapeVectorRenderer.h"
|
||||
|
||||
ShapeVectorRenderer::ShapeVectorRenderer(double sampleRate, double frequency)
|
||||
: currentSampleRate(sampleRate), frequency(frequency) {
|
||||
}
|
||||
|
||||
void ShapeVectorRenderer::setShapes(std::vector<std::unique_ptr<osci::Shape>> 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#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<std::unique_ptr<osci::Shape>> 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<std::unique_ptr<osci::Shape>> shapes;
|
||||
double shapesLength = 0.0;
|
||||
int currentShape = 0;
|
||||
double shapeDrawn = 0.0;
|
||||
double frameDrawn = 0.0;
|
||||
|
||||
void incrementShapeDrawing();
|
||||
};
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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<juce::Component> component) {
|
||||
this->component = component;
|
||||
this->component = component;
|
||||
addAndMakeVisible(component.get());
|
||||
}
|
||||
|
||||
void EffectComponent::setSliderOnValueChange() {
|
||||
slider.onValueChange = [this] {
|
||||
effect.setValue(index, slider.getValue());
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include "LabelledTextBox.h"
|
||||
#include "SvgButton.h"
|
||||
|
||||
|
@ -18,14 +19,13 @@ public:
|
|||
void setRangeEnabled(bool enabled);
|
||||
|
||||
void setComponent(std::shared_ptr<juce::Component> 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<void()> updateToggleState;
|
||||
|
||||
private:
|
||||
|
@ -145,9 +145,17 @@ private:
|
|||
|
||||
std::unique_ptr<SvgButton> 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)
|
||||
};
|
||||
|
|
|
@ -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<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());
|
||||
}
|
||||
};
|
||||
|
||||
list.setEnabled(selected.getToggleState());
|
||||
selected.onClick = [this, weakEffectComponent] {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
LuaListComponent::LuaListComponent(OscirenderAudioProcessor& p, osci::Effect& effect) {
|
||||
effectComponent = std::make_shared<EffectComponent>(effect);
|
||||
|
||||
effectComponent->slider.onValueChange = [this, &effect, &p] {
|
||||
effect.setValue(effectComponent->slider.getValue());
|
||||
};
|
||||
|
||||
addAndMakeVisible(*effectComponent);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
#include "SliderVisualiserComponent.h"
|
||||
|
||||
SliderVisualiserComponent::SliderVisualiserComponent(EffectAudioProcessor& processor)
|
||||
: audioProcessor(processor)
|
||||
{
|
||||
sliderVisualiser.setCropRectangle(juce::Rectangle<float>(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<void()> 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<std::unique_ptr<osci::Shape>> 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<osci::Line>(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<osci::Line>(xPos + circleRadius, 0.0, sliderEnd, 0.0)); // Right line
|
||||
}
|
||||
|
||||
// Add circle
|
||||
combinedShapes.push_back(std::make_unique<osci::CircleArc>(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));
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#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<void()> 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<std::unique_ptr<osci::Shape>> 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)
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -26,6 +26,8 @@ VisualiserComponent::VisualiserComponent(
|
|||
visualiserOnly(visualiserOnly),
|
||||
parent(parent),
|
||||
editor(pluginEditor) {
|
||||
setShouldBeRunning(true);
|
||||
|
||||
#if OSCI_PREMIUM
|
||||
addAndMakeVisible(editor.ffmpegDownloader);
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
#define VERSION_HINT 2
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#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<std::shared_ptr<EffectComponent>> 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<std::shared_ptr<EffectComponent>> effects;
|
||||
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(GroupedSettings)
|
||||
};
|
||||
|
||||
|
@ -62,9 +62,8 @@ private:
|
|||
std::make_shared<EffectComponent>(*parameters.lineSaturationEffect),
|
||||
std::make_shared<EffectComponent>(*parameters.intensityEffect),
|
||||
},
|
||||
"Line Colour"
|
||||
};
|
||||
|
||||
"Line Colour"};
|
||||
|
||||
#if OSCI_PREMIUM
|
||||
GroupedSettings screenColour{
|
||||
std::vector<std::shared_ptr<EffectComponent>>{
|
||||
|
@ -72,10 +71,9 @@ private:
|
|||
std::make_shared<EffectComponent>(*parameters.screenSaturationEffect),
|
||||
std::make_shared<EffectComponent>(*parameters.ambientEffect),
|
||||
},
|
||||
"Screen Colour"
|
||||
};
|
||||
"Screen Colour"};
|
||||
#endif
|
||||
|
||||
|
||||
GroupedSettings lightEffects{
|
||||
std::vector<std::shared_ptr<EffectComponent>>{
|
||||
std::make_shared<EffectComponent>(*parameters.persistenceEffect),
|
||||
|
@ -88,16 +86,14 @@ private:
|
|||
std::make_shared<EffectComponent>(*parameters.ambientEffect),
|
||||
#endif
|
||||
},
|
||||
"Light Effects"
|
||||
};
|
||||
|
||||
"Light Effects"};
|
||||
|
||||
GroupedSettings videoEffects{
|
||||
std::vector<std::shared_ptr<EffectComponent>>{
|
||||
std::make_shared<EffectComponent>(*parameters.noiseEffect),
|
||||
},
|
||||
"Video Effects"
|
||||
};
|
||||
|
||||
"Video Effects"};
|
||||
|
||||
GroupedSettings lineEffects{
|
||||
std::vector<std::shared_ptr<EffectComponent>>{
|
||||
std::make_shared<EffectComponent>(*parameters.smoothEffect),
|
||||
|
@ -105,28 +101,31 @@ private:
|
|||
std::make_shared<EffectComponent>(*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::shared_ptr<EffectComponent>>{
|
||||
std::make_shared<EffectComponent>(*parameters.scaleEffect, 0),
|
||||
std::make_shared<EffectComponent>(*parameters.scaleEffect, 1),
|
||||
},
|
||||
"Image Scale"};
|
||||
|
||||
GroupedSettings position{
|
||||
std::vector<std::shared_ptr<EffectComponent>>{
|
||||
std::make_shared<EffectComponent>(*parameters.offsetEffect, 0),
|
||||
std::make_shared<EffectComponent>(*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;
|
||||
};
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 3e2d2daa7e07e4317bdd096bcdb3dbb721241efa
|
||||
Subproject commit 4ddac1f6ff0853c989bf3ab619e0f1e0264f5a45
|
|
@ -81,6 +81,10 @@
|
|||
<GROUP id="{85A33213-D880-BD92-70D8-1901DA6D23F0}" name="audio">
|
||||
<FILE id="fwEcan" name="AutoGainControlEffect.h" compile="0" resource="0"
|
||||
file="Source/audio/AutoGainControlEffect.h"/>
|
||||
<FILE id="m3ONmD" name="ShapeVectorRenderer.cpp" compile="1" resource="0"
|
||||
file="Source/audio/ShapeVectorRenderer.cpp"/>
|
||||
<FILE id="EEZHBS" name="ShapeVectorRenderer.h" compile="0" resource="0"
|
||||
file="Source/audio/ShapeVectorRenderer.h"/>
|
||||
<FILE id="GSnwBW" name="SmoothEffect.h" compile="0" resource="0" file="Source/audio/SmoothEffect.h"/>
|
||||
<FILE id="jq3EXV" name="StereoEffect.h" compile="0" resource="0" file="Source/audio/StereoEffect.h"/>
|
||||
</GROUP>
|
||||
|
@ -93,6 +97,10 @@
|
|||
file="Source/components/MainMenuBarModel.cpp"/>
|
||||
<FILE id="t6oUhv" name="MainMenuBarModel.h" compile="0" resource="0"
|
||||
file="Source/components/MainMenuBarModel.h"/>
|
||||
<FILE id="i4SGze" name="SliderVisualiserComponent.cpp" compile="1"
|
||||
resource="0" file="Source/components/SliderVisualiserComponent.cpp"/>
|
||||
<FILE id="vRJQLf" name="SliderVisualiserComponent.h" compile="0" resource="0"
|
||||
file="Source/components/SliderVisualiserComponent.h"/>
|
||||
<FILE id="f2kkHV" name="SvgButton.h" compile="0" resource="0" file="Source/components/SvgButton.h"/>
|
||||
<FILE id="qzfstC" name="SwitchButton.h" compile="0" resource="0" file="Source/components/SwitchButton.h"/>
|
||||
</GROUP>
|
||||
|
|
|
@ -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'">
|
||||
<MAINGROUP id="j5Ge2T" name="osci-render">
|
||||
|
@ -53,6 +53,7 @@
|
|||
file="Resources/svg/fixed_rotate.svg"/>
|
||||
<FILE id="WIkl6l" name="fullscreen.svg" compile="0" resource="1" file="Resources/svg/fullscreen.svg"/>
|
||||
<FILE id="n1esUp" name="left_arrow.svg" compile="0" resource="1" file="Resources/svg/left_arrow.svg"/>
|
||||
<FILE id="WdfE7J" name="link.svg" compile="0" resource="1" file="Resources/svg/link.svg"/>
|
||||
<FILE id="PxYKbt" name="microphone.svg" compile="0" resource="1" file="Resources/svg/microphone.svg"/>
|
||||
<FILE id="eGMxwy" name="mute.svg" compile="0" resource="1" file="Resources/svg/mute.svg"/>
|
||||
<FILE id="hJHxFY" name="open_in_new.svg" compile="0" resource="1" file="Resources/svg/open_in_new.svg"/>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<JUCERPROJECT id="HH2E72" name="sosci" projectType="audioplug" useAppConfig="0"
|
||||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginManufacturer="jameshball"
|
||||
aaxIdentifier="sh.ball.sosci" cppLanguageStandard="20" projectLineFeed=" "
|
||||
headerPath="./include" version="1.1.6.2" companyName="James H Ball"
|
||||
headerPath="./include" version="1.1.7.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"
|
||||
pluginManufacturerCode="Jhba" pluginCode="Sosc" pluginAUMainType="'aufx'">
|
||||
|
@ -57,6 +57,7 @@
|
|||
file="Resources/svg/fixed_rotate.svg"/>
|
||||
<FILE id="WIkl6l" name="fullscreen.svg" compile="0" resource="1" file="Resources/svg/fullscreen.svg"/>
|
||||
<FILE id="n1esUp" name="left_arrow.svg" compile="0" resource="1" file="Resources/svg/left_arrow.svg"/>
|
||||
<FILE id="Q2mSgZ" name="link.svg" compile="0" resource="1" file="Resources/svg/link.svg"/>
|
||||
<FILE id="PxYKbt" name="microphone.svg" compile="0" resource="1" file="Resources/svg/microphone.svg"/>
|
||||
<FILE id="WoY9r2" name="mute.svg" compile="0" resource="1" file="Resources/svg/mute.svg"/>
|
||||
<FILE id="hJHxFY" name="open_in_new.svg" compile="0" resource="1" file="Resources/svg/open_in_new.svg"/>
|
||||
|
|
Ładowanie…
Reference in New Issue