Stage 1: mixer drawer (12 channel strips: fader, pan, FX sends, mute/solo, meter, mini-scope)#2
Merged
Merged
Conversation
…se 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.
The standing review checks that new features appear in the feature list, and the Stage 1 mixer was missing. One bullet, neutral wording (no FL references), matching the existing voice.
There was a problem hiding this comment.
Pull request overview
Adds a new “mixer drawer” UI to the dashboard, providing 12 orbit-backed channel strips (fader/pan/FX sends/mute/solo/meter/mini-scope) and unit-tested, single-sourced parameter mapping math shared between browser and Node tests.
Changes:
- Introduces
dashboard-mixer.js(window.AbxMixer) + wiring indashboard.jsto handle mixer inputs/clicks and rerender when state changes. - Extends
dashboard.htmlwith mixer drawer markup/CSS and adds the mixer module script. - Adds
mixer.test.tsfor fader/pan math and raises the test-count floor inrun-tests.mjs.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| mcp/src/mixer.test.ts | New unit tests for mixer fader/pan math (single-source import of dashboard module). |
| mcp/run-tests.mjs | Raises enforced test-count floor to account for new mixer tests. |
| mcp/dashboard.js | Hooks mixer delegate handlers into existing input/click dispatch and render rerender path. |
| mcp/dashboard.html | Adds mixer drawer UI markup, CSS, and loads dashboard-mixer.js. |
| mcp/dashboard-mixer.js | Implements mixer module (math + UI + render/tick loop). |
| mcp/dashboard-mixer.d.ts | Adds ambient typings for the mixer math surface exposed on globalThis.AbxMixer. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+92
to
+105
| // write a param via the same cmd:set path the card knobs use; ghost strips have nothing to write to. | ||
| function setParam(slot, param, value){ | ||
| if (!((Abx.state().slots || {})[slot])) return; | ||
| Abx.send({ cmd: "set", slot: slot, param: param, value: value }); | ||
| } | ||
| // delegates (mirror the AbxSeq/AbxCurves contract). M/S go through the core data-cmd dispatcher | ||
| // (shared with the cards), so mute/solo stay in sync both ways for free. | ||
| function handleInput(e){ | ||
| var el = e.target, slot = el.dataset && el.dataset.slot; if (!slot) return false; | ||
| if (el.classList.contains("fader")) { setParam(slot, "gain", +faderToGain(+el.value).toFixed(GAIN_DP)); return true; } | ||
| if (el.classList.contains("pan")) { setParam(slot, "pan", panStringToPosition(el.value)); return true; } | ||
| if (el.classList.contains("fx1")) { setParam(slot, "room", +(+el.value).toFixed(2)); return true; } | ||
| if (el.classList.contains("fx2")) { setParam(slot, "delay", +(+el.value).toFixed(2)); return true; } | ||
| return false; |
Comment on lines
+109
to
+115
| // re-render strips when the live slot set / mute / solo changes (called from dashboard.js render()). | ||
| // Keyed on structure only (not per-value), so dragging a fader does not yank itself mid-gesture. | ||
| var lastSig = ""; | ||
| function maybeRerender(){ | ||
| if (!isOpen()) return; | ||
| var st = Abx.state(); | ||
| var sig = JSON.stringify({ s: Object.keys(st.slots || {}), m: st.muted, so: st.solo }); |
Comment on lines
261
to
268
| <main><div class="grid" id="grid"></div></main> | ||
| <div id="mixwrap"> | ||
| <div class="mixbar"><span class="seqlabel">🎚 MIXER</span> | ||
| <span class="seqhint">12 channels (orbits) · fader = volume (dB, unity at 75%) · FX1 = reverb send · FX2 = delay send · pan · M/S. Empty channels are ghosted.</span> | ||
| </div> | ||
| <div id="mixrows"></div> | ||
| </div> | ||
| <div id="console"> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stage 1: mixer drawer (12 channel strips)
A bottom-drawer mixer alongside the cards: 12 orbit-backed strips (inactive ones ghosted), each with a dB-linear gain fader, pan, FX1 (reverb) and FX2 (delay) sends, mute/solo, a meter, and a live mini-scope. Rebased onto main after PR #1; single commit
15338c3.What changed
window.AbxMixer) — bottom drawer toggled from the transport (CSS height transition, no re-render). 12 strips = the 12 SuperDirt orbits (d1-d12);d13-d16have no orbit and are not shown (in-fileCONSTRAINTcomment so nobody "fixes" it back to 16).# gainvia the existingcmd:setpath.# pan,# room,# delay— the FX sends are SuperDirt's native per-orbit effects, so zero SuperCollider changes and no orbit-routing.data-cmddispatcher, so they stay in sync both ways for free./state.scopes; the mini-scope reusesAbxScope.draw(no reimplementation).dashboard-mixer.js(faderToGain/gainToFader/panPositionToString/panStringToPosition), unit-tested insrc/mixer.test.ts(12 cases). DOM/UI guarded bytypeof documentso the Node test imports the math cleanly. No separatesrc/mixer.tsto drift (same single-source pattern asdashboard-dsp.js).AbxMixer.handleClick/handleInputcalls carry the samewindow.AbxMixer &&guard as the Stage 0 dispatchers. This is the Stage 0 resilience fix applied to new code, not a new concern.run-tests.mjs(track 7 + dsp 12 + mixer 12), per the "raise the floor in the commit that adds tests" convention.Standing review
npm testgreen before and after. The new wiring-assertion test closes the Stage 0 gap (see Verification).dashboard-mixer.jsis one feature module (math + UI behindAbxMixer); reusesAbx.*andAbxScope.draw; no duplication. The 12-not-16 reality is a load-bearing in-file constraint tied tosc/superdirt_startup.scd.cmd:set->applyParampath as the card knobs. No new endpoints, no new persisted state.cmd:setsend. Cross-namespace calls guarded.AbxScope.draw; no new poll endpoints or SSE streams.data-cmd). Dispatch branches stay mutually exclusive, so adding the guardedAbxMixerdelegate changes nothing for existing controls. No keyboard shortcut or debounce threshold changed.#-param model and the strip UI; the per-orbit (not shared-bus) send choice is documented should Stage 5 want true aux sends. Harder than expected: nothing major; the rebase reconciliation against the Stage 0 guards was the only fiddly bit, handled in one pass.Verification
run-tests.mjs, floor 31 enforced (a discovery regression now fails loudly).#steps/#cheatsdeep-links work, zero console errors, with the dispatcher guards in place./cmdat the fetch layer (no boot/audio), actuates every interactive control, asserts each fires the correct command. A dead or mis-wired control fails. Result (exit 0):Manual checklist (needs your ears + hands)