feat: improve loop practice with continuous repeats and clearer controls#1016
feat: improve loop practice with continuous repeats and clearer controls#1016vo90 wants to merge 3 commits into
Conversation
📝 WalkthroughWalkthroughThe PR replaces direct A/B loop handling with a preference-aware controller, integrates loop restart policy into playback seeking and starts, adds frozen highway timing for count-ins, updates playback contracts, and introduces Practice & Loops UI with v3 timeline and indicator support. ChangesUnified A/B Loop Controller
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai full review |
✅ Action performedFull review finished. |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
static/js/juce-audio.js (1)
900-919: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winOnly set
restartActiveLoopWhilePlayingon the play-following pathstatic/js/juce-audio.js:900-919
In the pause-only branch, a seek whileS.isPlayingis still true can be rerouted into_restartLoopFromOutside()beforejucePlayer.pause()runs, which briefly restarts playback/count-in and then immediately stops it.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/js/juce-audio.js` around lines 900 - 919, The seek options in the wantsPause branch currently always enable restartActiveLoopWhilePlaying; restrict that option to the play-following path by setting it false or omitting it when forUpcomingPlay is false. Preserve the existing pause flow and enable loop restarting only when the seek will be followed by playback.static/capabilities/playback.js (1)
1034-1054: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
media.loop.lastRestartAtgoes stale relative toloop.lastRestartAton every valid loop-restarted event.
_updateLoopFromSnapshot()clonescurrentSession.loopintocurrentSession.media.loopinternally, but thelastRestartAttimestamp is only applied afterward (line 1051) directly oncurrentSession.loop. The clone captured inmedia.looptherefore keeps the previouslastRestartAt(inherited via the...currentSession.loopspread) instead of the new timestamp, so the two loop snapshots diverge on every restart with valid A/B bounds.🐛 Proposed fix: apply the timestamp before the clone
else if (name === 'loop-restarted') { const startTime = _number(source.loopA, null); const endTime = _number(source.loopB, null); if (startTime != null && endTime != null) { // The core loop bridge emits one legacy loop:restart event for // both the initial pass and later wraps. Promote a previously // armed snapshot to active here without requiring a duplicate // loop-set event. _updateLoopFromSnapshot({ ...currentSession.loop, startTime, endTime, enabled: true, state: 'active', requesterId: source.requesterId, + lastRestartAt: _now(), }); + } else if (currentSession.loop) { + currentSession.loop.lastRestartAt = _now(); + currentSession.media.loop = _clone(currentSession.loop); } - if (currentSession.loop) currentSession.loop.lastRestartAt = _now(); } else if (name === 'loop-stale') {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@static/capabilities/playback.js` around lines 1034 - 1054, Update the loop-restarted handling around _updateLoopFromSnapshot so a valid restart assigns the new lastRestartAt timestamp to the loop snapshot before cloning it into currentSession.media.loop. Preserve the existing timestamp update for currentSession.loop and ensure both loop snapshots receive the same timestamp on valid events.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@static/js/section-practice.js`:
- Around line 81-84: Add CSS selectors for
.section-practice-pill--section-selected in the existing section practice pill
styles within static/style.css and static/v3/v3.css, matching the intended
visual treatment of the toggled selected state. Preserve the separate --active
and --armed states managed by the loop controller.
In `@static/v3/v3.css`:
- Around line 823-843: Update the currentColor values in .v3-loop-indicator-icon
and .v3-loop-indicator-label to lowercase currentcolor, preserving the existing
styling and declarations.
---
Outside diff comments:
In `@static/capabilities/playback.js`:
- Around line 1034-1054: Update the loop-restarted handling around
_updateLoopFromSnapshot so a valid restart assigns the new lastRestartAt
timestamp to the loop snapshot before cloning it into currentSession.media.loop.
Preserve the existing timestamp update for currentSession.loop and ensure both
loop snapshots receive the same timestamp on valid events.
In `@static/js/juce-audio.js`:
- Around line 900-919: The seek options in the wantsPause branch currently
always enable restartActiveLoopWhilePlaying; restrict that option to the
play-following path by setting it false or omitting it when forUpcomingPlay is
false. Preserve the existing pause flow and enable loop restarting only when the
seek will be followed by playback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 734b81ee-c174-417a-a3de-b5f4888d26c2
⛔ Files ignored due to path filters (1)
static/tailwind.min.cssis excluded by!**/*.min.css
📒 Files selected for processing (23)
static/app.jsstatic/capabilities/playback.jsstatic/highway.jsstatic/js/count-in.jsstatic/js/juce-audio.jsstatic/js/loop-preferences.jsstatic/js/loops.jsstatic/js/section-practice.jsstatic/js/transport.jsstatic/style.cssstatic/v3/index.htmlstatic/v3/v3.csstests/js/highway_monotonic_clock.test.jstests/js/loop_api.test.jstests/js/loop_controller.test.jstests/js/loop_preferences.test.jstests/js/loop_restart.test.jstests/js/loop_ui.test.jstests/js/play_button_reroute_guard.test.jstests/js/playback_app_adapter.test.jstests/js/playback_domain.test.jstests/js/song_restart.test.jstests/js/song_seek.test.js
Signed-off-by: Viktor Olausson <viktor.olausson@gmail.com>
Signed-off-by: Viktor Olausson <viktor.olausson@gmail.com>
Signed-off-by: Viktor Olausson <viktor.olausson@gmail.com>
01ab216 to
a8db82b
Compare
|
@coderabbitai full review |
✅ Action performedFull review finished. |
What
This started with one practice improvement: let an A/B loop repeat continuously without forcing another count-in every time.
While testing that flow in feedback-desktop, it became clear that loops could also be easier to find, understand, reuse, and control. This PR expands the original change into a complete loop-practice experience:
Playback choices
These choices are saved and shared by manual A/B loops, saved loops, Practice Sections, and the editor handoff.
Technical implementation
Companion PR
Review feedback addressed
loop.lastRestartAtandmedia.loop.lastRestartAtnow stay synchronizedfeedpak surface
Checklist
CHANGELOG.md[Unreleased]updated (user-visible changes)git commit -s)Developer test checklist
Validation
npm run test:js— 1,168 passed after rebasing onto currentmaingit diff --check— cleanSummary by CodeRabbit
New Features
Bug Fixes
Documentation