All right, here's a "Studio Simulation" that tries to create a binaural simulation of a sound source in a studio room with wood walls, also a Reaper .jsfx file. I don't think Panagement has anything to worry about.

This version has a GUI - you can move the sliders to move the sound source and size of the room. It was another interesting experiment, showing that many effects are as much art as they are science. While it's cool to build your own stuff, it's also a bit of a distraction from actually making music. The common wisdom is that we need less effects that we use better, not more that we waste our time fiddling with. And this sort of thing is really a timesink.

desc: Binaural Room Spacializer
// Fixed scientific notation error and boosted feedback resonance.

slider1:0<-60, 12, 0.1>Output Gain (dB)
slider2:1.5<0.1, 10.0, 0.01>Distance (m)
slider3:0<-90, 90, 1>Angle (deg)
slider4:0.22<0.1, 0.5, 0.01>Head Width (m)
slider5:6<2, 20, 0.1>Room Width (m)
slider6:8<2, 20, 0.1>Room Depth (m)
slider7:0.6<0, 1.0, 0.01>Liveness (Space Presence)
slider8:0.75<0.1, 0.99, 0.01>Absorption (Muffle)

@init
B_SZ = 131072; B_MSK = B_SZ - 1;
buf_pos = 0;
data_ptr = 140000;

MAX_REFS = 30;
rT_L = data_ptr; data_ptr += MAX_REFS;
rG_L = data_ptr; data_ptr += MAX_REFS;
rS_L = data_ptr; data_ptr += MAX_REFS;
rT_R = data_ptr; data_ptr += MAX_REFS;
rG_R = data_ptr; data_ptr += MAX_REFS;
rS_R = data_ptr; data_ptr += MAX_REFS;
fL_h = data_ptr; data_ptr += MAX_REFS;
fR_h = data_ptr; data_ptr += MAX_REFS;

PI = 3.1415926535; C = 343;

function update_physics() (
d = slider2; a_rad = slider3 * (PI / 180); hw = slider4;
sx = sin(a_rad) * d; sy = cos(a_rad) * d;
exL = -(hw * 0.5); exR = (hw * 0.5);

dL = sqrt((sx - exL)^2 + sy^2); dR = sqrt((sx - exR)^2 + sy^2);
dT_L = (dL / C) * srate; dT_R = (dR / C) * srate;
dG_L = 1 / max(1.0, dL); dG_R = 1 / max(1.0, dR);

dC_L = min(1, max(0.01, 1 / (1 + (dL * 0.05) + max(0, slider3/45))));
dC_R = min(1, max(0.01, 1 / (1 + (dR * 0.05) + max(0, -slider3/45))));

idx = 0; absorb = slider8;
loop(6,
idx == 0 ? (vx = sx; vy = (slider6*0.6)*2 - sy);
idx == 1 ? (vx = sx; vy = (-slider6*0.4)*2 - sy);
idx == 2 ? (vx = (-slider5*0.5)*2 - sx; vy = sy);
idx == 3 ? (vx = (slider5*0.5)*2 - sx; vy = sy);
idx == 4 ? (vx = sx; vy = sy; vz = -2.4);
idx == 5 ? (vx = sx; vy = sy; vz = 3.6);
rdL = sqrt((vx - exL)^2 + vy^2); rdR = sqrt((vx - exR)^2 + vy^2);
rT_L[idx] = (rdL / C) * srate; rT_R[idx] = (rdR / C) * srate;
rG_L[idx] = (0.5 / max(1, rdL)) * (1 - absorb);
rG_R[idx] = (0.5 / max(1, rdR)) * (1 - absorb);
rS_L[idx] = 0.4 * (1 / (1 + rdL * 0.04));
rS_R[idx] = 0.4 * (1 / (1 + rdR * 0.04));
idx += 1;
);

seed = 42;
loop(24,
seed = (seed * 1103515245 + 12345) & 0x7FFFFFFF;
sc_ang = (seed / 0x7FFFFFFF) * 2 * PI;
room_dim = (slider5 + slider6) * 0.5;
sc_path = d + (room_dim * (0.4 + (seed % 100)/50));
vx = sx + cos(sc_ang) * sc_path; vy = sy + sin(sc_ang) * sc_path;
rdL = sqrt((vx - exL)^2 + vy^2); rdR = sqrt((vx - exR)^2 + vy^2);
rT_L[idx] = (rdL / C) * srate; rT_R[idx] = (rdR / C) * srate;
rG_L[idx] = (0.2 / max(1, rdL)) * (1 - absorb);
rG_R[idx] = (0.2 / max(1, rdR)) * (1 - absorb);
rS_L[idx] = 0.06; rS_R[idx] = 0.06;
idx += 1;
);
);

