mirror of
https://codeberg.org/armin/rmx-19.git
synced 2026-10-11 16:51:55 +02:00
Add delay tempo sync, rack faceplate, README, docs
- Add host-tempo-synced delay: FX screen gains a 4th SYNC row for rhythmic divisions (1/16 .. 1/1) that lock delay time to DAW tempo - Embed rack faceplate background (b2.png) via juce binary data - Add offline dev probe (rmx_probe) for GUI-less MIDI testing - Allow overriding JUCE_ROOT when configuring the build - Write comprehensive README describing the instrument, UI, MIDI, DSP, and build/install steps
This commit is contained in:
parent
d63db3af8a
commit
2e870380d4
17 changed files with 1413 additions and 324 deletions
|
|
@ -64,8 +64,9 @@ SynthEngine::Voice* SynthEngine::acquireVoice()
|
|||
|
||||
void SynthEngine::noteOn (int midiNote, float velocity)
|
||||
{
|
||||
const float noteFreq = (float) (440.0 * std::pow (2.0, (midiNote - 69.0) / 12.0));
|
||||
const float noteFreq = (float) (440.0 * std::exp2 ((midiNote - 69.0) / 12.0));
|
||||
Voice* v = acquireVoice();
|
||||
const bool fresh = ! v->active;
|
||||
v->active = true;
|
||||
v->note = midiNote;
|
||||
v->vel = 1.0f - std::pow (1.0f - velocity, 1.35f);
|
||||
|
|
@ -77,6 +78,7 @@ void SynthEngine::noteOn (int midiNote, float velocity)
|
|||
v->lfoIdx = 0;
|
||||
v->lfoSH = rng.nextFloat() * 2.0f - 1.0f;
|
||||
v->lp = v->bp = 0.0f;
|
||||
v->fcSm = 0.0f;
|
||||
v->outSmooth = 0.0f;
|
||||
|
||||
const float p = juce::jlimit (-1.0f, 1.0f, lastPan + (rng.nextFloat() - 0.5f) * 0.1f);
|
||||
|
|
@ -88,7 +90,7 @@ void SynthEngine::noteOn (int midiNote, float velocity)
|
|||
v->amp.reset(); v->flt.reset();
|
||||
v->amp.noteOn(); v->flt.noteOn();
|
||||
lastNoteFreq = noteFreq;
|
||||
++active;
|
||||
if (fresh) ++active;
|
||||
}
|
||||
|
||||
void SynthEngine::noteOff (int midiNote)
|
||||
|
|
@ -106,9 +108,6 @@ void SynthEngine::noteOff (int midiNote)
|
|||
|
||||
void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
||||
{
|
||||
float peakL = metL, peakR = metR;
|
||||
metL = metR = 0.0f;
|
||||
|
||||
for (auto& v : voices)
|
||||
{
|
||||
v.amp.setRates (p.ampA, p.ampD, p.ampS, p.ampR, sr);
|
||||
|
|
@ -120,8 +119,8 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
|||
|
||||
const float glideRate = p.glide <= 0.001f ? 1.0f
|
||||
: 1.0f - std::exp (-1.0f / (p.glide * (float) sr));
|
||||
const float detuneRatio = std::pow (2.0f, p.detune / 1200.0f);
|
||||
const float tuneRatio = std::pow (2.0f, p.tune / 12.0f);
|
||||
const float detuneRatio = std::exp2f (p.detune / 1200.0f);
|
||||
const float tuneRatio = std::exp2f (p.tune / 12.0f);
|
||||
const float maxFc = 18000.0f, minFc = 30.0f;
|
||||
|
||||
for (int s = 0; s < num; ++s)
|
||||
|
|
@ -160,10 +159,10 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
|||
// frequency, vibrato, bend, tune
|
||||
float freq = v.curFreq * tuneRatio;
|
||||
if ((int) p.lfoTarget == 1)
|
||||
freq *= std::pow (2.0f, lfo * p.lfoDepth * 1.6f / 12.0f);
|
||||
freq *= std::exp2f (lfo * p.lfoDepth * 1.6f / 12.0f);
|
||||
if (bendSmooth != bendCents)
|
||||
bendSmooth += (bendCents - bendSmooth) * 0.08f;
|
||||
freq *= std::pow (2.0f, bendSmooth / 1200.0f);
|
||||
freq *= std::exp2f (bendSmooth / 1200.0f);
|
||||
|
||||
const float d1 = (float) (freq / sr);
|
||||
const float d2 = d1 * detuneRatio;
|
||||
|
|
@ -180,16 +179,19 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
|||
|
||||
// cutoff with keytrack + filter envelope + LFO + mod wheel
|
||||
float fc = p.fltCut;
|
||||
fc *= std::pow (freq / 261.6256f, p.fltKey * 0.01f);
|
||||
fc *= std::pow (maxFc / fc, p.fltEnv * 0.01f * fe);
|
||||
fc *= std::exp2f (p.fltKey * 0.01f * std::log2f (freq / 261.6256f));
|
||||
fc *= std::exp2f (p.fltEnv * 0.01f * fe * std::log2f (maxFc / fc));
|
||||
if ((int) p.lfoTarget == 0)
|
||||
fc *= std::pow (2.0f, lfo * p.lfoDepth * 10.0f / 12.0f);
|
||||
fc *= std::pow (2.0f, mod);
|
||||
fc *= std::exp2f (lfo * p.lfoDepth * 10.0f / 12.0f);
|
||||
fc *= std::exp2f (mod);
|
||||
fc = std::min (maxFc, std::max (minFc, fc));
|
||||
v.fcSm += (fc - v.fcSm) * 0.25f;
|
||||
fc = v.fcSm;
|
||||
|
||||
// SVF
|
||||
const float f1 = 2.0f * std::sin (juce::MathConstants<float>::pi * fc / (float) sr);
|
||||
const float q1 = 1.0f / (0.5f + p.fltRes * 3.5f);
|
||||
// SVF (cap f1 to keep the Chamberlin structure stable; past ~1.5 the
|
||||
// resonance feedback can self-oscillate and sputter at maxed cutoff+res)
|
||||
const float f1 = std::min (1.50f, 2.0f * std::sin (juce::MathConstants<float>::pi * fc / (float) sr));
|
||||
const float q1 = 1.0f / (1.0f + p.fltRes * 3.0f);
|
||||
v.f1c = f1;
|
||||
const float hp = sig - v.lp - q1 * v.bp;
|
||||
v.bp += f1 * hp;
|
||||
|
|
@ -211,7 +213,8 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
|||
ampMod = 1.0f - p.lfoDepth * 0.5f * (1.0f + lfo);
|
||||
|
||||
v.outSmooth += (filtered - v.outSmooth) * 0.12f;
|
||||
const float o = v.outSmooth * (ae * ampMod) * v.vel;
|
||||
float o = v.outSmooth * (ae * ampMod) * v.vel;
|
||||
if (! std::isfinite (o)) o = 0.0f;
|
||||
|
||||
mixL += o * v.panL;
|
||||
mixR += o * v.panR;
|
||||
|
|
@ -223,13 +226,12 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
|||
}
|
||||
}
|
||||
|
||||
peakL = std::max (peakL, std::abs (mixL));
|
||||
peakR = std::max (peakR, std::abs (mixR));
|
||||
if (! std::isfinite (mixL)) mixL = 0.0f;
|
||||
if (! std::isfinite (mixR)) mixR = 0.0f;
|
||||
mixL = std::tanh (mixL);
|
||||
mixR = std::tanh (mixR);
|
||||
|
||||
left[s] += mixL;
|
||||
right[s] += mixR;
|
||||
}
|
||||
|
||||
metL = peakL;
|
||||
metR = peakR;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue