For fun I asked Gemini to write me a JSFX. Warning: I've not tried any of these yet, I'm just dumping them here as I play around with prompts.
1
A reaper JSFX effect with a single filter (cutoff and resonance, and possibly other parameters), with various models.
desc:Multi-Model State Variable Filter
author:Gemini
version:1.0
// --- SLIDERS ---
slider1:1000<20,20000,1,log>Cutoff (Hz)
slider2:0.707<0.1,10,0.01>Resonance (Q)
slider3:0<0,3,1{Lowpass,Highpass,Bandpass,Notch}>Filter Model
slider4:0<-12,12,0.1>Output Gain (dB)
@init
// Initialize variables
ic1eq = 0;
ic2eq = 0;
@slider
// Pre-calculate filter coefficients
// We use the trapezoidal integration method for better response at high frequencies
target_freq = slider1;
q_val = slider2;
model = slider3;
amp = 10^(slider4/20);
g = tan($pi * target_freq / srate);
k = 1 / q_val;
a1 = 1 / (1 + g * (g + k));
a2 = g * a1;
a3 = g * a2;
@sample
// The SVF Algorithm
// Inputs: spl0 (left), spl1 (right)
// Note: This implementation is Mono for simplicity, applied to both channels.
// Process Left Channel
v0 = spl0;
v3 = v0 - ic2eq;
v1 = a1 * ic1eq + a2 * v3;
v2 = ic2eq + a2 * ic1eq + a3 * v3;
ic1eq = 2 * v1 - ic1eq;
ic2eq = 2 * v2 - ic2eq;
// Select Output based on 'model' slider
model == 0 ? (out = v2); // Lowpass
model == 1 ? (out = v0 - k*v1 - v2); // Highpass
model == 2 ? (out = v1); // Bandpass
model == 3 ? (out = v0 - k*v1); // Notch
spl0 = out * amp;
// Process Right Channel (Re-using logic for stereo)
// Note: For true stereo, you'd want a second set of ic1eq/ic2eq variables.
v0_r = spl1;
v3_r = v0_r - ic2eq_r;
v1_r = a1 * ic1eq_r + a2 * v3_r;
v2_r = ic2eq_r + a2 * ic1eq_r + a3 * v3_r;
ic1eq_r = 2 * v1_r - ic1eq_r;
ic2eq_r = 2 * v2_r - ic2eq_r;
model == 0 ? (out_r = v2_r) :
model == 1 ? (out_r = v0_r - k*v1_r - v2_r) :
model == 2 ? (out_r = v1_r) :
model == 3 ? (out_r = v0_r - k*v1_r);
spl1 = out_r * amp;
2
Yes please add drive. Could you also add a midi triggered envelope and lfo.
desc:Power Filter (Drive, LFO, & MIDI Envelope)
author:Gemini
version:1.1
slider1:1000<20,20000,1,log>Cutoff (Hz)
slider2:0.707<0.1,10,0.01>Resonance (Q)
slider3:0<0,3,1{Lowpass,Highpass,Bandpass,Notch}>Filter Model
slider4:0<0,100,0.5>Drive (%)
slider5:0<-100,100,1>LFO Amount (%)
slider6:1<0.1,20,0.1>LFO Rate (Hz)
slider7:5000<0,15000,1>Env Mod (Hz)
slider8:10<1,1000,1>Env Attack (ms)
slider9:100<1,2000,1>Env Release (ms)
@init
ic1eq = ic2eq = ic1eq_r = ic2eq_r = 0; // State variables
lfo_phase = 0;
env_val = 0;
gate = 0;
@slider
drive_val = slider4 * 0.05;
lfo_inc = 2 * $pi * slider6 / srate;
// Convert ms to coefficients
a_coeff = 1 - exp(-1 / (slider8 * srate / 1000));
r_coeff = 1 - exp(-1 / (slider9 * srate / 1000));
@block
while (midirecv(offset, msg1, msg2, msg3)) (
status = msg1 & $F0;
status == $x90 && msg3 > 0 ? gate = 1; // Note On
status == $x80 || (status == $x90 && msg3 == 0) ? gate = 0; // Note Off
midisend(offset, msg1, msg2, msg3); // Pass MIDI through
);
@sample
// 1. Update Envelope
target = gate ? 1 : 0;
env_val += (target - env_val) * (gate ? a_coeff : r_coeff);
// 2. Update LFO
lfo_phase += lfo_inc;
lfo_phase > 2 * $pi ? lfo_phase -= 2 * $pi;
lfo_sig = sin(lfo_phase);
// 3. Combine Modulators for Cutoff
mod_cutoff = slider1 + (lfo_sig * slider5 * 20) + (env_val * slider7);
mod_cutoff = max(20, min(mod_cutoff, 20000)); // Safety Clamp
// 4. SVF Math with Drive
g = tan($pi * mod_cutoff / srate);
k = 1 / slider2;
a1 = 1 / (1 + g * (g + k));
a2 = g * a1;
a3 = g * a2;
// --- Left Channel ---
v0 = spl0;
v3 = v0 - ic2eq;
v1 = a1 * ic1eq + a2 * v3;
v2 = ic2eq + a2 * ic1eq + a3 * v3;
// Soft clipping 'Drive' inside the feedback loop
v1 = tanh(v1 * (1 + drive_val));
ic1eq = 2 * v1 - ic1eq;
ic2eq = 2 * v2 - ic2eq;
// --- Right Channel ---
v0_r = spl1;
v3_r = v0_r - ic2eq_r;
v1_r = a1 * ic1eq_r + a2 * v3_r;
v2_r = ic2eq_r + a2 * ic1eq_r + a3 * v3_r;
v1_r = tanh(v1_r * (1 + drive_val));
ic1eq_r = 2 * v1_r - ic1eq_r;
ic2eq_r = 2 * v2_r - ic2eq_r;
// Select Output
model = slider3;
model == 0 ? (spl0 = v2; spl1 = v2_r) :
model == 1 ? (spl0 = v0 - k*v1 - v2; spl1 = v0_r - k*v1_r - v2_r) :
model == 2 ? (spl0 = v1; spl1 = v1_r) :
(spl0 = v0 - k*v1; spl1 = v0_r - k*v1_r);
3
Yes please add a visualiser.
desc:Power Filter (Drive, LFO, Env & Visualizer)
author:Gemini
version:1.2
slider1:1000<20,20000,1,log>Cutoff (Hz)
slider2:0.707<0.1,10,0.01>Resonance (Q)
slider3:0<0,3,1{Lowpass,Highpass,Bandpass,Notch}>Filter Model
slider4:0<0,100,0.5>Drive (%)
slider5:0<-100,100,1>LFO Amount (%)
slider6:1<0.1,20,0.1>LFO Rate (Hz)
slider7:5000<0,15000,1>Env Mod (Hz)
slider8:10<1,1000,1>Env Attack (ms)
slider9:100<1,2000,1>Env Release (ms)
@init
ic1eq = ic2eq = ic1eq_r = ic2eq_r = 0;
lfo_phase = 0;
env_val = 0;
gate = 0;
@slider
drive_val = slider4 * 0.05;
lfo_inc = 2 * $pi * slider6 / srate;
a_coeff = 1 - exp(-1 / (slider8 * srate / 1000));
r_coeff = 1 - exp(-1 / (slider9 * srate / 1000));
@block
while (midirecv(offset, msg1, msg2, msg3)) (
status = msg1 & $F0;
status == $x90 && msg3 > 0 ? gate = 1 : (status == $x80 || (status == $x90 && msg3 == 0)) ? gate = 0;
midisend(offset, msg1, msg2, msg3);
);
@sample
target = gate ? 1 : 0;
env_val += (target - env_val) * (gate ? a_coeff : r_coeff);
lfo_phase += lfo_inc;
lfo_phase > 2 * $pi ? lfo_phase -= 2 * $pi;
lfo_sig = sin(lfo_phase);
// Calculate modulated cutoff
cur_cutoff = slider1 + (lfo_sig * slider5 * 20) + (env_val * slider7);
cur_cutoff = max(20, min(cur_cutoff, 20000));
// SVF DSP
g = tan($pi * cur_cutoff / srate);
k = 1 / slider2;
a1 = 1 / (1 + g * (g + k)); a2 = g * a1; a3 = g * a2;
v0 = spl0; v3 = v0 - ic2eq; v1 = a1 * ic1eq + a2 * v3; v2 = ic2eq + a2 * ic1eq + a3 * v3;
v1 = tanh(v1 * (1 + drive_val)); ic1eq = 2 * v1 - ic1eq; ic2eq = 2 * v2 - ic2eq;
v0r = spl1; v3r = v0r - ic2eq_r; v1r = a1 * ic1eq_r + a2 * v3r; v2r = ic2eq_r + a2 * ic1eq_r + a3 * v3r;
v1r = tanh(v1r * (1 + drive_val)); ic1eq_r = 2 * v1r - ic1eq_r; ic2eq_r = 2 * v2r - ic2eq_r;
slider3 == 0 ? (spl0=v2; spl1=v2_r) : slider3 == 1 ? (spl0=v0-k*v1-v2; spl1=v0r-k*v1r-v2r) :
slider3 == 2 ? (spl0=v1; spl1=v1r) : (spl0=v0-k*v1; spl1=v0r-k*v1r);
@gfx 400 200
// --- Background ---
gfx_r=0.1; gfx_g=0.1; gfx_b=0.15; gfx_a=1;
gfx_rect(0,0,gfx_w,gfx_h);
// --- Draw Frequency Response Curve (Simplified) ---
gfx_r=0.5; gfx_g=0.5; gfx_b=0.8; gfx_a=0.4;
ctx = cur_cutoff;
cx = (log(ctx/20) / log(20000/20)) * gfx_w; // Logarithmic X mapping
// Draw "Model" Shape
gfx_x = 0; gfx_y = gfx_h;
i = 0;
loop(gfx_w,
freq = 20 * exp(i/gfx_w * log(20000/20));
// Very basic approximation of the filter slope for the GUI
slider3 == 0 ? (h = freq > cur_cutoff ? exp(-(freq-cur_cutoff)/cur_cutoff) : 1); // LP
slider3 == 1 ? (h = freq < cur_cutoff ? exp(-(cur_cutoff-freq)/freq) : 1); // HP
slider3 == 2 ? (h = exp(-abs(freq-cur_cutoff)/(cur_cutoff*0.5))); // BP
slider3 == 3 ? (h = 1 - exp(-abs(freq-cur_cutoff)/(cur_cutoff*0.1))); // Notch
gfx_lineto(i, gfx_h - (h * gfx_h * 0.8), 1);
i += 1;
);
// --- Draw Modulation Indicator (The moving line) ---
gfx_r=1; gfx_g=0.8; gfx_b=0; gfx_a=0.8;
gfx_line(cx, 0, cx, gfx_h);
gfx_x = cx + 5; gfx_y = 10;
gfx_drawstr("CUTOFF");