From 8bdf0918d651faf18ee434da92c671fc8ec233f0 Mon Sep 17 00:00:00 2001 From: Misha Nasledov Date: Sun, 4 Oct 2026 14:21:22 -0700 Subject: [PATCH] web: time the MIDI out checks with a delay The worklet stamps a MIDI message with the time its frame sounds and posts it as it composes. At the page's default delay of 0, one that reaches the page a few milliseconds late is past due and goes out stamped now; headless audio has almost no output latency to absorb that, so on a loaded CI runner the clock check measured ticks 46 ms apart where 41.7 was due (master's #337 merge and several PRs), and a half-beat note once came out at 479 ms. The MIDI out block now runs at a 100 ms delay, which is what the setting is for; the delayed-note check keeps its 200, and the delay goes back to 0 at the end. The half-beat note now measures 500.0 ms exactly. --- wasm/web/pagetest.mjs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/wasm/web/pagetest.mjs b/wasm/web/pagetest.mjs index 5926467b..29cfd55f 100644 --- a/wasm/web/pagetest.mjs +++ b/wasm/web/pagetest.mjs @@ -2455,6 +2455,15 @@ try check(await page.$eval('#midioutstatus', (e) => e.textContent) === 'Fake Synth', 'and the status names the output'); + /* Timed with some delay, as anyone sending to a device sets it. + The worklet stamps a message with the time its frame sounds and + posts it as it composes; at a delay of 0 one that crosses to the + page a few milliseconds late is past due when it lands and goes + out stamped `now'. Headless audio has next to no output latency + to absorb that, so on a loaded runner the times measured below + would be the page's arrival times rather than the piece's. */ + await page.fill('#midioutdelay', '100'); + await page.dispatchEvent('#midioutdelay', 'change'); await page.evaluate(() => { window.midiSent.length = 0; }); await page.click('#play'); await page.waitForTimeout(2600); @@ -2525,7 +2534,7 @@ try `a delayed note waits in the page until shortly before its ` + `time: handed over ${lead.toFixed(1)} ms ahead`); - await page.fill('#midioutdelay', '0'); + await page.fill('#midioutdelay', '100'); await page.dispatchEvent('#midioutdelay', 'change'); await page.selectOption('select.midiroute', '@synth'); @@ -2564,6 +2573,8 @@ try `MIDI clock goes out: Start, ${ticks.length} ticks ` + `${gap.toFixed(1)} ms apart, Stop`); + await page.fill('#midioutdelay', '0'); + await page.dispatchEvent('#midioutdelay', 'change'); await page.$eval('input.midiclock', (e) => e.click()); await page.evaluate(() => {