Repository navigation
Clip > Loop... opens Clip Looping: Number of Loops, Loop Length, or until the next clip or the session end - #1030
Conversation
…ntil the next clip or the session end Clip > Loop... and Cmd+Alt+L looped the selected clip four times at once. They now open the Clip Looping dialog. Number of Loops counts the clip and its loops together, 4 by default as before. Loop Length is typed in the main counter's time format, and the last loop is cut short when the length is not a whole number of loops. Loop Until End of Session or Next Clip loops up to the next clip on the track, or else to the end of the last clip in the session. clip.loop takes the new fill option, and its length option now cuts the last loop short instead of rounding down to whole loops. A fill or a length that needs more than 1,000 loops is refused with an error instead of stopping at 1,000 loops. The cut-short loop is trimmed with Clip::trim_end_to, which shortens its fades to fit and removes a sync point that falls past the cut. Unloop takes a shorter last clip as part of the loop when it shares the loop, no clip of the loop starts where it ends, and either a whole copy comes before it or it holds the same material as the origin just before it. It removes a short last loop too, and leaves both halves of a split clip in place.
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Loop targets and copy generation crates/engine/src/cmd/clip.rs |
clip.loop supports fill-to-next-clip or session-end targets, trims a final copy to fit, skips nonpositive-length clips, and rejects requests above 1,000 loops. |
Shortened iterations and unloop handling crates/engine/src/cmd/clip_more.rs |
Loop-chain detection recognizes shortened final iterations. Tests cover trimming, unlooping edited copies, split clips, fill boundaries, and the loop limit. |
Dialog, menu wiring, and validation crates/ui-egui/src/dialogs.rs, crates/ui-egui/src/menus.rs, crates/ui-egui/tests/it/*, ROADMAP.md |
The menu opens a dialog for count, length, or fill looping. The dialog parses duration inputs and submits clip.loop; tests cover parameter selection and the end-to-end fill flow. The roadmap records the feature. |
Priority:
Estimated code review effort: 3 (Moderate) | ~25 minutes
Change: Feature
Sequence Diagram(s)
sequenceDiagram
participant Menu as Menu
participant Dialog as Clip Looping dialog
participant Engine as clip.loop command
Menu->>Dialog: Open dialog
Dialog->>Engine: Submit count, length, or fill parameters
Engine->>Engine: Generate copies and trim final copy when needed
Possibly related issues
- [Audit] Clip-multiplying edits end in a quadratic hang #915 — The PR adds a 1,000-loop limit to
clip.loop, but the available summary does not establish that it resolves the issue’s broader excessive-edit-work problem.
Possibly related PRs
- storytold/soundcraft#960 — This PR calls
trim_end_towhen trimming a shortened final loop copy.
Suggested reviewers: echelon, claudio911015, grovebit
Merge Risk: 
Minimal · up to d5be1
-
The identified looping edge cases do not block merging; the change is ready after normal checks.
Security architecture risk: 
Low · up to d5be1
-
The changes remain within the active session and preserve whole-edit rollback. However, the new dialog does not retain its original clip targets, so a selection change before confirmation can redirect the edit.
Retained concerns
- Low · architecture · inferred: The new loop dialog captures duration and start context from the opening selection but retains no clip IDs and submits only count, length, or fill. The engine resolves the selection again when OK executes. A selection changed through another authorized editing path can therefore redirect the pending operation while retaining values prepared for the original clip. This is an edit-ownership risk within the active session; whole-document undo contains its consequences.
Security review details
-
Security Blast Radius
- inferred — The inspected mutation scope is the active session’s targeted clips and their tracks. Fill reads the session-wide content endpoint but does not independently select other tracks for mutation. Its copy limit is per target, not a command-wide resource budget.
Trust Boundaries and Controls
- observed — Direct app.run calls still execute through Engine::execute rather than opening the dialog. Length retains the existing position parser and bounds, count is clamped to 1–512, and endpoint-driven looping rejects excessive repetitions. These controls constrain edit parameters without establishing authorization for external command transports.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passedFull details: Docstring Coverage
-
Explanation
Docstring coverage is 24.14% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 29 functions across 6 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches
-
🧪 Generate unit tests (beta)
-
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Warning
Some tools did not complete. Review the errors below.
🔧 Clippy (1.99.0)
-
Clippy execution failed
Comment @coderabbitai help to get the list of available commands.
On
main, Clip > Loop... and Cmd+Alt+L loop the selected clip four times at once, with no dialog. The reference guide's "Clip Looping" section (Pro Tools Reference Guide 2020.12, Chapter 42, pages 931 and 932) describes a Clip Looping dialog with Number of Loops; Loop Length, typed in the main timebase, where "the last loop iteration will be truncated" when the length is not a whole number of loops; and Loop Until End of Session or Next Clip: https://web.archive.org/web/20210805202452/https://resources.avid.com/SupportFiles/PT/Pro_Tools_Reference_Guide_2020.12.pdfThis changes what the menu item and the shortcut do: they open the dialog.
clip.looprun as a command (MCP, the CLI,engine.execute) still opens nothing.clip.loop'scount, which counts the clip and its loops together (4 by default, as before). With Loop Length chosen, OK is grayed out until the field reads as a duration longer than zero.clip.loop: the newfill: trueloops up to the next clip on the track, or else to the session end (the end of the last clip on any track).lengthnow cuts the last loop short to fit, where it used to round down to whole loops. The short last loop is trimmed withClip::trim_end_to, which shortens its fades to fit and removes a sync point that falls past the cut. A fill or length that needs more than 1,000 loops is refused with an error and no edit, wherelengthused to stop at 1,000 loops.clip.unloop: a shorter last clip counts as part of the loop when it shares the loop, no clip of the loop starts where it ends, and either a whole copy comes before it or it holds the same material as the origin just before it. Remove and Flatten take a short last loop too, and leave both halves of a split clip in place.ROADMAP.mdgets a progress log line.The Enable Crossfade option is not part of this pull request. SoundCraft takes the session end to be the end of the last clip on any track, so the last clip in a session has nothing to fill up to and gets no loops.
New tests, each failing without the change:
loop_length_cuts_the_last_iteration_short(engine): 250 samples of a 100-sample clip make a whole loop and a 50-sample one; 300 make two whole loops.a_short_last_iteration_fits_its_fades_and_drops_a_sync_point_past_the_cut(engine).a_fill_or_length_past_the_loop_limit_is_refused_without_an_edit(engine): 1,001 loops are refused, and 1,000 still loop.unloop_removes_a_short_last_iteration_too(engine), also after an edit to the source note or to the short loop's note: it passes onmain, wherelengthnever makes a short loop, and fails when Unloop takes only clips of equal length.unloop_keeps_both_halves_of_a_split_clip(engine): a split loop copy keeps both halves, and a split loop origin is not a loop. It passes onmainand fails when a shared loop alone makes clips of any length one loop.loop_fill_stops_at_the_next_clip(engine): the last loop ends where the next clip starts.loop_fill_without_a_next_clip_runs_to_the_session_end(engine): the loops reach the end of a clip on another track, and the last clip in the session makes none.clip_loop_opens_clip_looping_for_the_selected_clip(ui-egui): the dialog opens only with a clip selected, and an untouched length loops exactly four times even when the counter text rounds.loop_length_reads_a_duration_in_every_time_format(ui-egui): each time format reads back the length text that fills the field (format_length), for 2 s and 7.5 s.clip_looping_sends_the_chosen_option(ui-egui): each option sends itsclip.loopparams, and a length of zero or text that is not a time is refused.clip_looping_fills_up_to_the_next_clip(ui-egui, through the app): the menu opens the dialog without looping, and the third option with OK fills the gap on the demo's Kick track.Checks:
cargo test -p soundcraft-engine --lib cmd::clip_more: 15 passedcargo test -p soundcraft-ui-egui --lib --test it loop: 4 + 1 passedcargo clippy -p soundcraft-engine -p soundcraft-ui-egui --all-targets -- -D warnings -A unused-mut: no warnings (the allowedunused_mutatcrates/engine/src/autosave.rs:189is onmainon Windows)cargo fmt --all -- --check: cleancargo xtask assetsandcargo xtask layers: okOn Windows 11 (x86-64) with rustc 1.99.1, on top of
mainataa74bffwith #1017, #998 and #1018 merged in:cargo xtask cipasses its 7 steps: 1,282 tests passed, 0 failed, 1 ignored.git merge-treereports no conflicts withmainataa74bff. With #1016 (Vari-Fi) the code merges cleanly, andROADMAP.mdconflicts because both change its header line and add a line at the top of its progress log and revision tables.Summary by CodeRabbit