* feat(player): playback speed control (0.5x to 2.0x)
Adds a speed slider to the transport footer (desktop) and mixer tab
(mobile) so users can slow down or speed up tracks for practice.
- audioEngine: store _playbackRate, apply to new AudioBufferSourceNodes
on startSources(), and fix getCurrentTime() to account for rate so
the waveform playhead and loop detection stay accurate at non-1x speeds
- transport: applySpeed() propagates rate to both engine and streaming
paths; scroll-wheel support (+-0.25 per tick); double-click resets to 1x
- state: playbackSpeed variable + setter; speedEl/speedLabelEl DOM refs
- player: resetSpeed() called in destroyPlayer() so a new track always
starts at 1x
- mobile: speed slider in mixer transport section, state.speed reset on
track open
* fix(player): move speed control below play button, centered
* fix(player): tempo bar full-width below transport, TEMPO label + gold slider
* fix(player): center 1.0x on tempo slider (range 0-2, midpoint = 1.0)
* feat(audio): pitch-preserving tempo via SoundTouch AudioWorklet
Voices and instruments no longer pitch-shift when changing playback speed.
A WSOLA time-stretcher runs as an AudioWorkletProcessor on the master bus
so a single node handles all stems. Falls back to tape-effect if the worklet
API is unavailable.
* fix(audio): close array literal in Promise.all ([]) was missing ]