More advanced version
This commit is contained in:
@@ -42,8 +42,8 @@
|
||||
namespace ProjectInfo
|
||||
{
|
||||
const char* const projectName = "NeuralSynth";
|
||||
const char* const companyName = "";
|
||||
const char* const versionString = "1.0.0";
|
||||
const int versionNumber = 0x10000;
|
||||
const char* const companyName = "Samedi Dimanche";
|
||||
const char* const versionString = "0.0.1";
|
||||
const int versionNumber = 0x1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -47,10 +47,10 @@
|
||||
#define JucePlugin_Desc "NeuralSynth"
|
||||
#endif
|
||||
#ifndef JucePlugin_Manufacturer
|
||||
#define JucePlugin_Manufacturer "yourcompany"
|
||||
#define JucePlugin_Manufacturer "Samedi Dimanche"
|
||||
#endif
|
||||
#ifndef JucePlugin_ManufacturerWebsite
|
||||
#define JucePlugin_ManufacturerWebsite "www.yourcompany.com"
|
||||
#define JucePlugin_ManufacturerWebsite "www.samedidimanche.com"
|
||||
#endif
|
||||
#ifndef JucePlugin_ManufacturerEmail
|
||||
#define JucePlugin_ManufacturerEmail ""
|
||||
@@ -62,10 +62,10 @@
|
||||
#define JucePlugin_PluginCode 0x4d73347a
|
||||
#endif
|
||||
#ifndef JucePlugin_IsSynth
|
||||
#define JucePlugin_IsSynth 0
|
||||
#define JucePlugin_IsSynth 1
|
||||
#endif
|
||||
#ifndef JucePlugin_WantsMidiInput
|
||||
#define JucePlugin_WantsMidiInput 0
|
||||
#define JucePlugin_WantsMidiInput 1
|
||||
#endif
|
||||
#ifndef JucePlugin_ProducesMidiOutput
|
||||
#define JucePlugin_ProducesMidiOutput 0
|
||||
@@ -77,25 +77,25 @@
|
||||
#define JucePlugin_EditorRequiresKeyboardFocus 0
|
||||
#endif
|
||||
#ifndef JucePlugin_Version
|
||||
#define JucePlugin_Version 1.0.0
|
||||
#define JucePlugin_Version 0.0.1
|
||||
#endif
|
||||
#ifndef JucePlugin_VersionCode
|
||||
#define JucePlugin_VersionCode 0x10000
|
||||
#define JucePlugin_VersionCode 0x1
|
||||
#endif
|
||||
#ifndef JucePlugin_VersionString
|
||||
#define JucePlugin_VersionString "1.0.0"
|
||||
#define JucePlugin_VersionString "0.0.1"
|
||||
#endif
|
||||
#ifndef JucePlugin_VSTUniqueID
|
||||
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
|
||||
#endif
|
||||
#ifndef JucePlugin_VSTCategory
|
||||
#define JucePlugin_VSTCategory kPlugCategEffect
|
||||
#define JucePlugin_VSTCategory kPlugCategSynth
|
||||
#endif
|
||||
#ifndef JucePlugin_Vst3Category
|
||||
#define JucePlugin_Vst3Category "Fx"
|
||||
#define JucePlugin_Vst3Category "Instrument|Synth"
|
||||
#endif
|
||||
#ifndef JucePlugin_AUMainType
|
||||
#define JucePlugin_AUMainType 'aufx'
|
||||
#define JucePlugin_AUMainType 'aumu'
|
||||
#endif
|
||||
#ifndef JucePlugin_AUSubType
|
||||
#define JucePlugin_AUSubType JucePlugin_PluginCode
|
||||
@@ -110,10 +110,10 @@
|
||||
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode
|
||||
#endif
|
||||
#ifndef JucePlugin_CFBundleIdentifier
|
||||
#define JucePlugin_CFBundleIdentifier com.yourcompany.NeuralSynth
|
||||
#define JucePlugin_CFBundleIdentifier com.samedidimanche.NeuralSynth
|
||||
#endif
|
||||
#ifndef JucePlugin_AAXIdentifier
|
||||
#define JucePlugin_AAXIdentifier com.yourcompany.NeuralSynth
|
||||
#define JucePlugin_AAXIdentifier com.SamediDimanche.NeuralSynth
|
||||
#endif
|
||||
#ifndef JucePlugin_AAXManufacturerCode
|
||||
#define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode
|
||||
@@ -122,7 +122,7 @@
|
||||
#define JucePlugin_AAXProductId JucePlugin_PluginCode
|
||||
#endif
|
||||
#ifndef JucePlugin_AAXCategory
|
||||
#define JucePlugin_AAXCategory 0
|
||||
#define JucePlugin_AAXCategory 2048
|
||||
#endif
|
||||
#ifndef JucePlugin_AAXDisableBypass
|
||||
#define JucePlugin_AAXDisableBypass 0
|
||||
@@ -131,16 +131,16 @@
|
||||
#define JucePlugin_AAXDisableMultiMono 0
|
||||
#endif
|
||||
#ifndef JucePlugin_IAAType
|
||||
#define JucePlugin_IAAType 0x61757278
|
||||
#define JucePlugin_IAAType 0x61757269
|
||||
#endif
|
||||
#ifndef JucePlugin_IAASubType
|
||||
#define JucePlugin_IAASubType JucePlugin_PluginCode
|
||||
#endif
|
||||
#ifndef JucePlugin_IAAName
|
||||
#define JucePlugin_IAAName "yourcompany: NeuralSynth"
|
||||
#define JucePlugin_IAAName "Samedi Dimanche: NeuralSynth"
|
||||
#endif
|
||||
#ifndef JucePlugin_VSTNumMidiInputs
|
||||
#define JucePlugin_VSTNumMidiInputs 16
|
||||
#define JucePlugin_VSTNumMidiInputs 1
|
||||
#endif
|
||||
#ifndef JucePlugin_VSTNumMidiOutputs
|
||||
#define JucePlugin_VSTNumMidiOutputs 16
|
||||
@@ -152,11 +152,20 @@
|
||||
#define JucePlugin_ARATransformationFlags 0
|
||||
#endif
|
||||
#ifndef JucePlugin_ARAFactoryID
|
||||
#define JucePlugin_ARAFactoryID "com.yourcompany.NeuralSynth.factory"
|
||||
#define JucePlugin_ARAFactoryID "com.SamediDimanche.NeuralSynth.factory"
|
||||
#endif
|
||||
#ifndef JucePlugin_ARADocumentArchiveID
|
||||
#define JucePlugin_ARADocumentArchiveID "com.yourcompany.NeuralSynth.aradocumentarchive.1.0.0"
|
||||
#define JucePlugin_ARADocumentArchiveID "com.SamediDimanche.NeuralSynth.aradocumentarchive.0.0.1"
|
||||
#endif
|
||||
#ifndef JucePlugin_ARACompatibleArchiveIDs
|
||||
#define JucePlugin_ARACompatibleArchiveIDs ""
|
||||
#endif
|
||||
#ifndef JucePlugin_MaxNumInputChannels
|
||||
#define JucePlugin_MaxNumInputChannels 0
|
||||
#endif
|
||||
#ifndef JucePlugin_MaxNumOutputChannels
|
||||
#define JucePlugin_MaxNumOutputChannels 2
|
||||
#endif
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
#define JucePlugin_PreferredChannelConfigurations {0, 2}
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<JUCERPROJECT id="ms4Z8E" name="NeuralSynth" projectType="audioplug" useAppConfig="0"
|
||||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1">
|
||||
addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" companyWebsite="www.samedidimanche.com"
|
||||
bundleIdentifier="com.samedidimanche.NeuralSynth" pluginManufacturer="Samedi Dimanche"
|
||||
companyName="Samedi Dimanche" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn"
|
||||
pluginVSTNumMidiInputs="1" pluginChannelConfigs="{0, 2}" version="0.0.1">
|
||||
<MAINGROUP id="UQstsW" name="NeuralSynth">
|
||||
<GROUP id="{D5B48DA9-9A47-914A-8C72-EE5E8DD868A3}" name="Source">
|
||||
<FILE id="nmKMnf" name="GraphComponent.h" compile="0" resource="0"
|
||||
file="Source/GraphComponent.h"/>
|
||||
<FILE id="CjJ141" name="NeuralSharedParams.h" compile="0" resource="0"
|
||||
file="Source/NeuralSharedParams.h"/>
|
||||
<FILE id="ZrdRl1" name="AudioBufferQueue.h" compile="0" resource="0"
|
||||
|
||||
118
Source/GraphComponent.h
Normal file
118
Source/GraphComponent.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
GraphComponent.h
|
||||
Created: 4 Jul 2025 11:43:57pm
|
||||
Author: timot
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AudioBufferQueue.h"
|
||||
|
||||
//==============================================================================
|
||||
template <typename SampleType>
|
||||
class GraphComponent : public juce::Component,
|
||||
private juce::Timer
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
GraphComponent(SampleType min, SampleType max, int numPoints): func(func), min(min), max(max), numPoints(numPoints)
|
||||
{
|
||||
x.resize(numPoints);
|
||||
y.resize(numPoints);
|
||||
setFramesPerSecond(30);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setFramesPerSecond(int framesPerSecond)
|
||||
{
|
||||
jassert(framesPerSecond > 0 && framesPerSecond < 1000);
|
||||
startTimerHz(framesPerSecond);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void setFunction(const std::function<SampleType(SampleType)>& func) {
|
||||
this->func = func;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void paint(juce::Graphics& g) override
|
||||
{
|
||||
g.fillAll(juce::Colours::black);
|
||||
g.setColour(juce::Colours::white);
|
||||
|
||||
auto area = getLocalBounds();
|
||||
|
||||
if (hasData && area.isFinite()) {
|
||||
auto h = (SampleType)area.getHeight();
|
||||
auto w = (SampleType)area.getWidth();
|
||||
|
||||
for (size_t i = 1; i < numPoints; ++i) {
|
||||
auto px_prev = ((x[i - 1] - min) / (max - min)) * w;
|
||||
auto py_prev = h - ((y[i - 1] - minY) / (maxY - minY)) * h;
|
||||
|
||||
auto px_next = ((x[i] - min) / (max - min)) * w;
|
||||
auto py_next = h - ((y[i] - minY) / (maxY - minY)) * h;
|
||||
|
||||
juce::Line<SampleType> line(juce::Point<SampleType>(px_prev, py_prev), juce::Point<SampleType>(px_next, py_next));
|
||||
|
||||
g.drawLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void resized() override {}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
std::vector<SampleType> x, y;
|
||||
SampleType minY, maxY;
|
||||
SampleType min, max;
|
||||
int numPoints;
|
||||
std::function<SampleType(SampleType)> func;
|
||||
bool hasData = false;
|
||||
|
||||
//==============================================================================
|
||||
void timerCallback() override
|
||||
{
|
||||
float step = (max - min) / (SampleType)(numPoints - 1);
|
||||
|
||||
for (int i = 0; i < numPoints; i++) {
|
||||
x[i] = min + step * (SampleType)i;
|
||||
y[i] = func(x[i]);
|
||||
}
|
||||
|
||||
auto p = minmax_element(y.begin(), y.end());
|
||||
|
||||
minY = *p.first; maxY = *p.second;
|
||||
|
||||
hasData = true;
|
||||
repaint();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
/*static void plot(const SampleType* data,
|
||||
size_t numSamples,
|
||||
juce::Graphics& g,
|
||||
juce::Rectangle<SampleType> rect,
|
||||
SampleType scaler = SampleType(1),
|
||||
SampleType offset = SampleType(0))
|
||||
{
|
||||
auto w = rect.getWidth();
|
||||
auto h = rect.getHeight();
|
||||
auto right = rect.getRight();
|
||||
|
||||
auto center = rect.getBottom() - offset;
|
||||
auto gain = h * scaler;
|
||||
|
||||
for (size_t i = 1; i < numSamples; ++i)
|
||||
g.drawLine({ juce::jmap(SampleType(i - 1), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)),
|
||||
center - gain * data[i - 1],
|
||||
juce::jmap(SampleType(i), SampleType(0), SampleType(numSamples - 1), SampleType(right - w), SampleType(right)),
|
||||
center - gain * data[i] });
|
||||
}*/
|
||||
};
|
||||
@@ -12,12 +12,120 @@
|
||||
|
||||
#include <atomic>
|
||||
|
||||
struct SliderDetail {
|
||||
std::string label;
|
||||
|
||||
float min, max, interval, defValue;
|
||||
};
|
||||
|
||||
typedef std::unordered_map <std::string, SliderDetail> ParamMap;
|
||||
|
||||
// Each SliderDetail: { label, min, max, step, defaultValue }
|
||||
const std::unordered_map<std::string, ParamMap> PARAM_SETTINGS = {
|
||||
|
||||
{ "chorus", {
|
||||
{ "rate", { "Rate", 0.0f, 1.0f, 0.1f, 0.1f } }, // Modulation speed
|
||||
{ "depth", { "Depth", 0.0f, 1.0f, 0.1f, 0.1f } }, // Modulation amount
|
||||
{ "centre", { "Centre", 0.0f, 1.0f, 0.1f, 0.1f } }, // Center delay
|
||||
{ "feedback", { "Feedback", 0.0f, 1.0f, 0.1f, 0.1f } }, // Feedback amount
|
||||
{ "mix", { "Mix", 0.0f, 1.0f, 0.1f, 0.1f } } // Dry/wet blend
|
||||
}},
|
||||
|
||||
{ "delay", {
|
||||
{ "delay", { "Delay", 0.0f, 1.0f, 0.1f, 0.1f } } // Delay time
|
||||
}},
|
||||
|
||||
{ "reverb", {
|
||||
{ "roomSize", { "Room Size", 0.0f, 1.0f, 0.1f, 0.1f } }, // Size of reverb space
|
||||
{ "damping", { "Damping", 0.0f, 1.0f, 0.1f, 0.1f } }, // High-frequency attenuation
|
||||
{ "wetLevel", { "Wet Level", 0.0f, 1.0f, 0.1f, 0.1f } }, // Reverb amount
|
||||
{ "dryLevel", { "Dry Level", 0.0f, 1.0f, 0.1f, 0.1f } }, // Dry signal amount
|
||||
{ "width", { "Width", 0.0f, 1.0f, 0.1f, 0.1f } }, // Stereo width
|
||||
{ "freezeMode", { "Freeze Mode", 0.0f, 1.0f, 0.1f, 0.1f } } // Infinite decay toggle
|
||||
}},
|
||||
|
||||
{ "adsr", {
|
||||
{ "attack", { "Attack", 0.0f, 1.0f, 0.1f, 0.1f } }, // Attack time
|
||||
{ "decay", { "Decay", 0.0f, 1.0f, 0.1f, 0.1f } }, // Decay time
|
||||
{ "sustain", { "Sustain", 0.0f, 1.0f, 0.1f, 0.1f } }, // Sustain level
|
||||
{ "release", { "Release", 0.0f, 1.0f, 0.1f, 0.1f } } // Release time
|
||||
}},
|
||||
|
||||
{ "flanger", {
|
||||
{ "rate", { "Rate", 0.1f, 5.0f, 0.1f, 0.1f } }, // LFO speed
|
||||
{ "depth", { "Depth", 0.1f, 10.0f, 0.1f, 0.1f } }, // Mod depth in ms
|
||||
{ "feedback", { "Feedback", 0.0f, 1.0f, 0.1f, 0.1f } }, // Feedback amount
|
||||
{ "dryMix", { "Dry/Wet", 0.0f, 1.0f, 0.1f, 0.1f } }, // Mix control
|
||||
{ "phase", { "Phase", 0.0f, 1.0f, 0.1f, 0.1f } }, // LFO phase offset (for stereo)
|
||||
{ "delay", { "Delay", 0.0f, 3.0f, 0.1f, 0.1f } } // Base delay offset
|
||||
}},
|
||||
|
||||
{ "filter", {
|
||||
{ "cutoff", { "Cutoff", 0.2f, 20000.0f, 1.0f, 1000.0f } }, // Frequency cutoff
|
||||
{ "resonance", { "Resonance", 0.1f, 10.0f, 0.1f, 0.2f } }, // Resonance/Q factor
|
||||
{ "type", { "L/H/B", 0.0f, 2.0f, 1.0f, 0.0f } }, // 0 = LPF, 1 = HPF, 2 = BPF
|
||||
{ "drive", { "Drive", 0.0f, 1.0f, 0.01f, 0.0f } }, // Pre-gain into filter
|
||||
{ "mod", { "Mod", -1.0f, 1.0f, 0.1f, 0.0f } }, // Modulation amount
|
||||
{ "key", { "Key", 0.0f, 1.0f, 0.1f, 0.0f } } // Key tracking
|
||||
}},
|
||||
|
||||
{ "distortion", {
|
||||
{ "drive", { "Drive", 0.0f, 30.0f, 0.1f, 10.0f } }, // Input gain before shaping
|
||||
{ "mix", { "Mix", 0.0f, 1.0f, 0.01f, 0.5f } }, // Wet/dry blend
|
||||
{ "bias", { "Bias", -1.0f, 1.0f, 0.01f, 0.0f } }, // DC offset
|
||||
{ "tone", { "Tone", 100.0f, 8000.0f, 10.0f, 3000.0f } }, // LPF after distortion
|
||||
{ "shape", { "Shape", 0.0f, 2.0f, 1.0f, 0.0f } } // 0=tanh, 1=hard clip, 2=atan
|
||||
}}
|
||||
|
||||
};
|
||||
|
||||
struct NeuralSharedParams
|
||||
{
|
||||
std::atomic<int> waveform{ -1 };
|
||||
|
||||
std::atomic<float>* attack;
|
||||
std::atomic<float>* decay;
|
||||
std::atomic<float>* sustain;
|
||||
std::atomic<float>* release;
|
||||
std::atomic<float>* adsrAttack;
|
||||
std::atomic<float>* adsrDecay;
|
||||
std::atomic<float>* adsrSustain;
|
||||
std::atomic<float>* adsrRelease;
|
||||
|
||||
std::atomic<float>* delayTime;
|
||||
|
||||
std::atomic<float>* chorusRate;
|
||||
std::atomic<float>* chorusDepth;
|
||||
std::atomic<float>* chorusCentre;
|
||||
std::atomic<float>* chorusFeedback;
|
||||
std::atomic<float>* chorusMix;
|
||||
|
||||
std::atomic<float>* reverbRoomSize;
|
||||
std::atomic<float>* reverbDamping;
|
||||
std::atomic<float>* reverbWetLevel;
|
||||
std::atomic<float>* reverbDryLevel;
|
||||
std::atomic<float>* reverbWidth;
|
||||
std::atomic<float>* reverbFreezeMode;
|
||||
|
||||
std::atomic<float>* flangerRate;
|
||||
std::atomic<float>* flangerDepth;
|
||||
std::atomic<float>* flangerFeedback;
|
||||
std::atomic<float>* flangerDryMix;
|
||||
std::atomic<float>* flangerPhase;
|
||||
std::atomic<float>* flangerDelay;
|
||||
|
||||
std::atomic<float>* filterCutoff;
|
||||
std::atomic<float>* filterResonance;
|
||||
std::atomic<float>* filterType;
|
||||
std::atomic<float>* filterDrive;
|
||||
std::atomic<float>* filterMod;
|
||||
std::atomic<float>* filterKey;
|
||||
|
||||
std::atomic<float>* distortionDrive;
|
||||
std::atomic<float>* distortionMix;
|
||||
std::atomic<float>* distortionBias;
|
||||
std::atomic<float>* distortionTone;
|
||||
std::atomic<float>* distortionShape;
|
||||
|
||||
std::atomic<float>* lowGainDbls;
|
||||
std::atomic<float>* midGainDbls;
|
||||
std::atomic<float>* highGainDbls;
|
||||
|
||||
std::atomic<float>* masterDbls;
|
||||
};
|
||||
@@ -12,66 +12,336 @@
|
||||
|
||||
//==============================================================================
|
||||
NeuralSynthAudioProcessorEditor::NeuralSynthAudioProcessorEditor (NeuralSynthAudioProcessor& p)
|
||||
: AudioProcessorEditor (&p), audioProcessor (p), scopeComponent(audioProcessor.getAudioBufferQueue())
|
||||
: AudioProcessorEditor (&p), audioProcessor (p), mainScopeComponent(audioProcessor.getAudioBufferQueue())
|
||||
{
|
||||
// Make sure that before the constructor has finished, you've set the
|
||||
// editor's size to whatever you need it to be.
|
||||
setSize(400, 500);
|
||||
|
||||
auto& tree = audioProcessor.parameters;
|
||||
|
||||
auto area = getLocalBounds();
|
||||
scopeComponent.setTopLeftPosition(0, 0);
|
||||
scopeComponent.setSize(400, 200);
|
||||
//auto area = getLocalBounds();
|
||||
//mainScopeComponent.setBounds(5, 5, 800, 200);
|
||||
// scopeComponent.setSize(800, 200);
|
||||
|
||||
addAndMakeVisible(scopeComponent);
|
||||
addAndMakeVisible(mainScopeComponent);
|
||||
|
||||
waveformSelector.setModel(&waveformContents);
|
||||
|
||||
addAndMakeVisible(waveformSelector);
|
||||
|
||||
|
||||
chorusComponent.emplace(tree, "chorus");
|
||||
|
||||
chorusComponent->enableSampleScope(audioProcessor.getChorusAudioBufferQueue());
|
||||
addAndMakeVisible(*chorusComponent);
|
||||
|
||||
delayComponent.emplace(tree, "delay");
|
||||
delayComponent->enableSampleScope(audioProcessor.getChorusAudioBufferQueue());
|
||||
addAndMakeVisible(*delayComponent);
|
||||
|
||||
reverbComponent.emplace(tree, "reverb");
|
||||
|
||||
reverbComponent->enableSampleScope(audioProcessor.getChorusAudioBufferQueue());
|
||||
addAndMakeVisible(*reverbComponent);
|
||||
|
||||
|
||||
adsrComponent.emplace(tree, "adsr");
|
||||
|
||||
adsrComponent->enableGraphScope([this](float x) {
|
||||
auto& tree = this->audioProcessor.parameters;
|
||||
|
||||
float attackValue = tree.getParameter("adsr_attack")->getValue();
|
||||
float decayValue = tree.getParameter("adsr_decay")->getValue();
|
||||
float sustainValue = tree.getParameter("adsr_sustain")->getValue();
|
||||
float releaseValue = tree.getParameter("adsr_release")->getValue();
|
||||
|
||||
float sustainLength = 1.0f;
|
||||
float totalTime = attackValue + decayValue + sustainLength + releaseValue;
|
||||
|
||||
attackValue /= totalTime;
|
||||
decayValue /= totalTime;
|
||||
sustainLength /= totalTime;
|
||||
releaseValue /= totalTime;
|
||||
|
||||
float m, c;
|
||||
if (x < attackValue)
|
||||
{
|
||||
m = (1.0f / attackValue);
|
||||
c = 0;
|
||||
}
|
||||
else if (x < (attackValue + decayValue)) {
|
||||
m = (sustainValue - 1.0f) / decayValue;
|
||||
c = 1.0f - m * attackValue;
|
||||
}
|
||||
else if (x < (attackValue + decayValue + sustainLength)) {
|
||||
m = 0.0f;
|
||||
c = sustainValue;
|
||||
}
|
||||
else {
|
||||
m = (sustainValue / -releaseValue);
|
||||
c = -m;
|
||||
}
|
||||
|
||||
return m * x + c;
|
||||
});
|
||||
addAndMakeVisible(*adsrComponent);
|
||||
|
||||
//createADSR(5, 250);
|
||||
//createEQ();
|
||||
|
||||
addAndMakeVisible(masterLevelSlider);
|
||||
|
||||
eqComponent.emplace(tree);
|
||||
addAndMakeVisible(*eqComponent);
|
||||
|
||||
// Attach to parameter
|
||||
//waveformAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(
|
||||
// audioProcessor.parameters, "waveform", waveformSelector);
|
||||
|
||||
//attachments.push_back(std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(
|
||||
// tree, sliderDetail.name, *sliders.back()));
|
||||
|
||||
gainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(
|
||||
audioProcessor.parameters, "master", masterLevelSlider.slider
|
||||
);
|
||||
|
||||
|
||||
flangerComponent.emplace(tree, "flanger");
|
||||
flangerComponent->enableSampleScope(audioProcessor.getFlangerAudioBufferQueue());
|
||||
addAndMakeVisible(*flangerComponent);
|
||||
|
||||
distortionComponent.emplace(tree, "distortion");
|
||||
distortionComponent->enableSampleScope(audioProcessor.getFlangerAudioBufferQueue());
|
||||
addAndMakeVisible(*distortionComponent);
|
||||
|
||||
filterComponent.emplace(tree, "filter");
|
||||
filterComponent->enableSampleScope(audioProcessor.getFilterAudioBufferQueue());
|
||||
addAndMakeVisible(*filterComponent);
|
||||
|
||||
//addAndMakeVisible(midiKeyboardComponent);
|
||||
|
||||
|
||||
//scopeComponent.setSize(area.getWidth(), area.getHeight());
|
||||
|
||||
//midiKeyboardComponent.setMidiChannel(2);
|
||||
//midiKeyboardState.addListener(&audioProcessor.getMidiMessageCollector());
|
||||
|
||||
//midiKeyboardComponent.setBounds(area.removeFromTop(80).reduced(8));
|
||||
//midiKeyboardComponent.setTopLeftPosition(8, 420);
|
||||
|
||||
// Make sure that before the constructor has finished, you've set the
|
||||
// editor's size to whatever you need it to be.
|
||||
setSize(1400, 700);
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
NeuralSynthAudioProcessorEditor::~NeuralSynthAudioProcessorEditor()
|
||||
{
|
||||
}
|
||||
|
||||
/*void NeuralSynthAudioProcessorEditor::updateEQFromSliders()
|
||||
{
|
||||
using Coefficients = juce::dsp::IIR::Coefficients<float>;
|
||||
|
||||
auto& low = audioProcessor.getProcess.get<0>();
|
||||
auto& mid = audioProcessor.eqChain.get<1>();
|
||||
auto& high = audioProcessor.eqChain.get<2>();
|
||||
|
||||
low.coefficients = Coefficients::makeLowShelf(audioProcessor.sampleRate, 100.0f, 0.707f,
|
||||
juce::Decibels::decibelsToGain(lowGainSlider.getValue()));
|
||||
mid.coefficients = Coefficients::makePeakFilter(audioProcessor.sampleRate, 1000.0f, 0.707f,
|
||||
juce::Decibels::decibelsToGain(midGainSlider.getValue()));
|
||||
high.coefficients = Coefficients::makeHighShelf(audioProcessor.sampleRate, 8000.0f, 0.707f,
|
||||
juce::Decibels::decibelsToGain(highGainSlider.getValue()));
|
||||
}*/
|
||||
|
||||
//==============================================================================
|
||||
/*void NeuralSynthAudioProcessorEditor::createADSR(int xCoord, int yCoord) {
|
||||
adsrGraph.setFunction([this](float x) {
|
||||
auto& tree = this->audioProcessor.parameters;
|
||||
|
||||
float attackValue = tree.getParameter("attack")->getValue();
|
||||
float decayValue = tree.getParameter("decay")->getValue();
|
||||
float sustainValue = tree.getParameter("sustain")->getValue();
|
||||
float releaseValue = tree.getParameter("release")->getValue();
|
||||
|
||||
float sustainLength = 1.0f;
|
||||
float totalTime = attackValue + decayValue + sustainLength + releaseValue;
|
||||
|
||||
attackValue /= totalTime;
|
||||
decayValue /= totalTime;
|
||||
sustainLength /= totalTime;
|
||||
releaseValue /= totalTime;
|
||||
|
||||
float m, c;
|
||||
if (x < attackValue)
|
||||
{
|
||||
m = (1.0f / attackValue);
|
||||
c = 0;
|
||||
} else if (x < (attackValue + decayValue)) {
|
||||
m = (sustainValue - 1.0f) / decayValue;
|
||||
c = 1.0f - m * attackValue;
|
||||
} else if (x < (attackValue + decayValue + sustainLength)) {
|
||||
m = 0.0f;
|
||||
c = sustainValue;
|
||||
} else {
|
||||
m = (sustainValue / -releaseValue);
|
||||
c = -m;
|
||||
}
|
||||
|
||||
return m * x + c;
|
||||
});
|
||||
|
||||
|
||||
int fontSize = 11;
|
||||
int leftPosition = xCoord;
|
||||
const int sliderWidth = 60;
|
||||
const int sliderWidthWithPadding = sliderWidth + 20;
|
||||
|
||||
adsrGraph.setBounds(xCoord, yCoord, 240, 150);
|
||||
|
||||
addAndMakeVisible(adsrGraph);
|
||||
|
||||
|
||||
for (auto* slider : { &attackSlider, &decaySlider, &sustainSlider, &releaseSlider })
|
||||
{
|
||||
slider->setSliderStyle(juce::Slider::Rotary);
|
||||
slider->setTextBoxStyle(juce::Slider::TextBoxBelow, false, sliderWidth + 20, 20);
|
||||
//slider->setTopLeftPosition(0, 0);
|
||||
slider->setBounds(leftPosition, yCoord + 150, sliderWidth, sliderWidthWithPadding);
|
||||
addAndMakeVisible(*slider);
|
||||
leftPosition += sliderWidth;
|
||||
}
|
||||
|
||||
leftPosition = xCoord + 3; // (sliderWidth / 2);
|
||||
for (auto* label : { &attackLabel, &decayLabel, &sustainLabel, &releaseLabel })
|
||||
{
|
||||
label->setFont(juce::Font((float)fontSize, juce::Font::bold));
|
||||
//label->setTopLeftPosition(leftPosition, 300);
|
||||
label->setColour(juce::Label::textColourId, juce::Colours::lightgreen);
|
||||
label->setJustificationType(juce::Justification::centred);
|
||||
label->setBounds(leftPosition, yCoord + 240, 50, 20);
|
||||
//label->setText("");
|
||||
addAndMakeVisible(*label);
|
||||
leftPosition += sliderWidth;
|
||||
}
|
||||
|
||||
attackLabel.setText("Attack", juce::dontSendNotification); decayLabel.setText("Decay", juce::dontSendNotification);
|
||||
sustainLabel.setText("Sustain", juce::dontSendNotification); releaseLabel.setText("Release", juce::dontSendNotification);
|
||||
|
||||
auto& tree = this->audioProcessor.parameters;
|
||||
|
||||
attackAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "attack", attackSlider);
|
||||
decayAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "decay", decaySlider);
|
||||
sustainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "sustain", sustainSlider);
|
||||
releaseAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "release", releaseSlider);
|
||||
|
||||
addAndMakeVisible(waveformSelector);
|
||||
attackSlider.setRange(0.0, 1.0);
|
||||
decaySlider.setRange(0.0, 1.0);
|
||||
sustainSlider.setRange(0.0, 1.0);
|
||||
releaseSlider.setRange(0.0, 1.0);
|
||||
}*/
|
||||
|
||||
waveformSelector.setTopLeftPosition(15, 225);
|
||||
|
||||
int leftPosition = 15;
|
||||
|
||||
/*void NeuralSynthAudioProcessorEditor::createDelay(int xCoord, int yCoord) {
|
||||
int fontSize = 11;
|
||||
int leftPosition = xCoord;
|
||||
const int sliderWidth = 60;
|
||||
for (auto* slider : { &attackSlider, &decaySlider, &sustainSlider, &releaseSlider })
|
||||
const int sliderWidthWithPadding = sliderWidth + 20;
|
||||
|
||||
delayScopeComponent.setBounds(xCoord, yCoord, 300, 150);
|
||||
|
||||
addAndMakeVisible(delayScopeComponent);
|
||||
|
||||
|
||||
for (auto* slider : { &delayDelaySlider })
|
||||
{
|
||||
slider->setSliderStyle(juce::Slider::Rotary);
|
||||
slider->setTextBoxStyle(juce::Slider::TextBoxBelow, false, sliderWidth, 20);
|
||||
slider->setTextBoxStyle(juce::Slider::TextBoxBelow, false, sliderWidth + 20, 20);
|
||||
//slider->setTopLeftPosition(0, 0);
|
||||
slider->setBounds(leftPosition, yCoord + 150, sliderWidth, sliderWidthWithPadding);
|
||||
addAndMakeVisible(*slider);
|
||||
slider->setTopLeftPosition(leftPosition, 250);
|
||||
leftPosition += (sliderWidth + 40);
|
||||
leftPosition += sliderWidth;
|
||||
}
|
||||
|
||||
waveformSelector.addItem("Sine", 1);
|
||||
waveformSelector.addItem("Saw", 2);
|
||||
waveformSelector.addItem("Square", 3);
|
||||
waveformSelector.addItem("Triangle", 4);
|
||||
|
||||
|
||||
// Attach to parameter
|
||||
waveformAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(
|
||||
audioProcessor.parameters, "waveform", waveformSelector);
|
||||
|
||||
addAndMakeVisible(midiKeyboardComponent);
|
||||
|
||||
|
||||
//scopeComponent.setSize(area.getWidth(), area.getHeight());
|
||||
|
||||
midiKeyboardComponent.setMidiChannel(2);
|
||||
midiKeyboardState.addListener(&audioProcessor.getMidiMessageCollector());
|
||||
|
||||
midiKeyboardComponent.setBounds(area.removeFromTop(80).reduced(8));
|
||||
midiKeyboardComponent.setTopLeftPosition(8, 420);
|
||||
}
|
||||
|
||||
NeuralSynthAudioProcessorEditor::~NeuralSynthAudioProcessorEditor()
|
||||
leftPosition = xCoord + 3; // (sliderWidth / 2);
|
||||
for (auto* label : { &delayDelayLabel })
|
||||
{
|
||||
label->setFont(juce::Font((float)fontSize, juce::Font::bold));
|
||||
//label->setTopLeftPosition(leftPosition, 300);
|
||||
label->setColour(juce::Label::textColourId, juce::Colours::lightgreen);
|
||||
label->setJustificationType(juce::Justification::centred);
|
||||
label->setBounds(leftPosition, yCoord + 240, 50, 20);
|
||||
//label->setText("");
|
||||
addAndMakeVisible(*label);
|
||||
leftPosition += sliderWidth;
|
||||
}
|
||||
|
||||
delayDelayLabel.setText("Delay", juce::dontSendNotification);
|
||||
|
||||
auto& tree = this->audioProcessor.parameters;
|
||||
delayDelayAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "delayDelay", reverbRoomSizeSlider);
|
||||
|
||||
delayDelaySlider.setRange(0.0, 1.0);
|
||||
}
|
||||
*/
|
||||
|
||||
/*void NeuralSynthAudioProcessorEditor::createReverb(int xCoord, int yCoord) {
|
||||
|
||||
int fontSize = 11;
|
||||
int leftPosition = xCoord;
|
||||
const int sliderWidth = 60;
|
||||
const int sliderWidthWithPadding = sliderWidth + 20;
|
||||
|
||||
reverbScopeComponent.setBounds(xCoord, yCoord, 360, 150);
|
||||
|
||||
addAndMakeVisible(reverbScopeComponent);
|
||||
|
||||
|
||||
for (auto* slider : { &reverbRoomSizeSlider, &reverbDampingSlider, &reverbWetLevelSlider, &reverbDryLevelSlider, &reverbWidthSlider, &reverbFreezeModeSlider })
|
||||
{
|
||||
slider->setSliderStyle(juce::Slider::Rotary);
|
||||
slider->setTextBoxStyle(juce::Slider::TextBoxBelow, false, sliderWidth + 20, 20);
|
||||
//slider->setTopLeftPosition(0, 0);
|
||||
slider->setBounds(leftPosition, yCoord + 150, sliderWidth, sliderWidthWithPadding);
|
||||
addAndMakeVisible(*slider);
|
||||
leftPosition += sliderWidth;
|
||||
}
|
||||
|
||||
leftPosition = xCoord + 3; // (sliderWidth / 2);
|
||||
for (auto* label : { &reverbRoomSizeLabel, &reverbDampingLabel, &reverbWetLevelLabel, &reverbDryLevelLabel, &reverbWidthLabel, &reverbFreezeModeLabel })
|
||||
{
|
||||
label->setFont(juce::Font((float)fontSize, juce::Font::bold));
|
||||
//label->setTopLeftPosition(leftPosition, 300);
|
||||
label->setColour(juce::Label::textColourId, juce::Colours::lightgreen);
|
||||
label->setJustificationType(juce::Justification::centred);
|
||||
label->setBounds(leftPosition, yCoord + 240, 50, 20);
|
||||
//label->setText("");
|
||||
addAndMakeVisible(*label);
|
||||
leftPosition += sliderWidth;
|
||||
}
|
||||
|
||||
reverbRoomSizeLabel.setText("Room Size", juce::dontSendNotification); reverbDampingLabel.setText("Damping", juce::dontSendNotification);
|
||||
reverbWetLevelLabel.setText("Wet Level", juce::dontSendNotification); reverbDryLevelLabel.setText("Dry Level", juce::dontSendNotification);
|
||||
reverbWidthLabel.setText("Width", juce::dontSendNotification); reverbFreezeModeLabel.setText("Freeze Mode", juce::dontSendNotification);
|
||||
|
||||
auto& tree = this->audioProcessor.parameters;
|
||||
|
||||
reverbRoomSizeAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "reverbRoomSize", reverbRoomSizeSlider);
|
||||
reverbDampingAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "reverbDamping", reverbDampingSlider);
|
||||
reverbWetLevelAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "reverbWetLevel", reverbWetLevelSlider);
|
||||
reverbDryLevelAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "reverbDryLevel", reverbDryLevelSlider);
|
||||
reverbWidthAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "reverbWidth", reverbWidthSlider);
|
||||
reverbFreezeModeAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "reverbFreezeMode", reverbFreezeModeSlider);
|
||||
|
||||
|
||||
reverbRoomSizeSlider.setRange(0.0, 1.0);
|
||||
reverbDampingSlider.setRange(0.0, 1.0);
|
||||
reverbWetLevelSlider.setRange(0.0, 1.0);
|
||||
reverbDryLevelSlider.setRange(0.0, 1.0);
|
||||
reverbWidthSlider.setRange(0.0, 1.0);
|
||||
reverbFreezeModeSlider.setRange(0.0, 1.0);
|
||||
}*/
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthAudioProcessorEditor::paint (juce::Graphics& g)
|
||||
{
|
||||
@@ -83,19 +353,52 @@ void NeuralSynthAudioProcessorEditor::paint (juce::Graphics& g)
|
||||
//g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthAudioProcessorEditor::resized()
|
||||
{
|
||||
// This is generally where you'll want to lay out the positions of any
|
||||
// subcomponents in your editor..
|
||||
auto bounds = getLocalBounds().reduced(20);
|
||||
auto row = bounds.removeFromTop(150);
|
||||
//auto row = bounds.removeFromTop(150);
|
||||
|
||||
int knobWidth = row.getWidth() / 4;
|
||||
//int knobWidth = row.getWidth() / 4;
|
||||
|
||||
attackSlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));
|
||||
juce::Grid grid;
|
||||
grid.templateRows = { juce::Grid::TrackInfo(juce::Grid::Fr(20)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(40)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(40)) };
|
||||
|
||||
grid.templateColumns = {
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(22)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(22)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(22)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(22)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(8))
|
||||
};
|
||||
|
||||
grid.items = {
|
||||
juce::GridItem(mainScopeComponent).withArea({}, juce::GridItem::Span(4)),
|
||||
juce::GridItem(waveformSelector),
|
||||
juce::GridItem(*adsrComponent),
|
||||
juce::GridItem(*chorusComponent),
|
||||
juce::GridItem(*delayComponent),
|
||||
juce::GridItem(*reverbComponent),
|
||||
juce::GridItem(masterLevelSlider).withArea(juce::GridItem::Span(2), {}),
|
||||
juce::GridItem(*eqComponent),
|
||||
juce::GridItem(*flangerComponent),
|
||||
juce::GridItem(*distortionComponent),
|
||||
juce::GridItem(*filterComponent),
|
||||
};
|
||||
|
||||
|
||||
grid.performLayout(bounds);
|
||||
|
||||
/*attackSlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));
|
||||
decaySlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));
|
||||
sustainSlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));
|
||||
releaseSlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));
|
||||
releaseSlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));*/
|
||||
|
||||
//waveformSelector.setBounds(20, 20, 120, 30);
|
||||
|
||||
|
||||
waveformSelector.setBounds(20, 20, 120, 30);
|
||||
}
|
||||
|
||||
@@ -10,8 +10,227 @@
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "PluginProcessor.h"
|
||||
#include "GraphComponent.h"
|
||||
#include "ScopeComponent.h"
|
||||
|
||||
|
||||
class ScopeSliderComponent : public juce::Component {
|
||||
static const int fontSize = 11;
|
||||
|
||||
public:
|
||||
ScopeSliderComponent(juce::AudioProcessorValueTreeState& tree, const std::string paramGroup) {
|
||||
const auto& sliderDetails = PARAM_SETTINGS.at(paramGroup);
|
||||
|
||||
for (const auto& [name, sliderDetail] : sliderDetails) {
|
||||
sliders.push_back(std::make_unique<juce::Slider>());
|
||||
labels.push_back(std::make_unique<juce::Label>());
|
||||
attachments.push_back(std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(
|
||||
tree, paramGroup + "_" + name, *sliders.back()));
|
||||
|
||||
labels.back()->setText(sliderDetail.label, juce::NotificationType::dontSendNotification);
|
||||
sliders.back()->setRange(sliderDetail.min, sliderDetail.max);
|
||||
}
|
||||
|
||||
for (auto& slider : sliders)
|
||||
{
|
||||
slider->setSliderStyle(juce::Slider::Rotary);
|
||||
slider->setTextBoxStyle(juce::Slider::TextBoxBelow, false, 50, 20);
|
||||
addAndMakeVisible(*slider);
|
||||
}
|
||||
|
||||
for (auto& label : labels)
|
||||
{
|
||||
label->setFont(juce::Font((float)fontSize, juce::Font::bold));
|
||||
label->setColour(juce::Label::textColourId, juce::Colours::lightgreen);
|
||||
label->setJustificationType(juce::Justification::centred);
|
||||
//label->setBoundsToFit()
|
||||
addAndMakeVisible(*label);
|
||||
}
|
||||
}
|
||||
|
||||
void enableSampleScope(AudioBufferQueue<float>& audioBufferQueue) {
|
||||
scope.emplace(audioBufferQueue);
|
||||
useGraphScope = false;
|
||||
addAndMakeVisible(*scope);
|
||||
|
||||
}
|
||||
|
||||
void enableGraphScope(const std::function<float(float)>& func) {
|
||||
graphScope.emplace(0.0f, 1.0f, 100);
|
||||
graphScope->setFunction(func);
|
||||
useGraphScope = true;
|
||||
addAndMakeVisible(*graphScope);
|
||||
}
|
||||
|
||||
private:
|
||||
void paint(juce::Graphics& g) override
|
||||
{
|
||||
//juce::Random rng;
|
||||
//g.fillAll(juce::Colour::fromFloatRGBA(rng.nextFloat(), rng.nextFloat(), rng.nextFloat(), 1.0f));
|
||||
|
||||
g.fillAll(juce::Colours::darkgrey);
|
||||
|
||||
g.setColour(juce::Colours::white);
|
||||
g.drawRect(getLocalBounds());
|
||||
}
|
||||
|
||||
void resized() override {
|
||||
juce::Grid grid;
|
||||
grid.templateRows = { juce::Grid::TrackInfo(juce::Grid::Fr(50)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(30)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(10)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(10))
|
||||
};
|
||||
|
||||
grid.templateColumns.resize(sliders.size());
|
||||
for (int i = 0; i < sliders.size(); i++) {
|
||||
grid.templateColumns.getReference(i) = juce::Grid::TrackInfo(juce::Grid::Fr(1));
|
||||
}
|
||||
|
||||
std::optional<juce::GridItem> scopeGridItem;
|
||||
scopeGridItem.emplace(useGraphScope ? juce::GridItem(*graphScope).withArea({}, juce::GridItem::Span(sliders.size()))
|
||||
: juce::GridItem(*scope).withArea({}, juce::GridItem::Span(sliders.size())));
|
||||
|
||||
grid.items.resize(1 + 2 * sliders.size());
|
||||
|
||||
grid.items.getReference(0) = *scopeGridItem;
|
||||
|
||||
for (int i = 0; i < sliders.size(); i++) {
|
||||
grid.items.getReference(i + 1) = juce::GridItem(*sliders[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < sliders.size(); i++) {
|
||||
grid.items.getReference(i + sliders.size() + 1) = juce::GridItem(*labels[i]);
|
||||
};
|
||||
|
||||
auto bounds = getLocalBounds().reduced(10);
|
||||
grid.performLayout(bounds);
|
||||
}
|
||||
|
||||
bool useGraphScope{ false };
|
||||
std::optional<ScopeComponent<float> > scope;
|
||||
std::optional<GraphComponent<float> > graphScope;
|
||||
|
||||
std::vector<std::unique_ptr<juce::Slider> > sliders;
|
||||
std::vector<std::unique_ptr<juce::Label> > labels;
|
||||
std::vector<std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> > attachments;
|
||||
};
|
||||
|
||||
|
||||
class EqualizerComponent : public juce::Component {
|
||||
static const int fontSize = 11;
|
||||
|
||||
public:
|
||||
EqualizerComponent(juce::AudioProcessorValueTreeState& tree) {
|
||||
setupSlider(lowGainSlider);
|
||||
setupSlider(midGainSlider);
|
||||
setupSlider(highGainSlider);
|
||||
|
||||
setupLabel(lowGainLabel, "L");
|
||||
setupLabel(midGainLabel, "M");
|
||||
setupLabel(highGainLabel, "H");
|
||||
|
||||
lowGainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "lowEQ", lowGainSlider);
|
||||
midGainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "midEQ", midGainSlider);
|
||||
highGainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "highEQ", highGainSlider);
|
||||
}
|
||||
|
||||
void setupSlider(juce::Slider& slider) {
|
||||
slider.setRange(-24.0f, 24.0f, 0.1f);
|
||||
|
||||
slider.setSliderStyle(juce::Slider::LinearBarVertical);
|
||||
slider.setTextBoxStyle(juce::Slider::TextBoxBelow, false, 50, 20);
|
||||
|
||||
addAndMakeVisible(slider);
|
||||
}
|
||||
|
||||
void setupLabel(juce::Label &lbl, juce::String txt) {
|
||||
lbl.setFont(juce::Font((float)fontSize, juce::Font::bold));
|
||||
lbl.setColour(juce::Label::textColourId, juce::Colours::lightgreen);
|
||||
lbl.setJustificationType(juce::Justification::centred);
|
||||
lbl.setText(txt, juce::NotificationType::dontSendNotification);
|
||||
|
||||
addAndMakeVisible(lbl);
|
||||
}
|
||||
|
||||
private:
|
||||
void paint(juce::Graphics& g) override
|
||||
{
|
||||
g.fillAll(juce::Colours::darkgrey);
|
||||
g.setColour(juce::Colours::white);
|
||||
g.drawRect(getLocalBounds());
|
||||
}
|
||||
|
||||
void resized() override {
|
||||
juce::Grid grid;
|
||||
grid.templateRows = {
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1))
|
||||
};
|
||||
|
||||
grid.templateColumns = {
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1)),
|
||||
juce::Grid::TrackInfo(juce::Grid::Fr(1))
|
||||
};
|
||||
|
||||
grid.items = {
|
||||
lowGainSlider, midGainSlider, highGainSlider,
|
||||
lowGainLabel, midGainLabel, highGainLabel
|
||||
};
|
||||
|
||||
auto bounds = getLocalBounds().reduced(10);
|
||||
grid.performLayout(bounds);
|
||||
}
|
||||
|
||||
juce::Slider lowGainSlider, midGainSlider, highGainSlider;
|
||||
juce::Label lowGainLabel, midGainLabel, highGainLabel;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> lowGainAttachment, midGainAttachment, highGainAttachment;
|
||||
};
|
||||
|
||||
struct WaveformSelectorContents final : public juce::ListBoxModel
|
||||
{
|
||||
// The following methods implement the necessary virtual functions from ListBoxModel,
|
||||
// telling the listbox how many rows there are, painting them, etc.
|
||||
int getNumRows() override
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void paintListBoxItem(int rowNumber, juce::Graphics& g,
|
||||
int width, int height, bool rowIsSelected) override
|
||||
{
|
||||
if (rowIsSelected)
|
||||
g.fillAll(juce::Colours::lightblue);
|
||||
|
||||
g.setColour(juce::LookAndFeel::getDefaultLookAndFeel().findColour(juce::Label::textColourId));
|
||||
g.setFont((float)height * 0.7f);
|
||||
|
||||
g.drawText(waves[rowNumber], 5, 0, width, height, juce::Justification::centredLeft, true);
|
||||
}
|
||||
|
||||
std::vector<juce::String> waves = { "Sine", "Saw", "Square", "Triangle" };
|
||||
};
|
||||
|
||||
class MasterVolumeComponent : public juce::Component
|
||||
{
|
||||
public:
|
||||
MasterVolumeComponent()
|
||||
{
|
||||
slider.setSliderStyle(juce::Slider::LinearBarVertical);
|
||||
slider.setTextBoxStyle(juce::Slider::NoTextBox, false, 20, 20); // Optional
|
||||
addAndMakeVisible(slider);
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto padded = getLocalBounds().reduced(30); // Adjust padding here
|
||||
slider.setBounds(padded);
|
||||
}
|
||||
|
||||
juce::Slider slider;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
*/
|
||||
@@ -21,6 +240,7 @@ public:
|
||||
NeuralSynthAudioProcessorEditor (NeuralSynthAudioProcessor&);
|
||||
~NeuralSynthAudioProcessorEditor() override;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
void paint (juce::Graphics&) override;
|
||||
void resized() override;
|
||||
@@ -32,17 +252,117 @@ private:
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralSynthAudioProcessorEditor)
|
||||
|
||||
juce::ComboBox waveformSelector;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment> waveformAttachment;
|
||||
juce::ListBox waveformSelector;
|
||||
WaveformSelectorContents waveformContents;
|
||||
|
||||
//std::unique_ptr<juce::AudioProcessorValueTreeState::ListBoxAttachment> waveformAttachment;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// ADSR
|
||||
|
||||
/*void createADSR(int xCoord, int yCoord);
|
||||
juce::Slider attackSlider, decaySlider, sustainSlider, releaseSlider;
|
||||
juce::Label attackLabel, decayLabel, sustainLabel, releaseLabel;
|
||||
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> attackAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> decayAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> sustainAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> releaseAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> releaseAttachment;*/
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
||||
std::optional<ScopeSliderComponent> adsrComponent;
|
||||
std::optional<ScopeSliderComponent> chorusComponent;
|
||||
std::optional<ScopeSliderComponent> delayComponent;
|
||||
std::optional<ScopeSliderComponent> reverbComponent;
|
||||
|
||||
|
||||
std::optional<ScopeSliderComponent> flangerComponent;
|
||||
std::optional<ScopeSliderComponent> distortionComponent;
|
||||
std::optional<ScopeSliderComponent> filterComponent;
|
||||
|
||||
|
||||
|
||||
|
||||
/*//==============================================================================
|
||||
// Chorus
|
||||
|
||||
void createChorus(int xCoord, int yCoord);
|
||||
juce::Slider chorusRateSlider, chorusDepthSlider, chorusCentreSlider, chorusFeedbackSlider, chorusMixSlider;
|
||||
juce::Label chorusRateLabel, chorusDepthLabel, chorusCentreLabel, chorusFeedbackLabel, chorusMixLabel;
|
||||
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> chorusRateAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> chorusDepthAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> chorusCentreAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> chorusFeedbackAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> chorusMixAttachment;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//==============================================================================
|
||||
// Delay
|
||||
|
||||
void createDelay(int xCoord, int yCoord);
|
||||
juce::Slider delayDelaySlider;
|
||||
juce::Label delayDelayLabel;
|
||||
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> delayDelayAttachment;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// Reverb
|
||||
|
||||
void createReverb(int xCoord, int yCoord);
|
||||
juce::Slider reverbRoomSizeSlider, reverbDampingSlider, reverbWetLevelSlider, reverbDryLevelSlider, reverbWidthSlider,
|
||||
reverbFreezeModeSlider;
|
||||
juce::Label reverbRoomSizeLabel, reverbDampingLabel, reverbWetLevelLabel, reverbDryLevelLabel, reverbWidthLabel,
|
||||
reverbFreezeModeLabel;
|
||||
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> reverbRoomSizeAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> reverbDampingAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> reverbWetLevelAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> reverbDryLevelAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> reverbWidthAttachment;
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> reverbFreezeModeAttachment;*/
|
||||
|
||||
//==============================================================================
|
||||
// Master
|
||||
|
||||
MasterVolumeComponent masterLevelSlider;
|
||||
juce::Label masterLevelLabel;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//==============================================================================
|
||||
// EQ
|
||||
|
||||
//void updateEQFromSliders();
|
||||
std::optional<EqualizerComponent> eqComponent;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//==============================================================================
|
||||
// Master
|
||||
|
||||
juce::Slider gainSlider;
|
||||
juce::Label gainLabel;
|
||||
|
||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> gainAttachment;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
//juce::MidiKeyboardState midiKeyboardState;
|
||||
//juce::MidiKeyboardComponent midiKeyboardComponent{ midiKeyboardState, juce::MidiKeyboardComponent::horizontalKeyboard };
|
||||
ScopeComponent<float> mainScopeComponent;
|
||||
/*ScopeComponent<float> chorusScopeComponent;
|
||||
ScopeComponent<float> delayScopeComponent;
|
||||
ScopeComponent<float> reverbScopeComponent;
|
||||
|
||||
GraphComponent<float> adsrGraph;*/
|
||||
|
||||
juce::MidiKeyboardState midiKeyboardState;
|
||||
juce::MidiKeyboardComponent midiKeyboardComponent{ midiKeyboardState, juce::MidiKeyboardComponent::horizontalKeyboard };
|
||||
ScopeComponent<float> scopeComponent;
|
||||
};
|
||||
|
||||
@@ -15,16 +15,105 @@ NeuralSynthAudioProcessor::NeuralSynthAudioProcessor() : parameters(*this, nullp
|
||||
, audioEngine(sp)
|
||||
{
|
||||
parameters.addParameterListener("waveform", this);
|
||||
//sp.waveform = parameters.getRawParameterValue("waveform");
|
||||
|
||||
parameters.addParameterListener("attack", this);
|
||||
parameters.addParameterListener("decay", this);
|
||||
parameters.addParameterListener("sustain", this);
|
||||
parameters.addParameterListener("release", this);
|
||||
// === Chorus ===
|
||||
parameters.addParameterListener("chorus_rate", this);
|
||||
parameters.addParameterListener("chorus_depth", this);
|
||||
parameters.addParameterListener("chorus_centre", this);
|
||||
parameters.addParameterListener("chorus_feedback", this);
|
||||
parameters.addParameterListener("chorus_mix", this);
|
||||
|
||||
sp.attack = parameters.getRawParameterValue("attack");
|
||||
sp.decay = parameters.getRawParameterValue("decay");
|
||||
sp.sustain = parameters.getRawParameterValue("sustain");
|
||||
sp.release = parameters.getRawParameterValue("release");
|
||||
sp.chorusRate = parameters.getRawParameterValue("chorus_rate");
|
||||
sp.chorusDepth = parameters.getRawParameterValue("chorus_depth");
|
||||
sp.chorusCentre = parameters.getRawParameterValue("chorus_centre");
|
||||
sp.chorusFeedback = parameters.getRawParameterValue("chorus_feedback");
|
||||
sp.chorusMix = parameters.getRawParameterValue("chorus_mix");
|
||||
|
||||
// === Delay ===
|
||||
parameters.addParameterListener("delay_delay", this);
|
||||
sp.delayTime = parameters.getRawParameterValue("delay_delay");
|
||||
|
||||
// === Reverb ===
|
||||
parameters.addParameterListener("reverb_roomSize", this);
|
||||
parameters.addParameterListener("reverb_damping", this);
|
||||
parameters.addParameterListener("reverb_wetLevel", this);
|
||||
parameters.addParameterListener("reverb_dryLevel", this);
|
||||
parameters.addParameterListener("reverb_width", this);
|
||||
parameters.addParameterListener("reverb_freezeMode", this);
|
||||
|
||||
sp.reverbRoomSize = parameters.getRawParameterValue("reverb_roomSize");
|
||||
sp.reverbDamping = parameters.getRawParameterValue("reverb_damping");
|
||||
sp.reverbWetLevel = parameters.getRawParameterValue("reverb_wetLevel");
|
||||
sp.reverbDryLevel = parameters.getRawParameterValue("reverb_dryLevel");
|
||||
sp.reverbWidth = parameters.getRawParameterValue("reverb_width");
|
||||
sp.reverbFreezeMode = parameters.getRawParameterValue("reverb_freezeMode");
|
||||
|
||||
// === ADSR ===
|
||||
parameters.addParameterListener("adsr_attack", this);
|
||||
parameters.addParameterListener("adsr_decay", this);
|
||||
parameters.addParameterListener("adsr_sustain", this);
|
||||
parameters.addParameterListener("adsr_release", this);
|
||||
|
||||
sp.adsrAttack = parameters.getRawParameterValue("adsr_attack");
|
||||
sp.adsrDecay = parameters.getRawParameterValue("adsr_decay");
|
||||
sp.adsrSustain = parameters.getRawParameterValue("adsr_sustain");
|
||||
sp.adsrRelease = parameters.getRawParameterValue("adsr_release");
|
||||
|
||||
// === Flanger ===
|
||||
parameters.addParameterListener("flanger_rate", this);
|
||||
parameters.addParameterListener("flanger_depth", this);
|
||||
parameters.addParameterListener("flanger_feedback", this);
|
||||
parameters.addParameterListener("flanger_dryMix", this);
|
||||
parameters.addParameterListener("flanger_phase", this);
|
||||
parameters.addParameterListener("flanger_delay", this);
|
||||
|
||||
sp.flangerRate = parameters.getRawParameterValue("flanger_rate");
|
||||
sp.flangerDepth = parameters.getRawParameterValue("flanger_depth");
|
||||
sp.flangerFeedback = parameters.getRawParameterValue("flanger_feedback");
|
||||
sp.flangerDryMix = parameters.getRawParameterValue("flanger_dryMix");
|
||||
sp.flangerPhase = parameters.getRawParameterValue("flanger_phase");
|
||||
sp.flangerDelay = parameters.getRawParameterValue("flanger_delay");
|
||||
|
||||
// === Filter ===
|
||||
parameters.addParameterListener("filter_cutoff", this);
|
||||
parameters.addParameterListener("filter_resonance", this);
|
||||
parameters.addParameterListener("filter_type", this);
|
||||
parameters.addParameterListener("filter_drive", this);
|
||||
parameters.addParameterListener("filter_mod", this);
|
||||
parameters.addParameterListener("filter_key", this);
|
||||
|
||||
sp.filterCutoff = parameters.getRawParameterValue("filter_cutoff");
|
||||
sp.filterResonance = parameters.getRawParameterValue("filter_resonance");
|
||||
sp.filterType = parameters.getRawParameterValue("filter_type");
|
||||
sp.filterDrive = parameters.getRawParameterValue("filter_drive");
|
||||
sp.filterMod = parameters.getRawParameterValue("filter_mod");
|
||||
sp.filterKey = parameters.getRawParameterValue("filter_key");
|
||||
|
||||
// === Distortion ===
|
||||
parameters.addParameterListener("distortion_drive", this);
|
||||
parameters.addParameterListener("distortion_mix", this);
|
||||
parameters.addParameterListener("distortion_bias", this);
|
||||
parameters.addParameterListener("distortion_tone", this);
|
||||
parameters.addParameterListener("distortion_shape", this);
|
||||
|
||||
sp.distortionDrive = parameters.getRawParameterValue("distortion_drive");
|
||||
sp.distortionMix = parameters.getRawParameterValue("distortion_mix");
|
||||
sp.distortionBias = parameters.getRawParameterValue("distortion_bias");
|
||||
sp.distortionTone = parameters.getRawParameterValue("distortion_tone");
|
||||
sp.distortionShape = parameters.getRawParameterValue("distortion_shape");
|
||||
|
||||
|
||||
|
||||
parameters.addParameterListener("master", this);
|
||||
parameters.addParameterListener("lowEQ", this);
|
||||
parameters.addParameterListener("midEQ", this);
|
||||
parameters.addParameterListener("highEQ", this);
|
||||
|
||||
sp.masterDbls = parameters.getRawParameterValue("master");
|
||||
sp.lowGainDbls = parameters.getRawParameterValue("lowEQ");
|
||||
sp.midGainDbls = parameters.getRawParameterValue("midEQ");
|
||||
sp.highGainDbls = parameters.getRawParameterValue("highEQ");
|
||||
}
|
||||
|
||||
NeuralSynthAudioProcessor::~NeuralSynthAudioProcessor()
|
||||
@@ -179,6 +268,16 @@ juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
return new NeuralSynthAudioProcessor();
|
||||
}
|
||||
|
||||
void NeuralSynthAudioProcessor::buildParams(std::vector<std::unique_ptr<juce::RangedAudioParameter>> ¶ms, const std::string& paramGroup) {
|
||||
const auto& paramGroupSettings = PARAM_SETTINGS.at(paramGroup);
|
||||
|
||||
for (const auto& [name, paramSettings] : paramGroupSettings) {
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>(paramGroup + "_" + name, paramSettings.label,
|
||||
juce::NormalisableRange<float>(paramSettings.min, paramSettings.max, paramSettings.interval),
|
||||
paramSettings.defValue));
|
||||
}
|
||||
}
|
||||
|
||||
juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::createParameterLayout()
|
||||
{
|
||||
std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;
|
||||
@@ -187,15 +286,23 @@ juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::c
|
||||
"waveform", "Waveform",
|
||||
juce::StringArray{ "Sine", "Saw", "Square", "Triangle" }, 0));
|
||||
|
||||
// Start/end/interval
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("attack", "Attack",
|
||||
juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.1f));
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("decay", "Decay",
|
||||
juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f));
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("sustain", "Sustain",
|
||||
juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.8f));
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("release", "Release",
|
||||
juce::NormalisableRange<float>(0.01f, 1.0f, 0.01f), 1.0f));
|
||||
buildParams(params, "adsr");
|
||||
buildParams(params, "chorus");
|
||||
buildParams(params, "delay");
|
||||
buildParams(params, "reverb");
|
||||
buildParams(params, "flanger");
|
||||
buildParams(params, "distortion");
|
||||
buildParams(params, "filter");
|
||||
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("master", "Master",
|
||||
juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 0.1f));
|
||||
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("lowEQ", "Low Gain",
|
||||
juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 0.5f));
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("midEQ", "Mid EQ",
|
||||
juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 0.8f));
|
||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("highEQ", "High EQ",
|
||||
juce::NormalisableRange<float>(-24.0f, 24.0f, 0.1f), 1.0f));
|
||||
|
||||
return { params.begin(), params.end() };
|
||||
}
|
||||
@@ -28,6 +28,7 @@ public:
|
||||
//==============================================================================
|
||||
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
||||
void releaseResources() override;
|
||||
bool isBusesLayoutSupported(const BusesLayout& layouts) const;
|
||||
|
||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||
bool isBusesLayoutSupported(const BusesLayout& layouts) const override;
|
||||
@@ -61,6 +62,8 @@ public:
|
||||
//==============================================================================
|
||||
void parameterChanged(const juce::String& id, float newValue) override;
|
||||
|
||||
void buildParams(std::vector<std::unique_ptr<juce::RangedAudioParameter>>& params, const std::string& paramGroup);
|
||||
|
||||
juce::MidiMessageCollector& getMidiMessageCollector() noexcept { return midiMessageCollector; }
|
||||
|
||||
juce::MidiMessageCollector midiMessageCollector;
|
||||
@@ -68,12 +71,30 @@ public:
|
||||
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
||||
|
||||
AudioBufferQueue<float>& getAudioBufferQueue() noexcept { return audioBufferQueue; }
|
||||
|
||||
AudioBufferQueue<float>& getChorusAudioBufferQueue() noexcept { return chorusBufferQueue; }
|
||||
AudioBufferQueue<float>& getDelayAudioBufferQueue() noexcept { return delayBufferQueue; }
|
||||
AudioBufferQueue<float>& getReverbAudioBufferQueue() noexcept { return reverbBufferQueue; }
|
||||
|
||||
AudioBufferQueue<float>& getFlangerAudioBufferQueue() noexcept { return flangerBufferQueue; }
|
||||
AudioBufferQueue<float>& getDistortionAudioBufferQueue() noexcept { return distortionBufferQueue; }
|
||||
AudioBufferQueue<float>& getFilterAudioBufferQueue() noexcept { return filterBufferQueue; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NeuralSynthAudioProcessor)
|
||||
|
||||
NeuralAudioEngine audioEngine;
|
||||
AudioBufferQueue<float> audioBufferQueue;
|
||||
|
||||
AudioBufferQueue<float> chorusBufferQueue;
|
||||
AudioBufferQueue<float> delayBufferQueue;
|
||||
AudioBufferQueue<float> reverbBufferQueue;
|
||||
|
||||
AudioBufferQueue<float> flangerBufferQueue;
|
||||
AudioBufferQueue<float> distortionBufferQueue;
|
||||
AudioBufferQueue<float> filterBufferQueue;
|
||||
|
||||
ScopeDataCollector<float> scopeDataCollector{ audioBufferQueue };
|
||||
|
||||
NeuralSharedParams sp;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "SynthVoice.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
//==============================================================================
|
||||
NeuralSynthVoice::NeuralSynthVoice(NeuralSharedParams& sp) : shared(sp) {}
|
||||
|
||||
@@ -10,47 +12,15 @@ void NeuralSynthVoice::prepare(const juce::dsp::ProcessSpec& spec)
|
||||
tempBlock = juce::dsp::AudioBlock<float>(heapBlock, spec.numChannels, spec.maximumBlockSize);
|
||||
processorChain.prepare(spec);
|
||||
adsr.setSampleRate(spec.sampleRate);
|
||||
|
||||
this->spec = spec;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::noteStarted()
|
||||
{
|
||||
auto velocity = getCurrentlyPlayingNote().noteOnVelocity.asUnsignedFloat();
|
||||
auto freqHz = (float)getCurrentlyPlayingNote().getFrequencyInHertz();
|
||||
|
||||
processorChain.get<synthIndex>().setFrequency(freqHz, true);
|
||||
|
||||
juce::ADSR::Parameters p;
|
||||
p.attack = shared.attack->load();
|
||||
p.decay = shared.decay->load();
|
||||
p.sustain = shared.sustain->load();
|
||||
p.release = shared.release->load();
|
||||
|
||||
adsr.setParameters(p);
|
||||
adsr.noteOn();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::notePitchbendChanged()
|
||||
{
|
||||
auto freqHz = (float)getCurrentlyPlayingNote().getFrequencyInHertz();
|
||||
processorChain.get<synthIndex>().setFrequency(freqHz, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::noteStopped(bool allowTailOff)
|
||||
{
|
||||
adsr.noteOff(); //Triggers release phase
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::notePressureChanged() {}
|
||||
void NeuralSynthVoice::noteTimbreChanged() {}
|
||||
void NeuralSynthVoice::noteKeyStateChanged() {}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
|
||||
{
|
||||
if (numSamples <= 0) return;
|
||||
|
||||
if (!adsr.isActive())
|
||||
clearCurrentNote();
|
||||
|
||||
@@ -59,10 +29,124 @@ void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer<float>& outputBuffer, i
|
||||
waveform = -1;
|
||||
}
|
||||
|
||||
const int numChannels = outputBuffer.getNumChannels();
|
||||
|
||||
auto block = tempBlock.getSubBlock(0, (size_t)numSamples);
|
||||
block.clear();
|
||||
juce::dsp::ProcessContextReplacing<float> context(block);
|
||||
processorChain.process(context);
|
||||
|
||||
// =====================================================================
|
||||
// Oscillator
|
||||
// =====================================================================
|
||||
auto& osc = processorChain.get<oscIndex>();
|
||||
juce::dsp::ProcessContextReplacing<float> oscContext(block);
|
||||
osc.process(oscContext);
|
||||
|
||||
// =====================================================================
|
||||
// Distortion
|
||||
// =====================================================================
|
||||
const float driveDb = shared.distortionDrive->load(); // 0..30
|
||||
//const float distMix = juce::jlimit(0.0f, 1.0f, shared.distortionMix->load());
|
||||
const float bias = juce::jlimit(-1.0f, 1.0f, shared.distortionBias->load());
|
||||
const float toneHz = juce::jlimit(100.0f, 8000.0f, shared.distortionTone->load());
|
||||
const int shape = (int)std::lround(juce::jlimit(0.0f, 2.0f, shared.distortionShape->load()));
|
||||
|
||||
auto& distDry = processorChain.get<distortionPreGain>();
|
||||
|
||||
auto& distWaveshaper = processorChain.template get<distortionIndex>();
|
||||
|
||||
if (shape == 0) {
|
||||
distWaveshaper.functionToUse = [bias](float x) noexcept {
|
||||
return std::tanh(x + bias);
|
||||
};
|
||||
}
|
||||
else if (shape == 1) {
|
||||
distWaveshaper.functionToUse = [bias](float x) noexcept {
|
||||
const float v = x + bias;
|
||||
return juce::jlimit(-1.0f, 1.0f, v);
|
||||
};
|
||||
}
|
||||
else if (shape == 2) {
|
||||
distWaveshaper.functionToUse = [bias](float x) noexcept {
|
||||
const float v = x + bias;
|
||||
return (float)(std::atan(v) * (2.0 / juce::MathConstants<double>::pi));
|
||||
};
|
||||
}
|
||||
auto& distPreGain = processorChain.template get<distortionPreGain>(); // [5]
|
||||
distPreGain.setGainDecibels(driveDb); // [6]
|
||||
|
||||
auto& distPostLPF = processorChain.template get<distortionPostLPF>();
|
||||
distPostLPF.coefficients = *juce::dsp::IIR::Coefficients<float>::makePeakFilter(
|
||||
spec.sampleRate,
|
||||
toneHz, // cutoff
|
||||
0.707f, // Q
|
||||
juce::Decibels::decibelsToGain(shared.highGainDbls->load())
|
||||
);
|
||||
|
||||
// =====================================================================
|
||||
// Flanger
|
||||
// =====================================================================
|
||||
// Get pointer to writable data
|
||||
auto flanger = processorChain.get<flangerIndex>();
|
||||
auto rate = shared.flangerPhase->load();
|
||||
auto lfoPhase = shared.flangerPhase->load();
|
||||
auto flangerDepth = shared.flangerDepth->load();
|
||||
auto mix = shared.flangerDryMix->load();
|
||||
auto feedback = shared.flangerFeedback->load();
|
||||
|
||||
// Step 2: Apply flanger sample-by-sample to the block
|
||||
auto* raw = block.getChannelPointer(0);
|
||||
|
||||
for (int i = 0; i < numSamples; ++i)
|
||||
{
|
||||
float in = raw[i];
|
||||
|
||||
float lfo = std::sin(lfoPhase);
|
||||
float delayTime = (1.0f + lfo) * 0.5f * flangerDepth * spec.sampleRate;
|
||||
|
||||
flanger.setDelay(delayTime);
|
||||
|
||||
float delayed = flanger.popSample(0);
|
||||
flanger.pushSample(0, in + delayed * feedback);
|
||||
|
||||
raw[i] = in * (1.0f - mix) + delayed * mix;
|
||||
|
||||
lfoPhase += juce::MathConstants<float>::twoPi * rate / spec.sampleRate;
|
||||
if (lfoPhase > juce::MathConstants<float>::twoPi)
|
||||
lfoPhase -= juce::MathConstants<float>::twoPi;
|
||||
}
|
||||
|
||||
// Step 3: Run through ProcessorChain (filter + distortion)
|
||||
juce::dsp::ProcessContextReplacing<float> fxContext(block);
|
||||
processorChain.process(fxContext);
|
||||
|
||||
auto& master = processorChain.get<masterIndex>();
|
||||
const auto ex = shared.masterDbls->load();
|
||||
master.setGainDecibels(shared.masterDbls->load());
|
||||
|
||||
auto& lowEQ = processorChain.get<eqLowIndex>();
|
||||
lowEQ.coefficients = juce::dsp::IIR::Coefficients<float>::makeLowShelf(
|
||||
spec.sampleRate,
|
||||
100.0f, // cutoff
|
||||
0.707f, // Q, not used by all filters
|
||||
juce::Decibels::decibelsToGain(shared.lowGainDbls->load())
|
||||
);
|
||||
|
||||
auto& midEQ = processorChain.get<eqMidIndex>();
|
||||
midEQ.coefficients = *juce::dsp::IIR::Coefficients<float>::makePeakFilter(
|
||||
spec.sampleRate,
|
||||
1000.0f, // center frequency
|
||||
1.0f, // Q
|
||||
juce::Decibels::decibelsToGain(shared.midGainDbls->load())
|
||||
);
|
||||
|
||||
// HIGH SHELF
|
||||
auto& highEQ = processorChain.get<eqHighIndex>();
|
||||
highEQ.coefficients = *juce::dsp::IIR::Coefficients<float>::makePeakFilter(
|
||||
spec.sampleRate,
|
||||
10000.0f, // cutoff
|
||||
0.707f, // Q
|
||||
juce::Decibels::decibelsToGain(shared.highGainDbls->load())
|
||||
);
|
||||
|
||||
// 3. Apply ADSR envelope to tempBlock
|
||||
std::vector<float*> channelPtrs;
|
||||
@@ -80,9 +164,65 @@ void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer<float>& outputBuffer, i
|
||||
.add(tempBlock);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::noteStarted()
|
||||
{
|
||||
auto velocity = getCurrentlyPlayingNote().noteOnVelocity.asUnsignedFloat();
|
||||
auto freqHz = (float)getCurrentlyPlayingNote().getFrequencyInHertz();
|
||||
|
||||
processorChain.get<oscIndex>().setFrequency(freqHz, true);
|
||||
|
||||
auto& chorus = processorChain.get<chorusIndex>();
|
||||
chorus.setCentreDelay(shared.chorusCentre->load());
|
||||
chorus.setDepth(shared.chorusDepth->load());
|
||||
chorus.setFeedback(shared.chorusFeedback->load());
|
||||
chorus.setMix(shared.chorusMix->load());
|
||||
chorus.setRate(shared.chorusRate->load());
|
||||
|
||||
processorChain.get<delayIndex>().setDelay(shared.delayTime->load());
|
||||
|
||||
juce::Reverb::Parameters rp;
|
||||
|
||||
rp.damping = shared.reverbDamping->load();
|
||||
rp.dryLevel = shared.reverbDryLevel->load();
|
||||
rp.freezeMode = shared.reverbFreezeMode->load();
|
||||
rp.roomSize = shared.reverbRoomSize->load();
|
||||
rp.wetLevel = shared.reverbWetLevel->load();
|
||||
rp.width = shared.reverbWidth->load();
|
||||
processorChain.get<reverbIndex>().setParameters(rp);
|
||||
|
||||
juce::ADSR::Parameters p;
|
||||
p.attack = shared.adsrAttack->load();
|
||||
p.decay = shared.adsrDecay->load();
|
||||
p.sustain = shared.adsrSustain->load();
|
||||
p.release = shared.adsrRelease->load();
|
||||
|
||||
adsr.setParameters(p);
|
||||
adsr.noteOn();
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::notePitchbendChanged()
|
||||
{
|
||||
auto freqHz = (float)getCurrentlyPlayingNote().getFrequencyInHertz();
|
||||
processorChain.get<oscIndex>().setFrequency(freqHz, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::noteStopped(bool allowTailOff)
|
||||
{
|
||||
adsr.noteOff(); //Triggers release phase
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void NeuralSynthVoice::notePressureChanged() {}
|
||||
void NeuralSynthVoice::noteTimbreChanged() {}
|
||||
void NeuralSynthVoice::noteKeyStateChanged() {}
|
||||
|
||||
void NeuralSynthVoice::setWaveform(int waveformType)
|
||||
{
|
||||
auto& osc = processorChain.template get<synthIndex>();
|
||||
auto& osc = processorChain.template get<oscIndex>();
|
||||
|
||||
switch (waveformType)
|
||||
{
|
||||
|
||||
@@ -82,13 +82,37 @@ private:
|
||||
|
||||
enum
|
||||
{
|
||||
synthIndex
|
||||
oscIndex,
|
||||
distortionPreGain,
|
||||
distortionIndex,
|
||||
distortionPostLPF,
|
||||
flangerIndex,
|
||||
chorusIndex,
|
||||
delayIndex,
|
||||
reverbIndex,
|
||||
eqLowIndex,
|
||||
eqMidIndex,
|
||||
eqHighIndex,
|
||||
masterIndex
|
||||
};
|
||||
|
||||
juce::dsp::ProcessorChain<
|
||||
juce::dsp::Oscillator<float>
|
||||
juce::dsp::Oscillator<float>,
|
||||
juce::dsp::Gain<float>,
|
||||
juce::dsp::WaveShaper<float, std::function<float(float)>>,
|
||||
juce::dsp::IIR::Filter<float>,
|
||||
juce::dsp::DelayLine<float, juce::dsp::DelayLineInterpolationTypes::Linear>,
|
||||
juce::dsp::Chorus<float>,
|
||||
juce::dsp::DelayLine<float>,
|
||||
juce::dsp::Reverb,
|
||||
juce::dsp::IIR::Filter<float>, // Low shelf
|
||||
juce::dsp::IIR::Filter<float>, // Mid peak
|
||||
juce::dsp::IIR::Filter<float>, // High shelf
|
||||
juce::dsp::Gain<float>
|
||||
> processorChain;
|
||||
|
||||
juce::dsp::ProcessSpec spec;
|
||||
|
||||
juce::ADSR adsr;
|
||||
NeuralSharedParams& shared;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user