From 563723609be2f6d44b2c24ab9dfe95bd6cb1e935 Mon Sep 17 00:00:00 2001 From: "Len (Smooth Electrons)" Date: Sat, 29 Aug 2026 23:08:48 +0000 Subject: [PATCH] mimid: add constant-rate (linear) glide mode Portamento was implemented solely as a one-pole low-pass filter on the note pitch (Voice.h), i.e. an exponential/RC-style slew whose glide time is independent of interval size. This adds a "Glide Mode" selector in the Main group with two options: Exp (default) - existing one-pole LPF; behaviour unchanged. Rate - linear slew at a constant rate (semitones/sec), so glide time is proportional to interval, for Minimoog-style lead lines where large leaps glide but chromatic runs don't turn to mush. The Portamento knob drives both curves; in Rate mode it maps to the time to traverse one octave (~2 ms .. 4 s). PortamentoSpread and the per-note portamento enable/disable gating apply to both modes. Default is Exp, so existing patches are unaffected. - ParamDefs.h: SP_PORTAMODE scale point + PORTAMODE param (setPortaMode) - SynthEngine.h: derive linear rate in setPortamento; add setPortaMode - Voice.h: portaRate/portaRateSaved + portaLinear state; linear-slew branch in the glide path; setPortaRate(); NoteOn gating - UILayoutDefs.h: expose Glide Mode on the Main page (Cairo UI) --- plugins/mimid/Engine/ParamDefs.h | 5 +++++ plugins/mimid/Engine/SynthEngine.h | 13 +++++++++++ plugins/mimid/Engine/Voice.h | 35 ++++++++++++++++++++++++++++-- plugins/mimid/UI/UILayoutDefs.h | 1 + 4 files changed, 52 insertions(+), 2 deletions(-) diff --git a/plugins/mimid/Engine/ParamDefs.h b/plugins/mimid/Engine/ParamDefs.h index a9e3f3b..bc62075 100644 --- a/plugins/mimid/Engine/ParamDefs.h +++ b/plugins/mimid/Engine/ParamDefs.h @@ -88,6 +88,7 @@ PARAMPOINTS(SP_KEYSYNC, 0, "FreeRun", "KeySync") PARAMPOINTS(SP_OSC3WAVE, 0, " Off ", "-1 Squ", "-2 Squ", "-2 Pul", "Noise") PARAMPOINTS(SP_ENVMODE, 0, "Exp/Lin ", "Lin/Lin", "Lin/Exp") + PARAMPOINTS(SP_PORTAMODE, 0, " Exp ", " Rate ") PARAMHINTS(SP_INTS, kParameterIsInteger) @@ -98,6 +99,10 @@ // Main PARAM(VOLUME, PG_MAIN, SP_NONE, "Volume", "volume", 0, 10, 5, setVolume) PARAM(PORTAMENTO, PG_MAIN, SP_NONE, "Portamento", "portamento", 0, 10, 0, setPortamento) + // Glide curve: Exp = one-pole LPF (exponential/RC-style, glide time + // independent of interval); Rate = linear slew at constant rate + // (semitones/sec, glide time proportional to interval, Minimoog-style). + PARAM(PORTAMODE, PG_MAIN, SP_PORTAMODE, "Glide Mode", "glidemode", SP_MIN, SP_MAX, 0, setPortaMode) PARAM(TUNE, PG_MAIN, SP_NONE, "Tune", "tune", -1.0, 1.0, 0.0, setTune) PARAM(OCTAVE, PG_MAIN, SP_INTS, "Octave", "octave", -2, 2, 0, setOctave) diff --git a/plugins/mimid/Engine/SynthEngine.h b/plugins/mimid/Engine/SynthEngine.h index 5fafcb1..db0cebe 100644 --- a/plugins/mimid/Engine/SynthEngine.h +++ b/plugins/mimid/Engine/SynthEngine.h @@ -361,9 +361,22 @@ public: } void setPortamento(float param) { + // Exp mode: one-pole LPF cutoff (0.14 .. 250 Hz). float porta = timesc(10 - param, 0.14f, 250); ForEachVoice(setPorta(porta)); + + // Rate mode: constant glide rate in semitones/sec, expressed as + // time to traverse one octave (~2 ms fast .. 4 s slow). + float octaveTime = timesc(param, 0.002f, 4.0f); + float portaRate = 12.0f / octaveTime; + + ForEachVoice(setPortaRate(portaRate)); + } + void setPortaMode(float param) + { + int intval = roundToInt(param); + ForEachVoice(portaLinear = intval != 0); } void setVolume(float param) { diff --git a/plugins/mimid/Engine/Voice.h b/plugins/mimid/Engine/Voice.h index 125be3d..f7a9956 100644 --- a/plugins/mimid/Engine/Voice.h +++ b/plugins/mimid/Engine/Voice.h @@ -161,7 +161,9 @@ public: float fltKF; float porta, portaSaved; + float portaRate, portaRateSaved; // constant-rate glide, semitones/sec bool portaEnable; + bool portaLinear; // false = exp (LPF), true = constant-rate (linear) float pitchWheel, pitchWheelAmt; @@ -213,7 +215,9 @@ public: FltSpreadAmt = 0; levelSpreadAmt = 1; portaSaved = porta = 0; + portaRateSaved = portaRate = 0; portaEnable = false; + portaLinear = false; oschpfst = hpfst = prtst = 0; fltKF = false; cutoff = 0; @@ -284,8 +288,27 @@ public: // osc pitch is 24 (semitones), resulting in // 440 Hz + 2 octaves = 440 * 2 * 2 = 1760 Hz. // (Default osc tuning at midi 60 is middle C = C4 = 261.63 Hz) - // Portamento on osc input voltage using LPF - float ptNote = tptlpupw(prtst, midiIndx-93, porta * PortaSpreadAmt, modRateInv); + // Portamento on osc input voltage (note-number domain). + // Exp mode (default): one-pole LPF => exponential/RC-style slew, + // glide time independent of interval. + // Rate mode: linear slew at constant rate (semitones/sec) => glide + // time proportional to interval, for Minimoog-style lead glides. + // prtst holds the current glided note; at a note boundary the + // previous glide has settled, so the linear slew starts correctly. + float ptNote; + float ptTarget = midiIndx - 93; + if (portaLinear) { + float step = portaRate * PortaSpreadAmt * modRateInv; + float diff = ptTarget - prtst; + if (diff > step) + prtst += step; + else if (diff < -step) + prtst -= step; + else + prtst = ptTarget; + ptNote = prtst; + } else + ptNote = tptlpupw(prtst, ptTarget, porta * PortaSpreadAmt, modRateInv); osc.notePlaying = ptNote; // Filter cutoff and resonance @@ -567,6 +590,12 @@ public: if (portaEnable) porta = portaSaved; } + void setPortaRate(float newRate) + { + portaRateSaved = newRate; + if (portaEnable) + portaRate = portaRateSaved; + } void setSampleRate(float sr, int oversamplingRatio, int modulationRatio) { modulationRatio += (modulationRatio == 0); // avoid div by 0 @@ -653,6 +682,8 @@ public: Active = true; portaEnable = doPorta; porta = portaEnable ? portaSaved : 250; + // Large rate => snap to pitch when portamento is disabled for the note. + portaRate = portaEnable ? portaRateSaved : 1.0e6f; } void NoteOff() { diff --git a/plugins/mimid/UI/UILayoutDefs.h b/plugins/mimid/UI/UILayoutDefs.h index 8f151b1..fd282cc 100644 --- a/plugins/mimid/UI/UILayoutDefs.h +++ b/plugins/mimid/UI/UILayoutDefs.h @@ -116,6 +116,7 @@ UILAYOUT_MODULE(0, PG_MAIN, VERT, 0, 0) UILAYOUT_PARAM(TUNE) UILAYOUT_PARAM(OCTAVE) UILAYOUT_PARAM(PORTAMENTO, "Portam") + UILAYOUT_PARAM(PORTAMODE, "Glide") UILAYOUT_MODULE_END // =========================================================================== -- 2.43.0