osci-render/Source/PluginProcessor.h

267 wiersze
13 KiB
C
Czysty Zwykły widok Historia

2023-01-09 21:58:49 +00:00
/*
==============================================================================
This file contains the basic framework code for a JUCE plugin processor.
==============================================================================
*/
#pragma once
#define VERSION_HINT 2
2023-01-09 21:58:49 +00:00
#include <JuceHeader.h>
2023-08-28 21:06:21 +00:00
#include "audio/ShapeSound.h"
#include "audio/ShapeVoice.h"
2023-12-14 21:26:40 +00:00
#include "audio/PublicSynthesiser.h"
#include "audio/SampleRateManager.h"
#include <numbers>
#include "audio/DelayEffect.h"
#include "audio/WobbleEffect.h"
2023-07-21 16:42:29 +00:00
#include "audio/PerspectiveEffect.h"
2023-09-10 16:43:37 +00:00
#include "obj/ObjectServer.h"
#include "UGen/Env.h"
#include "UGen/ugen_JuceEnvelopeComponent.h"
#include "audio/CustomEffect.h"
2024-02-20 14:14:24 +00:00
#include "audio/DashedLineEffect.h"
#include "CommonPluginProcessor.h"
2023-01-09 21:58:49 +00:00
//==============================================================================
/**
*/
class OscirenderAudioProcessor : public CommonAudioProcessor, juce::AudioProcessorParameter::Listener, public EnvelopeComponentListener
2023-01-09 21:58:49 +00:00
#if JucePlugin_Enable_ARA
, public juce::AudioProcessorARAExtension
#endif
{
public:
OscirenderAudioProcessor();
~OscirenderAudioProcessor() override;
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
juce::AudioProcessorEditor* createEditor() override;
void setAudioThreadCallback(std::function<void(const juce::AudioBuffer<float>&)> callback);
void getStateInformation(juce::MemoryBlock& destData) override;
void setStateInformation(const void* data, int sizeInBytes) override;
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void envelopeChanged(EnvelopeComponent* changedEnvelope) override;
std::vector<std::shared_ptr<osci::Effect>> toggleableEffects;
std::vector<std::shared_ptr<osci::Effect>> luaEffects;
std::atomic<double> luaValues[26] = { 0.0 };
std::shared_ptr<osci::Effect> frequencyEffect = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
frequency = values[0].load();
return input;
}, new osci::EffectParameter(
2023-12-17 21:32:56 +00:00
"Frequency",
"Controls how many times per second the image is drawn, thereby controlling the pitch of the sound. Lower frequencies result in more-accurately drawn images, but more flickering, and vice versa.",
"frequency",
2025-01-17 18:26:24 +00:00
VERSION_HINT, 220.0, 0.0, 4200.0
2023-12-17 21:32:56 +00:00
)
);
std::shared_ptr<osci::Effect> trace = std::make_shared<osci::Effect>(
std::vector<osci::EffectParameter*>{
new osci::EffectParameter(
2025-01-19 18:35:46 +00:00
"Trace Start",
"Defines how far into the frame the drawing is started at. This has the effect of 'tracing' out the image from a single dot when animated. By default, we start drawing from the beginning of the frame, so this value is 0.0.",
"traceStart",
VERSION_HINT, 0.0, 0.0, 1.0, 0.001
2025-01-19 18:35:46 +00:00
),
new osci::EffectParameter(
2025-01-19 18:35:46 +00:00
"Trace Length",
"Defines how much of the frame is drawn per cycle. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw the whole frame, corresponding to a value of 1.0.",
"traceLength",
VERSION_HINT, 1.0, 0.0, 1.0, 0.001
2025-01-19 18:35:46 +00:00
),
}
2023-08-28 21:06:21 +00:00
);
std::shared_ptr<DelayEffect> delayEffect = std::make_shared<DelayEffect>();
2024-02-20 14:14:24 +00:00
std::shared_ptr<DashedLineEffect> dashedLineEffect = std::make_shared<DashedLineEffect>();
std::function<void(int, juce::String, juce::String)> errorCallback = [this](int lineNum, juce::String fileName, juce::String error) { notifyErrorListeners(lineNum, fileName, error); };
std::shared_ptr<CustomEffect> customEffect = std::make_shared<CustomEffect>(errorCallback, luaValues);
std::shared_ptr<osci::Effect> custom = std::make_shared<osci::Effect>(
customEffect,
new osci::EffectParameter("Lua Effect", "Controls the strength of the custom Lua effect applied. You can write your own custom effect using Lua by pressing the edit button on the right.", "customEffectStrength", VERSION_HINT, 1.0, 0.0, 1.0)
);
std::shared_ptr<PerspectiveEffect> perspectiveEffect = std::make_shared<PerspectiveEffect>();
std::shared_ptr<osci::Effect> perspective = std::make_shared<osci::Effect>(
perspectiveEffect,
std::vector<osci::EffectParameter*>{
new osci::EffectParameter("Perspective", "Controls the strength of the 3D perspective projection.", "perspectiveStrength", VERSION_HINT, 1.0, 0.0, 1.0),
new osci::EffectParameter("Focal Length", "Controls the focal length of the 3D perspective effect. A higher focal length makes the image look more flat, and a lower focal length makes the image look more 3D.", "perspectiveFocalLength", VERSION_HINT, 2.0, 0.0, 10.0),
}
);
2024-08-10 19:26:56 +00:00
osci::BooleanParameter* midiEnabled = new osci::BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false, "Enable MIDI input for the synth. If disabled, the synth will play a constant tone, as controlled by the frequency slider.");
osci::BooleanParameter* inputEnabled = new osci::BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false, "Enable to use input audio, instead of the generated audio.");
std::atomic<double> frequency = 220.0;
2023-09-07 21:04:08 +00:00
juce::SpinLock parsersLock;
std::vector<std::shared_ptr<FileParser>> parsers;
2023-08-28 21:06:21 +00:00
std::vector<ShapeSound::Ptr> sounds;
std::vector<std::shared_ptr<juce::MemoryBlock>> fileBlocks;
std::vector<juce::String> fileNames;
int currentFileId = 0;
std::vector<int> fileIds;
std::atomic<int> currentFile = -1;
juce::ChangeBroadcaster broadcaster;
2023-09-10 16:43:37 +00:00
std::atomic<bool> objectServerRendering = false;
juce::ChangeBroadcaster fileChangeBroadcaster;
osci::FloatParameter* attackTime = new osci::FloatParameter("Attack Time", "attackTime", VERSION_HINT, 0.005, 0.0, 1.0);
osci::FloatParameter* attackLevel = new osci::FloatParameter("Attack Level", "attackLevel", VERSION_HINT, 1.0, 0.0, 1.0);
osci::FloatParameter* decayTime = new osci::FloatParameter("Decay Time", "decayTime", VERSION_HINT, 0.095, 0.0, 1.0);
osci::FloatParameter* sustainLevel = new osci::FloatParameter("Sustain Level", "sustainLevel", VERSION_HINT, 0.6, 0.0, 1.0);
osci::FloatParameter* releaseTime = new osci::FloatParameter("Release Time", "releaseTime", VERSION_HINT, 0.4, 0.0, 1.0);
osci::FloatParameter* attackShape = new osci::FloatParameter("Attack Shape", "attackShape", VERSION_HINT, 5, -50, 50);
osci::FloatParameter* decayShape = new osci::FloatParameter("Decay osci::Shape", "decayShape", VERSION_HINT, -20, -50, 50);
osci::FloatParameter* releaseShape = new osci::FloatParameter("Release Shape", "releaseShape", VERSION_HINT, -5,-50, 50);
Env adsrEnv = Env::adsr(
2023-11-25 16:38:09 +00:00
attackTime->getValueUnnormalised(),
decayTime->getValueUnnormalised(),
sustainLevel->getValueUnnormalised(),
releaseTime->getValueUnnormalised(),
1.0,
std::vector<EnvCurve>{ attackShape->getValueUnnormalised(), decayShape->getValueUnnormalised(), releaseShape->getValueUnnormalised() }
);
juce::MidiKeyboardState keyboardState;
osci::IntParameter* voices = new osci::IntParameter("Voices", "voices", VERSION_HINT, 4, 1, 16);
osci::BooleanParameter* animateFrames = new osci::BooleanParameter("Animate", "animateFrames", VERSION_HINT, true, "Enables animation for files that have multiple frames, such as GIFs or Line Art.");
osci::BooleanParameter* loopAnimation = new osci::BooleanParameter("Loop Animation", "loopAnimation", VERSION_HINT, true, "Loops the animation. If disabled, the animation will stop at the last frame.");
osci::BooleanParameter* animationSyncBPM = new osci::BooleanParameter("Sync To BPM", "animationSyncBPM", VERSION_HINT, false, "Synchronises the animation's framerate with the BPM of your DAW.");
osci::FloatParameter* animationRate = new osci::FloatParameter("Animation Rate", "animationRate", VERSION_HINT, 30, -1000, 1000);
osci::FloatParameter* animationOffset = new osci::FloatParameter("Animation Offset", "animationOffset", VERSION_HINT, 0, -10000, 10000);
2024-05-11 22:10:54 +00:00
osci::BooleanParameter* invertImage = new osci::BooleanParameter("Invert Image", "invertImage", VERSION_HINT, false, "Inverts the image so that dark pixels become light, and vice versa.");
std::shared_ptr<osci::Effect> imageThreshold = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
2024-05-11 22:10:54 +00:00
return input;
}, new osci::EffectParameter(
2024-05-11 22:10:54 +00:00
"Image Threshold",
"Controls the probability of visiting a dark pixel versus a light pixel. Darker pixels are less likely to be visited, so turning the threshold to a lower value makes it more likely to visit dark pixels.",
2024-05-11 22:10:54 +00:00
"imageThreshold",
VERSION_HINT, 0.5, 0, 1
)
);
std::shared_ptr<osci::Effect> imageStride = std::make_shared<osci::Effect>(
[this](int index, osci::Point input, const std::vector<std::atomic<double>>& values, double sampleRate) {
2024-05-11 22:10:54 +00:00
return input;
}, new osci::EffectParameter(
2024-05-11 22:10:54 +00:00
"Image Stride",
"Controls the spacing between pixels when drawing an image. Larger values mean more of the image can be drawn, but at a lower fidelity.",
2024-05-11 22:10:54 +00:00
"imageStride",
VERSION_HINT, 4, 1, 50, 1
2024-05-11 22:10:54 +00:00
)
);
std::atomic<double> animationFrame = 0.f;
2023-09-07 21:04:08 +00:00
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(*this);
2025-04-04 13:59:17 +00:00
const double FONT_SIZE = 1.0f;
juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), FONT_SIZE, juce::Font::plain);
2023-08-27 18:33:42 +00:00
2023-09-10 16:43:37 +00:00
ShapeSound::Ptr objectServerSound = new ShapeSound();
std::function<void()> haltRecording;
2023-09-10 16:43:37 +00:00
// Add a callback to notify the editor when a file is removed
std::function<void(int)> fileRemovedCallback;
2023-07-04 13:58:36 +00:00
void addLuaSlider();
void updateEffectPrecedence();
void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> 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<juce::MemoryBlock> data);
void removeFile(int index);
int numFiles();
void changeCurrentFile(int index);
2023-08-27 18:33:42 +00:00
void openFile(int index);
int getCurrentFileIndex();
2023-07-04 13:58:36 +00:00
std::shared_ptr<FileParser> getCurrentFileParser();
juce::String getCurrentFileName();
juce::String getFileName(int index);
juce::String getFileId(int index);
std::shared_ptr<juce::MemoryBlock> getFileBlock(int index);
2023-09-10 16:43:37 +00:00
void setObjectServerRendering(bool enabled);
void setObjectServerPort(int port);
2023-12-20 17:13:38 +00:00
void addErrorListener(ErrorListener* listener);
void removeErrorListener(ErrorListener* listener);
void notifyErrorListeners(int lineNumber, juce::String id, juce::String error);
// Setter for the callback
void setFileRemovedCallback(std::function<void(int)> callback);
// Added declaration for the new `removeParser` method.
void removeParser(FileParser* parser);
2023-01-09 21:58:49 +00:00
private:
2023-09-07 21:04:08 +00:00
std::atomic<bool> prevMidiEnabled = !midiEnabled->getBoolValue();
juce::SpinLock audioThreadCallbackLock;
std::function<void(const juce::AudioBuffer<float>&)> audioThreadCallback;
2023-12-20 17:13:38 +00:00
juce::SpinLock errorListenersLock;
std::vector<ErrorListener*> errorListeners;
2024-05-11 22:10:54 +00:00
ShapeSound::Ptr defaultSound = new ShapeSound(*this, std::make_shared<FileParser>(*this));
2023-12-14 21:26:40 +00:00
PublicSynthesiser synth;
bool retriggerMidi = true;
2023-07-06 16:57:10 +00:00
2023-09-10 16:43:37 +00:00
ObjectServer objectServer{*this};
const double VOLUME_BUFFER_SECONDS = 0.1;
std::vector<double> volumeBuffer;
int volumeBufferIndex = 0;
2024-01-01 16:21:10 +00:00
double squaredVolume = 0;
double currentVolume = 0;
std::pair<std::shared_ptr<osci::Effect>, osci::EffectParameter*> effectFromLegacyId(const juce::String& id, bool updatePrecedence = false);
osci::LfoType lfoTypeFromLegacyAnimationType(const juce::String& type);
double valueFromLegacy(double value, const juce::String& id);
2023-09-07 21:04:08 +00:00
void changeSound(ShapeSound::Ptr sound);
void parseVersion(int result[3], const juce::String& input) {
std::istringstream parser(input.toStdString());
parser >> result[0];
for (int idx = 1; idx < 3; idx++) {
parser.get(); //Skip period
parser >> result[idx];
}
}
bool lessThanVersion(const juce::String& a, const juce::String& b) {
int parsedA[3], parsedB[3];
parseVersion(parsedA, a);
parseVersion(parsedB, b);
return std::lexicographical_compare(parsedA, parsedA + 3, parsedB, parsedB + 3);
}
const double MIN_LENGTH_INCREMENT = 0.000001;
2024-04-15 19:54:25 +00:00
juce::AudioPlayHead* playHead;
2023-01-09 21:58:49 +00:00
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessor)
};