From 15338c3e643aa2bebd11b16245819c15fb4c3290 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 25 May 2026 22:37:42 +1000 Subject: [PATCH 1/2] Stage 1: mixer drawer (12 strips) + propagate dispatcher guards + raise test floor Mixer drawer (window.AbxMixer): 12 orbit-backed channel strips with a dB-linear gain fader (unity at 75%, top = 2.0 to match the card gain knob), pan, FX1=room / FX2=delay sends (native per-orbit SuperDirt effects, no SC changes), mute/solo (shared data-cmd path with the cards so they stay in sync), a meter, and a mini-scope (reuses AbxScope.draw). Pure math single-sourced in dashboard-mixer.js, unit-tested via src/mixer.test.ts (12 cases). In-file CONSTRAINT: 12 strips, not 16 (SuperDirt numOrbits=12). Rebased onto main after PR #1. Resolved the dashboard.js dispatcher region by keeping main's window.AbxX guards AND adding the same guard to the new AbxMixer.handleClick / handleInput call-sites: propagation of the Stage 0 fix to new code, not a new concern. Raised run-tests.mjs FLOOR 19 -> 31 (track 7 + dsp 12 + mixer 12), applying the "raise the floor in the same commit that adds tests" convention so a discovery regression cannot silently drop the mixer tests. --- mcp/dashboard-mixer.d.ts | 17 +++++ mcp/dashboard-mixer.js | 140 +++++++++++++++++++++++++++++++++++++++ mcp/dashboard.html | 35 ++++++++++ mcp/dashboard.js | 3 + mcp/run-tests.mjs | 2 +- mcp/src/mixer.test.ts | 79 ++++++++++++++++++++++ 6 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 mcp/dashboard-mixer.d.ts create mode 100644 mcp/dashboard-mixer.js create mode 100644 mcp/src/mixer.test.ts diff --git a/mcp/dashboard-mixer.d.ts b/mcp/dashboard-mixer.d.ts new file mode 100644 index 0000000..9c5d9db --- /dev/null +++ b/mcp/dashboard-mixer.d.ts @@ -0,0 +1,17 @@ +// Ambient types for dashboard-mixer.js (window.AbxMixer), the mixer module. The browser loads it +// as a classic + diff --git a/mcp/dashboard.js b/mcp/dashboard.js index 9ca67cc..6af8b1f 100644 --- a/mcp/dashboard.js +++ b/mcp/dashboard.js @@ -194,6 +194,7 @@ if(el.type==="range") lastInput=Date.now(); // suppress card re-render while dragging any slider if(window.AbxSeq && AbxSeq.handleInput(e)) return; // seqname / seqvol / seqSwing if(el.type!=="range")return; + if(window.AbxMixer && AbxMixer.handleInput(e)) return; // mixer fader / pan / FX1 / FX2 (range + data-slot, no data-param) if(el.dataset.tempo!==undefined){ document.getElementById("bpm").textContent=el.value; pendingTempo=+el.value; return; } if(el.dataset.slot){ var vb=document.getElementById("v-"+el.dataset.slot+"-"+el.dataset.param); if(vb)vb.textContent=el.value; pending[el.dataset.slot+"|"+el.dataset.param]={slot:el.dataset.slot,param:el.dataset.param,value:+el.value}; } @@ -210,6 +211,7 @@ if(!b)return; if(window.AbxSeq && AbxSeq.handleClick(b)) return; // steps / seqadd / seqclear / row mute+clear / pattern / song if(window.AbxCurves && AbxCurves.handleClick(b)) return; // curves panel / curve toggle+preset + if(window.AbxMixer && AbxMixer.handleClick(b)) return; // mixer drawer toggle (strip M/S use data-cmd, shared with cards) if(b.dataset.act==="run"){ runCode(); return; } if(b.dataset.act==="boot"){ var o=document.getElementById("consoleOut"); o.textContent="booting the sound engine… (~30-40s) — watch the status light top-left"; o.className=""; send({cmd:"boot"}).then(function(){poll();}); return; } if(b.dataset.act==="reset"){ if(confirm("Reboot the engine? Wipes everything, fresh start (~30s).")) cmd("reset"); return; } @@ -318,6 +320,7 @@ } grid.innerHTML=h; if(window.AbxCurves) AbxCurves.maybeRerender(keys); + if(window.AbxMixer) AbxMixer.maybeRerender(); } function poll(){ fetch("/state").then(function(r){return r.json();}).then(render).catch(function(){ document.getElementById("engstate").textContent="disconnected"; document.getElementById("engdot").className="dot error"; }); } diff --git a/mcp/run-tests.mjs b/mcp/run-tests.mjs index 96e29a2..a33e80e 100644 --- a/mcp/run-tests.mjs +++ b/mcp/run-tests.mjs @@ -21,7 +21,7 @@ import { dirname, join } from "node:path"; import { run } from "node:test"; import { spec } from "node:test/reporters"; -const FLOOR = 19; // current test count on this branch (track 7 + dsp 12) +const FLOOR = 31; // current test count: track 7 + dsp 12 + mixer 12 (raised with the mixer tests) const distDir = join(dirname(fileURLToPath(import.meta.url)), "dist"); let files = []; diff --git a/mcp/src/mixer.test.ts b/mcp/src/mixer.test.ts new file mode 100644 index 0000000..ab4294f --- /dev/null +++ b/mcp/src/mixer.test.ts @@ -0,0 +1,79 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +// Single source: the browser loads dashboard-mixer.js (window.AbxMixer) as a classic