/* ============================================================================== This file contains the basic framework code for a JUCE plugin processor. ============================================================================== */ #pragma once #include #include "shape/Shape.h" #include "parser/FileParser.h" #include "parser/FrameProducer.h" #include "parser/FrameConsumer.h" #include "audio/Effect.h" #include #include "concurrency/BufferProducer.h" #include "audio/AudioWebSocketServer.h" #include "audio/DelayEffect.h" #include "audio/PitchDetector.h" #include "audio/WobbleEffect.h" #include "audio/PerspectiveEffect.h" //============================================================================== /** */ class OscirenderAudioProcessor : public juce::AudioProcessor #if JucePlugin_Enable_ARA , public juce::AudioProcessorARAExtension #endif , public FrameConsumer { 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&, 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; 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; std::atomic currentSampleRate = 0.0; juce::SpinLock effectsLock; std::vector> toggleableEffects; std::vector> luaEffects; std::shared_ptr frequencyEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { frequency = values[0]; return input; }, new EffectParameter("Frequency", "frequency", 440.0, 0.0, 12000.0, 0.1) ); std::shared_ptr volumeEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { volume = values[0]; return input; }, new EffectParameter("Volume", "volume", 1.0, 0.0, 3.0) ); std::shared_ptr thresholdEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { threshold = values[0]; return input; }, new EffectParameter("Threshold", "threshold", 1.0, 0.0, 1.0) ); std::shared_ptr focalLength = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { if (getCurrentFileIndex() != -1) { auto camera = getCurrentFileParser()->getCamera(); if (camera == nullptr) return input; camera->setFocalLength(values[0]); } return input; }, new EffectParameter("Focal length", "objFocalLength", 1.0, 0.0, 2.0) ); BooleanParameter* fixedRotateX = new BooleanParameter("Object Fixed Rotate X", "objFixedRotateX", false); BooleanParameter* fixedRotateY = new BooleanParameter("Object Fixed Rotate Y", "objFixedRotateY", false); BooleanParameter* fixedRotateZ = new BooleanParameter("Object Fixed Rotate Z", "objFixedRotateZ", false); std::shared_ptr rotateX = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { if (getCurrentFileIndex() != -1) { auto obj = getCurrentFileParser()->getObject(); if (obj == nullptr) return input; auto rotation = values[0] * std::numbers::pi; if (fixedRotateX->getBoolValue()) { obj->setCurrentRotationX(rotation); } else { obj->setBaseRotationX(rotation); } } return input; }, new EffectParameter("Rotate X", "objRotateX", 1.0, -1.0, 1.0) ); std::shared_ptr rotateY = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { if (getCurrentFileIndex() != -1) { auto obj = getCurrentFileParser()->getObject(); if (obj == nullptr) return input; auto rotation = values[0] * std::numbers::pi; if (fixedRotateY->getBoolValue()) { obj->setCurrentRotationY(rotation); } else { obj->setBaseRotationY(rotation); } } return input; }, new EffectParameter("Rotate Y", "objRotateY", 1.0, -1.0, 1.0) ); std::shared_ptr rotateZ = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { if (getCurrentFileIndex() != -1) { auto obj = getCurrentFileParser()->getObject(); if (obj == nullptr) return input; auto rotation = values[0] * std::numbers::pi; if (fixedRotateZ->getBoolValue()) { obj->setCurrentRotationZ(rotation); } else { obj->setBaseRotationZ(rotation); } } return input; }, new EffectParameter("Rotate Z", "objRotateZ", 0.0, -1.0, 1.0) ); std::shared_ptr rotateSpeed = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { if (getCurrentFileIndex() != -1) { auto obj = getCurrentFileParser()->getObject(); if (obj == nullptr) return input; obj->setRotationSpeed(values[0]); } return input; }, new EffectParameter("Rotate Speed", "objRotateSpeed", 0.0, -1.0, 1.0) ); std::shared_ptr delayEffect = std::make_shared(); std::shared_ptr perspectiveEffect = std::make_shared(); juce::SpinLock parsersLock; std::vector> parsers; std::vector> fileBlocks; std::vector fileNames; std::atomic currentFile = -1; juce::ChangeBroadcaster broadcaster; FrameProducer producer = FrameProducer(*this, std::make_shared()); BufferProducer audioProducer; PitchDetector pitchDetector{audioProducer}; std::shared_ptr wobbleEffect = std::make_shared(pitchDetector); // 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; void addLuaSlider(); void addFrame(std::vector> frame, int fileIndex) override; void updateEffectPrecedence(); void updateFileBlock(int index, std::shared_ptr block); void addFile(juce::File file); void addFile(juce::String fileName, const char* data, const int size); void addFile(juce::String fileName, std::shared_ptr data); void removeFile(int index); int numFiles(); void changeCurrentFile(int index); int getCurrentFileIndex(); std::shared_ptr getCurrentFileParser(); juce::String getCurrentFileName(); juce::String getFileName(int index); std::shared_ptr getFileBlock(int index); private: std::atomic frequency = 440.0f; std::atomic volume = 1.0; std::atomic threshold = 1.0; juce::AbstractFifo frameFifo{ 10 }; std::vector> frameBuffer[10]; int frameBufferIndices[10]; int currentShape = 0; std::vector> frame; int currentBufferIndex = -1; double frameLength; double shapeDrawn = 0.0; double frameDrawn = 0.0; double lengthIncrement = 0.0; bool invalidateFrameBuffer = false; std::vector booleanParameters; std::vector> allEffects; std::vector> permanentEffects; std::shared_ptr traceMax = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { traceMaxValue = values[0]; traceMaxEnabled = true; return input; }, new EffectParameter("Trace max", "traceMax", 1.0, 0.0, 1.0) ); std::shared_ptr traceMin = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { traceMinValue = values[0]; traceMinEnabled = true; return input; }, new EffectParameter("Trace min", "traceMin", 0.0, 0.0, 1.0) ); const double MIN_TRACE = 0.005; double traceMaxValue = traceMax->getValue(); double traceMinValue = traceMin->getValue(); double actualTraceMax = traceMaxValue; double actualTraceMin = traceMinValue; bool traceMaxEnabled = false; bool traceMinEnabled = false; AudioWebSocketServer softwareOscilloscopeServer{audioProducer}; void updateFrame(); void updateLengthIncrement(); void incrementShapeDrawing(); void openFile(int index); void updateLuaValues(); void updateObjValues(); std::shared_ptr getEffect(juce::String id); BooleanParameter* getBooleanParameter(juce::String id); void openLegacyProject(const juce::XmlElement* xml); std::pair, EffectParameter*> effectFromLegacyId(const juce::String& id, bool updatePrecedence = false); LfoType lfoTypeFromLegacyAnimationType(const juce::String& type); double valueFromLegacy(double value, const juce::String& id); const double MIN_LENGTH_INCREMENT = 0.000001; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessor) };