Add ping-pong loop mode - #14
Merged
Merged
Conversation
Loop was on/off. Make it a tri-state (Play Once / Loop / Ping-Pong), where ping-pong reverses direction at each end of the range instead of jumping back to the start. - playback/loopmode.py: the frame-advance rule as a pure function, so the stepping behaviour (including the bounce at each end) is unit testable with no reader, timer or Qt. Preserves the existing off/loop semantics exactly. - playback/player.py: SequencePlayer tracks a playback direction and steps through loopmode.advance(); a scrub resets to forward. set_loop_mode() added to SequencePlayer, MoviePlayer and the MediaPlayer facade; set_loop(bool) is kept and maps onto it. - constants/__init__.py: LOOP_MODES. - widgets/__init__.py: a "Loop Mode" submenu under Playback. loop_mode is the single source of truth and syncs the menu action, the submenu and the timeline loop button. The mode (including ping-pong) now survives a media load. Limitation: ping-pong reverses for image sequences only. Movie playback here is time-driven and audio-synchronized (packets decode forward into a queue against a monotonic clock), so it cannot run backwards without backward decoding and breaking A/V sync; movies therefore treat ping-pong as a normal loop. This is documented on MoviePlayer.set_loop_mode. (reviewapp gets ping-pong for free because it frame-steps with no audio at all.) Tests: tests/test_loopmode.py covers preserved off/loop behaviour, the bounce at both ends, a full ping-pong cycle (1-2-3-4-3-2-1-2...), no endpoint repeated on a bounce, and degenerate single-frame/empty ranges. 9 tests pass. Depends on the test harness (PR D-Mad#7). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0177bi2WkPjrGytrarheKFDc Claude-Session-Id: 45a11a50-85fe-4349-b853-e1db9cd20276
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Loop was on/off. This makes it a tri-state — Play Once / Loop / Ping-Pong — where ping-pong reverses direction at each end of the range instead of jumping back to the start.
What's included
playback/loopmode.py- the frame-advance rule as a pure function, so the stepping behaviour (including the bounce at each end) is unit-testable with no reader, timer, or Qt. Preserves the existing off/loop semantics exactly.playback/player.py-SequencePlayertracks a playback direction and steps throughloopmode.advance(); a scrub resets to forward.set_loop_mode()added toSequencePlayer,MoviePlayerand theMediaPlayerfacade;set_loop(bool)is kept and maps onto it.constants/__init__.py-LOOP_MODES.widgets/__init__.py- a "Loop Mode" submenu under Playback.loop_modeis the single source of truth and syncs the menu action, the submenu, and the timeline loop button. The mode (including ping-pong) now survives a media load.Known limitation (please read)
Ping-pong reverses for image sequences only. FrameDeck's movie playback is time-driven and audio-synchronized — packets decode forward into a queue against a monotonic clock — so it cannot run backwards without backward decoding and breaking A/V sync. Movies therefore treat ping-pong as a normal loop, documented on
MoviePlayer.set_loop_mode.(reviewapp gets ping-pong for free because it frame-steps with a RAM cache and has no audio at all. Making it work for FrameDeck movies would mean a frame-stepping playback mode — a much larger change, happy to do it separately if you want it.)
Test evidence
Covers preserved off/loop behaviour, the bounce at both ends, a full ping-pong cycle (
1-2-3-4-3-2-1-2...), no endpoint repeated on a bounce, and degenerate single-frame/empty ranges.Verification notes
Dependencies
Branched on the test harness (#7).
🤖 Generated with Claude Code