Skip to content

[Feature]: Better playback speed control and fix for stuttering #701

Description

@goermezer

What problem does this solve?

stemdeck-speed-slider-and-dropout-fix.patch

The playback control never worked like I wanted. Also when playing slower, then playback begins to stutter. So I thought lets try vibe coding. To my surprise the result is impressive. No stuttering anymore and a smooth slider for speeds between 80% and 100%.

Since I can not create a pull request, I`m sharing a patch file against origin/main (ba97092) .

git checkout main && git pull
git checkout -b speed-slider-and-dropout-fix
git apply /path/to/stemdeck-speed-slider-and-dropout-fix.patch
git commit -am "fix(ui): practice-speed slider; fix(engine): dropout-free slowed playback"
git push -u origin speed-slider-and-dropout-fix # then open the PR from that branch

Description:

fix(player): practice-speed slider (0.8x-1.0x) and dropout-free slow playback

The Speed control was a two-position 0.75x/1x switch; some fills only
need a small nudge, so it is now a continuous 0.8x-1.0x dial in 0.01
steps (desktop footer and mobile player share the same range and step).
0.8x stays the floor because below that time-stretch artefacts dominate
(#433), and the snap-to-step keeps the readout and the applied rate from
ever disagreeing.

Backing the dial up with anything below 1x exposed a scheduling bug in
the chunked engine. _scheduledTo is measured in source seconds, which
advance at 1.0x, but the lookahead gate compared it against
getCurrentTime(), the output playhead scaled by _playbackRate. At
rate < 1 that playhead shrinks faster than the wall clock, so the engine
read itself as further ahead than it really was, waited too long to
schedule, and let the pre-scheduled margin drain at (1 - rate) seconds
per second until the chunk sources ran dry: audible gaps that arrived
sooner the slower the speed (~2 min at 0.8x, ~3 min at 0.9x).

The gate now uses an input-rate clock (_scheduledPlayhead), the media-
time inverse of the scheduling _scheduleNext already performs, so at
any rate the engine keeps chunk sources at least LOOKAHEAD_SEC ahead.
Rate 1x behaviour is unchanged.

Mobile: the speed dial matches the desktop range and step.
Tests: transpose.spec.mjs drives the slider instead of the removed
buttons.

Proposed solution

Check if provided patch can be accepted.

Alternatives considered

No response

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. thcp commented on Sep 29, 2026

    @thcp
    Collaborator

    Thanks for this, and for tracking down where the stutter comes from. You are right about the cause. The lookahead gate compares source seconds against the output playhead, and below 1x those drift apart until the sources run dry. That is a good catch.

    I split the two halves of the patch, because they are different kinds of change.

    The stutter. I opened #722 for it and will put up a fix shortly, based on your approach with two small adjustments. The new clock follows the source rate, so it also holds in the tape-effect fallback where the sources really do play slower. It also leaves out the pipeline latency, because that delay happens after the sources, not before. You are credited in the commit.

    The slider. The two fixed speeds were a deliberate choice in #433, so moving to a continuous dial is a design question I want to think about on its own rather than fold into a bug fix. I will leave this issue open for that. The patch also no longer applies cleanly against main, since the transport code changed in #713.

    Let me know if the stutter comes back for you once the fix lands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions