diff --git a/index.html b/index.html index cd58954..02bf149 100644 --- a/index.html +++ b/index.html @@ -95,7 +95,7 @@

🛠️ Program on the web, play on any device

-

The website is the workbench: design in the editor, and the same +

The website is the workbench: design in the editor, and the same program string loads into whichever form factor fits the moment. One engine, one language.

@@ -110,7 +110,7 @@
-
PM_E‑1 EditorSpecs & info ⓘ  ·  Open full page ↗
+
PM_E‑2 EditorSpecs & info ⓘ  ·  Open full page ↗
@@ -159,8 +159,8 @@ const SAMPLES = {}; let state = { bpm:120, volume:0.85 }, meters = [], muteWindo /*@BUILD:include:src/setlists.js@*/ const VERSIONS = [ - { key:"editor", file:"/editor.html", name:"PM_E‑1 Editor", chip:"app", h:620, sum:"Design grooves: stack meter lanes, per‑step accents/ghosts/mutes, swing & polyrhythm, set lists, per‑lane dB gain." }, - { key:"pme2", file:"/pm_e-2.html", name:"PM_E‑2 Editor", chip:"app", h:640, sum:"Second‑generation editor built around engraved drum notation — a 5‑line percussion staff (Bravura/SMuFL) with Staff / TUBS / Konnakol views, edit‑on‑staff, plus flams/drags/rolls, odd meters & clave." }, + // PM_E-1 (editor.html) is hidden from the landing — PM_E-2 is the focus. The page still exists. + { key:"pme2", file:"/pm_e-2.html", name:"PM_E‑2 Editor", chip:"app", h:640, sum:"The PolyMeter editor, built around engraved drum notation — a 5‑line percussion staff (Bravura/SMuFL) with Staff / TUBS / Konnakol views, edit‑on‑staff, plus flams/drags/rolls, odd meters & clave." }, { key:"kit", file:"/kit.html", name:"PM_K‑1 Kit", chip:"hw", h:560, sum:"Build it today — a Raspberry Pi Pico on the 52Pi touchscreen kit; tap the 3.5″ screen, joystick tempo, RGB beat light, buzzer. MicroPython firmware included." }, { key:"explorer", file:"/explorer.html", name:"PM_X‑1 Explorer", chip:"hw", h:500, sum:"Off‑the‑shelf — the Pimoroni Explorer (RP2350, 2.8″ LCD, 6 buttons, piezo) as a button‑driven sibling to the Kit. Edit on the web with Live sync; the device mirrors play/stop/tempo/track changes both ways." }, { key:"grid", file:"/grid.html", name:"PM_G‑1 Grid", chip:"hw", h:470, sum:"Off‑the‑shelf — a Pimoroni Pico Scroll Pack (17×7 white LED matrix + 4 buttons) on a Raspberry Pi Pico. The matrix IS the editor's lane × step pad grid in miniature; edit on the web with Live sync." }, @@ -175,7 +175,7 @@ const VERSIONS = [ const infoOf = (f) => f.startsWith("/info-") ? f : f.replace("/", "/info-"); const DEFAULT_PROG = (typeof SEED_SETLISTS !== "undefined" && SEED_SETLISTS[0] && SEED_SETLISTS[0].items[0] && SEED_SETLISTS[0].items[0][1]) || "v1;t120;kick:4;snare:4=.X.X;hat:4/2"; -let cur = "editor", userEditing = false; +let cur = "pme2", userEditing = false; const vp = $("vp"), box = $("prog"); const verOf = (k) => VERSIONS.find((v) => v.key === k); @@ -246,7 +246,7 @@ addEventListener("message", (e) => { renderPanes(); // default = each device's built-in set lists (no forced program); the box fills from what the device reports -loadVersion("editor"); +loadVersion("pme2"); /*@BUILD:include:src/chrome.js@*/ diff --git a/pm_e-2.html b/pm_e-2.html index c560fb7..5a41635 100644 --- a/pm_e-2.html +++ b/pm_e-2.html @@ -274,6 +274,11 @@ .sect > summary::before { content:"\25be\00a0"; } /* ▾ open */ .sect:not([open]) > summary::before { content:"\25b8\00a0"; } /* ▸ closed */ .sect > summary .shint { text-transform:none; letter-spacing:normal; font-size:11px; opacity:.7; } + /* the two device controls in the header — matching pills (state shown via colour/border in JS) */ + .devctrl{ font:inherit; font-size:12px; line-height:1.5; padding:3px 10px; border-radius:7px; + border:1px solid var(--edge); background:transparent; color:var(--muted); + white-space:nowrap; cursor:pointer; } + .devctrl:hover{ border-color:var(--muted); } @@ -288,8 +293,11 @@

PM_E‑2 PolyMeter Editor Notation

-
- ◎ connect device +
+ ◎ connect device +
@@ -311,7 +319,7 @@
 
-
+
@@ -1211,10 +1219,14 @@ async function loadFromDevice() { let _midiAccess = null, _midiOn = false, _midiFlash = 0, _midiBeat = 0, _saveCb = null, _verCb = null; function _midiInputs() { return _midiAccess ? [..._midiAccess.inputs.values()] : []; } function _midiOutputs() { return _midiAccess ? [..._midiAccess.outputs.values()] : []; } -function _isDevicePort(p) { // recognise PM_K-1 (Pico) and PM_X-1 (Pimoroni Explorer RP2350) USB-MIDI ports; +function _isDevicePort(p) { // recognise the PM devices' USB-MIDI ports by name; const n = (p.name || "").toLowerCase(); // anything unrecognised triggers a broadcast to all outputs - bad for ACK routing. return n.includes("pico") || n.includes("circuitpython") || n.includes("usb_midi") || - n.includes("pimoroni") || n.includes("explorer") || n.includes("rp2350") || n.includes("varasys"); + n.includes("pimoroni") || n.includes("explorer") || n.includes("rp2350") || n.includes("varasys") || + // native Rust firmware enumerates as e.g. "PM_G-1 Grid" / "PM_K-1" / "PM_X-1" (VARASYS / PolyMeter) + n.includes("pm_g") || n.includes("pm_k") || n.includes("pm_x") || + n.includes("pm-g") || n.includes("pm-k") || n.includes("pm-x") || + n.includes("grid") || n.includes("polymeter"); } function _send(bytes) { // send only to the PM_K-1 (not loopback ports like "Midi Through", which just echo) const outs = _midiOutputs(), dev = outs.filter(_isDevicePort); @@ -1260,10 +1272,14 @@ function _heartbeat(on) { // while Device audio is on: tell the device a host } function updateMidiBtn() { const b = $("midiBtn"); if (!b) return; - if (!_midiOn) { b.textContent = "🎹 Device audio"; b.classList.remove("primary"); b.style.boxShadow = ""; return; } - const names = _midiInputs().map((i) => i.name || "MIDI"); - b.textContent = names.length ? "🎹 " + names[0].slice(0, 16) : "🎹 no device"; - b.classList.add("primary"); + if (!_midiOn) { // off: muted pill, matching the device badge's idle look + b.textContent = "🎹 Device audio"; + b.style.color = "var(--muted)"; b.style.borderColor = "var(--edge)"; b.style.boxShadow = ""; + return; + } + const names = _midiInputs().map((i) => i.name || "MIDI"); // on: green, like the connected badge + b.textContent = "🎹 " + (names.length ? names[0].slice(0, 16) : "audio on"); + b.style.color = "#2fe07a"; b.style.borderColor = "#2fe07a"; } async function toggleDeviceAudio() { if (_midiOn) { _midiOn = false; _heartbeat(false); updateMidiBtn(); return; } // inputs stay bound (for Save ACKs)