osci-render/Source/PluginProcessor.h

285 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
#include <JuceHeader.h>
#include "shape/Shape.h"
#include "concurrency/BufferConsumer.h"
2023-03-25 20:24:10 +00:00
#include "audio/Effect.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 <numbers>
#include "audio/AudioWebSocketServer.h"
#include "audio/DelayEffect.h"
#include "audio/PitchDetector.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"
2023-01-09 21:58:49 +00:00
//==============================================================================
/**
*/
class OscirenderAudioProcessor : public juce::AudioProcessor, 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 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;
void setAudioThreadCallback(std::function<void(const juce::AudioBuffer<float>&)> callback);
2023-01-09 21:58:49 +00:00
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;
2023-09-05 19:46:05 +00:00
std::shared_ptr<BufferConsumer> consumerRegister(std::vector<float>& buffer);
void consumerStop(std::shared_ptr<BufferConsumer> consumer);
void consumerRead(std::shared_ptr<BufferConsumer> consumer);
void parameterValueChanged(int parameterIndex, float newValue) override;
void parameterGestureChanged(int parameterIndex, bool gestureIsStarting) override;
void envelopeChanged(EnvelopeComponent* changedEnvelope) override;
2023-09-01 18:52:36 +00:00
int VERSION_HINT = 2;
2023-01-09 21:58:49 +00:00
std::atomic<double> currentSampleRate = 0.0;
juce::SpinLock effectsLock;
std::vector<std::shared_ptr<Effect>> toggleableEffects;
2023-07-04 13:58:36 +00:00
std::vector<std::shared_ptr<Effect>> luaEffects;
double luaValues[26] = { 0.0 };
std::shared_ptr<Effect> frequencyEffect = std::make_shared<Effect>(
2024-01-07 16:17:20 +00:00
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
frequency = values[0];
return input;
2023-12-17 21:32:56 +00:00
}, new EffectParameter(
"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",
VERSION_HINT, 440.0, 0.0, 12000.0, 0.1
)
);
std::shared_ptr<Effect> volumeEffect = std::make_shared<Effect>(
2024-01-07 16:17:20 +00:00
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
volume = values[0];
return input;
2023-12-17 21:32:56 +00:00
}, new EffectParameter(
"Volume",
"Controls the volume of the sound. Works by scaling the image and sound by a factor.",
"volume",
VERSION_HINT, 1.0, 0.0, 3.0
)
);
std::shared_ptr<Effect> thresholdEffect = std::make_shared<Effect>(
2024-01-07 16:17:20 +00:00
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
threshold = values[0];
return input;
2023-12-17 21:32:56 +00:00
}, new EffectParameter(
"Threshold",
"Clips the sound and image to a maximum value. Applying a harsher threshold results in a more distorted sound.",
"threshold",
VERSION_HINT, 1.0, 0.0, 1.0
)
);
2023-08-28 21:06:21 +00:00
std::shared_ptr<Effect> traceMax = std::make_shared<Effect>(
2024-01-07 16:17:20 +00:00
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
2023-08-28 21:06:21 +00:00
return input;
2023-12-17 21:32:56 +00:00
}, new EffectParameter(
"Trace max",
"Defines the maximum proportion of the image that is drawn before skipping to the next frame. This has the effect of 'tracing' out the image from a single dot when animated. By default, we draw until the end of the frame, so this value is 1.0.",
"traceMax",
2024-02-20 14:57:52 +00:00
VERSION_HINT, 0.75, 0.0, 1.0
2023-12-17 21:32:56 +00:00
)
2023-08-28 21:06:21 +00:00
);
std::shared_ptr<Effect> traceMin = std::make_shared<Effect>(
2024-01-07 16:17:20 +00:00
[this](int index, Point input, const std::vector<double>& values, double sampleRate) {
2023-08-28 21:06:21 +00:00
return input;
2023-12-17 21:32:56 +00:00
}, new EffectParameter(
"Trace min",
"Defines the proportion of the image that drawing starts from. 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.",
"traceMin",
2024-02-20 14:57:52 +00:00
VERSION_HINT, 0.25, 0.0, 1.0
2023-12-17 21:32:56 +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<Effect> custom = std::make_shared<Effect>(
customEffect,
new 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<Effect> perspective = std::make_shared<Effect>(
perspectiveEffect,
std::vector<EffectParameter*>{
new EffectParameter("3D Perspective", "Controls the strength of the 3D perspective projection.", "perspectiveStrength", VERSION_HINT, 1.0, 0.0, 1.0),
new 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),
}
);
BooleanParameter* midiEnabled = new BooleanParameter("MIDI Enabled", "midiEnabled", VERSION_HINT, false);
2023-12-14 21:26:40 +00:00
BooleanParameter* inputEnabled = new BooleanParameter("Audio Input Enabled", "inputEnabled", VERSION_HINT, false);
2023-09-07 21:04:08 +00:00
std::atomic<float> frequency = 440.0f;
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;
FloatParameter* attackTime = new FloatParameter("Attack Time", "attackTime", VERSION_HINT, 0.005, 0.0, 1.0);
FloatParameter* attackLevel = new FloatParameter("Attack Level", "attackLevel", VERSION_HINT, 1.0, 0.0, 1.0);
FloatParameter* decayTime = new FloatParameter("Decay Time", "decayTime", VERSION_HINT, 0.095, 0.0, 1.0);
FloatParameter* sustainLevel = new FloatParameter("Sustain Level", "sustainLevel", VERSION_HINT, 0.6, 0.0, 1.0);
FloatParameter* releaseTime = new FloatParameter("Release Time", "releaseTime", VERSION_HINT, 0.4, 0.0, 1.0);
2023-11-25 16:38:09 +00:00
FloatParameter* attackShape = new FloatParameter("Attack Shape", "attackShape", VERSION_HINT, 5, -50, 50);
FloatParameter* decayShape = new FloatParameter("Decay Shape", "decayShape", VERSION_HINT, -20, -50, 50);
FloatParameter* releaseShape = new 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;
IntParameter* voices = new IntParameter("Voices", "voices", VERSION_HINT, 4, 1, 16);
2023-09-07 21:04:08 +00:00
private:
juce::SpinLock consumerLock;
std::vector<std::shared_ptr<BufferConsumer>> consumers;
public:
PitchDetector pitchDetector{*this};
std::shared_ptr<WobbleEffect> wobbleEffect = std::make_shared<WobbleEffect>(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;
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-09-10 16:43:37 +00:00
ShapeSound::Ptr objectServerSound = new ShapeSound();
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);
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);
2023-01-09 21:58:49 +00:00
private:
std::atomic<double> volume = 1.0;
std::atomic<double> threshold = 1.0;
2023-09-07 21:04:08 +00:00
bool prevMidiEnabled = !midiEnabled->getBoolValue();
juce::SpinLock audioThreadCallbackLock;
std::function<void(const juce::AudioBuffer<float>&)> audioThreadCallback;
std::vector<BooleanParameter*> booleanParameters;
std::vector<FloatParameter*> floatParameters;
std::vector<IntParameter*> intParameters;
std::vector<std::shared_ptr<Effect>> allEffects;
std::vector<std::shared_ptr<Effect>> permanentEffects;
2023-12-20 17:13:38 +00:00
juce::SpinLock errorListenersLock;
std::vector<ErrorListener*> errorListeners;
ShapeSound::Ptr defaultSound = new ShapeSound(std::make_shared<FileParser>());
2023-12-14 21:26:40 +00:00
PublicSynthesiser synth;
bool retriggerMidi = true;
2023-07-06 16:57:10 +00:00
AudioWebSocketServer softwareOscilloscopeServer{*this};
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::shared_ptr<Effect> getEffect(juce::String id);
BooleanParameter* getBooleanParameter(juce::String id);
FloatParameter* getFloatParameter(juce::String id);
IntParameter* getIntParameter(juce::String id);
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-09-07 21:04:08 +00:00
void changeSound(ShapeSound::Ptr sound);
const double MIN_LENGTH_INCREMENT = 0.000001;
2023-01-09 21:58:49 +00:00
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscirenderAudioProcessor)
};