2026-09-07 21:03:31 +02:00
|
|
|
#pragma once
|
|
|
|
|
#include <juce_audio_processors/juce_audio_processors.h>
|
|
|
|
|
#include "Params.h"
|
|
|
|
|
|
|
|
|
|
struct VParams
|
|
|
|
|
{
|
|
|
|
|
float osc1w = 2, osc2w = 3, detune = 8, oscMix = 52.f;
|
|
|
|
|
float fltType = 1, fltCut = 3400.f, fltRes = 0.18f, fltEnv = 52.f, fltKey = 40.f;
|
|
|
|
|
float ampA = 0.008f, ampD = 0.26f, ampS = 0.70f, ampR = 0.35f;
|
|
|
|
|
float fA = 0.02f, fD = 0.16f, fS = 0.26f, fR = 0.25f;
|
|
|
|
|
float lfoRate = 4.2f, lfoShape = 0, lfoDepth = 0.22f, lfoTarget = 0;
|
|
|
|
|
float fxType = 1, fxAmt = 0.30f, fxTime = 0.28f;
|
|
|
|
|
float vol = 0.85f, pan = 0.0f, poly = 10.f, glide = 0.f, tune = 0.f;
|
2026-09-08 15:10:12 +02:00
|
|
|
float hq = 0.0f;
|
2026-09-07 21:03:31 +02:00
|
|
|
|
|
|
|
|
void update (const juce::AudioProcessorValueTreeState& apvts)
|
|
|
|
|
{
|
|
|
|
|
auto get = [&] (const char* id) { return apvts.getRawParameterValue (id) -> load(); };
|
|
|
|
|
osc1w = get ("osc1wave"); osc2w = get ("osc2wave"); detune = get ("detune");
|
|
|
|
|
oscMix = get ("oscmix");
|
|
|
|
|
fltType = get ("flttype"); fltCut = get ("fltcut"); fltRes = get ("fltres");
|
|
|
|
|
fltEnv = get ("fltenv"); fltKey = get ("fltkey");
|
|
|
|
|
ampA = get ("ampA"); ampD = get ("ampD"); ampS = get ("ampS");
|
|
|
|
|
ampR = get ("ampR");
|
|
|
|
|
fA = get ("fenvA"); fD = get ("fenvD"); fS = get ("fenvS");
|
|
|
|
|
fR = get ("fenvR");
|
|
|
|
|
lfoRate = get ("lforate"); lfoShape = get ("lfoshape"); lfoDepth = get ("lfodepth");
|
|
|
|
|
lfoTarget= get ("lfotarget");
|
|
|
|
|
fxType = get ("fxtype"); fxAmt = get ("fxamt"); fxTime = get ("fxtime");
|
|
|
|
|
vol = get ("vol"); pan = get ("pan"); poly = get ("poly");
|
|
|
|
|
glide = get ("glide"); tune = get ("tune");
|
2026-09-08 15:10:12 +02:00
|
|
|
hq = get ("hq");
|
2026-09-07 21:03:31 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
class Env
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
enum State { Off, Atk, Dec, Sus, Rel };
|
|
|
|
|
|
|
|
|
|
void setRates (float a, float d, float s, float r, double sr)
|
|
|
|
|
{
|
|
|
|
|
srInv = 1.0 / sr;
|
|
|
|
|
attTau = 1.0f / (0.001f + 0.25f * a);
|
|
|
|
|
decTau = 1.0f / (0.001f + 0.35f * d);
|
|
|
|
|
relTau = 1.0f / (0.001f + 0.30f * r);
|
|
|
|
|
susL = s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void noteOn () { st = Atk; }
|
|
|
|
|
|
|
|
|
|
void noteOff ()
|
|
|
|
|
{
|
|
|
|
|
if (st != Off && st != Rel) st = Rel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void reset (float v = 0.0f) { cur = v; st = Off; }
|
|
|
|
|
|
|
|
|
|
float next()
|
|
|
|
|
{
|
|
|
|
|
switch (st)
|
|
|
|
|
{
|
|
|
|
|
case Atk:
|
|
|
|
|
cur += (1.0f - cur) * std::min (1.0f, (float) (attTau * srInv) * 8.0f);
|
|
|
|
|
if (cur >= 1.0f) { cur = 1.0f; st = Dec; }
|
|
|
|
|
break;
|
|
|
|
|
case Dec:
|
|
|
|
|
cur = susL + (cur - susL) * std::exp (-decTau * (float) srInv);
|
|
|
|
|
if (std::abs (cur - susL) < 0.001f) { cur = susL; st = Sus; }
|
|
|
|
|
break;
|
|
|
|
|
case Sus: break;
|
|
|
|
|
case Rel:
|
|
|
|
|
cur *= std::exp (-relTau * (float) srInv);
|
|
|
|
|
if (cur < 0.0002f) { cur = 0.0f; st = Off; }
|
|
|
|
|
break;
|
|
|
|
|
case Off: break;
|
|
|
|
|
}
|
|
|
|
|
return cur;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool running() const { return st != Off; }
|
|
|
|
|
float getCur() const { return cur; }
|
|
|
|
|
State getState() const { return st; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
float cur = 0.0f, susL = 0.7f;
|
|
|
|
|
float attTau = 1.0f, decTau = 1.0f, relTau = 1.0f;
|
|
|
|
|
double srInv = 1.0 / 48000.0;
|
|
|
|
|
State st = Off;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
class SynthEngine
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
SynthEngine();
|
|
|
|
|
~SynthEngine();
|
|
|
|
|
|
|
|
|
|
void setSampleRate (double sr);
|
|
|
|
|
void noteOn (int midiNote, float velocity);
|
|
|
|
|
void noteOff (int midiNote);
|
|
|
|
|
void allNotesOff (bool immediate);
|
|
|
|
|
void sustainPedal (bool down);
|
|
|
|
|
void pitchBend (float cents);
|
|
|
|
|
void modWheel (float v);
|
|
|
|
|
|
|
|
|
|
void render (const VParams& p, float* left, float* right, int numSamples);
|
|
|
|
|
|
|
|
|
|
int activeVoices() const noexcept { return active; }
|
|
|
|
|
|
|
|
|
|
private:
|
2026-09-08 16:29:47 +02:00
|
|
|
void glideParams (VParams& sp, const VParams& tgt, float k);
|
|
|
|
|
|
2026-09-07 21:03:31 +02:00
|
|
|
struct Voice
|
|
|
|
|
{
|
|
|
|
|
bool active = false;
|
|
|
|
|
int note = 0;
|
|
|
|
|
float vel = 1.0f;
|
|
|
|
|
float panL = 0.71f, panR = 0.71f;
|
|
|
|
|
float curFreq = 0.0f, targetFreq = 0.0f;
|
|
|
|
|
double ph1 = 0.0, ph2 = 0.0, lfoPh = 0.0;
|
|
|
|
|
float lfoSH = 0.0f;
|
|
|
|
|
int lfoIdx = 0;
|
|
|
|
|
Env amp, flt;
|
|
|
|
|
float lp = 0.0f, bp = 0.0f, f1c = 0.0f;
|
2026-09-08 15:10:12 +02:00
|
|
|
float fcSm = 0.0f;
|
2026-09-07 21:03:31 +02:00
|
|
|
float outSmooth = 0.0f;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static constexpr int maxVoice = 16;
|
|
|
|
|
Voice voices[maxVoice];
|
|
|
|
|
double sr = 48000.0;
|
|
|
|
|
int active = 0;
|
|
|
|
|
int polyLimit = 16;
|
|
|
|
|
float mod = 0.0f, bendCents = 0.0f, bendSmooth = 0.0f;
|
2026-09-08 16:29:47 +02:00
|
|
|
VParams sv; // smoothed parameter values (per-sample glide)
|
|
|
|
|
bool svValid = false;
|
2026-09-07 21:03:31 +02:00
|
|
|
bool pedal = false;
|
|
|
|
|
float lastNoteFreq = 440.0f, lastGlide = 0.0f, lastPan = 0.0f;
|
|
|
|
|
int stealIdx = 0;
|
|
|
|
|
juce::Random rng;
|
|
|
|
|
juce::Array<int> sustainedNotes;
|
|
|
|
|
|
|
|
|
|
float osc (int wave, double ph);
|
|
|
|
|
Voice* acquireVoice();
|
|
|
|
|
};
|