wasm: fix track stopping after seconds — backpressure + drain-before-advance
The decode loop's happy path has no real await, so it chains through segments
on microtasks only. The worklet's 'level' reports (which update wlQueued) are
macrotask port messages and never got processed during the burst — wlQueued
stayed 0, backpressure never engaged, the whole track decoded and posted in
seconds, then the segment loop "finished" → advanceAfterEnd → stop →
flushWorklet dropped everything still queued. You heard only the few seconds
that played during the burst. (Masked until now: before the port-routing fix,
stop()'s flush was silently dropped, so the fully-posted track played on.)
Fixes:
- postPcm counts queued frames locally (wlQueued += frames); the worklet's
periodic reports overwrite with the truth. Backpressure now engages
regardless of message timing, capping memory at ~3 s of queued audio.
- waitForDrain(): after a track finishes decoding (segment loop AND buffered
path), wait for the worklet to actually PLAY the queue before
advanceAfterEnd — never flush un-played audio. Handles pause-during-drain,
seek-rewind (buffered path streams again), and a 2 s stall watchdog so
broken reports can't wedge queue advancement.
- startTrack pauses the worklet during load/prebuffer so track starts are
clean (segment loop unpauses at prebuffer, buffered path on play).
- decodeSegment returns success so metadata isn't marked as read when the
first segment is pure ID3/album-art bytes.