add generative evolution, cross-mod and reverb FX; expand preset bank

- 4 new params (xmod/xmix/evolve/warp) with free-running brownian evolution
- evolution: tune drift, osc2 detune sway, osc-mix crossfade, filter cutoff
  wander, resonance growl, warp drive/bias, cross-mod bloom
- per-voice unison detune spread for fat stacks
- FX engine: 3-tap ensemble chorus, filtered-feedback ping-pong delay,
  Schroeder hall reverb; new FX types 4-7 (REV HALL/ENS+REV/DLY+REV/CATHEDRAL)
- 195 presets: 40-field realignment, genre-tuned FX identities, showcase eval
- calibrate banks vs attack-inclusive peak (median 0.263); fix square-LFO
  on filter sustain collapse
This commit is contained in:
Armin 2026-09-10 16:55:47 +02:00
commit 35d6cd014a
8 changed files with 2016 additions and 601 deletions

View file

@ -14,7 +14,8 @@ static inline void dbgNote (const char* ev, int note, float vel)
}
class RMX19AudioProcessor::FxEngine
{public:
{
public:
void prepare (double sampleRate, int maxSamples)
{
sr = sampleRate;
@ -28,6 +29,9 @@ class RMX19AudioProcessor::FxEngine
chorusR.setMaximumDelayInSamples (maxDelay);
delL.setMaximumDelayInSamples (maxDelay);
delR.setMaximumDelayInSamples (maxDelay);
rebuildCombBuffers (true);
rebuildCombBuffers (false);
}
void reset()
@ -35,6 +39,11 @@ class RMX19AudioProcessor::FxEngine
chorusL.reset(); chorusR.reset();
delL.reset(); delR.reset();
chPh = 0.0;
lpL = lpR = 0.0f;
for (auto& c : combL) c.reset();
for (auto& c : combR) c.reset();
for (auto& a : apL) a.reset();
for (auto& a : apR) a.reset();
}
void process (const VParams& p, float* L, float* R, int num, int syncIdx, double bpm)
@ -43,53 +52,137 @@ class RMX19AudioProcessor::FxEngine
if (type == 0) return;
const float wet = p.fxAmt;
const float dry = 1.0f - wet * 0.5f;
const float chBase = 0.008f * (float) sr; // 8 ms chorus base
const float chDepth = 0.0035f * (float) sr; // +-3.5 ms wobble
const float dryCh = 1.0f - wet * 0.5f;
const float chBase = 0.009f * (float) sr; // 9 ms ensemble base
const float chDepth = 0.006f * (float) sr; // +-6 ms wobble
float delSamples = (float) (p.fxTime * sr);
if (syncIdx > 0 && bpm > 1.0)
delSamples = (float) ((60.0 / bpm) * prm::delaySyncBeats (syncIdx) * sr);
const float feedback = 0.40f;
const float fb = 0.20f + 0.35f * wet;
const float ff = 0.18f; // darkening of repeats
const bool doChorus = (type == 1 || type == 3 || type == 5 || type == 7);
const bool doDelay = (type == 2 || type == 3 || type == 6 || type == 7);
const bool doVerb = (type >= 4);
for (int s = 0; s < num; ++s)
{
const float inL = L[s], inR = R[s];
float outL = inL * dry, outR = inR * dry;
float outL = inL * dryCh, outR = inR * dryCh;
if (type == 1 || type == 3)
if (doChorus)
{
const double rate = type == 3 ? 0.55 : 0.38;
chPh += rate / sr;
if (chPh >= 1.0) chPh -= 1.0;
const float m = 0.5f + 0.5f * (float) std::sin (chPh * 2.0 * juce::MathConstants<double>::pi);
// 3-voice ensemble with a touch of internal regeneration
const double w = chPh * 2.0 * juce::MathConstants<double>::pi;
const float t1 = (float) std::sin (w);
const float t2 = (float) std::sin (w + 2.09439f);
const float t3 = (float) std::sin (w + 4.18879f);
chPh += 0.34 / sr; if (chPh >= 1.0) chPh -= 1.0;
chorusL.pushSample (0, inL);
chorusR.pushSample (0, inR);
const float mL = chorusL.popSample (0, chBase + chDepth * m);
const float mR = chorusR.popSample (0, chBase + chDepth * (1.0f - m));
outL += mL * wet * 0.7f;
outR += mR * wet * 0.7f;
const float mL = 0.5f * (chorusL.popSample (0, chBase + chDepth * t1)
+ chorusL.popSample (0, chBase + chDepth * t2));
const float mR = 0.5f * (chorusR.popSample (0, chBase + chDepth * t2)
+ chorusR.popSample (0, chBase + chDepth * t3));
chorusL.pushSample (0, mL * 0.30f);
chorusR.pushSample (0, mR * 0.30f);
outL += mL * wet * 0.8f;
outR += mR * wet * 0.8f;
}
if (type == 2 || type == 3)
if (doDelay)
{
const float rL = delL.popSample (0, delSamples);
const float rR = delR.popSample (0, delSamples);
delL.pushSample (0, inL + rR * feedback);
delR.pushSample (0, inR + rL * feedback);
lpL += ff * (rL - lpL); lpR += ff * (rR - lpR);
delL.pushSample (0, inL + lpR * fb);
delR.pushSample (0, inR + lpL * fb);
outL += rL * wet;
outR += rR * wet;
}
if (doVerb)
{
float revL = 0.0f, revR = 0.0f;
for (int i = 0; i < 6; ++i)
{
float o = combL[i].process (inL * 0.25f, 0.84f, 0.30f);
revL += o * 0.22f;
o = combR[i].process (inR * 0.25f, 0.84f, 0.30f);
revR += o * 0.22f;
}
apL[0].process (revL, 0.70f); apL[1].process (revL, 0.57f);
apR[0].process (revR, 0.70f); apR[1].process (revR, 0.57f);
const float revWet = (type == 7 ? wet : wet * 0.8f);
outL += revL * revWet;
outR += revR * revWet;
}
L[s] = outL;
R[s] = outR;
}
}
private:
struct Comb
{
void prepare (double s, double seconds)
{
size = (int) (s * seconds);
buf.assign (size, 0.0f);
idx = 0; out = 0.0f; lpIn = 0.0f; lpOut = 0.0f;
}
void reset() { std::fill (buf.begin(), buf.end(), 0.0f); idx = 0; out = 0.0f; lpIn = lpOut = 0.0f; }
float process (float in, float fb, float damp)
{
out = buf[idx];
lpOut = out * (1.0f - damp) + lpIn * damp;
lpIn = out;
buf[idx] = in + lpOut * fb;
if (++idx >= size) idx = 0;
return out;
}
std::vector<float> buf;
int size = 0, idx = 0;
float out = 0.0f, lpIn = 0.0f, lpOut = 0.0f;
};
struct Allpass
{
void prepare (double s, double seconds)
{
size = (int) (s * seconds);
buf.assign (size, 0.0f);
idx = 0;
}
void reset() { std::fill (buf.begin(), buf.end(), 0.0f); idx = 0; }
void process (float& x, float fb)
{
const float w = buf[idx];
buf[idx] = x + w * fb;
x = w - fb * buf[idx];
if (++idx >= size) idx = 0;
}
std::vector<float> buf;
int size = 0, idx = 0;
};
void rebuildCombBuffers (bool left)
{
const double combs[6] = { 0.0297, 0.0371, 0.0411, 0.0437, 0.0311, 0.0290 };
const double off = left ? 0.0 : 0.008;
for (int i = 0; i < 6; ++i)
(left ? combL[i] : combR[i]).prepare (sr, combs[i] + off);
(left ? apL[0] : apR[0]).prepare (sr, 0.0050 + (left ? 0.0 : 0.0011));
(left ? apL[1] : apR[1]).prepare (sr, 0.0161 + (left ? 0.0 : 0.0023));
}
juce::dsp::DelayLine<float> chorusL, chorusR, delL, delR;
std::array<Comb, 6> combL, combR;
std::array<Allpass, 2> apL, apR;
double sr = 48000.0;
double chPh = 0.0;
float lpL = 0.0f, lpR = 0.0f;
};
// ---------------------------------------------------------------------------