editor: MIDI 'Device audio' diagnostics (show device name + pulse on note)

User reported no computer audio + 'no device being controlled'. Add visibility to
diagnose: the button now shows the connected MIDI input's name (or 'no device'), the
toggle alert lists detected inputs, and the button pulses green on each Note-On
received — so it's clear whether the device is seen and whether notes are arriving.
Also call ensureAudio() in the message handler as a guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Me Here 2026-05-28 22:55:57 -05:00
parent 8433c5bfe2
commit aaf5c4d260

View file

@ -1117,25 +1117,35 @@ async function loadFromDevice() {
/* Device audio (Phase 3): a connected PM_K-1 sends a USB-MIDI note per click; we voice it through /* Device audio (Phase 3): a connected PM_K-1 sends a USB-MIDI note per click; we voice it through
this page's synth, so the device drives sound out the computer's speakers, locked to its clock. */ this page's synth, so the device drives sound out the computer's speakers, locked to its clock. */
let _midiAccess = null, _midiOn = false; let _midiAccess = null, _midiOn = false, _midiFlash = 0;
function _midiInputs() { return _midiAccess ? [..._midiAccess.inputs.values()] : []; }
function onDeviceMidi(e) { function onDeviceMidi(e) {
const d = e.data; if (!d || d.length < 3) return; const d = e.data; if (!d || d.length < 3) return;
if ((d[0] & 0xf0) === 0x90 && d[2] > 0) { // Note On if ((d[0] & 0xf0) === 0x90 && d[2] > 0) { // Note On
const v = d[2], gain = v >= 110 ? 1.0 : v >= 70 ? 0.6 : 0.25; // accent / normal / ghost const v = d[2], gain = v >= 110 ? 1.0 : v >= 70 ? 0.6 : 0.25; // accent / normal / ghost
try { playInstrument(GM_NUM[d[1]] || "beep", audioCtx.currentTime, gain); } catch (_) {} try { ensureAudio(); playInstrument(GM_NUM[d[1]] || "beep", audioCtx.currentTime, gain); } catch (_) {}
const b = $("midiBtn"); if (b) { b.style.boxShadow = "0 0 0 2px #2fe07a"; clearTimeout(_midiFlash); _midiFlash = setTimeout(() => b.style.boxShadow = "", 90); }
} }
} }
function _bindMidi() { if (_midiAccess) for (const inp of _midiAccess.inputs.values()) inp.onmidimessage = _midiOn ? onDeviceMidi : null; } function _bindMidi() { for (const inp of _midiInputs()) inp.onmidimessage = _midiOn ? onDeviceMidi : null; }
function updateMidiBtn() { const b = $("midiBtn"); if (b) { b.textContent = _midiOn ? "🎹 Device audio: ON" : "🎹 Device audio"; b.classList.toggle("primary", _midiOn); } } 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"; // shows the connected MIDI device
b.classList.add("primary");
}
async function toggleDeviceAudio() { async function toggleDeviceAudio() {
if (_midiOn) { _midiOn = false; _bindMidi(); updateMidiBtn(); return; } if (_midiOn) { _midiOn = false; _bindMidi(); updateMidiBtn(); return; }
if (!navigator.requestMIDIAccess) return alert("Playing the device through this computer needs the Web MIDI API — use Chrome or Edge."); if (!navigator.requestMIDIAccess) return alert("Playing the device through this computer needs the Web MIDI API — use Chrome or Edge.");
try { if (!_midiAccess) { _midiAccess = await navigator.requestMIDIAccess(); _midiAccess.onstatechange = _bindMidi; } } try { if (!_midiAccess) { _midiAccess = await navigator.requestMIDIAccess(); _midiAccess.onstatechange = () => { _bindMidi(); updateMidiBtn(); }; } }
catch (e) { return alert("MIDI access was denied."); } catch (e) { return alert("MIDI access was denied."); }
ensureAudio(); if (audioCtx && audioCtx.state === "suspended") audioCtx.resume(); ensureAudio(); if (audioCtx && audioCtx.state === "suspended") audioCtx.resume();
_midiOn = true; _bindMidi(); updateMidiBtn(); _midiOn = true; _bindMidi(); updateMidiBtn();
if (![..._midiAccess.inputs.values()].length) const names = _midiInputs().map((i) => i.name || "MIDI");
alert("No MIDI device detected yet — plug in the PM_K-1 (CircuitPython firmware) and press play on it. (It stays armed; new devices connect automatically.)"); alert(names.length
? "Device audio ON.\nMIDI input(s): " + names.join(", ") + "\nPress play on the device — the button pulses green on each note received."
: "Device audio is armed, but NO MIDI input is connected.\nPlug in the PM_K-1 running the CircuitPython firmware — it should appear here as a MIDI device. (New devices connect automatically.)");
} }
// Apply a shared link on load. Returns true if it set the metronome state. // Apply a shared link on load. Returns true if it set the metronome state.