wavetable/plugin/Source/PluginProcessor.cpp

1309 lines
54 KiB
C++
Raw Normal View History

2020-05-20 16:44:46 -07:00
#include "PluginProcessor.h"
#include "PluginEditor.h"
#include "WavetableVoice.h"
2023-08-26 11:17:03 -07:00
static juce::String subTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
2023-08-26 11:17:03 -07:00
switch (int (v))
2020-05-20 16:44:46 -07:00
{
2023-08-26 11:17:03 -07:00
case 0: return "Sine";
case 1: return "Triangle";
case 2: return "Saw";
2023-10-03 08:15:17 -07:00
case 3: return "Pulse 50%";
case 4: return "Pulse 25%";
case 5: return "Pulse 12%";
2020-05-20 16:44:46 -07:00
default:
jassertfalse;
return {};
}
}
2023-08-31 19:19:28 -07:00
static juce::String noiseTextFunction (const gin::Parameter&, float v)
{
switch (int (v))
{
case 0: return "White";
case 1: return "Pink";
default:
jassertfalse;
return {};
}
}
2023-08-23 08:45:23 -07:00
static juce::String lfoTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
switch ((gin::LFO::WaveShape)int (v))
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
case gin::LFO::WaveShape::none: return "None";
case gin::LFO::WaveShape::sine: return "Sine";
case gin::LFO::WaveShape::triangle: return "Triangle";
case gin::LFO::WaveShape::sawUp: return "Saw Up";
case gin::LFO::WaveShape::sawDown: return "Saw Down";
case gin::LFO::WaveShape::square: return "Square";
case gin::LFO::WaveShape::squarePos: return "Square+";
case gin::LFO::WaveShape::sampleAndHold: return "S&H";
case gin::LFO::WaveShape::noise: return "Noise";
case gin::LFO::WaveShape::stepUp3: return "Step Up 3";
case gin::LFO::WaveShape::stepUp4: return "Step Up 4";
case gin::LFO::WaveShape::stepup8: return "Step Up 8";
case gin::LFO::WaveShape::stepDown3: return "Step Down 3";
case gin::LFO::WaveShape::stepDown4: return "Step Down 4";
case gin::LFO::WaveShape::stepDown8: return "Step Down 8";
case gin::LFO::WaveShape::pyramid3: return "Pyramid 3";
case gin::LFO::WaveShape::pyramid5: return "Pyramid 5";
case gin::LFO::WaveShape::pyramid9: return "Pyramid 9";
2020-05-20 16:44:46 -07:00
default:
jassertfalse;
return {};
}
}
2023-08-23 08:45:23 -07:00
static juce::String enableTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
return v > 0.0f ? "On" : "Off";
}
2023-08-23 08:45:23 -07:00
static juce::String durationTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
return gin::NoteDuration::getNoteDurations()[size_t (v)].getName();
2020-05-20 16:44:46 -07:00
}
2023-08-23 08:45:23 -07:00
static juce::String distortionAmountTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
return juce::String (v * 5.0f - 1.0f, 1);
2020-05-20 16:44:46 -07:00
}
2023-08-23 08:45:23 -07:00
static juce::String filterTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
switch (int (v))
{
case 0: return "LP 12";
case 1: return "LP 24";
case 2: return "HP 12";
case 3: return "HP 24";
case 4: return "BP 12";
case 5: return "BP 24";
case 6: return "NT 12";
case 7: return "NT 24";
default:
jassertfalse;
return {};
}
}
2023-08-23 08:45:23 -07:00
static juce::String freqTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
return juce::String (int (gin::getMidiNoteInHertz (v)));
2020-05-20 16:44:46 -07:00
}
2023-08-23 08:45:23 -07:00
static juce::String glideModeTextFunction (const gin::Parameter&, float v)
2020-05-20 16:44:46 -07:00
{
switch (int (v))
{
case 0: return "Off";
case 1: return "Glissando";
case 2: return "Portamento";
default:
jassertfalse;
return {};
}
}
//==============================================================================
void WavetableAudioProcessor::OSCParams::setup (WavetableAudioProcessor& p, int idx)
{
2023-08-23 08:45:23 -07:00
juce::String id = "osc" + juce::String (idx + 1);
juce::String nm = "OSC" + juce::String (idx + 1) + " ";
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, idx == 0 ? 1.0f : 0.0f, 0.0f);
2023-10-03 08:11:07 -07:00
retrig = p.addIntParam (id + "retrig", nm + "Retrig", "Retrig", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
2020-05-20 16:44:46 -07:00
voices = p.addIntParam (id + "unison", nm + "Unison", "Unison", "", { 1.0, 8.0, 1.0, 1.0 }, 1.0, 0.0f);
tune = p.addExtParam (id + "tune", nm + "Tune", "Tune", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f);
finetune = p.addExtParam (id + "finetune", nm + "Fine Tune", "Fine", "ct", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
level = p.addExtParam (id + "level", nm + "Level", "Level", "db", { -100.0, 0.0, 1.0, 4.0 }, 0.0, 0.0f);
pos = p.addExtParam (id + "pos", nm + "Pos", "Pos", "%", { 0.0, 100.0, 0.0, 1.0 }, 00.0, 0.0f);
2020-05-20 16:44:46 -07:00
detune = p.addExtParam (id + "detune", nm + "Detune", "Detune", "", { 0.0, 0.5, 0.0, 1.0 }, 0.0, 0.0f);
spread = p.addExtParam (id + "spread", nm + "Spread", "Spread", "%", { -100.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
2023-08-31 11:17:49 -07:00
formant = p.addExtParam (id + "formant", nm + "Formant", "Formant", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
bend = p.addExtParam (id + "bend", nm + "Bend", "Bend", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
level->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); };
2020-05-20 16:44:46 -07:00
}
//==============================================================================
void WavetableAudioProcessor::SubParams::setup (WavetableAudioProcessor& p)
{
juce::String id = "sub";
juce::String nm = "SUB ";
2023-10-03 08:11:07 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
retrig = p.addIntParam (id + "retrig", nm + "Retrig", "Retrig", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
2023-10-03 08:15:17 -07:00
wave = p.addIntParam (id + "wave", nm + "Wave", "Wave", "", { 0.0, 5.0, 1.0, 1.0 }, 1.0, 0.0f, subTextFunction);
tune = p.addExtParam (id + "tune", nm + "Tune", "Tune", "st", { -36.0, 36.0, 1.0, 1.0 }, 0.0, 0.0f);
level = p.addExtParam (id + "level", nm + "Level", "Level", "db", { -100.0, 0.0, 1.0, 4.0 }, 0.0, 0.0f);
pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
level->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); };
}
//==============================================================================
void WavetableAudioProcessor::NoiseParams::setup (WavetableAudioProcessor& p)
{
juce::String id = "noise";
juce::String nm = "Noise ";
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f);
2023-08-31 19:19:28 -07:00
type = p.addIntParam (id + "type", nm + "Type", "Type", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, noiseTextFunction);
level = p.addExtParam (id + "level", nm + "Level", "Level", "db", { -100.0, 0.0, 1.0, 4.0 }, 0.0, 0.0f);
pan = p.addExtParam (id + "pan", nm + "Pan", "Pan", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
level->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); };
}
2020-05-20 16:44:46 -07:00
//==============================================================================
2023-08-23 11:21:37 -07:00
void WavetableAudioProcessor::FilterParams::setup (WavetableAudioProcessor& p)
2020-05-20 16:44:46 -07:00
{
2023-08-23 11:21:37 -07:00
juce::String id = "flt";
juce::String nm = "FLT ";
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
float maxFreq = float (gin::getMidiNoteFromHertz (20000.0));
2020-05-20 16:44:46 -07:00
2023-10-07 10:57:13 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 1.0f, 0.0f);
retrig = p.addIntParam (id + "retrig", nm + "Retrig", "Retrig", "", { 0.0, 1.0, 1.0, 1.0 }, 1.0, 0.0f, enableTextFunction);
type = p.addIntParam (id + "type", nm + "Type", "Type", "", { 0.0, 7.0, 1.0, 1.0 }, 0.0, 0.0f, filterTextFunction);
keyTracking = p.addExtParam (id + "key", nm + "Key", "Key", "%", { 0.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
velocityTracking = p.addExtParam (id + "vel", nm + "Vel", "Vel", "%", { 0.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
frequency = p.addExtParam (id + "freq", nm + "Freq", "Freq", "Hz", { 0.0, maxFreq, 0.0, 1.0 }, 64.0, 0.0f, freqTextFunction);
resonance = p.addExtParam (id + "res", nm + "Res", "Res", "", { 0.0, 100.0, 0.0, 1.0 }, 0.0, 0.0f);
amount = p.addExtParam (id + "amount", nm + "Amount", "Amnt", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
decay = p.addExtParam (id + "decay", nm + "Decay", "D", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain = p.addExtParam (id + "sustain", nm + "Sustain", "S", "%", { 0.0, 100.0, 0.0, 1.0 }, 80.0f, 0.0f);
release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
wt1 = p.addIntParam (id + "wt1", nm + "WT1", "", "", { 0.0, 1.0, 1.0, 1.0f }, 1.0f, 0.0f);
wt2 = p.addIntParam (id + "wt2", nm + "WT2", "", "", { 0.0, 1.0, 1.0, 1.0f }, 1.0f, 0.0f);
sub = p.addIntParam (id + "sub", nm + "sub", "", "", { 0.0, 1.0, 1.0, 1.0f }, 1.0f, 0.0f);
noise = p.addIntParam (id + "noise", nm + "noise", "", "", { 0.0, 1.0, 1.0, 1.0f }, 1.0f, 0.0f);
2020-05-20 16:44:46 -07:00
sustain->conversionFunction = [] (float in) { return in / 100.0f; };
velocityTracking->conversionFunction = [] (float in) { return in / 100.0f; };
keyTracking->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::EnvParams::setup (WavetableAudioProcessor& p, int idx)
{
2023-08-23 08:45:23 -07:00
juce::String id = "env" + juce::String (idx + 1);
juce::String nm = "ENV" + juce::String (idx + 1) + " ";
2020-05-20 16:44:46 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
2023-10-07 10:57:13 -07:00
retrig = p.addIntParam (id + "retrig", nm + "Retrig", "Retrig", "", { 0.0, 1.0, 1.0, 1.0 }, 1.0, 0.0f, enableTextFunction);
2020-05-20 16:44:46 -07:00
attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
decay = p.addExtParam (id + "decay", nm + "Decay", "D", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain = p.addExtParam (id + "sustain", nm + "Sustain", "S", "%", { 0.0, 100.0, 0.0, 1.0 }, 80.0f, 0.0f);
release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::LFOParams::setup (WavetableAudioProcessor& p, int idx)
{
2023-08-23 08:45:23 -07:00
juce::String id = "lfo" + juce::String (idx + 1);
juce::String nm = "LFO" + juce::String (idx + 1) + " ";
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
auto notes = gin::NoteDuration::getNoteDurations();
2020-05-20 16:44:46 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
sync = p.addIntParam (id + "sync", nm + "Sync", "Sync", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
2023-09-24 17:49:53 -07:00
retrig = p.addIntParam (id + "retrig", nm + "Retrig", "Retrig", "", { 0.0, 1.0, 1.0, 1.0 }, 1.0, 0.0f, enableTextFunction);
2020-05-20 16:44:46 -07:00
wave = p.addIntParam (id + "wave", nm + "Wave", "Wave", "", { 1.0, 17.0, 1.0, 1.0 }, 1.0, 0.0f, lfoTextFunction);
rate = p.addExtParam (id + "rate", nm + "Rate", "Rate", "Hz", { 0.0, 50.0, 0.0, 0.3f }, 10.0, 0.0f);
beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 13.0, 0.0f, durationTextFunction);
depth = p.addExtParam (id + "depth", nm + "Depth", "Depth", "", { -1.0, 1.0, 0.0, 1.0 }, 1.0, 0.0f);
phase = p.addExtParam (id + "phase", nm + "Phase", "Phase", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
offset = p.addExtParam (id + "offset", nm + "Offset", "Offset", "", { -1.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f);
2023-09-02 07:34:08 -07:00
fade = p.addExtParam (id + "fade", nm + "Fade", "Fade", "s", { -60.0, 60.0, 0.0, 0.2f, true }, 0.0f, 0.0f);
delay = p.addExtParam (id + "delay", nm + "Delay", "Delay", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.0f, 0.0f);
2024-02-14 19:34:53 -08:00
xgrid = p.addIntParam (id + "xgrid", nm + "XGrid", "XGrid", "", { 2.0, 32.0, 1.0, 1.0 }, 8.0, 0.0f);
ygrid = p.addIntParam (id + "ygrid", nm + "YGrid", "YGrid", "", { 2.0, 32.0, 1.0, 1.0 }, 2.0, 0.0f);
2020-05-20 16:44:46 -07:00
}
//==============================================================================
void WavetableAudioProcessor::StepLFOParams::setup (WavetableAudioProcessor& p)
{
2023-08-23 08:45:23 -07:00
juce::String id = "slfo";
juce::String nm = "Step LFO";
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
auto notes = gin::NoteDuration::getNoteDurations();
2020-05-20 16:44:46 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 13.0, 0.0f, durationTextFunction);
2023-09-28 08:16:31 -07:00
length = p.addIntParam (id + "length", nm + "Length", "Length", "", { 2.0, 32.0, 1.0, 1.0f }, 8.0f, 0.0f);
retrig = p.addIntParam (id + "retrig", nm + "Retrig", "Retrig", "", { 0.0, 1.0, 1.0, 1.0 }, 1.0, 0.0f, enableTextFunction);
2020-05-20 16:44:46 -07:00
2023-08-26 19:04:45 -07:00
for (int i = 0; i < Cfg::numStepLFOSteps; i++)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
auto num = juce::String (i + 1);
2023-08-26 19:04:45 -07:00
level[i] = p.addIntParam (id + "step" + num, nm + "Step " + num, "", "", { -1.0, 1.0, 0.0, 1.0f }, 0.0f, 0.0f);
2020-05-20 16:44:46 -07:00
}
}
//==============================================================================
void WavetableAudioProcessor::GateParams::setup (WavetableAudioProcessor& p)
{
2023-08-23 08:45:23 -07:00
juce::String id = "gate";
juce::String nm = "Gate";
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
auto notes = gin::NoteDuration::getNoteDurations();
2020-05-20 16:44:46 -07:00
enable = p.addIntParam (id + "enable", nm + "Enable", "Enable", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
beat = p.addIntParam (id + "beat", nm + "Beat", "Beat", "", { 0.0, float (notes.size() - 1), 1.0, 1.0 }, 7.0, 0.0f, durationTextFunction);
2023-08-26 19:04:45 -07:00
length = p.addIntParam (id + "length", nm + "Length", "Length", "", { 2.0, Cfg::numGateSteps, 1.0, 1.0f }, 8.0f, 0.0f);
2023-08-23 08:45:23 -07:00
attack = p.addExtParam (id + "attack", nm + "Attack", "A", "s", { 0.0, 1.0, 0.0, 0.2f }, 0.1f, 0.0f);
release = p.addExtParam (id + "release", nm + "Release", "R", "s", { 0.0, 1.0, 0.0, 0.2f }, 0.1f, 0.0f);
2020-05-20 16:44:46 -07:00
2023-08-26 19:04:45 -07:00
for (int i = 0; i < Cfg::numGateSteps; i++)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
auto num = juce::String (i + 1);
2020-05-20 16:44:46 -07:00
l[i] = p.addIntParam (id + "l" + num, nm + "L " + num, "", "", { 0.0, 1.0, 1.0, 1.0f }, (i % 2 == 0 || i % 5 == 0) ? 1.0f : 0.0f, 0.0f);
r[i] = p.addIntParam (id + "r" + num, nm + "R " + num, "", "", { 0.0, 1.0, 1.0, 1.0f }, (i % 2 == 0 || i % 5 == 0) ? 1.0f : 0.0f, 0.0f);
}
}
//==============================================================================
void WavetableAudioProcessor::ADSRParams::setup (WavetableAudioProcessor& p)
{
2023-10-07 10:57:13 -07:00
retrig = p.addIntParam ("retrig", "Retrig", "Retrig","", { 0.0, 1.0, 1.0, 1.0 }, 1.0f, 0.0f, enableTextFunction);
velocityTracking = p.addExtParam ("vel", "Vel", "Vel", "", { 0.0, 100.0, 0.0, 1.0 }, 100.0f, 0.0f);
2020-05-20 16:44:46 -07:00
attack = p.addExtParam ("attack", "Attack", "A", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
decay = p.addExtParam ("decay", "Decay", "D", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain = p.addExtParam ("sustain", "Sustain", "S", "%", { 0.0, 100.0, 0.0, 1.0 }, 80.0f, 0.0f);
release = p.addExtParam ("release", "Release", "R", "s", { 0.0, 60.0, 0.0, 0.2f }, 0.1f, 0.0f);
sustain->conversionFunction = [] (float in) { return in / 100.0f; };
velocityTracking->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::GlobalParams::setup (WavetableAudioProcessor& p)
{
2023-09-29 09:01:29 -07:00
mono = p.addIntParam ("mono", "Mono", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
glideMode = p.addIntParam ("gMode", "Glide Mode", "Glide", "", { 0.0, 2.0, 0.0, 1.0 }, 0.0f, 0.0f, glideModeTextFunction);
glideRate = p.addExtParam ("gRate", "Glide Time", "Time", "s", { 0.001f, 20.0, 0.0, 0.2f }, 0.3f, 0.0f);
legato = p.addIntParam ("legato", "Legato", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0, 0.0f, enableTextFunction);
level = p.addExtParam ("level", "Level", "", "db", { -100.0, 0.0, 1.0, 4.0f }, 0.0, 0.0f);
voices = p.addIntParam ("voices", "Voices", "", "", { 2.0, 40.0, 1.0, 1.0 }, 40.0f, 0.0f);
mpe = p.addIntParam ("mpe", "MPE", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
pitchBend = p.addIntParam ("pitchbend", "Pitch Bend", "PB Range", "", { 0.0, 48.0, 1.0, 1.0 }, 2.0f, 0.0f);
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
level->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); };
2023-10-20 18:49:05 -07:00
if (auto props = p.getSettings())
mpe->setUserValue (props->getBoolValue ("mpe", false) ? 1.0f : 0.0f);
2020-05-20 16:44:46 -07:00
}
2023-08-26 20:57:45 -07:00
//==============================================================================
void WavetableAudioProcessor::UIParams::setup (WavetableAudioProcessor& p)
{
2023-08-26 21:59:44 -07:00
activeLFO = p.addIntParam ("uiLFO", "LFO", "", "", { 0.0, 2.0, 0.0, 1.0 }, 0.0, 0.0f);
activeENV = p.addIntParam ("uiENV", "ENV", "", "", { 0.0, 2.0, 0.0, 1.0 }, 0.0f, 0.0f);
2023-09-08 17:11:50 -07:00
activeMOD = p.addIntParam ("uiMOD", "MOD", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.0f, 0.0f);
2023-08-26 20:57:45 -07:00
}
2020-05-20 16:44:46 -07:00
//==============================================================================
void WavetableAudioProcessor::ChorusParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("chEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
delay = p.addExtParam ("chDelay", "Delay", "", "ms", {0.1f, 30.0f, 0.0f, 1.0f}, 1.0f, 0.0f);
depth = p.addExtParam ("chDepth", "Depth", "", "ms", {0.1f, 20.0f, 0.0f, 1.0f}, 1.0f, 0.0f);
rate = p.addExtParam ("chSpeed", "Speed", "", "Hz", {0.1f, 10.0f, 0.0f, 1.0f}, 3.0f, 0.0f);
width = p.addExtParam ("chWidth", "Width", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.5f, 0.0f);
mix = p.addExtParam ("chMix", "Mix", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.5f, 0.0f);
delay->conversionFunction = [] (float in) { return in / 1000.0f; };
depth->conversionFunction = [] (float in) { return in / 1000.0f; };
}
//==============================================================================
void WavetableAudioProcessor::DistortionParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("dsEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
amount = p.addExtParam ("dsAmount", "Amount", "", "", { 0.0, 1.0, 0.0, 1.0 }, 0.2f, 0.0f, distortionAmountTextFunction);
}
//==============================================================================
void WavetableAudioProcessor::DelayParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("dlEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0f, 0.0f, enableTextFunction);
2023-08-23 08:45:23 -07:00
float mxd = float (gin::NoteDuration::getNoteDurations().size()) - 1.0f;
2020-05-20 16:44:46 -07:00
sync = p.addExtParam ("dlSync", "Sync", "", "", { 0.0f, 1.0f, 1.0f, 1.0f}, 0.0f, 0.0f, enableTextFunction);
time = p.addExtParam ("dlTime", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 0.3f}, 1.0f, 0.0f);
beat = p.addExtParam ("dlBeat", "Delay", "", "", { 0.0f, mxd, 1.0f, 1.0f}, 13.0f, 0.0f, durationTextFunction);
2023-09-29 09:01:29 -07:00
fb = p.addExtParam ("dlFb", "FB", "", "dB", {-100.0f, 0.0f, 0.0f, 5.0f}, -10.0f, 0.0f);
cf = p.addExtParam ("dlCf", "CF", "", "dB", {-100.0f, 0.0f, 0.0f, 5.0f}, -100.0f, 0.0f);
mix = p.addExtParam ("dlMix", "Mix", "", "%", { 0.0f, 100.0f, 0.0f, 1.0f}, 0.5f, 0.0f);
2020-05-20 16:44:46 -07:00
2023-09-29 09:01:29 -07:00
delay = p.addIntParam ("dlDelay", "Delay", "", "", { 0.0f, 120.0f, 0.0f, 1.0f}, 1.0f, 0.0f);
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
fb->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); };
cf->conversionFunction = [] (float in) { return juce::Decibels::decibelsToGain (in); };
2020-05-20 16:44:46 -07:00
mix->conversionFunction = [] (float in) { return in / 100.0f; };
}
//==============================================================================
void WavetableAudioProcessor::ReverbParams::setup (WavetableAudioProcessor& p)
{
enable = p.addIntParam ("rvEnable", "Enable", "", "", { 0.0, 1.0, 1.0, 1.0 }, 0.0, 0.0f);
size = p.addExtParam ("rvbSize", "Size", "", "", {0.0f, 1.0f, 0.0f, 2.0f}, 0.0f, 0.0f);
decay = p.addExtParam ("rvbDecay", "Decay", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
2023-08-25 13:26:25 -07:00
lowpass = p.addExtParam ("rvbLowpass", "Lowpass", "", "", {16.0f, 20000.0f, 0.0f, 0.3f}, 10000.0f, 0.0f);
damping = p.addExtParam ("rvbDamping", "Damping", "", "", {16.0f, 20000.0f, 0.0f, 0.3f}, 10000.0f, 0.0f);
predelay = p.addExtParam ("rvbPredelay", "Predelay", "", "", {0.0f, 0.1f, 0.0f, 1.0f}, 0.0f, 0.0f);
mix = p.addExtParam ("rvbMix", "Mix", "", "", {0.0f, 1.0f, 0.0f, 1.0f}, 0.0f, 0.0f);
2020-05-20 16:44:46 -07:00
}
2023-10-08 14:31:14 -07:00
//==============================================================================
void WavetableAudioProcessor::FXParams::setup (WavetableAudioProcessor& p)
{
2023-10-08 22:44:56 -07:00
fx1 = p.addIntParam ("fxOrder1", "FX1", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxGate, 0.0f);
fx2 = p.addIntParam ("fxOrder2", "FX2", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxChorus, 0.0f);
fx3 = p.addIntParam ("fxOrder3", "FX3", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxDistort, 0.0f);
fx4 = p.addIntParam ("fxOrder4", "FX4", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxDelay, 0.0f);
fx5 = p.addIntParam ("fxOrder5", "FX5", "", "", { 0.0, 4.0, 1.0, 1.0 }, fxReverb, 0.0f);
distMode = p.addIntParam ("distMode", "Dist Mode", "", "", { 0.0, 3.0, 1.0, 1.0 }, 0, 0.0f);
}
//==============================================================================
void WavetableAudioProcessor::BitCrusherParams::setup (WavetableAudioProcessor& p)
{
juce::String id = "crush";
juce::String nm = "Crush ";
rate = p.addExtParam (id + "rate", nm + "Rate", "Rate", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
rez = p.addExtParam (id + "rez", nm + "Rez", "Rez", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
hard = p.addExtParam (id + "hard", nm + "Hard", "Hard", "", { 0.0, 1.0, 0.0, 1.0f }, 0.8f, 0.0f);
mix = p.addExtParam (id + "mix", nm + "Mix", "Mix", "", { 0.0, 1.0, 0.0, 1.0f }, 1.0f, 0.0f);
}
//==============================================================================
void WavetableAudioProcessor::FireAmpParams::setup (WavetableAudioProcessor& p)
{
juce::String id = "fire";
juce::String nm = "Fire ";
gain = p.addExtParam (id + "gain", nm + "Gain", "Gain", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
tone = p.addExtParam (id + "tone", nm + "Tone", "Tone", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
output = p.addExtParam (id + "output", nm + "Output", "Output", "", { 0.0, 1.0, 0.0, 1.0f }, 0.8f, 0.0f);
mix = p.addExtParam (id + "mix", nm + "Mix", "Mix", "", { 0.0, 1.0, 0.0, 1.0f }, 1.0f, 0.0f);
}
//==============================================================================
void WavetableAudioProcessor::GrindAmpParams::setup (WavetableAudioProcessor& p)
{
juce::String id = "grind";
juce::String nm = "Grind ";
gain = p.addExtParam (id + "gain", nm + "Gain", "Gain", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
tone = p.addExtParam (id + "tone", nm + "Tone", "Tone", "", { 0.0, 1.0, 0.0, 1.0f }, 0.5f, 0.0f);
output = p.addExtParam (id + "output", nm + "Output", "Output", "", { 0.0, 1.0, 0.0, 1.0f }, 0.8f, 0.0f);
mix = p.addExtParam (id + "mix", nm + "Mix", "Mix", "", { 0.0, 1.0, 0.0, 1.0f }, 1.0f, 0.0f);
2023-10-08 14:31:14 -07:00
}
2023-09-20 08:57:41 -07:00
#if 0
void convertWavetables()
{
auto src = juce::File (__FILE__).getChildFile ("../../Resources/Wavetables");
if (! src.isDirectory())
return;
auto files = src.findChildFiles (juce::File::findFiles, true, "*.wav");
for (auto srcFile : files)
{
int sz = gin::getWavetableSize (srcFile);
jassert (sz > 0);
auto path = srcFile.withFileExtension (".wt" + juce::String (sz)).getFullPathName().replace ("Wavetables", "WavetablesFLAC");
auto dstFile = juce::File (path);
dstFile.deleteFile();
dstFile.getParentDirectory().createDirectory();
if (auto is = srcFile.createInputStream())
{
if (auto reader = std::unique_ptr<juce::AudioFormatReader> (juce::WavAudioFormat().createReaderFor (is.release(), true)))
{
juce::AudioSampleBuffer buffer (1, int (reader->lengthInSamples));
reader->read (&buffer, 0, int (reader->lengthInSamples), 0, true, false);
if (auto writer = std::unique_ptr<juce::AudioFormatWriter> (juce::FlacAudioFormat().createWriterFor (dstFile.createOutputStream().release(), reader->sampleRate, 1, 16, {}, 8)))
{
writer->writeFromAudioReader (*reader, 0, reader->lengthInSamples);
}
}
}
}
}
2023-09-20 08:57:41 -07:00
void extractWavetables()
{
auto save = [] (const juce::File& dst, const juce::String& name, const juce::MemoryBlock& mb)
{
auto sz = gin::getWavetableSize (mb);
jassert (sz > 0);
if (sz <= 0)
return;
auto f = dst.getChildFile (name + ".wav");
f.replaceWithData (mb.getData(), mb.getSize());
};
auto src = juce::File ("/Users/rrabien/Downloads/HavenSteps");
auto dst = juce::File ("/Users/rrabien/Downloads/HavenSteps_out");
for (auto f : src.findChildFiles (juce::File::findFiles, false, "*.xml"))
{
auto preset = juce::ValueTree::fromXml (f.loadFileAsString());
2023-09-20 08:57:41 -07:00
auto state = preset.getChildWithName ("state");
auto name1 = state.getProperty ("wt1");
auto name2 = state.getProperty ("wt2");
juce::MemoryBlock mb1;
juce::MemoryBlock mb2;
mb1.fromBase64Encoding (state.getProperty ("wt1Data").toString());
mb2.fromBase64Encoding (state.getProperty ("wt1Data").toString());
auto changed = false;
if (mb1.getSize() > 0)
{
save (dst, name1, mb1);
mb1.reset();
state.setProperty ("wt1Data", mb1.toBase64Encoding(), nullptr);
changed = true;
}
if (mb2.getSize() > 0)
{
save (dst, name2, mb2);
mb2.reset();
state.setProperty ("wt2Data", mb2.toBase64Encoding(), nullptr);
changed = true;
}
if (changed)
dst.getChildFile (f.getFileName()).replaceWithText (preset.toXmlString());
else
f.copyFileTo (dst.getChildFile (f.getFileName()));
}
}
#endif
//==============================================================================
namespace
{
juce::File userResourceRoot()
{
#if JUCE_MAC
return juce::File::getSpecialLocation (juce::File::userHomeDirectory)
.getChildFile ("Library/Audio/Presets/SocaLabs/Wavetable");
#elif JUCE_WINDOWS
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("SocaLabs/Wavetable");
#else
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("SocaLabs/Wavetable");
#endif
}
juce::File systemResourceRoot()
{
#if JUCE_MAC
return juce::File ("/Library/Audio/Presets/SocaLabs/Wavetable");
#elif JUCE_WINDOWS
return juce::File::getSpecialLocation (juce::File::commonApplicationDataDirectory)
.getChildFile ("SocaLabs/Wavetable");
#else
return juce::File ("/usr/share/SocaLabs/Wavetable");
#endif
}
// Pre-installer location used when factory presets were embedded and gin's
// default getProgramDirectory() chose a "programs" folder under devId.
juce::File legacyUserProgramDirectory()
{
#if JUCE_MAC
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("Application Support/com.socalabs/Wavetable/programs");
#else
return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory)
.getChildFile ("com.socalabs/Wavetable/programs");
#endif
}
}
2020-05-20 16:44:46 -07:00
//==============================================================================
2026-01-10 13:47:20 -08:00
static gin::ProcessorOptions createProcessorOptions()
{
return gin::ProcessorOptions()
.withMidiLearn();
2026-01-10 13:47:20 -08:00
}
2023-09-28 08:24:34 -07:00
WavetableAudioProcessor::WavetableAudioProcessor()
2026-01-10 13:47:20 -08:00
: gin::Processor (juce::AudioProcessor::BusesProperties().withOutput ("Output", juce::AudioChannelSet::stereo(), true), false, createProcessorOptions()),
2023-10-08 22:44:56 -07:00
bitcrusher (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })),
fireAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })),
grindAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); }))
2020-05-20 16:44:46 -07:00
{
// One-time migration of any user presets from the pre-installer location.
// Factory presets now live in systemResourceRoot()/Presets and are surfaced
// via getFactoryProgramDirectories(). User saves go to userResourceRoot()/Presets.
{
auto oldDir = legacyUserProgramDirectory();
auto newDir = userResourceRoot().getChildFile ("Presets");
if (oldDir.isDirectory()
&& newDir.findChildFiles (juce::File::findFiles, false, "*.xml").isEmpty())
{
if (! newDir.isDirectory())
newDir.createDirectory();
for (auto f : oldDir.findChildFiles (juce::File::findFiles, false, "*.xml"))
f.copyFileTo (newDir.getChildFile (f.getFileName()));
}
2023-09-20 08:57:41 -07:00
}
2020-05-21 21:39:47 -07:00
2024-10-11 21:02:25 -07:00
mtsClient = MTS_RegisterClient();
2020-05-20 16:44:46 -07:00
enableLegacyMode();
setVoiceStealingEnabled (true);
2023-08-25 13:26:25 -07:00
osc1Table = "Analog PWM Square 01";
osc2Table = "Analog PWM Saw 01";
reloadWavetables();
2023-08-25 08:24:15 -07:00
2023-08-23 08:45:23 -07:00
for (int i = 0; i < juce::numElementsInArray (oscParams); i++)
2020-05-20 16:44:46 -07:00
oscParams[i].setup (*this, i);
subParams.setup (*this);
noiseParams.setup (*this);
2023-08-23 11:21:37 -07:00
filterParams.setup (*this);
2020-05-20 16:44:46 -07:00
2023-08-23 08:45:23 -07:00
for (int i = 0; i < juce::numElementsInArray (envParams); i++)
2020-05-20 16:44:46 -07:00
envParams[i].setup (*this, i);
2023-08-23 08:45:23 -07:00
for (int i = 0; i < juce::numElementsInArray (lfoParams); i++)
2020-05-20 16:44:46 -07:00
lfoParams[i].setup (*this, i);
stepLfoParams.setup (*this);
adsrParams.setup (*this);
globalParams.setup (*this);
2023-08-26 20:57:45 -07:00
uiParams.setup (*this);
2020-05-20 16:44:46 -07:00
gateParams.setup (*this);
chorusParams.setup (*this);
distortionParams.setup (*this);
delayParams.setup (*this);
reverbParams.setup (*this);
2023-10-08 14:31:14 -07:00
fxParams.setup (*this);
2020-05-20 16:44:46 -07:00
2023-10-08 22:44:56 -07:00
versionHint++;
bitcrusherParams.setup (*this);
fireAmpParams.setup (*this);
grindAmpParams.setup (*this);
2020-05-20 16:44:46 -07:00
for (int i = 0; i < 50; i++)
{
2023-08-23 11:21:37 -07:00
auto voice = new WavetableVoice (*this);
2020-05-20 16:44:46 -07:00
modMatrix.addVoice (voice);
addVoice (voice);
}
setupModMatrix();
2023-08-31 11:17:49 -07:00
init();
2023-09-28 08:44:58 -07:00
lastMono = globalParams.mono->isOn();
2023-10-20 18:49:05 -07:00
reset();
2020-05-20 16:44:46 -07:00
}
WavetableAudioProcessor::~WavetableAudioProcessor()
{
2024-10-11 21:02:25 -07:00
MTS_DeregisterClient (mtsClient);
mtsClient = nullptr;
2020-05-20 16:44:46 -07:00
}
2023-08-25 13:26:25 -07:00
void WavetableAudioProcessor::reloadWavetables()
{
auto loadMemory = [&] (const juce::String& name) -> juce::MemoryBlock
{
auto file = systemResourceRoot().getChildFile ("Wavetables").getChildFile (name + ".wt2048");
if (file.existsAsFile())
2023-08-25 13:26:25 -07:00
{
juce::MemoryBlock mb;
if (file.loadFileAsData (mb))
return mb;
2023-08-25 13:26:25 -07:00
}
return {};
};
auto shouldLoad = [&] (int osc, const juce::String& name, double sr)
{
auto& v = curTables[osc];
2023-09-21 21:25:40 -07:00
if (v.name == name && juce::exactlyEqual (v.sampleRate, sr))
return false;
v.name = name;
v.sampleRate = sr;
return true;
};
auto sr = gin::Processor::getSampleRate();
if (sr == 0)
return;
2023-08-27 07:57:23 -07:00
if (userTable1.getSize() > 0)
{
if (shouldLoad (0, osc1Table.toString(), sr))
2023-09-29 21:06:19 -07:00
loadWaveTable (osc1Tables, sr, userTable1, "wav", osc1Size);
2023-08-27 07:57:23 -07:00
}
else if (auto mb = loadMemory (osc1Table.toString()); mb.getSize() > 0)
{
if (shouldLoad (0, osc1Table.toString(), sr))
2023-09-29 21:06:19 -07:00
loadWaveTable (osc1Tables, sr, mb, "flac", osc1Size);
2023-08-27 07:57:23 -07:00
}
2023-08-25 13:26:25 -07:00
2023-08-27 07:57:23 -07:00
if (userTable2.getSize() > 0)
{
2023-10-03 08:23:48 -07:00
if (shouldLoad (1, osc2Table.toString(), sr))
2023-09-29 21:06:19 -07:00
loadWaveTable (osc2Tables, sr, userTable2, "wav", osc2Size);
2023-08-27 07:57:23 -07:00
}
2023-08-31 14:45:48 -07:00
else if (auto mb = loadMemory (osc2Table.toString()); mb.getSize() > 0)
2023-08-27 07:57:23 -07:00
{
if (shouldLoad (1, osc2Table.toString(), sr))
2023-09-29 21:06:19 -07:00
loadWaveTable (osc2Tables, sr, mb, "flac", osc2Size);
2023-08-27 07:57:23 -07:00
}
2023-08-25 13:26:25 -07:00
}
void WavetableAudioProcessor::incWavetable (int osc, int delta)
{
auto& table = osc == 0 ? osc1Table : osc2Table;
2023-08-31 14:45:48 -07:00
if (osc == 0)
userTable1.reset();
else
userTable2.reset();
auto tables = getWavetableNames();
2023-08-25 13:26:25 -07:00
auto idx = tables.indexOf (table.toString());
idx += delta;
if (idx < 0) idx = tables.size() - 1;
if (idx >= tables.size()) idx = 0;
table = tables[idx];
reloadWavetables();
}
2023-09-29 21:06:19 -07:00
bool WavetableAudioProcessor::loadUserWavetable (int osc, const juce::File& f, int sz)
2023-08-27 07:57:23 -07:00
{
auto& table = osc == 0 ? osc1Tables : osc2Tables;
auto& mb = osc == 0 ? userTable1 : userTable2;
auto& name = osc == 0 ? osc1Table : osc2Table;
2023-09-29 21:06:19 -07:00
auto& size = osc == 0 ? osc1Size : osc2Size;
2023-08-27 07:57:23 -07:00
juce::MemoryBlock raw;
f.loadFileAsData (raw);
2023-09-29 21:06:19 -07:00
if (loadWaveTable (table, gin::Processor::getSampleRate(), raw, "wav", sz))
2023-08-27 07:57:23 -07:00
{
mb = raw;
name = f.getFileNameWithoutExtension();
2023-09-29 21:06:19 -07:00
size = sz;
return true;
2023-08-27 07:57:23 -07:00
}
2023-09-29 21:06:19 -07:00
return false;
2023-08-27 07:57:23 -07:00
}
2020-05-20 16:44:46 -07:00
//==============================================================================
void WavetableAudioProcessor::stateUpdated()
{
modMatrix.stateUpdated (state);
2023-08-25 13:26:25 -07:00
osc1Table = state.getProperty ("wt1");
osc2Table = state.getProperty ("wt2");
2023-09-29 21:06:19 -07:00
osc1Size = state.getProperty ("wt1Size", -1);
osc2Size = state.getProperty ("wt2Size", -1);
2023-09-09 05:55:48 -07:00
userTable1.reset();
userTable2.reset();
2023-08-27 07:57:23 -07:00
userTable1.fromBase64Encoding (state.getProperty ("wt1Data").toString());
userTable2.fromBase64Encoding (state.getProperty ("wt2Data").toString());
2023-08-25 13:26:25 -07:00
reloadWavetables();
2023-09-23 11:10:21 -07:00
presetLoaded = true;
2023-09-28 08:44:58 -07:00
lastMono = globalParams.mono->isOn();
2020-05-20 16:44:46 -07:00
}
void WavetableAudioProcessor::updateState()
{
modMatrix.updateState (state);
2023-08-25 13:26:25 -07:00
state.setProperty ("wt1", osc1Table, nullptr);
state.setProperty ("wt2", osc2Table, nullptr);
2023-08-27 07:57:23 -07:00
2023-09-29 21:06:19 -07:00
state.setProperty ("wt1Size", osc1Size, nullptr);
state.setProperty ("wt2Size", osc2Size, nullptr);
2023-08-27 07:57:23 -07:00
state.setProperty ("wt1Data", userTable1.toBase64Encoding(), nullptr);
state.setProperty ("wt2Data", userTable2.toBase64Encoding(), nullptr);
2020-05-20 16:44:46 -07:00
}
//==============================================================================
juce::File WavetableAudioProcessor::getProgramDirectory()
{
auto dir = userResourceRoot().getChildFile ("Presets");
if (! dir.isDirectory())
dir.createDirectory();
return dir;
}
juce::Array<juce::File> WavetableAudioProcessor::getFactoryProgramDirectories()
{
return { systemResourceRoot().getChildFile ("Presets") };
}
juce::StringArray WavetableAudioProcessor::getWavetableNames() const
{
juce::StringArray tables;
auto wtDir = systemResourceRoot().getChildFile ("Wavetables");
if (wtDir.isDirectory())
for (auto f : wtDir.findChildFiles (juce::File::findFiles, false, "*.wt2048"))
tables.add (f.getFileNameWithoutExtension());
tables.sortNatural();
return tables;
}
2020-05-20 16:44:46 -07:00
//==============================================================================
void WavetableAudioProcessor::setupModMatrix()
{
modSrcPressure = modMatrix.addPolyModSource ("mpep", "MPE Pressure", false);
modSrcTimbre = modMatrix.addPolyModSource ("mpet", "MPE Timbre", false);
2023-09-29 09:01:29 -07:00
modSrcPitchbend = modMatrix.addPolyModSource ("mpebp", "MPE Pitch Bend", true);
2020-05-20 16:44:46 -07:00
modScrPitchBend = modMatrix.addMonoModSource ("pb", "Pitch Bend", true);
modSrcNote = modMatrix.addPolyModSource ("note", "MIDI Note Number", false);
modSrcVelocity = modMatrix.addPolyModSource ("vel", "MIDI Velocity", false);
for (int i = 0; i < Cfg::numLFOs; i++)
2023-08-23 08:45:23 -07:00
modSrcMonoLFO.add (modMatrix.addMonoModSource (juce::String::formatted ("mlfo%d", i + 1), juce::String::formatted ("LFO %d (Mono)", i + 1), true));
2020-05-20 16:44:46 -07:00
for (int i = 0; i < Cfg::numLFOs; i++)
2023-08-23 08:45:23 -07:00
modSrcLFO.add (modMatrix.addPolyModSource (juce::String::formatted ("lfo%d", i + 1), juce::String::formatted ("LFO %d", i + 1), true));
2020-05-20 16:44:46 -07:00
modSrcMonoStep = modMatrix.addMonoModSource ("mstep", "Step LFO (Mono)", true);
modSrcStep = modMatrix.addPolyModSource ("step", "Step LFO", true);
2023-08-23 11:21:37 -07:00
modSrcFilter = modMatrix.addPolyModSource ("fenv", "Filter Envelope", false);
2020-05-20 16:44:46 -07:00
for (int i = 0; i < Cfg::numENVs; i++)
2023-08-23 08:45:23 -07:00
modSrcEnv.add (modMatrix.addPolyModSource (juce::String::formatted ("env%d", i + 1), juce::String::formatted ("Envelope %d", i + 1), false));
2020-05-20 16:44:46 -07:00
2023-08-31 21:02:23 -07:00
for (int i = 0; i <= 119; i++)
{
juce::String name = juce::MidiMessage::getControllerName (i);
if (name.isEmpty())
modSrcCC.add (modMatrix.addMonoModSource (juce::String::formatted ("cc%d", i), juce::String::formatted ("CC %d", i), false));
else
modSrcCC.add (modMatrix.addMonoModSource (juce::String::formatted ("cc%d", i), juce::String::formatted ("CC %d ", i) + name, false));
}
2020-05-20 16:44:46 -07:00
auto firstMonoParam = globalParams.mono;
bool polyParam = true;
for (auto pp : getPluginParameters())
{
if (pp == firstMonoParam)
polyParam = false;
if (! pp->isInternal() || pp == delayParams.delay)
2023-09-29 20:07:45 -07:00
modMatrix.addParameter (pp, polyParam, getSmoothingTime (pp));
2020-05-20 16:44:46 -07:00
}
modMatrix.build();
}
bool WavetableAudioProcessor::isParamLocked (gin::Parameter* p)
{
if (p == uiParams.activeMOD) return true;
if (p == uiParams.activeLFO) return true;
if (p == uiParams.activeENV) return true;
return false;
}
2023-10-03 08:47:14 -07:00
float WavetableAudioProcessor::getSmoothingTime (gin::Parameter*)
2023-09-29 20:07:45 -07:00
{
return 0.02f;
}
2020-05-20 16:44:46 -07:00
void WavetableAudioProcessor::reset()
{
Processor::reset();
gate.reset();
chorus.reset();
stereoDelay.reset();
reverb.reset();
2023-10-20 18:49:05 -07:00
bitcrusher.reset();
fireAmp.reset();
grindAmp.reset();
2020-05-20 16:44:46 -07:00
for (auto& l : modLFOs)
l.reset();
modStepLFO.reset();
2023-08-25 13:26:25 -07:00
reloadWavetables();
2020-05-20 16:44:46 -07:00
}
void WavetableAudioProcessor::prepareToPlay (double newSampleRate, int newSamplesPerBlock)
{
Processor::prepareToPlay (newSampleRate, newSamplesPerBlock);
setCurrentPlaybackSampleRate (newSampleRate);
modMatrix.setSampleRate (newSampleRate);
gate.setSampleRate (newSampleRate);
chorus.setSampleRate (newSampleRate);
stereoDelay.setSampleRate (newSampleRate);
reverb.setSampleRate (float (newSampleRate));
2020-05-20 16:44:46 -07:00
for (auto& l : modLFOs)
l.setSampleRate (newSampleRate);
modStepLFO.setSampleRate (newSampleRate);
2023-08-25 13:26:25 -07:00
reloadWavetables();
2023-08-26 11:17:03 -07:00
analogTables.setSampleRate (newSampleRate);
2020-05-20 16:44:46 -07:00
}
void WavetableAudioProcessor::releaseResources()
{
}
2023-09-30 07:42:15 -07:00
bool WavetableAudioProcessor::isBusesLayoutSupported (const BusesLayout& layout) const
{
if (layout.inputBuses.size() != 0 || layout.outputBuses.size() != 1)
return false;
return layout.outputBuses[0].size() == 2;
}
2023-08-23 08:45:23 -07:00
void WavetableAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midi)
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
juce::ScopedNoDenormals noDenormals;
if (! dspLock.tryEnter())
2023-09-20 22:08:18 -07:00
{
blockMissed = true;
return;
2023-09-20 22:08:18 -07:00
}
2023-09-30 07:42:15 -07:00
if (buffer.getNumChannels() != 2)
return;
2023-09-20 22:08:18 -07:00
2026-01-10 14:27:58 -08:00
if (midiLearn)
midiLearn->processBlock (midi, buffer.getNumSamples());
2026-01-10 13:47:20 -08:00
2024-10-11 21:02:25 -07:00
if (mtsClient)
for (auto itr : midi)
if (auto m = itr.getMessage(); m.isSysEx())
MTS_ParseMIDIDataU (mtsClient, itr.data, itr.numBytes);
2023-09-28 08:44:58 -07:00
if (blockMissed || presetLoaded || lastMono != globalParams.mono->isOn())
2023-09-20 22:08:18 -07:00
{
2023-09-23 11:10:21 -07:00
blockMissed = presetLoaded = false;
2023-09-29 20:07:45 -07:00
lastMono = globalParams.mono->isOn();
2023-09-29 09:35:19 -07:00
stereoDelay.reset();
reverb.reset();
2023-09-20 22:08:18 -07:00
turnOffAllVoices (false);
}
2020-05-20 16:44:46 -07:00
startBlock();
setMPE (globalParams.mpe->isOn());
2023-09-29 09:01:29 -07:00
setPitchBendRange (globalParams.pitchBend->getUserValueInt());
2020-05-20 16:44:46 -07:00
playhead = getPlayHead();
int pos = 0;
int todo = buffer.getNumSamples();
buffer.clear();
2023-09-28 08:44:58 -07:00
setMono (lastMono);
2020-05-20 16:44:46 -07:00
setLegato (globalParams.legato->isOn());
setGlissando (globalParams.glideMode->getProcValue() == 1.0f);
setPortamento (globalParams.glideMode->getProcValue() == 2.0f);
setGlideRate (globalParams.glideRate->getProcValue());
setNumVoices (int (globalParams.voices->getProcValue()));
while (todo > 0)
{
int thisBlock = std::min (todo, 32);
updateParams (thisBlock);
renderNextBlock (buffer, midi, pos, thisBlock);
2023-08-23 08:45:23 -07:00
auto bufferSlice = gin::sliceBuffer (buffer, pos, thisBlock);
applyEffects (bufferSlice);
2020-05-20 16:44:46 -07:00
modMatrix.finishBlock (thisBlock);
pos += thisBlock;
todo -= thisBlock;
}
2023-10-01 04:50:41 -07:00
playhead = nullptr;
2020-05-20 16:44:46 -07:00
2023-09-30 07:42:15 -07:00
if (buffer.getNumSamples() <= scopeFifo.getFreeSpace() && buffer.getNumChannels() == scopeFifo.getNumChannels())
2023-08-25 13:26:25 -07:00
scopeFifo.write (buffer);
2020-05-20 16:44:46 -07:00
endBlock (buffer.getNumSamples());
2024-09-02 10:10:14 -07:00
dspLock.exit();
2020-05-20 16:44:46 -07:00
}
2023-08-23 11:21:37 -07:00
juce::Array<float> WavetableAudioProcessor::getLiveFilterCutoff()
2020-05-20 16:44:46 -07:00
{
2023-08-23 08:45:23 -07:00
juce::Array<float> values;
2020-05-20 16:44:46 -07:00
for (auto v : voices)
{
if (v->isActive())
{
auto vav = dynamic_cast<WavetableVoice*>(v);
2023-08-23 11:21:37 -07:00
values.add (vav->getFilterCutoffNormalized());
2020-05-20 16:44:46 -07:00
}
}
return values;
}
2023-08-25 08:24:15 -07:00
gin::WTOscillator::Params WavetableAudioProcessor::getLiveWTParams (int osc)
{
for (auto v : voices)
if (v->isActive())
if (auto vav = dynamic_cast<WavetableVoice*>(v))
return vav->getLiveWTParams (osc);
gin::WTOscillator::Params p;
2023-08-31 11:17:49 -07:00
p.position = oscParams[osc].pos->getUserValue() / 100.0f;
p.formant = oscParams[osc].formant->getUserValue();
p.bend = oscParams[osc].bend->getUserValue();
2023-08-25 08:24:15 -07:00
return p;
}
2023-10-08 14:31:14 -07:00
void WavetableAudioProcessor::applyEffect (juce::AudioSampleBuffer& buffer, int fxId)
2020-05-20 16:44:46 -07:00
{
// Apply gate
2023-10-08 14:31:14 -07:00
if (fxId == fxGate && gateParams.enable->isOn())
2020-05-20 16:44:46 -07:00
gate.process (buffer, noteOnIndex, noteOffIndex);
// Apply Chorus
2023-10-08 14:31:14 -07:00
if (fxId == fxChorus && chorusParams.enable->isOn())
2020-05-20 16:44:46 -07:00
chorus.process (buffer);
// Apply Distortion
2023-10-08 14:31:14 -07:00
if (fxId == fxDistort && distortionParams.enable->isOn())
2023-08-26 11:17:03 -07:00
{
2023-10-08 22:44:56 -07:00
auto mode = fxParams.distMode->getUserValueInt();
if (mode == 0)
{
auto clip = 1.0f / (2.0f * distortionVal);
gin::Distortion::processBlock (buffer, distortionVal, -clip, clip);
}
else if (mode == 1)
{
bitcrusher.processReplacing ((float**)buffer.getArrayOfWritePointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
}
else if (mode == 2)
{
fireAmp.processReplacing ((float**)buffer.getArrayOfWritePointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
}
else if (mode == 3)
{
grindAmp.processReplacing ((float**)buffer.getArrayOfWritePointers(), (float**)buffer.getArrayOfWritePointers(), buffer.getNumSamples());
}
2023-08-26 11:17:03 -07:00
}
2020-05-20 16:44:46 -07:00
// Apply Delay
2023-10-08 14:31:14 -07:00
if (fxId == fxDelay && delayParams.enable->isOn())
2023-10-03 08:47:14 -07:00
{
if (delayParams.sync->isOn())
stereoDelay.process (buffer);
else
stereoDelay.processSmoothed (buffer);
}
2020-05-20 16:44:46 -07:00
// Apply Reverb
2023-10-08 14:31:14 -07:00
if (fxId == fxReverb && reverbParams.enable->isOn())
reverb.process (buffer.getWritePointer (0), buffer.getWritePointer (1), buffer.getNumSamples ());
2023-10-08 14:31:14 -07:00
}
void WavetableAudioProcessor::applyEffects (juce::AudioSampleBuffer& buffer)
{
applyEffect (buffer, fxParams.fx1->getUserValueInt());
applyEffect (buffer, fxParams.fx2->getUserValueInt());
applyEffect (buffer, fxParams.fx3->getUserValueInt());
applyEffect (buffer, fxParams.fx4->getUserValueInt());
applyEffect (buffer, fxParams.fx5->getUserValueInt());
2020-05-20 16:44:46 -07:00
// Output gain
outputGain.process (buffer);
}
void WavetableAudioProcessor::updateParams (int newBlockSize)
{
// Update Mono LFOs
for (int i = 0; i < Cfg::numLFOs; i++)
{
if (lfoParams[i].enable->isOn())
{
2023-08-23 08:45:23 -07:00
gin::LFO::Parameters params;
2020-05-20 16:44:46 -07:00
float freq = 0;
if (lfoParams[i].sync->getProcValue() > 0.0f)
2023-08-23 08:45:23 -07:00
freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (lfoParams[i].beat->getProcValue())].toSeconds (playhead);
2020-05-20 16:44:46 -07:00
else
freq = modMatrix.getValue (lfoParams[i].rate);
2023-08-23 08:45:23 -07:00
params.waveShape = (gin::LFO::WaveShape) int (lfoParams[i].wave->getProcValue());
2020-05-20 16:44:46 -07:00
params.frequency = freq;
params.phase = modMatrix.getValue (lfoParams[i].phase);
params.offset = modMatrix.getValue (lfoParams[i].offset);
params.depth = modMatrix.getValue (lfoParams[i].depth);
params.delay = 0;
params.fade = 0;
modLFOs[i].setParameters (params);
modLFOs[i].process (newBlockSize);
modMatrix.setMonoValue (modSrcMonoLFO[i], modLFOs[i].getOutput());
}
else
{
modMatrix.setMonoValue (modSrcMonoLFO[i], 0);
}
}
// Update Mono Step LFO
if (stepLfoParams.enable->isOn())
{
2023-08-23 08:45:23 -07:00
float freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (stepLfoParams.beat->getProcValue())].toSeconds (playhead);
2020-05-20 16:44:46 -07:00
modStepLFO.setFreq (freq);
int n = int (stepLfoParams.length->getProcValue());
modStepLFO.setNumPoints (n);
for (int i = n; --i >= 0;)
modStepLFO.setPoint (i, stepLfoParams.level[i]->getProcValue());
modStepLFO.process (newBlockSize);
modMatrix.setMonoValue (modSrcMonoStep, modStepLFO.getOutput());
}
else
{
modMatrix.setMonoValue (modSrcMonoStep, 0);
}
// Update Gate
if (gateParams.enable->isOn())
{
2023-08-23 08:45:23 -07:00
float freq = 1.0f / gin::NoteDuration::getNoteDurations()[size_t (gateParams.beat->getProcValue())].toSeconds (playhead);
2020-05-20 16:44:46 -07:00
int n = int (gateParams.length->getProcValue());
gate.setLength (n);
for (int i = 0; i < n; i++)
gate.setStep (i, gateParams.l[i]->isOn(), gateParams.r[i]->isOn());
gate.setFrequency (freq);
gate.setAttack (modMatrix.getValue (gateParams.attack));
gate.setRelease (modMatrix.getValue (gateParams.release));
}
// Update Chorus
if (chorusParams.enable->isOn())
{
chorus.setParams (modMatrix.getValue (chorusParams.delay),
modMatrix.getValue (chorusParams.rate),
modMatrix.getValue (chorusParams.depth),
modMatrix.getValue (chorusParams.width),
modMatrix.getValue (chorusParams.mix));
}
// Update Distortion
if (distortionParams.enable->isOn())
2023-10-08 22:44:56 -07:00
{
auto mode = fxParams.distMode->getUserValueInt();
if (mode == 0)
{
distortionVal = modMatrix.getValue (distortionParams.amount);
}
else if (mode == 1)
{
bitcrusher.setParameter (0, modMatrix.getValue (bitcrusherParams.rez));
bitcrusher.setParameter (1, modMatrix.getValue (bitcrusherParams.rate));
bitcrusher.setParameter (2, modMatrix.getValue (bitcrusherParams.hard));
bitcrusher.setParameter (3, modMatrix.getValue (bitcrusherParams.mix));
}
else if (mode == 2)
{
fireAmp.setParameter (0, modMatrix.getValue (fireAmpParams.gain));
fireAmp.setParameter (1, modMatrix.getValue (fireAmpParams.tone));
fireAmp.setParameter (2, modMatrix.getValue (fireAmpParams.output));
fireAmp.setParameter (3, modMatrix.getValue (fireAmpParams.mix));
}
else if (mode == 3)
{
grindAmp.setParameter (0, modMatrix.getValue (grindAmpParams.gain));
grindAmp.setParameter (1, modMatrix.getValue (grindAmpParams.tone));
grindAmp.setParameter (2, modMatrix.getValue (grindAmpParams.output));
grindAmp.setParameter (3, modMatrix.getValue (grindAmpParams.mix));
}
}
2020-05-20 16:44:46 -07:00
// Update Delay
if (delayParams.enable->isOn())
{
if (delayParams.sync->isOn())
{
2023-09-29 20:07:45 -07:00
auto& duration = gin::NoteDuration::getNoteDurations()[(size_t)modMatrix.getValue (delayParams.beat)];
2020-05-20 16:44:46 -07:00
delayParams.delay->setUserValue (duration.toSeconds (getPlayHead()));
2023-10-03 08:47:14 -07:00
stereoDelay.setParams (delayParams.delay->getUserValue(),
modMatrix.getValue (delayParams.mix),
modMatrix.getValue (delayParams.fb),
modMatrix.getValue (delayParams.cf));
2020-05-20 16:44:46 -07:00
}
else
{
2023-09-29 20:07:45 -07:00
delayParams.delay->setUserValue (modMatrix.getValue (delayParams.time));
2023-10-03 08:47:14 -07:00
stereoDelay.setParams (modMatrix.getValue (delayParams.delay),
modMatrix.getValue (delayParams.mix),
modMatrix.getValue (delayParams.fb),
modMatrix.getValue (delayParams.cf));
2020-05-20 16:44:46 -07:00
}
}
// Update Reverb
if (reverbParams.enable->isOn())
{
reverb.setSize (modMatrix.getValue (reverbParams.size));
reverb.setDecay (modMatrix.getValue (reverbParams.decay));
reverb.setLowpass (modMatrix.getValue (reverbParams.lowpass));
reverb.setDamping (modMatrix.getValue (reverbParams.damping));
reverb.setPredelay (modMatrix.getValue (reverbParams.predelay));
reverb.setMix (modMatrix.getValue (reverbParams.mix));
2020-05-20 16:44:46 -07:00
}
// Output gain
outputGain.setGain (modMatrix.getValue (globalParams.level));
}
2025-11-13 20:10:21 -08:00
bool WavetableAudioProcessor::loadWaveTable (gin::Wavetable& table, double sr, const juce::MemoryBlock& wav, const juce::String& format, int size)
{
auto is = new juce::MemoryInputStream (wav, false);
if (format == "wav")
{
if (auto reader = std::unique_ptr<juce::AudioFormatReader> (juce::WavAudioFormat().createReaderFor (is, true)))
{
2023-09-29 21:06:19 -07:00
if (size <= 0)
size = gin::getWavetableSize (wav);
if (size > 0)
{
2023-09-29 21:06:19 -07:00
int samplesToUse = int (reader->lengthInSamples);
int frames = samplesToUse / size;
samplesToUse = frames * size;
juce::AudioSampleBuffer buf (1, samplesToUse);
reader->read (&buf, 0, samplesToUse, 0, true, false);
2025-11-13 20:10:21 -08:00
gin::Wavetable t;
2023-09-29 21:06:19 -07:00
loadWavetables (t, sr, buf, reader->sampleRate, size);
juce::ScopedLock sl (dspLock);
std::swap (t, table);
return true;
}
}
}
else if (format == "flac")
{
if (auto reader = std::unique_ptr<juce::AudioFormatReader> (juce::FlacAudioFormat().createReaderFor (is, true)))
{
juce::AudioSampleBuffer buf (1, int (reader->lengthInSamples));
reader->read (&buf, 0, int (reader->lengthInSamples), 0, true, false);
2025-11-13 20:10:21 -08:00
gin::Wavetable t;
loadWavetables (t, sr, buf, reader->sampleRate, 2048);
2025-11-13 20:10:21 -08:00
juce::ScopedLock sl (dspLock);
std::swap (t, table);
2025-11-13 20:10:21 -08:00
return true;
}
}
return false;
}
2023-08-23 08:45:23 -07:00
void WavetableAudioProcessor::handleMidiEvent (const juce::MidiMessage& m)
2020-05-20 16:44:46 -07:00
{
2023-10-12 09:20:28 -07:00
gin::Synthesiser::handleMidiEvent (m);
2020-05-20 16:44:46 -07:00
if (m.isPitchWheel())
modMatrix.setMonoValue (modScrPitchBend, float (m.getPitchWheelValue()) / 0x2000 - 1.0f);
}
void WavetableAudioProcessor::handleController ([[maybe_unused]] int ch, int num, int val)
{
2023-09-20 16:47:42 -07:00
if (num >= 0 && num <= 119)
modMatrix.setMonoValue (modSrcCC[num], val / 127.0f);
2020-05-20 16:44:46 -07:00
}
//==============================================================================
bool WavetableAudioProcessor::hasEditor() const
{
return true;
}
2023-08-23 08:45:23 -07:00
juce::AudioProcessorEditor* WavetableAudioProcessor::createEditor()
2020-05-20 16:44:46 -07:00
{
2023-09-01 20:23:08 -07:00
return new gin::ScaledPluginEditor (new WavetableAudioProcessorEditor (*this), state);
2020-05-20 16:44:46 -07:00
}
//==============================================================================
// This creates new instances of the plugin..
2023-08-23 08:45:23 -07:00
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
2020-05-20 16:44:46 -07:00
{
return new WavetableAudioProcessor();
}