/* ============================================================================== 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" //============================================================================== /** */ 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> allEffects; std::vector> enabledEffects; 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; }, std::vector(1, { "Frequency", "frequency", 440.0, 0.0, 12000.0, 0.1, true }) ); std::shared_ptr volumeEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { volume = values[0]; return input; }, std::vector(1, { "Volume", "volume", 1.0, 0.0, 3.0, 0.001, true }) ); std::shared_ptr thresholdEffect = std::make_shared( [this](int index, Vector2 input, const std::vector& values, double sampleRate) { threshold = values[0]; return input; }, std::vector(1, { "Threshold", "threshold", 3.0, 0.0, 3.0, 0.001, true }) ); 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; }, std::vector(1, { "Focal length", "focalLength", 1.0, 0.0, 2.0, 0.001, true }) ); 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; obj->setBaseRotationX(values[0] * std::numbers::pi); } return input; }, std::vector(1, { "Rotate x", "rotateX", 1.0, -1.0, 1.0, 0.001, true }) ); 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; obj->setBaseRotationY(values[0] * std::numbers::pi); } return input; }, std::vector(1, { "Rotate y", "rotateY", 1.0, -1.0, 1.0, 0.001, true }) ); 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; obj->setBaseRotationZ(values[0] * std::numbers::pi); } return input; }, std::vector(1, { "Rotate z", "rotateZ", 0.0, -1.0, 1.0, 0.001, true }) ); std::shared_ptr currentRotateX = 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->setCurrentRotationX(values[0] * std::numbers::pi); } return input; }, std::vector(1, { "Current Rotate x", "currentRotateX", 0.0, 0.0, 1.0, 0.001, false }) ); std::shared_ptr currentRotateY = 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->setCurrentRotationY(values[0] * std::numbers::pi); } return input; }, std::vector(1, { "Current Rotate y", "currentRotateY", 0.0, 0.0, 1.0, 0.001, false }) ); std::shared_ptr currentRotateZ = 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->setCurrentRotationZ(values[0] * std::numbers::pi); } return input; }, std::vector(1, { "Current Rotate z", "currentRotateZ", 0.0, 0.0, 1.0, 0.001, false }) ); 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; }, std::vector(1, { "Rotate speed", "rotateSpeed", 0.0, -1.0, 1.0, 0.001, true }) ); std::atomic fixedRotateX = false; std::atomic fixedRotateY = false; std::atomic fixedRotateZ = false; std::shared_ptr delayEffect = std::make_shared(); juce::SpinLock parsersLock; std::vector> parsers; std::vector> fileBlocks; std::vector fileNames; std::atomic currentFile = -1; FrameProducer producer = FrameProducer(*this, std::make_shared()); BufferProducer audioProducer; PitchDetector pitchDetector{audioProducer}; std::shared_ptr wobbleEffect = std::make_shared(pitchDetector); void addLuaSlider(); void addFrame(std::vector> frame, int fileIndex) override; void enableEffect(std::shared_ptr effect); void disableEffect(std::shared_ptr effect); 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 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> 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; }, std::vector(1, { "Trace max", "traceMax", 1.0, 0.0, 1.0, 0.001, true }) ); 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; }, std::vector(1, { "Trace min", "traceMin", 0.0, 0.0, 1.0, 0.001, true }) ); 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(); const double MIN_LENGTH_INCREMENT = 0.000001; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessor) };