2023-01-09 21:58:49 +00:00
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
This file contains the basic framework code for a JUCE plugin processor.
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <JuceHeader.h>
|
2023-01-15 17:01:27 +00:00
|
|
|
#include "shape/Shape.h"
|
|
|
|
#include "parser/FileParser.h"
|
|
|
|
#include "parser/FrameProducer.h"
|
|
|
|
#include "parser/FrameConsumer.h"
|
2023-03-25 20:24:10 +00:00
|
|
|
#include "audio/Effect.h"
|
2023-07-05 11:02:28 +00:00
|
|
|
#include <numbers>
|
2023-07-08 12:25:35 +00:00
|
|
|
#include "concurrency/BufferProducer.h"
|
2023-07-10 21:00:36 +00:00
|
|
|
#include "audio/AudioWebSocketServer.h"
|
2023-07-11 12:32:52 +00:00
|
|
|
#include "audio/DelayEffect.h"
|
2023-07-13 19:11:24 +00:00
|
|
|
#include "audio/PitchDetector.h"
|
|
|
|
#include "audio/WobbleEffect.h"
|
2023-07-21 16:42:29 +00:00
|
|
|
#include "audio/PerspectiveEffect.h"
|
2023-01-09 21:58:49 +00:00
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
/**
|
|
|
|
*/
|
|
|
|
class OscirenderAudioProcessor : public juce::AudioProcessor
|
|
|
|
#if JucePlugin_Enable_ARA
|
|
|
|
, public juce::AudioProcessorARAExtension
|
|
|
|
#endif
|
2023-01-15 17:01:27 +00:00
|
|
|
, public FrameConsumer
|
2023-01-09 21:58:49 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
OscirenderAudioProcessor();
|
|
|
|
~OscirenderAudioProcessor() override;
|
|
|
|
|
|
|
|
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
|
|
|
|
void releaseResources() override;
|
|
|
|
|
|
|
|
#ifndef JucePlugin_PreferredChannelConfigurations
|
|
|
|
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
|
|
|
|
|
|
|
juce::AudioProcessorEditor* createEditor() override;
|
|
|
|
bool hasEditor() const override;
|
|
|
|
|
|
|
|
const juce::String getName() const override;
|
|
|
|
|
|
|
|
bool acceptsMidi() const override;
|
|
|
|
bool producesMidi() const override;
|
|
|
|
bool isMidiEffect() const override;
|
|
|
|
double getTailLengthSeconds() const override;
|
|
|
|
int getNumPrograms() override;
|
|
|
|
int getCurrentProgram() override;
|
2023-07-28 12:55:44 +00:00
|
|
|
void setCurrentProgram(int index) override;
|
|
|
|
const juce::String getProgramName(int index) override;
|
|
|
|
void changeProgramName(int index, const juce::String& newName) override;
|
|
|
|
void getStateInformation(juce::MemoryBlock& destData) override;
|
|
|
|
void setStateInformation(const void* data, int sizeInBytes) override;
|
2023-01-09 21:58:49 +00:00
|
|
|
|
2023-07-10 16:42:22 +00:00
|
|
|
std::atomic<double> currentSampleRate = 0.0;
|
2023-01-15 17:01:27 +00:00
|
|
|
|
2023-07-05 14:17:17 +00:00
|
|
|
juce::SpinLock effectsLock;
|
2023-07-18 16:25:09 +00:00
|
|
|
std::vector<std::shared_ptr<Effect>> toggleableEffects;
|
2023-07-04 13:58:36 +00:00
|
|
|
std::vector<std::shared_ptr<Effect>> luaEffects;
|
2023-07-05 11:02:28 +00:00
|
|
|
|
2023-07-14 14:34:24 +00:00
|
|
|
std::shared_ptr<Effect> frequencyEffect = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
|
|
|
frequency = values[0];
|
|
|
|
return input;
|
2023-07-18 18:20:54 +00:00
|
|
|
}, new EffectParameter("Frequency", "frequency", 440.0, 0.0, 12000.0, 0.1)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
std::shared_ptr<Effect> volumeEffect = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
|
|
|
volume = values[0];
|
|
|
|
return input;
|
2023-07-18 18:20:54 +00:00
|
|
|
}, new EffectParameter("Volume", "volume", 1.0, 0.0, 3.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
std::shared_ptr<Effect> thresholdEffect = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
|
|
|
threshold = values[0];
|
|
|
|
return input;
|
2023-07-18 18:20:54 +00:00
|
|
|
}, new EffectParameter("Threshold", "threshold", 1.0, 0.0, 1.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
2023-03-30 20:09:53 +00:00
|
|
|
|
2023-07-14 14:34:24 +00:00
|
|
|
std::shared_ptr<Effect> focalLength = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
2023-07-05 11:02:28 +00:00
|
|
|
if (getCurrentFileIndex() != -1) {
|
|
|
|
auto camera = getCurrentFileParser()->getCamera();
|
|
|
|
if (camera == nullptr) return input;
|
2023-07-14 14:34:24 +00:00
|
|
|
camera->setFocalLength(values[0]);
|
2023-07-05 11:02:28 +00:00
|
|
|
}
|
|
|
|
return input;
|
2023-07-25 11:23:27 +00:00
|
|
|
}, new EffectParameter("Focal length", "objFocalLength", 1.0, 0.0, 2.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
2023-07-19 20:40:31 +00:00
|
|
|
|
2023-07-22 14:07:11 +00:00
|
|
|
BooleanParameter* fixedRotateX = new BooleanParameter("Object Fixed Rotate X", "objFixedRotateX", false);
|
|
|
|
BooleanParameter* fixedRotateY = new BooleanParameter("Object Fixed Rotate Y", "objFixedRotateY", false);
|
|
|
|
BooleanParameter* fixedRotateZ = new BooleanParameter("Object Fixed Rotate Z", "objFixedRotateZ", false);
|
2023-07-14 14:34:24 +00:00
|
|
|
std::shared_ptr<Effect> rotateX = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
2023-07-05 16:57:41 +00:00
|
|
|
if (getCurrentFileIndex() != -1) {
|
|
|
|
auto obj = getCurrentFileParser()->getObject();
|
|
|
|
if (obj == nullptr) return input;
|
2023-07-19 20:40:31 +00:00
|
|
|
auto rotation = values[0] * std::numbers::pi;
|
2023-07-22 14:07:11 +00:00
|
|
|
if (fixedRotateX->getBoolValue()) {
|
2023-07-19 20:40:31 +00:00
|
|
|
obj->setCurrentRotationX(rotation);
|
|
|
|
} else {
|
|
|
|
obj->setBaseRotationX(rotation);
|
|
|
|
}
|
2023-07-05 16:57:41 +00:00
|
|
|
}
|
|
|
|
return input;
|
2023-07-21 16:42:29 +00:00
|
|
|
}, new EffectParameter("Rotate X", "objRotateX", 1.0, -1.0, 1.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
|
|
|
std::shared_ptr<Effect> rotateY = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
2023-07-05 16:57:41 +00:00
|
|
|
if (getCurrentFileIndex() != -1) {
|
|
|
|
auto obj = getCurrentFileParser()->getObject();
|
|
|
|
if (obj == nullptr) return input;
|
2023-07-19 20:40:31 +00:00
|
|
|
auto rotation = values[0] * std::numbers::pi;
|
2023-07-22 14:07:11 +00:00
|
|
|
if (fixedRotateY->getBoolValue()) {
|
2023-07-19 20:40:31 +00:00
|
|
|
obj->setCurrentRotationY(rotation);
|
|
|
|
} else {
|
|
|
|
obj->setBaseRotationY(rotation);
|
|
|
|
}
|
2023-07-05 16:57:41 +00:00
|
|
|
}
|
|
|
|
return input;
|
2023-07-21 16:42:29 +00:00
|
|
|
}, new EffectParameter("Rotate Y", "objRotateY", 1.0, -1.0, 1.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
|
|
|
std::shared_ptr<Effect> rotateZ = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
2023-07-05 16:57:41 +00:00
|
|
|
if (getCurrentFileIndex() != -1) {
|
|
|
|
auto obj = getCurrentFileParser()->getObject();
|
|
|
|
if (obj == nullptr) return input;
|
2023-07-19 20:40:31 +00:00
|
|
|
auto rotation = values[0] * std::numbers::pi;
|
2023-07-22 14:07:11 +00:00
|
|
|
if (fixedRotateZ->getBoolValue()) {
|
2023-07-19 20:40:31 +00:00
|
|
|
obj->setCurrentRotationZ(rotation);
|
|
|
|
} else {
|
|
|
|
obj->setBaseRotationZ(rotation);
|
|
|
|
}
|
2023-07-05 16:57:41 +00:00
|
|
|
}
|
|
|
|
return input;
|
2023-07-21 16:42:29 +00:00
|
|
|
}, new EffectParameter("Rotate Z", "objRotateZ", 0.0, -1.0, 1.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
|
|
|
std::shared_ptr<Effect> rotateSpeed = std::make_shared<Effect>(
|
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
|
|
|
if (getCurrentFileIndex() != -1) {
|
|
|
|
auto obj = getCurrentFileParser()->getObject();
|
|
|
|
if (obj == nullptr) return input;
|
|
|
|
obj->setRotationSpeed(values[0]);
|
|
|
|
}
|
|
|
|
return input;
|
2023-07-21 16:42:29 +00:00
|
|
|
}, new EffectParameter("Rotate Speed", "objRotateSpeed", 0.0, -1.0, 1.0)
|
2023-07-14 14:34:24 +00:00
|
|
|
);
|
2023-07-11 12:32:52 +00:00
|
|
|
|
|
|
|
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();
|
2023-07-21 16:42:29 +00:00
|
|
|
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>();
|
2023-07-04 19:47:54 +00:00
|
|
|
|
|
|
|
juce::SpinLock parsersLock;
|
2023-03-30 16:28:47 +00:00
|
|
|
std::vector<std::shared_ptr<FileParser>> parsers;
|
|
|
|
std::vector<std::shared_ptr<juce::MemoryBlock>> fileBlocks;
|
2023-07-05 21:45:51 +00:00
|
|
|
std::vector<juce::String> fileNames;
|
2023-07-04 19:47:54 +00:00
|
|
|
std::atomic<int> currentFile = -1;
|
2023-07-25 13:09:21 +00:00
|
|
|
|
|
|
|
juce::ChangeBroadcaster broadcaster;
|
2023-03-30 16:28:47 +00:00
|
|
|
|
2023-07-11 21:28:54 +00:00
|
|
|
FrameProducer producer = FrameProducer(*this, std::make_shared<FileParser>());
|
2023-01-15 17:01:27 +00:00
|
|
|
|
2023-07-08 12:25:35 +00:00
|
|
|
BufferProducer audioProducer;
|
|
|
|
|
2023-07-13 19:11:24 +00:00
|
|
|
PitchDetector pitchDetector{audioProducer};
|
|
|
|
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(pitchDetector);
|
|
|
|
|
2023-07-25 19:44:18 +00:00
|
|
|
// shouldn't be accessed by audio thread, but needs to persist when GUI is closed
|
|
|
|
// so should only be accessed by message thread
|
|
|
|
juce::String currentProjectFile;
|
|
|
|
|
2023-08-27 18:33:42 +00:00
|
|
|
juce::SpinLock fontLock;
|
|
|
|
juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), 1.0f, juce::Font::plain);
|
|
|
|
|
2023-07-04 13:58:36 +00:00
|
|
|
void addLuaSlider();
|
2023-03-30 20:09:53 +00:00
|
|
|
void addFrame(std::vector<std::unique_ptr<Shape>> frame, int fileIndex) override;
|
2023-03-28 15:21:18 +00:00
|
|
|
void updateEffectPrecedence();
|
2023-03-30 16:28:47 +00:00
|
|
|
void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block);
|
|
|
|
void addFile(juce::File file);
|
2023-07-05 21:45:51 +00:00
|
|
|
void addFile(juce::String fileName, const char* data, const int size);
|
2023-07-25 13:09:21 +00:00
|
|
|
void addFile(juce::String fileName, std::shared_ptr<juce::MemoryBlock> data);
|
2023-03-30 16:28:47 +00:00
|
|
|
void removeFile(int index);
|
|
|
|
int numFiles();
|
2023-03-30 20:09:53 +00:00
|
|
|
void changeCurrentFile(int index);
|
2023-08-27 18:33:42 +00:00
|
|
|
void openFile(int index);
|
2023-03-30 20:09:53 +00:00
|
|
|
int getCurrentFileIndex();
|
2023-07-04 13:58:36 +00:00
|
|
|
std::shared_ptr<FileParser> getCurrentFileParser();
|
2023-07-05 21:45:51 +00:00
|
|
|
juce::String getCurrentFileName();
|
|
|
|
juce::String getFileName(int index);
|
2023-03-30 16:28:47 +00:00
|
|
|
std::shared_ptr<juce::MemoryBlock> getFileBlock(int index);
|
2023-01-09 21:58:49 +00:00
|
|
|
private:
|
2023-07-14 14:34:24 +00:00
|
|
|
std::atomic<float> frequency = 440.0f;
|
|
|
|
std::atomic<double> volume = 1.0;
|
|
|
|
std::atomic<double> threshold = 1.0;
|
2023-01-15 17:01:27 +00:00
|
|
|
|
|
|
|
juce::AbstractFifo frameFifo{ 10 };
|
|
|
|
std::vector<std::unique_ptr<Shape>> frameBuffer[10];
|
2023-03-30 20:09:53 +00:00
|
|
|
int frameBufferIndices[10];
|
2023-01-15 17:01:27 +00:00
|
|
|
|
|
|
|
int currentShape = 0;
|
2023-01-15 22:34:02 +00:00
|
|
|
std::vector<std::unique_ptr<Shape>> frame;
|
2023-03-30 20:09:53 +00:00
|
|
|
int currentBufferIndex = -1;
|
2023-01-15 17:01:27 +00:00
|
|
|
double frameLength;
|
|
|
|
double shapeDrawn = 0.0;
|
|
|
|
double frameDrawn = 0.0;
|
|
|
|
double lengthIncrement = 0.0;
|
2023-03-30 20:09:53 +00:00
|
|
|
bool invalidateFrameBuffer = false;
|
2023-01-15 17:01:27 +00:00
|
|
|
|
2023-07-25 13:09:21 +00:00
|
|
|
std::vector<BooleanParameter*> booleanParameters;
|
2023-07-25 11:23:27 +00:00
|
|
|
std::vector<std::shared_ptr<Effect>> allEffects;
|
2023-07-14 14:34:24 +00:00
|
|
|
std::vector<std::shared_ptr<Effect>> permanentEffects;
|
|
|
|
|
2023-07-06 16:57:10 +00:00
|
|
|
std::shared_ptr<Effect> traceMax = std::make_shared<Effect>(
|
2023-07-14 14:34:24 +00:00
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
|
|
|
traceMaxValue = values[0];
|
2023-07-06 16:57:10 +00:00
|
|
|
traceMaxEnabled = true;
|
|
|
|
return input;
|
2023-07-18 18:20:54 +00:00
|
|
|
}, new EffectParameter("Trace max", "traceMax", 1.0, 0.0, 1.0)
|
2023-07-06 16:57:10 +00:00
|
|
|
);
|
|
|
|
std::shared_ptr<Effect> traceMin = std::make_shared<Effect>(
|
2023-07-14 14:34:24 +00:00
|
|
|
[this](int index, Vector2 input, const std::vector<double>& values, double sampleRate) {
|
|
|
|
traceMinValue = values[0];
|
2023-07-06 16:57:10 +00:00
|
|
|
traceMinEnabled = true;
|
|
|
|
return input;
|
2023-07-18 18:20:54 +00:00
|
|
|
}, new EffectParameter("Trace min", "traceMin", 0.0, 0.0, 1.0)
|
2023-07-06 16:57:10 +00:00
|
|
|
);
|
|
|
|
const double MIN_TRACE = 0.005;
|
2023-07-14 14:34:24 +00:00
|
|
|
double traceMaxValue = traceMax->getValue();
|
|
|
|
double traceMinValue = traceMin->getValue();
|
|
|
|
double actualTraceMax = traceMaxValue;
|
|
|
|
double actualTraceMin = traceMinValue;
|
2023-07-06 16:57:10 +00:00
|
|
|
bool traceMaxEnabled = false;
|
|
|
|
bool traceMinEnabled = false;
|
|
|
|
|
2023-07-10 21:00:36 +00:00
|
|
|
AudioWebSocketServer softwareOscilloscopeServer{audioProducer};
|
|
|
|
|
2023-01-15 17:01:27 +00:00
|
|
|
void updateFrame();
|
|
|
|
void updateLengthIncrement();
|
2023-07-06 16:57:10 +00:00
|
|
|
void incrementShapeDrawing();
|
2023-07-05 11:02:28 +00:00
|
|
|
void updateLuaValues();
|
|
|
|
void updateObjValues();
|
2023-07-25 13:09:21 +00:00
|
|
|
std::shared_ptr<Effect> getEffect(juce::String id);
|
|
|
|
BooleanParameter* getBooleanParameter(juce::String id);
|
2023-07-28 12:55:44 +00:00
|
|
|
void openLegacyProject(const juce::XmlElement* xml);
|
|
|
|
std::pair<std::shared_ptr<Effect>, EffectParameter*> effectFromLegacyId(const juce::String& id, bool updatePrecedence = false);
|
|
|
|
LfoType lfoTypeFromLegacyAnimationType(const juce::String& type);
|
|
|
|
double valueFromLegacy(double value, const juce::String& id);
|
2023-01-15 17:01:27 +00:00
|
|
|
|
|
|
|
const double MIN_LENGTH_INCREMENT = 0.000001;
|
|
|
|
|
2023-01-09 21:58:49 +00:00
|
|
|
//==============================================================================
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessor)
|
|
|
|
};
|