Skip to content

fix(player): drive playback through the audio engine, not the silent multitrack - #536

Merged
thcp merged 1 commit into
0.16.1from
fix/515-transport-clock
Aug 31, 2026
Merged

fix(player): drive playback through the audio engine, not the silent multitrack#536
thcp merged 1 commit into
0.16.1from
fix/515-transport-clock

Conversation

@thcp

@thcp thcp commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Fixes #515. Independent; branches off 0.16.1.

The bug

engineMode() returns "chunked" unless the user set localStorage["stemdeck.audioEngine"] = "0". On that path audioEngine owns the clock and the multitrack is mounted with url: null for visuals only -- its own comment says so: "The engine emits no play/pause events (the multitrack stays silent)".

transport.js handled this everywhere via audioEngine ?? multitrack (lines 74, 386, 423, 596). main.js imported only multitrack and called it bare.

On the default configuration, therefore:

control behaviour
Footer scrub bar nothing at all -- full-size cursor: pointer overlay, click or drag moved neither playhead nor audio
[ / ] (seek +/-5s) nothing
I (loop in at playhead) always wrote 0, regardless of playhead position
O (loop out at playhead) always wrote max(0, loopStart + 0.5)

Space and ruler clicks were fine -- they live in transport.js.

The fix

Rather than patching five call sites, transport.js now exports the accessor it was already using internally, plus setPlayheadTime.

Seeking goes through setPlayheadTime, which also updates the playhead marker, footer times and presence playhead. The old multitrack.setTime call did none of that, so the scrub bar would have left the marker stale even if it had worked.

Same anti-drift shape as apply_ffmpeg_path in #506: one accessor, no re-deriving.

multitrack is no longer imported by main.js at all.

Also fixed

The keydown guard excluded HTMLInputElement but not HTMLTextAreaElement, so Space in the settings log viewer started playback instead of scrolling. Filed as a separate low finding in #515; fixed here since the guard was being edited anyway.

Verification

New tests/js/transport-clock.test.mjs. It is structural rather than DOM-driven on purpose: the bug was not a function returning a wrong value, it was reaching for the wrong object. It asserts main.js contains no bare multitrack.setTime / getCurrentTime / getDuration, that the accessor exists and prefers the engine, and that the textarea guard is present.

Confirmed not vacuous -- reverting main.js and transport.js fails all 8 checks.

node tests/js/transport-clock.test.mjs   8/8
npm run test:js                          48/48
node --check                             main.js, transport.js OK

One thing I did not do

There are 83 Playwright e2e tests but none covers the scrub bar. A real drag-and-assert-playhead test would be stronger than the structural check. I did not add one because the e2e suite needs a processed track fixture and that is a bigger lift than this fix warrants -- worth knowing the gap exists.

…multitrack

engineMode() returns "chunked" unless the user has set the audioEngine flag to
"0", and on that path audioEngine owns the clock while the multitrack is
mounted with url: null for visuals only. transport.js handled this everywhere
via `audioEngine ?? multitrack`; main.js imported only `multitrack` and called
it bare.

So on the default configuration:

- The footer scrub bar did nothing. It is a full-size cursor: pointer overlay,
  and clicking or dragging anywhere on it moved neither the playhead nor the
  audio.
- [ and ] seeked the silent multitrack, so nothing happened.
- I and O read multitrack.getCurrentTime(), which is pinned at 0 there, so
  "set loop in at playhead" always wrote 0 no matter where the playhead was,
  and "set loop out" always wrote max(0, loopStart + 0.5).

Space and ruler clicks were unaffected because they live in transport.js.

Rather than patching five call sites, transport.js exports the accessor it was
already using internally, plus setPlayheadTime. Seeking now goes through
setPlayheadTime, which also updates the playhead marker, footer times and
presence playhead -- none of which the old multitrack.setTime call did, so the
scrub bar would have left the marker stale even had it worked. Same anti-drift
shape as apply_ffmpeg_path in #506.

The keydown guard excluded HTMLInputElement but not HTMLTextAreaElement, so
Space in the settings log viewer started playback instead of scrolling. Fixed
alongside, since the guard was being edited anyway.

The test is structural rather than DOM-driven: the bug was not a wrong value
from a function, it was reaching for the wrong object, so it asserts main.js
holds no bare multitrack playback calls. All 8 checks fail against the previous
code.

Refs #515
@thcp
thcp marked this pull request as ready for review August 31, 2026 21:18
@thcp
thcp merged commit fb23927 into 0.16.1 Aug 31, 2026
8 checks passed
@thcp
thcp deleted the fix/515-transport-clock branch August 31, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant