Skip to content

Add persistent playback speed controls - #29

Merged
D-Mad merged 2 commits into
mainfrom
agent/pr23-review
Jul 15, 2026
Merged

D-Mad merged 2 commits into
mainfrom
agent/pr23-review

Conversation

@D-Mad

@D-Mad D-Mad commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • rebase Eric Levy's playback-speed feature from Add playback speed multiplier #23 onto current main
  • add 0.25x–4x transport controls for movie and image-sequence playback
  • silence un-resampled movie audio away from 1x to prevent drift/pitch errors
  • preserve selected speed across source changes, playlist transitions, A/B Compare and .fdplaylist restore
  • add a regression test for concrete player replacement

Review fix

The original feature stored speed only on the concrete MoviePlayer/SequencePlayer. Loading a new shot replaced that object at 1x while the UI still displayed 2x, and a later B-side Compare player could run at a different speed. The wrapper now owns the transport setting and applies it to every replacement player.

Original feature authored by @emlcpfx in #23.

Validation

  • playback-speed test module passed (37 tests)
  • full pytest -q passed
  • git diff --check

Replaces #23.

emlcpfx and others added 2 commits July 15, 2026 11:05
Playback > Speed, a timeline combobox, and Ctrl+[ / Ctrl+] / Ctrl+\ select
0.25x through 4x.

The two players keep time differently, so speed applies differently to each,
and playback/speed.py holds the arithmetic so it can be tested on its own:

- SequencePlayer is timer-driven: it fires every 1000/fps ms and steps one
  frame. Speed scales that interval, so every frame is still shown, just
  sooner or later. A running timer is re-armed immediately rather than
  waiting for the next tick.
- MoviePlayer is clock-driven: it reads a monotonic elapsed time and presents
  whichever decoded frame is due. Speed scales the elapsed clock, so more (or
  fewer) frames fall due per tick. Presentation stays timestamp-driven, so
  frames still appear in order and none are decoded twice.

Changing speed mid-playback re-anchors the movie clock first: it banks the
position already reached at the old speed and restarts the elapsed timer, so
the multiplier applies only from that point. Without this the whole elapsed
span is rescaled retroactively and playback jumps.

Audio is dropped at any speed other than 1x. The samples decode at their
native rate, so submitting them against a scaled video clock drifts steadily
out of sync, and playing them faster without resampling shifts the pitch.
Off-speed playback is silent, which is what a reviewer expects when they
shuttle, and it keeps A/V sync honest. The status bar says so.

An unusable fps yields no interval rather than dividing by zero, and a junk
speed falls back to 1x rather than stopping playback.

23 tests. Note SequencePlayer starts a decode QThread in its constructor and
only reset() shuts it down; the test fixtures do that, or the process dies at
exit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TexnzYbmCjjTDB8zzZuUPb
@D-Mad
D-Mad marked this pull request as ready for review July 15, 2026 04:08
@D-Mad
D-Mad merged commit 53c9ae3 into main Jul 15, 2026
3 checks passed
@D-Mad
D-Mad deleted the agent/pr23-review branch July 15, 2026 04:08
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.

2 participants