🐛 seed the synth idle (not 0) for cableless tweaked voices — fix-verify regression

The fix-verify pass flagged one CONFIRMED regression in the #1 floor loop: it
pinned any tweaked cableless non-note dest to a hard 0, but two dests have a
non-zero synth idle — filter.cutoff (d(...,0.3)) and tempo.nudge (d(...,0.5)).
So touching the filter knob in zero (or, before this, merely opening OMNI, which
created a neutral tweak) pinned filter.cutoff to 0 → 250 Hz, darkening the lead.

- new DEST_IDLE map; the floor loop seeds destVals[k]=DEST_IDLE[k]||0, so a
  neutral/reset knob returns a voice to its idle (filter 0.3), an offset rides
  from the idle, and mute still → 0. (verified: reset filter → 0.3 not 0;
  drag up → 0.767; mute → 0)
- OMNI update() + knob pointerdown now read tweaks[k] directly instead of tw(k),
  so rendering/opening the panel no longer creates phantom neutral tweaks (which
  also kept the tweaks map — and saved dimensions — clean). (verified: open OMNI
  → filter has no tweak)

A clean instrument (no tweaks) stays byte-identical; the public house patch was
already masked (filter.cutoff is cabled to sun.speed). Deploy gate: GO.
Console clean, syntax clean, 21 knobs, field healthy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-11 01:26:01 +10:00
parent fa948823a7
commit fb32d10991

View File

@ -2448,6 +2448,10 @@
return v;
}
// Synth-side idle for the two dests whose "no value" default isn't 0 (see the
// d("filter.cutoff",0.3) / d("tempo.nudge",0.5) fallbacks). The cableless-tweak
// floor seeds THIS, so a neutral/reset knob returns a voice to its idle, not to 0.
const DEST_IDLE = { "filter.cutoff": 0.3, "tempo.nudge": 0.5 };
let clientMatrix = false; // true once the house patch has been cloned in
function computeDests() {
if (!clientMatrix) return;
@ -2491,8 +2495,8 @@
for (const k in destVals) destVals[k] = clamp(destVals[k], 0, 1);
for (const k in notes) destVals[k] = notes[k];
for (const [k, n] of dests) { // any hand-tweaked cableless voice: emit its ACTUAL value —
if (n.isNote || (k in destVals)) continue; // an offset floor gives it a level from nothing, and a
if (tweaks[k]) destVals[k] = 0; // reset/mute/lower writes 0 too (else the last value sticks in the synth)
if (n.isNote || (k in destVals)) continue; // an offset floor gives it a level from its idle, and a
if (tweaks[k]) destVals[k] = DEST_IDLE[k] || 0; // reset/mute/lower re-emit too — seed the synth idle (not a blank 0), so a neutral filter/tempo doesn't pin dark
}
for (const k in destVals) {
const tk = tweaks[k];
@ -3500,7 +3504,7 @@
const val = wrap.querySelector(".val"), dot = wrap.querySelector(".dot");
if (!isNote) {
let dragging = false, sy = 0, so = 0;
wrap.addEventListener("pointerdown", (e) => { if (e.button !== 0) return; dragging = true; sy = e.clientY; so = (tw(key).offset || 0); try { wrap.setPointerCapture(e.pointerId); } catch (x) {} e.preventDefault(); });
wrap.addEventListener("pointerdown", (e) => { if (e.button !== 0) return; dragging = true; sy = e.clientY; so = ((tweaks[key] && tweaks[key].offset) || 0); try { wrap.setPointerCapture(e.pointerId); } catch (x) {} e.preventDefault(); });
wrap.addEventListener("pointermove", (e) => { if (!dragging) return; setParamLocal(key, "offset", clamp(so + (sy - e.clientY) / 150, -1, 1)); });
const end = (e) => { dragging = false; try { wrap.releasePointerCapture(e.pointerId); } catch (x) {} if (zeroMode) zeroUI.persist(); }; // a knob shape is part of the zero build
wrap.addEventListener("pointerup", end); wrap.addEventListener("pointercancel", end);
@ -3519,7 +3523,7 @@
const frac = n ? clamp(n.normDisp != null ? n.normDisp : (n.norm || 0), 0, 1) : 0;
k.val.style.strokeDashoffset = (1000 * (1 - frac)).toFixed(1);
if (!k.isNote) {
const off = tw(k.key).offset || 0;
const off = (tweaks[k.key] && tweaks[k.key].offset) || 0; // read-only — don't create a neutral tweak just by rendering
const a = (135 + ((off + 1) / 2) * 270) * Math.PI / 180;
k.dot.setAttribute("cx", (50 + 34 * Math.cos(a)).toFixed(1));
k.dot.setAttribute("cy", (50 + 34 * Math.sin(a)).toFixed(1));