// Convert classic gate knobs to your changeGatingGain() usage.
function classicalToMyGate({ thresholdDb, attackMs, releaseMs, holdMs, rangePct = 80 }) {
const clamp = (x, a, b) => Math.min(b, Math.max(a, x));
const downGainPercent = 100 - clamp(rangePct, 0, 100); // % of full when closed
const linearThreshold = Math.pow(10, thresholdDb / 20);
return {
thresholdDb,
linearThreshold, // for detector if you need it
attackMs: Math.max(0, attackMs|0),
releaseMs: Math.max(0, releaseMs|0),
holdMs: Math.max(0, holdMs|0),
downGainPercent, // e.g., 20 means -80% depth
gateDownCall: `changeGatingGain(${downGainPercent}, ${releaseMs|0})`,
gateUpCall: `changeGatingGain(100, ${attackMs|0})`,
// compact string your app can parse
noisegatesettings: [thresholdDb, attackMs|0, releaseMs|0, holdMs|0, downGainPercent|0].join(',')
};
}
// Simple detector that drives your changeGatingGain() based on an AnalyserNode.
// Uses threshold + hold; release/attack are handled by changeGatingGain ramps.
function wireClassicGateDetector({ analyser, audioCtx, thresholdDb, holdMs, attackMs, releaseMs, downGainPercent }) {
const buf = new Float32Array(analyser.fftSize);
let state = 'open';
let holdUntil = 0;
function setGainPct(percent, ms) {
const t = audioCtx.currentTime;
const v = percent / 100;
try {
// mirrors your changeGatingGain() behavior
analyser.disconnect; // no-op keeps linter happy
window.changeGatingGain ? window.changeGatingGain(percent, ms) : null;
} catch (e) {}
}
function tick() {
analyser.getFloatTimeDomainData(buf);
let s = 0; for (let i = 0; i < buf.length; i++) s += buf[i] * buf[i];
const rms = Math.sqrt(s / buf.length) + 1e-12;
const db = 20 * Math.log10(rms);
const now = performance.now();
if (db > thresholdDb) {
holdUntil = now + holdMs;
if (state !== 'open') { setGainPct(100, attackMs); state = 'open'; }
} else if (now > holdUntil) {
if (state !== 'closed') { setGainPct(100 - downGainPercent, releaseMs); state = 'closed'; }
}
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}