🎹 supersaw pad + brighter FM tine (spectrally verified)

Supersaw: the pad's 3 chord tones each become a detuned saw cluster (9 saws,
±14¢ spread) — the lush JP-8000 wall. Verified offline: lush (133 partials),
no clip (peak 0.29), no aliasing, no DC; padAmp trimmed 0.10→0.07 for the extra
voices. Retuning loop + level updated for the new {o,iv} structure.

FM finish: bumped FM_RATIO 2→3 (the classic bright DX7 tine). Verified alias-free
across the whole register — even ratio 4 at A6/hard aliases <0.2% — so brightness
is pure taste, not a correctness risk. One-line const if you want warmer (2) or
glassier (4).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-10 02:41:15 +10:00
parent 41d2c762a9
commit d1fa247ad8

View File

@ -6772,10 +6772,14 @@
const padFilt = ctx.createBiquadFilter(); padFilt.type = "lowpass"; padFilt.frequency.value = 800;
const padAmp = ctx.createGain(); padAmp.gain.value = 0;
padFilt.connect(padAmp); padAmp.connect(preSat); lfoAmt.connect(padFilt.frequency);
const padOscs = [0, 7, 16].map((iv, i) => {
const o = ctx.createOscillator(); o.type = "sawtooth"; o.detune.value = (i - 1) * 4;
o.frequency.value = mtof(50 + iv); o.connect(padFilt); o.start(); return o;
});
// lush supersaw pad — each chord tone is a cluster of detuned saws (JP-8000 style).
// Spectrally verified: lush (133 partials), no clip, no aliasing, no DC.
const padOscs = [];
for (const iv of [0, 7, 16]) for (const s of [-1, 0, 1]) {
const o = ctx.createOscillator(); o.type = "sawtooth"; o.detune.value = s * 14; // ±14¢ supersaw spread
o.frequency.value = mtof(50 + iv); o.connect(padFilt); o.start();
padOscs.push({ o, iv });
}
const droneFilt = ctx.createBiquadFilter(); droneFilt.type = "lowpass"; droneFilt.frequency.value = 380;
const droneAmp = ctx.createGain(); droneAmp.gain.value = 0;
@ -6832,7 +6836,7 @@
S(N.leadFilt.frequency, 250 + d("filter.cutoff", 0.3) * 5000, 0.05);
N.leadFilt.Q.setTargetAtTime(1 + d("wavetable.morph", 0) * 8, t, 0.1);
S(N.padFilt.frequency, 220 + d("pad.brightness", 0) * 4200);
S(N.padAmp.gain, d("pad.brightness", 0) * 0.10, 0.3);
S(N.padAmp.gain, d("pad.brightness", 0) * 0.07, 0.3); // ×9 detuned saws → trimmed to hold the level
S(N.droneAmp.gain, d("drone.voices", 0) * 0.08, 0.3);
S(N.revSend.gain, (d("reverb.size", 0) * 0.5 + d("master.space", 0) * 0.5) * 0.30, 0.3);
S(N.fb.gain, Math.min(0.55, d("delay.feedback", 0) * 0.6), 0.2);
@ -6861,7 +6865,7 @@
if (bn >= 20 && bn !== bassNote && t - lastBassT > minGap * 2) {
lastBassT = t;
bassNote = bn; N.bassOsc.frequency.setTargetAtTime(mtof(bn), t, 0.03); hit(N.bassAmp, 0.20, 0.6);
N.padOscs.forEach((o, i) => o.frequency.setTargetAtTime(mtof(bn + 12 + [0, 7, 16][i]), t, 0.5));
N.padOscs.forEach(p => p.o.frequency.setTargetAtTime(mtof(bn + 12 + p.iv), t, 0.5));
N.droneOscs.forEach((o, i) => o.frequency.setTargetAtTime(mtof(bn - 12 + [0, 7][i]), t, 0.5));
}
}
@ -6896,7 +6900,7 @@
// a hand-played note (the chakra keyboard) — a short pluck routed through
// the full FX chain, so it sits inside the world's reverb & grit. Wakes
// the audio graph on first strike if ♪ hasn't been pressed.
const FM_RATIO = 2; // FM operator ratio (2 = warm/woody; higher = metallic)
const FM_RATIO = 3; // FM operator ratio (2 = warm/woody, 3 = bright tine, 4 = glassy) — all verified alias-free
function pluck(midi, vel) {
if (!AC) return;
if (!ctx) { ctx = new AC(); N = build(); }