@slider
g_out = 10^(slider1/20);
update_physics();
// We do not clear filters here to avoid pops, but clear on major room changes
// memset(fL_h, 0, MAX_REFS);

@sample
// 1. FIXED FEEDBACK LOGIC
// Standard decimal notation used for denormal protection
fb_sig = (last_rSumL + last_rSumR) * 0.5 * slider7 * 1.9;

fb_hpf += 0.05 * (fb_sig - fb_hpf);
fb_sig -= fb_hpf;

mono_in = (spl0 + spl1) * 0.5 + fb_sig;
abs(mono_in) < 0.000000000000001 ? mono_in = 0;
0[buf_pos] = mono_in;

// 2. Direct Path
pL = buf_pos - dT_L; iL = floor(pL); fL = pL - iL;
sL = (0[iL & B_MSK] * (1-fL) + 0[(iL-1) & B_MSK] * fL) * dG_L;
fL_lp += dC_L * (sL - fL_lp);

pR = buf_pos - dT_R; iR = floor(pR); fR = pR - iR;
sR = (0[iR & B_MSK] * (1-fR) + 0[(iR-1) & B_MSK] * fR) * dG_R;
fR_lp += dC_R * (sR - fR_lp);

// 3. 30 Reflections
rSumL = 0; rSumR = 0; i = 0;
loop(MAX_REFS,
tpL = buf_pos - rT_L[i]; ipL = floor(tpL); fpL = tpL - ipL;
tL = (0[ipL & B_MSK] * (1-fpL) + 0[(ipL-1) & B_MSK] * fpL) * rG_L[i];
fL_h[i] += rS_L[i] * (tL - fL_h[i]);
rSumL += fL_h[i];

tpR = buf_pos - rT_R[i]; ipR = floor(tpR); fpR = tpR - ipR;
tR = (0[ipR & B_MSK] * (1-fpR) + 0[(ipR-1) & B_MSK] * fpR) * rG_R[i];
fR_h[i] += rS_R[i] * (tR - fR_h[i]);
rSumR += fR_h[i];
i += 1;
);

last_rSumL = rSumL; last_rSumR = rSumR;
buf_pos = (buf_pos + 1) & B_MSK;

// Master Output
spl0 = (fL_lp + rSumL) * g_out;
spl1 = (fR_lp + rSumR) * g_out;

// Soft Clipper
spl0 = spl0 / (1 + abs(spl0));
spl1 = spl1 / (1 + abs(spl1));

@gfx 400 300
gfx_r=0.05; gfx_g=0.05; gfx_b=0.08; gfx_rect(0,0,gfx_w,gfx_h);
gfx_r=0.8; gfx_g=0.8; gfx_b=1; gfx_x=10; gfx_y=10;
gfx_printf("Geometric Presence | Liveness: %d%%", slider7*100);

cx = gfx_w/2; cy = gfx_h*0.5; scale = 12;
rw = slider5 * scale; rd = slider6 * scale;
gfx_r=0.2; gfx_g=0.2; gfx_b=0.3;
gfx_rect(cx - rw/2, cy - rd/2, rw, rd, 0);

gfx_r=1; gfx_circle(cx, cy, 3, 1);
sx_v = cx + sin(slider3*PI/180)*slider2*scale;
sy_v = cy - cos(slider3*PI/180)*slider2*scale;
gfx_r=0.2; gfx_g=0.6; gfx_b=1; gfx_circle(sx_v, sy_v, 5, 1);


-- David Cuny

My virtual singer development blog
Vocal control, you say. Never heard of it. Is that some kind of ProTools thing?

BiaB 2025 | Windows 11 | Reaper | Way too many VSTis.