Skip to content

Startup race: stems takeover pause() aborts core togglePlay()'s audio.play(), leaving Play button stuck #39

Description

@carochacs

Summary

On the very first Play press after a sloppak with stems finishes loading, core.play() can be aborted by the stems plugin's takeover sequence, leaving the transport UI showing "Paused" even though the song may (or may not) actually be playing underneath.

Sequence

  1. playSong()togglePlay() (static/js/transport.js:310) calls audio.play() (native HTML5, since the stems plugin hasn't finished detecting the sloppak yet).
  2. Concurrently, stems' onSongReady() (plugins/stems/src/main.js:772) detects the multi-stem sloppak and, once its audio shims are installed, calls nativeCorePause(core) (plugins/stems/src/main.js:815) to silence the native single-track playback before taking over with its own Web Audio-driven multi-stem transport.
  3. The in-flight native play() promise rejects with AbortError: The play() request was interrupted by a call to pause().
  4. togglePlay()'s catch block (static/js/transport.js:348-364) only has a carve-out for this exact shape of race when window._juceRerouteInProgress is set (the JUCE HTML5→native reroute case) — there is no equivalent carve-out for the stems plugin's HTML5→Web-Audio takeover, so the catch treats it as a real failure: S.isPlaying = false; setPlayButtonState(false);.
  5. Meanwhile, stems has already captured play-intent correctly (S.pendingPlay) and resumes playback on its own transport once the graph finishes building (plugins/stems/src/main.js:928) — so audio may actually be playing, but the transport button/S.isPlaying state is now out of sync with it, OR (depending on shim install timing) the button and displayed time genuinely never resume.

Impact

  • First Play click after a fresh page load intermittently shows "Paused" / doesn't produce audio, even though a second click doesn't reliably fix it.
  • Reported in practice with a 6-stem sloppak (feedpakr-converted GP8 import), Docker/Chromium, stems + stem_mixer plugins both installed.

Suggested fix

Extend the existing window._juceRerouteInProgress guard in togglePlay()'s catch block (static/js/transport.js:360) to also recognize an HTML5→stems-takeover reroute — e.g. a window._stemsRerouteInProgress flag (or equivalent) set by stems' onSongReady() around its nativeCorePause() call, mirroring how the JUCE case is handled, so togglePlay() doesn't stomp S.isPlaying/the button state when the pause was a deliberate takeover rather than a genuine failure.

Repro environment

  • docker compose up (this repo's own Dockerfile/docker-compose.yml)
  • Sloppak song with 6 stems (guitar/bass/drums/vocals/piano/other), feedpakr-imported from a GP8 file
  • stems + stem_mixer plugins both active

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions