memlnaut-nisps/playground/faust/eoc-delay.dsp

104 lines
3.8 KiB
Text
Raw Normal View History

// eoc-delay.dsp — Stereo delay effect for the EOC rack.
//
// Parameters (7):
// time [250ms, 1-2000ms] delay time (ms) when sync=0
// feedback [0.3, 0-0.95] feedback amount
// lp_cutoff [8000, 500-20000] LP filter cutoff on feedback path (Hz)
// ping_pong [0.0, 0-1] 0=normal stereo, 1=full ping-pong L<->R
// spread [0.5, 0-1] stereo width of delay tails
// sync [0] nentry: 0=free, 1=half, 2=quarter, 3=eighth (120bpm)
// mix [0.3, 0-1] dry/wet mix
import("stdfaust.lib");
// ---------------------------------------------------------------------------
// UI
// ---------------------------------------------------------------------------
time = hslider("time[unit:ms]", 250, 1, 2000, 0.1);
feedback = hslider("feedback", 0.3, 0, 0.95, 0.001);
lp_cutoff = hslider("lp_cutoff[unit:Hz]", 8000, 500, 20000, 1);
ping_pong = hslider("ping_pong", 0.0, 0, 1, 0.001);
spread = hslider("spread", 0.5, 0, 1, 0.001);
sync = nentry("sync", 0, 0, 3, 1);
mix = hslider("mix", 0.3, 0, 1, 0.001);
// ---------------------------------------------------------------------------
// Derived delay time: free or tempo-synced at 120bpm
// ---------------------------------------------------------------------------
bpm = 120.0;
beat_ms = 60000.0 / bpm;
sync_time_ms =
ba.if(sync < 0.5, time,
ba.if(sync < 1.5, beat_ms * 2.0,
ba.if(sync < 2.5, beat_ms,
beat_ms * 0.5)));
// Fixed max delay: 96000 samples (1s at 96kHz, covers all tempos for eighth notes at 30bpm+)
max_delay_samp = 96000;
// ---------------------------------------------------------------------------
// Stereo ping-pong delay.
// Uses f ~ (_, _) pattern for stereo feedback loop.
// de.delay used for variable delay (no LP on feedback path inside the loop —
// LP is applied to the whole wet signal for efficiency).
// ---------------------------------------------------------------------------
dSamp = int(sync_time_ms * float(ma.SR) / 1000.0);
delayCore(inL, inR, fbL, fbR) = wetL, wetR
with {
pp = ping_pong;
fb = feedback;
mixL = inL + (fbL * (1.0 - pp) + fbR * pp) * fb;
mixR = inR + (fbR * (1.0 - pp) + fbL * pp) * fb;
wetL = de.delay(max_delay_samp, dSamp, mixL);
wetR = de.delay(max_delay_samp, dSamp, mixR);
};
stereoDelay = (_, _) : delayCore ~ (_, _);
// ---------------------------------------------------------------------------
// Post-delay LP filter (tone shaping on the feedback tail)
// ---------------------------------------------------------------------------
delayLP(x) = fi.lowpass(1, lp_cutoff, x);
// ---------------------------------------------------------------------------
// Mid-side stereo spread
// ---------------------------------------------------------------------------
msSpread(inL, inR) = outL, outR
with {
w = spread;
mid = (inL + inR) * 0.5;
side = (inL - inR) * 0.5;
outL = mid + side * w * 2.0;
outR = mid - side * w * 2.0;
};
// ---------------------------------------------------------------------------
// Main process: dry/wet mix
// ---------------------------------------------------------------------------
// Delay + LP + spread on two signals
delayAndProcess(inL, inR) = wideL, wideR
with {
wetL0 = stereoDelay(inL, inR) : _,!;
wetR0 = stereoDelay(inL, inR) : !,_;
wetL1 = delayLP(wetL0);
wetR1 = delayLP(wetR0);
wideL = msSpread(wetL1, wetR1) : _,!;
wideR = msSpread(wetL1, wetR1) : !,_;
};
process(inL, inR) = outL, outR
with {
wideL = delayAndProcess(inL, inR) : _,!;
wideR = delayAndProcess(inL, inR) : !,_;
outL = inL * (1.0 - mix) + wideL * mix;
outR = inR * (1.0 - mix) + wideR * mix;
};