fix(player): drive playback through the audio engine, not the silent multitrack - #536
Merged
Conversation
…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
marked this pull request as ready for review
August 31, 2026 21:18
This was referenced Aug 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #515. Independent; branches off
0.16.1.The bug
engineMode()returns"chunked"unless the user setlocalStorage["stemdeck.audioEngine"] = "0". On that pathaudioEngineowns the clock and the multitrack is mounted withurl: nullfor visuals only -- its own comment says so: "The engine emits no play/pause events (the multitrack stays silent)".transport.jshandled this everywhere viaaudioEngine ?? multitrack(lines 74, 386, 423, 596).main.jsimported onlymultitrackand called it bare.On the default configuration, therefore:
cursor: pointeroverlay, click or drag moved neither playhead nor audio[/](seek +/-5s)I(loop in at playhead)O(loop out at playhead)max(0, loopStart + 0.5)Spaceand ruler clicks were fine -- they live intransport.js.The fix
Rather than patching five call sites,
transport.jsnow exports the accessor it was already using internally, plussetPlayheadTime.Seeking goes through
setPlayheadTime, which also updates the playhead marker, footer times and presence playhead. The oldmultitrack.setTimecall 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_pathin #506: one accessor, no re-deriving.multitrackis no longer imported bymain.jsat all.Also fixed
The keydown guard excluded
HTMLInputElementbut notHTMLTextAreaElement, 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 assertsmain.jscontains no baremultitrack.setTime/getCurrentTime/getDuration, that the accessor exists and prefers the engine, and that the textarea guard is present.Confirmed not vacuous -- reverting
main.jsandtransport.jsfails all 8 checks.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.