diff --git a/simulator/static/app.js b/simulator/static/app.js index dca6b71..7cba5a1 100644 --- a/simulator/static/app.js +++ b/simulator/static/app.js @@ -920,8 +920,29 @@ function devLiveReload() { // (e.g. on an altitude change). audio on = the current scale's soundtrack. const aud = $("aud"); let audUrl = null; // soundtrack url currently loaded (null = silent) +let audLastErr = ""; // last play() rejection / element error (for the readout) const FADE_MS = 500; +// --- Live audio status readout (diagnostic) — so a "no sound" report is legible --- +const AUD_ERR = ["", "aborted", "network", "decode", "format-not-supported"]; +function audioStatusText() { + if (!$("audio").checked) return "audio: off"; + if (aud.error) return `audio: MEDIA ERROR ${aud.error.code} (${AUD_ERR[aud.error.code] || "?"})`; + if (audLastErr) return "audio: " + audLastErr; + if (aud.paused) return `audio: on, PAUSED (readyState ${aud.readyState})`; + return `audio: ▶ playing ${aud.currentTime.toFixed(1)}s · vol ${Math.round(aud.volume * 100)}% · rs ${aud.readyState}`; +} +function updateAudioStatus() { + const el = $("audio-status"); + if (!el) return; + el.textContent = audioStatusText(); + const playing = $("audio").checked && !aud.paused && !aud.error && !audLastErr; + el.classList.toggle("playing", playing); + el.classList.toggle("blocked", !!aud.error || !!audLastErr); +} +aud.addEventListener("error", () => { audLastErr = ""; updateAudioStatus(); }); +setInterval(updateAudioStatus, 400); + function fadeVolume(el, to, ms, done) { // setInterval (not requestAnimationFrame): rAF throttles/pauses when the tab is // backgrounded, which would stall the gain ramp; a timer fires regardless. @@ -947,8 +968,12 @@ function soundtrackUrl() { function playUrl(url) { aud.src = mediaUrl(url); aud.volume = 0; + audLastErr = ""; const pr = aud.play(); - if (pr) pr.catch(() => {}); + if (pr) { + pr.then(() => { audLastErr = ""; updateAudioStatus(); }) + .catch((e) => { audLastErr = "play BLOCKED: " + ((e && e.name) || e); updateAudioStatus(); }); + } fadeVolume(aud, 1, FADE_MS); } diff --git a/simulator/static/index.html b/simulator/static/index.html index 974f3e1..7f33646 100644 --- a/simulator/static/index.html +++ b/simulator/static/index.html @@ -40,6 +40,15 @@ Audio +
audio: off
+ + +
+ Audio check (diagnostic) +

Press play below to test the soundtrack file directly in the + browser's own player — this bypasses all app code. If you hear it here but + not from the Audio toggle, the file is fine and it's a code/autoplay issue.

+
diff --git a/simulator/static/style.css b/simulator/static/style.css index 35d47cc..6c96e27 100644 --- a/simulator/static/style.css +++ b/simulator/static/style.css @@ -49,6 +49,10 @@ main { display: flex; gap: 1rem; padding: 1rem; flex-wrap: wrap; align-items: fl .hidden { display: none; } /* Stack the two Output toggles (Video / Audio) with a little breathing room. */ .dev-switch + .dev-switch { margin-top: 0.45rem; } +/* Live audio status readout (diagnostic) — turns green when actually playing. */ +.audio-status { margin-top: 0.5rem; font: 11px/1.4 monospace; color: #9ab; } +.audio-status.playing { color: #4e9; } +.audio-status.blocked { color: #f97; } .panel { flex: 0 0 280px; display: flex; flex-direction: column; gap: 0.8rem; max-height: calc(100vh - 2rem); overflow-y: auto; position: sticky; top: 1rem; }