Skip to content

feat(player): playback speed control with pitch preservation#241

Merged
thcp merged 6 commits into
mainfrom
feat/playback-speed
Jun 30, 2026
Merged

feat(player): playback speed control with pitch preservation#241
thcp merged 6 commits into
mainfrom
feat/playback-speed

Conversation

@thcp

@thcp thcp commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a tempo (speed) control to the player for practice use cases - slow down or speed up tracks without pitch shifting.

  • Desktop: full-width TEMPO bar in the transport footer. Slider centered so 1.0x is at the midpoint. Scroll wheel adjusts in 0.25x steps. Double-click resets to 1.0x.
  • Mobile: horizontal slider in the mixer tab transport section.
  • Range: 0.25x to 2.0x (slider input range 0-2 so 1.0 sits at 50%).
  • Speed resets to 1.0x when a new track is loaded.
  • Pitch preservation: uses a SoundTouch WSOLA AudioWorklet on the master bus - voices and instruments stay at original pitch when tempo changes. Falls back to tape-effect if AudioWorklet is unavailable.
  • Single worklet node handles all stems (not one per stem).

Test plan

  • Load a vocal track, slow to 0.75x - voice pitch should stay the same
  • Speed up to 1.5x - no chipmunk effect on vocals
  • 1.0x sounds identical to no speed control
  • Double-click TEMPO slider resets to 1.0x
  • Scroll wheel on TEMPO slider adjusts speed
  • Speed resets when loading a new track
  • Loop works correctly at non-1x speeds
  • Seek mid-playback at non-1x speed resumes correctly
  • Mobile slider works in mixer tab

thcp added 6 commits June 30, 2026 09:38
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
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.
@thcp thcp changed the title feat(player): playback speed control (0.5x to 2.0x) feat(player): playback speed control with pitch preservation Jun 30, 2026
@thcp thcp marked this pull request as ready for review June 30, 2026 10:43
@thcp thcp merged commit 2a40dc5 into main Jun 30, 2026
8 checks passed
@thcp thcp deleted the feat/playback-speed branch June 30, 2026 10:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant