More advanced version
This commit is contained in:
@@ -42,8 +42,8 @@
|
|||||||
namespace ProjectInfo
|
namespace ProjectInfo
|
||||||
{
|
{
|
||||||
const char* const projectName = "NeuralSynth";
|
const char* const projectName = "NeuralSynth";
|
||||||
const char* const companyName = "";
|
const char* const companyName = "Samedi Dimanche";
|
||||||
const char* const versionString = "1.0.0";
|
const char* const versionString = "0.0.1";
|
||||||
const int versionNumber = 0x10000;
|
const int versionNumber = 0x1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,10 +47,10 @@
|
|||||||
#define JucePlugin_Desc "NeuralSynth"
|
#define JucePlugin_Desc "NeuralSynth"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_Manufacturer
|
#ifndef JucePlugin_Manufacturer
|
||||||
#define JucePlugin_Manufacturer "yourcompany"
|
#define JucePlugin_Manufacturer "Samedi Dimanche"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_ManufacturerWebsite
|
#ifndef JucePlugin_ManufacturerWebsite
|
||||||
#define JucePlugin_ManufacturerWebsite "www.yourcompany.com"
|
#define JucePlugin_ManufacturerWebsite "www.samedidimanche.com"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_ManufacturerEmail
|
#ifndef JucePlugin_ManufacturerEmail
|
||||||
#define JucePlugin_ManufacturerEmail ""
|
#define JucePlugin_ManufacturerEmail ""
|
||||||
@@ -62,10 +62,10 @@
|
|||||||
#define JucePlugin_PluginCode 0x4d73347a
|
#define JucePlugin_PluginCode 0x4d73347a
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_IsSynth
|
#ifndef JucePlugin_IsSynth
|
||||||
#define JucePlugin_IsSynth 0
|
#define JucePlugin_IsSynth 1
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_WantsMidiInput
|
#ifndef JucePlugin_WantsMidiInput
|
||||||
#define JucePlugin_WantsMidiInput 0
|
#define JucePlugin_WantsMidiInput 1
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_ProducesMidiOutput
|
#ifndef JucePlugin_ProducesMidiOutput
|
||||||
#define JucePlugin_ProducesMidiOutput 0
|
#define JucePlugin_ProducesMidiOutput 0
|
||||||
@@ -77,25 +77,25 @@
|
|||||||
#define JucePlugin_EditorRequiresKeyboardFocus 0
|
#define JucePlugin_EditorRequiresKeyboardFocus 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_Version
|
#ifndef JucePlugin_Version
|
||||||
#define JucePlugin_Version 1.0.0
|
#define JucePlugin_Version 0.0.1
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_VersionCode
|
#ifndef JucePlugin_VersionCode
|
||||||
#define JucePlugin_VersionCode 0x10000
|
#define JucePlugin_VersionCode 0x1
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_VersionString
|
#ifndef JucePlugin_VersionString
|
||||||
#define JucePlugin_VersionString "1.0.0"
|
#define JucePlugin_VersionString "0.0.1"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_VSTUniqueID
|
#ifndef JucePlugin_VSTUniqueID
|
||||||
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
|
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_VSTCategory
|
#ifndef JucePlugin_VSTCategory
|
||||||
#define JucePlugin_VSTCategory kPlugCategEffect
|
#define JucePlugin_VSTCategory kPlugCategSynth
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_Vst3Category
|
#ifndef JucePlugin_Vst3Category
|
||||||
#define JucePlugin_Vst3Category "Fx"
|
#define JucePlugin_Vst3Category "Instrument|Synth"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_AUMainType
|
#ifndef JucePlugin_AUMainType
|
||||||
#define JucePlugin_AUMainType 'aufx'
|
#define JucePlugin_AUMainType 'aumu'
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_AUSubType
|
#ifndef JucePlugin_AUSubType
|
||||||
#define JucePlugin_AUSubType JucePlugin_PluginCode
|
#define JucePlugin_AUSubType JucePlugin_PluginCode
|
||||||
@@ -110,10 +110,10 @@
|
|||||||
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode
|
#define JucePlugin_AUManufacturerCode JucePlugin_ManufacturerCode
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_CFBundleIdentifier
|
#ifndef JucePlugin_CFBundleIdentifier
|
||||||
#define JucePlugin_CFBundleIdentifier com.yourcompany.NeuralSynth
|
#define JucePlugin_CFBundleIdentifier com.samedidimanche.NeuralSynth
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_AAXIdentifier
|
#ifndef JucePlugin_AAXIdentifier
|
||||||
#define JucePlugin_AAXIdentifier com.yourcompany.NeuralSynth
|
#define JucePlugin_AAXIdentifier com.SamediDimanche.NeuralSynth
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_AAXManufacturerCode
|
#ifndef JucePlugin_AAXManufacturerCode
|
||||||
#define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode
|
#define JucePlugin_AAXManufacturerCode JucePlugin_ManufacturerCode
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
#define JucePlugin_AAXProductId JucePlugin_PluginCode
|
#define JucePlugin_AAXProductId JucePlugin_PluginCode
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_AAXCategory
|
#ifndef JucePlugin_AAXCategory
|
||||||
#define JucePlugin_AAXCategory 0
|
#define JucePlugin_AAXCategory 2048
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_AAXDisableBypass
|
#ifndef JucePlugin_AAXDisableBypass
|
||||||
#define JucePlugin_AAXDisableBypass 0
|
#define JucePlugin_AAXDisableBypass 0
|
||||||
@@ -131,16 +131,16 @@
|
|||||||
#define JucePlugin_AAXDisableMultiMono 0
|
#define JucePlugin_AAXDisableMultiMono 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_IAAType
|
#ifndef JucePlugin_IAAType
|
||||||
#define JucePlugin_IAAType 0x61757278
|
#define JucePlugin_IAAType 0x61757269
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_IAASubType
|
#ifndef JucePlugin_IAASubType
|
||||||
#define JucePlugin_IAASubType JucePlugin_PluginCode
|
#define JucePlugin_IAASubType JucePlugin_PluginCode
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_IAAName
|
#ifndef JucePlugin_IAAName
|
||||||
#define JucePlugin_IAAName "yourcompany: NeuralSynth"
|
#define JucePlugin_IAAName "Samedi Dimanche: NeuralSynth"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_VSTNumMidiInputs
|
#ifndef JucePlugin_VSTNumMidiInputs
|
||||||
#define JucePlugin_VSTNumMidiInputs 16
|
#define JucePlugin_VSTNumMidiInputs 1
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_VSTNumMidiOutputs
|
#ifndef JucePlugin_VSTNumMidiOutputs
|
||||||
#define JucePlugin_VSTNumMidiOutputs 16
|
#define JucePlugin_VSTNumMidiOutputs 16
|
||||||
@@ -152,11 +152,20 @@
|
|||||||
#define JucePlugin_ARATransformationFlags 0
|
#define JucePlugin_ARATransformationFlags 0
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_ARAFactoryID
|
#ifndef JucePlugin_ARAFactoryID
|
||||||
#define JucePlugin_ARAFactoryID "com.yourcompany.NeuralSynth.factory"
|
#define JucePlugin_ARAFactoryID "com.SamediDimanche.NeuralSynth.factory"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_ARADocumentArchiveID
|
#ifndef JucePlugin_ARADocumentArchiveID
|
||||||
#define JucePlugin_ARADocumentArchiveID "com.yourcompany.NeuralSynth.aradocumentarchive.1.0.0"
|
#define JucePlugin_ARADocumentArchiveID "com.SamediDimanche.NeuralSynth.aradocumentarchive.0.0.1"
|
||||||
#endif
|
#endif
|
||||||
#ifndef JucePlugin_ARACompatibleArchiveIDs
|
#ifndef JucePlugin_ARACompatibleArchiveIDs
|
||||||
#define JucePlugin_ARACompatibleArchiveIDs ""
|
#define JucePlugin_ARACompatibleArchiveIDs ""
|
||||||
#endif
|
#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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<JUCERPROJECT id="ms4Z8E" name="NeuralSynth" projectType="audioplug" useAppConfig="0"
|
<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">
|
<MAINGROUP id="UQstsW" name="NeuralSynth">
|
||||||
<GROUP id="{D5B48DA9-9A47-914A-8C72-EE5E8DD868A3}" name="Source">
|
<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 id="CjJ141" name="NeuralSharedParams.h" compile="0" resource="0"
|
||||||
file="Source/NeuralSharedParams.h"/>
|
file="Source/NeuralSharedParams.h"/>
|
||||||
<FILE id="ZrdRl1" name="AudioBufferQueue.h" compile="0" resource="0"
|
<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>
|
#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
|
struct NeuralSharedParams
|
||||||
{
|
{
|
||||||
std::atomic<int> waveform{ -1 };
|
std::atomic<int> waveform{ -1 };
|
||||||
|
|
||||||
std::atomic<float>* attack;
|
std::atomic<float>* adsrAttack;
|
||||||
std::atomic<float>* decay;
|
std::atomic<float>* adsrDecay;
|
||||||
std::atomic<float>* sustain;
|
std::atomic<float>* adsrSustain;
|
||||||
std::atomic<float>* release;
|
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)
|
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& tree = audioProcessor.parameters;
|
||||||
|
|
||||||
auto area = getLocalBounds();
|
//auto area = getLocalBounds();
|
||||||
scopeComponent.setTopLeftPosition(0, 0);
|
//mainScopeComponent.setBounds(5, 5, 800, 200);
|
||||||
scopeComponent.setSize(400, 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);
|
attackAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "attack", attackSlider);
|
||||||
decayAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "decay", decaySlider);
|
decayAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "decay", decaySlider);
|
||||||
sustainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "sustain", sustainSlider);
|
sustainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "sustain", sustainSlider);
|
||||||
releaseAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(tree, "release", releaseSlider);
|
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;
|
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->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);
|
addAndMakeVisible(*slider);
|
||||||
slider->setTopLeftPosition(leftPosition, 250);
|
leftPosition += sliderWidth;
|
||||||
leftPosition += (sliderWidth + 40);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
waveformSelector.addItem("Sine", 1);
|
leftPosition = xCoord + 3; // (sliderWidth / 2);
|
||||||
waveformSelector.addItem("Saw", 2);
|
for (auto* label : { &delayDelayLabel })
|
||||||
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()
|
|
||||||
{
|
{
|
||||||
|
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)
|
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);
|
//g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
void NeuralSynthAudioProcessorEditor::resized()
|
void NeuralSynthAudioProcessorEditor::resized()
|
||||||
{
|
{
|
||||||
// This is generally where you'll want to lay out the positions of any
|
// This is generally where you'll want to lay out the positions of any
|
||||||
// subcomponents in your editor..
|
// subcomponents in your editor..
|
||||||
auto bounds = getLocalBounds().reduced(20);
|
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));
|
decaySlider.setBounds(row.removeFromLeft(knobWidth).reduced(10));
|
||||||
sustainSlider.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 <JuceHeader.h>
|
||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
|
#include "GraphComponent.h"
|
||||||
#include "ScopeComponent.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 (NeuralSynthAudioProcessor&);
|
||||||
~NeuralSynthAudioProcessorEditor() override;
|
~NeuralSynthAudioProcessorEditor() override;
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void paint (juce::Graphics&) override;
|
void paint (juce::Graphics&) override;
|
||||||
void resized() override;
|
void resized() override;
|
||||||
@@ -32,17 +252,117 @@ private:
|
|||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralSynthAudioProcessorEditor)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NeuralSynthAudioProcessorEditor)
|
||||||
|
|
||||||
juce::ComboBox waveformSelector;
|
juce::ListBox waveformSelector;
|
||||||
std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment> waveformAttachment;
|
WaveformSelectorContents waveformContents;
|
||||||
|
|
||||||
|
//std::unique_ptr<juce::AudioProcessorValueTreeState::ListBoxAttachment> waveformAttachment;
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// ADSR
|
||||||
|
|
||||||
|
/*void createADSR(int xCoord, int yCoord);
|
||||||
juce::Slider attackSlider, decaySlider, sustainSlider, releaseSlider;
|
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> attackAttachment;
|
||||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> decayAttachment;
|
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> decayAttachment;
|
||||||
std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> sustainAttachment;
|
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)
|
, audioEngine(sp)
|
||||||
{
|
{
|
||||||
parameters.addParameterListener("waveform", this);
|
parameters.addParameterListener("waveform", this);
|
||||||
|
//sp.waveform = parameters.getRawParameterValue("waveform");
|
||||||
|
|
||||||
parameters.addParameterListener("attack", this);
|
// === Chorus ===
|
||||||
parameters.addParameterListener("decay", this);
|
parameters.addParameterListener("chorus_rate", this);
|
||||||
parameters.addParameterListener("sustain", this);
|
parameters.addParameterListener("chorus_depth", this);
|
||||||
parameters.addParameterListener("release", this);
|
parameters.addParameterListener("chorus_centre", this);
|
||||||
|
parameters.addParameterListener("chorus_feedback", this);
|
||||||
|
parameters.addParameterListener("chorus_mix", this);
|
||||||
|
|
||||||
sp.attack = parameters.getRawParameterValue("attack");
|
sp.chorusRate = parameters.getRawParameterValue("chorus_rate");
|
||||||
sp.decay = parameters.getRawParameterValue("decay");
|
sp.chorusDepth = parameters.getRawParameterValue("chorus_depth");
|
||||||
sp.sustain = parameters.getRawParameterValue("sustain");
|
sp.chorusCentre = parameters.getRawParameterValue("chorus_centre");
|
||||||
sp.release = parameters.getRawParameterValue("release");
|
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()
|
NeuralSynthAudioProcessor::~NeuralSynthAudioProcessor()
|
||||||
@@ -179,6 +268,16 @@ juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
|||||||
return new NeuralSynthAudioProcessor();
|
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()
|
juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::createParameterLayout()
|
||||||
{
|
{
|
||||||
std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;
|
std::vector<std::unique_ptr<juce::RangedAudioParameter>> params;
|
||||||
@@ -187,15 +286,23 @@ juce::AudioProcessorValueTreeState::ParameterLayout NeuralSynthAudioProcessor::c
|
|||||||
"waveform", "Waveform",
|
"waveform", "Waveform",
|
||||||
juce::StringArray{ "Sine", "Saw", "Square", "Triangle" }, 0));
|
juce::StringArray{ "Sine", "Saw", "Square", "Triangle" }, 0));
|
||||||
|
|
||||||
// Start/end/interval
|
buildParams(params, "adsr");
|
||||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("attack", "Attack",
|
buildParams(params, "chorus");
|
||||||
juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.1f));
|
buildParams(params, "delay");
|
||||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("decay", "Decay",
|
buildParams(params, "reverb");
|
||||||
juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.5f));
|
buildParams(params, "flanger");
|
||||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("sustain", "Sustain",
|
buildParams(params, "distortion");
|
||||||
juce::NormalisableRange<float>(0.0f, 1.0f, 0.01f), 0.8f));
|
buildParams(params, "filter");
|
||||||
params.push_back(std::make_unique<juce::AudioParameterFloat>("release", "Release",
|
|
||||||
juce::NormalisableRange<float>(0.01f, 1.0f, 0.01f), 1.0f));
|
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() };
|
return { params.begin(), params.end() };
|
||||||
}
|
}
|
||||||
@@ -28,6 +28,7 @@ public:
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
||||||
void releaseResources() override;
|
void releaseResources() override;
|
||||||
|
bool isBusesLayoutSupported(const BusesLayout& layouts) const;
|
||||||
|
|
||||||
#ifndef JucePlugin_PreferredChannelConfigurations
|
#ifndef JucePlugin_PreferredChannelConfigurations
|
||||||
bool isBusesLayoutSupported(const BusesLayout& layouts) const override;
|
bool isBusesLayoutSupported(const BusesLayout& layouts) const override;
|
||||||
@@ -61,6 +62,8 @@ public:
|
|||||||
//==============================================================================
|
//==============================================================================
|
||||||
void parameterChanged(const juce::String& id, float newValue) override;
|
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& getMidiMessageCollector() noexcept { return midiMessageCollector; }
|
||||||
|
|
||||||
juce::MidiMessageCollector midiMessageCollector;
|
juce::MidiMessageCollector midiMessageCollector;
|
||||||
@@ -68,12 +71,30 @@ public:
|
|||||||
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
||||||
|
|
||||||
AudioBufferQueue<float>& getAudioBufferQueue() noexcept { return audioBufferQueue; }
|
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:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NeuralSynthAudioProcessor)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(NeuralSynthAudioProcessor)
|
||||||
|
|
||||||
NeuralAudioEngine audioEngine;
|
NeuralAudioEngine audioEngine;
|
||||||
AudioBufferQueue<float> audioBufferQueue;
|
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 };
|
ScopeDataCollector<float> scopeDataCollector{ audioBufferQueue };
|
||||||
|
|
||||||
NeuralSharedParams sp;
|
NeuralSharedParams sp;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "SynthVoice.h"
|
#include "SynthVoice.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
NeuralSynthVoice::NeuralSynthVoice(NeuralSharedParams& sp) : shared(sp) {}
|
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);
|
tempBlock = juce::dsp::AudioBlock<float>(heapBlock, spec.numChannels, spec.maximumBlockSize);
|
||||||
processorChain.prepare(spec);
|
processorChain.prepare(spec);
|
||||||
adsr.setSampleRate(spec.sampleRate);
|
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)
|
void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
|
||||||
{
|
{
|
||||||
|
if (numSamples <= 0) return;
|
||||||
|
|
||||||
if (!adsr.isActive())
|
if (!adsr.isActive())
|
||||||
clearCurrentNote();
|
clearCurrentNote();
|
||||||
|
|
||||||
@@ -59,10 +29,124 @@ void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer<float>& outputBuffer, i
|
|||||||
waveform = -1;
|
waveform = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int numChannels = outputBuffer.getNumChannels();
|
||||||
|
|
||||||
auto block = tempBlock.getSubBlock(0, (size_t)numSamples);
|
auto block = tempBlock.getSubBlock(0, (size_t)numSamples);
|
||||||
block.clear();
|
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
|
// 3. Apply ADSR envelope to tempBlock
|
||||||
std::vector<float*> channelPtrs;
|
std::vector<float*> channelPtrs;
|
||||||
@@ -80,9 +164,65 @@ void NeuralSynthVoice::renderNextBlock(juce::AudioBuffer<float>& outputBuffer, i
|
|||||||
.add(tempBlock);
|
.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)
|
void NeuralSynthVoice::setWaveform(int waveformType)
|
||||||
{
|
{
|
||||||
auto& osc = processorChain.template get<synthIndex>();
|
auto& osc = processorChain.template get<oscIndex>();
|
||||||
|
|
||||||
switch (waveformType)
|
switch (waveformType)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -82,13 +82,37 @@ private:
|
|||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
synthIndex
|
oscIndex,
|
||||||
|
distortionPreGain,
|
||||||
|
distortionIndex,
|
||||||
|
distortionPostLPF,
|
||||||
|
flangerIndex,
|
||||||
|
chorusIndex,
|
||||||
|
delayIndex,
|
||||||
|
reverbIndex,
|
||||||
|
eqLowIndex,
|
||||||
|
eqMidIndex,
|
||||||
|
eqHighIndex,
|
||||||
|
masterIndex
|
||||||
};
|
};
|
||||||
|
|
||||||
juce::dsp::ProcessorChain<
|
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;
|
> processorChain;
|
||||||
|
|
||||||
|
juce::dsp::ProcessSpec spec;
|
||||||
|
|
||||||
juce::ADSR adsr;
|
juce::ADSR adsr;
|
||||||
NeuralSharedParams& shared;
|
NeuralSharedParams& shared;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user