wasm: Rockbox pcmbuf crossfade in JS + crossfade settings UI
Port the original pcmbuf crossfade (via its faithful extraction in
crates/rockbox-playback/src/crossfade.rs) into the decoder worker — no second
player needed: like pcmbuf, we mix at the PCM level into the single output
stream.
Worker:
- Q16 gains (MIXFADE_UNITY), Bresenham MixFader ramps, mixfade_sample
rounding and saturating clip16 — byte-faithful to apps/pcmbuf.c.
- Same parameters/semantics as the firmware: mode (off / auto track change /
manual skip / shuffle / shuffle-or-manual / always), fade-out delay 0-7 s +
duration 0-15 s, fade-in delay + duration, and fade-out mixmode
(crossfade = both fade, mix = outgoing stays full volume).
- Output pipeline: crossfade mixer -> tail holdback -> worklet. The newest
fade_out_delay+duration seconds are held back so a transition always has an
outgoing tail; natural end hands it to the next track (no flush, seamless),
manual skip fades from ~now (worklet high-water drops to 0.6 s when a
manual-capable mode is on so the response is quick).
- finalizeCrossfade plays the tail's fade out when the incoming track is
shorter than the region; per-track elapsed epoch (trackBase) keeps the
progress display sane across unflushed transitions.
API: setCrossfade(mode, {fadeOutDelay, fadeOutDuration, fadeInDelay,
fadeInDuration, mixMode}) with CrossfadeMode + CrossfadeMixMode exported as
TypeScript enums (raw ints also accepted); persisted and restored like the
other settings.
Example: Crossfade card in the DSP panel (mode, four fade sliders with
Rockbox ranges, mixmode), persisted via jotai.