Skip to content

feat(editor): bootstrap tempo from click stems and filename BPM - #311

Merged
ChrisBeWithYou merged 2 commits into
mainfrom
feat/editor-metronome-bootstrap
Jul 19, 2026
Merged

feat(editor): bootstrap tempo from click stems and filename BPM#311
ChrisBeWithYou merged 2 commits into
mainfrom
feat/editor-metronome-bootstrap

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

The next TEMPO-ASSIST charrette slice (design A's name-based easy win + design B), after the map-health worklist (#310).

G bootstraps off a shipped metronome stem

With no tempo guide locked, Suggest (G) detects a stem named like a click track ("Click", "Metronome" — word-bounded, so "Clickbait" and the master never match) and analyzes it with the existing metronome engine instead of the mix, announcing the choice in the status bar with a hint that ♩ pins or changes it.

  • A locked guide stays supreme — the lock is the user's explicit choice, and locking the master as a plain-audio guide is the opt-out.
  • Nothing persists — the bootstrap is an analysis default, never a session edit; the default track session stays default (pinned by test).
  • Post-await revalidation extends to the bootstrap route: a lock arriving on another source, the stem vanishing/renaming, or its url/offset changing all discard the stale analysis.
  • An undecodable click falls back to ordinary mix analysis instead of erroring out.

Filename BPM seeding (opportunistic, never required)

Song-147bpm.mp3-style names parse as a soft tempo prior (standalone 2–3 digit values next to "bpm" in either order, 40–300 sanity window; longer digit runs are treated as hashes/ids and rejected):

  • The create flow's starter grid is spaced at the parsed BPM instead of the hardcoded 120 (uploaded file's name first, then the create modal's track rows master-first, then the audio_url basename).
  • Song Fit ▸ Set constant tempo pre-fills the value (editable — a wrong hint costs one keystroke). The hint scans each source's name, id, then URL basename: a built pack collapses display names to title/ids, but a live create/import session's upload-cache path keeps the original file name.
  • No BPM in any name → byte-identical behavior to today (pinned: the default seed equals the old literal).

Verified end-to-end (testbed, isolated preview on :8002)

  • A pack created from Test Song-147bpm.wav + Metronome.wav — via the API and via the Create New Arrangement modal — lands with song_timeline.json seeded at 147 (0.408163s beat spacing, tempos [{time: 0, bpm: 147}]).
  • Loading it and pressing G (menu path): status shows Analyzing the click stem “metronome” (auto-detected)… then Suggested 1 barline from the click stem “metronome” (♩ on its row pins or changes the guide)…, ghost handle on the proposed downbeat.
  • Set constant tempo pre-fills 147 with the pre-filled label after Replace Audio with the bpm-named file; the no-hint session correctly shows the plain label and empty field.

Suites: 210/210 JS, 338 passed pytest, lint clean. New coverage: tests/tempo_bootstrap_seed.test.mjs (detector table, route supremacy/bootstrap/null, no-persistence, revalidation, BPM parse table, hint order/URL fallback), tests/test_filename_bpm.py (parse table, seed-grid equivalence + spacing + 6-decimal precision).

USER-GUIDE §6 updated (auto-detection + prefill notes); CHANGELOG entries added.

🤖 Generated with Claude Code

https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW

Summary by CodeRabbit

  • New Features

    • Audio filenames containing a valid BPM hint now pre-fill tempo settings and initialize the chart grid at that tempo.
    • Pressing G can automatically bootstrap the metronome from a detected click or metronome stem.
    • Locked tempo guides are respected, with fallback behavior when click audio cannot be decoded.
    • Status messages now identify whether tempo suggestions come from a guide or click track.
  • Documentation

    • Updated the user guide with filename tempo hints and expanded metronome behavior instructions.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ChrisBeWithYou, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d576edbc-a291-4536-9264-da8128ca8f52

📥 Commits

Reviewing files that changed from the base of the PR and between e803bdd and 0fe848f.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/USER-GUIDE.md
  • routes.py
  • src/input.js
  • src/song-fit.js
  • src/track-session.js
  • tests/tempo_bootstrap_seed.test.mjs
  • tests/test_filename_bpm.py
📝 Walkthrough

Walkthrough

The PR adds click-stem tempo bootstrapping for suggest-fit, validates asynchronous analysis requests, and derives BPM hints from audio filenames for tempo dialogs and starter grids. It also adds documentation and automated tests covering routing, parsing, fallback behavior, and timing.

Changes

Tempo assistance

Layer / File(s) Summary
Click-source detection and route selection
src/track-session.js, src/input.js, docs/USER-GUIDE.md, CHANGELOG.md, tests/tempo_bootstrap_seed.test.mjs
Click or metronome stems are detected and used for tempo analysis unless a locked guide overrides them.
Async tempo analysis flow
src/input.js, tests/tempo_bootstrap_seed.test.mjs
Suggest-fit analyzes routed stems, revalidates requests after async decoding, falls back to mix analysis, and updates status messaging.
Filename BPM parsing and seeding
src/song-fit.js, routes.py, tests/tempo_bootstrap_seed.test.mjs, tests/test_filename_bpm.py, docs/USER-GUIDE.md
BPM values from source names or URL basenames pre-fill constant-tempo prompts and determine starter-grid timing within the 40–300 BPM range.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant _editorTempoSuggestFit
  participant _tempoAnalysisRoutePure
  participant ensureGuideOnsetsShifted
  participant _tempoAnalysisRequestStillCurrentPure
  User->>_editorTempoSuggestFit: Press G to suggest tempo fit
  _editorTempoSuggestFit->>_tempoAnalysisRoutePure: Resolve locked guide or click bootstrap
  _tempoAnalysisRoutePure-->>_editorTempoSuggestFit: Return routed source
  _editorTempoSuggestFit->>ensureGuideOnsetsShifted: Decode and analyze routed audio
  ensureGuideOnsetsShifted-->>_editorTempoSuggestFit: Return onsets
  _editorTempoSuggestFit->>_tempoAnalysisRequestStillCurrentPure: Validate route, URL, and offset
  _tempoAnalysisRequestStillCurrentPure-->>_editorTempoSuggestFit: Accept or discard analysis
Loading

Possibly related PRs

Suggested reviewers: byrongamatos

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the two main changes: click-stem tempo bootstrap and filename-based BPM seeding.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-metronome-bootstrap

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@src/input.js`:
- Around line 1096-1105: The bootstrap fallback in the onsets handling block
must analyze the master mix explicitly rather than the currently active source.
Replace the `_ensureOnsetsShifted()` fallback with
`ensureGuideOnsetsShifted(...)` using the master source, then revalidate the
relevant state after the await before continuing or returning. Add a regression
test covering a non-master active source and an undecodable click fallback.
- Around line 1061-1063: Refresh the route after the asynchronous validation
step in the surrounding analysis flow, before the logic at lines 1082–1092
selects the success label or failure policy. Recompute it using the current
track-session and source state so bootstrap-to-locked-guide transitions are
honored when validation changes the route, while preserving the existing
metronome handling.
🪄 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: 12afdfae-46f0-4570-b0be-6c755355beda

📥 Commits

Reviewing files that changed from the base of the PR and between 6243cf8 and e803bdd.

📒 Files selected for processing (8)
  • CHANGELOG.md
  • docs/USER-GUIDE.md
  • routes.py
  • src/input.js
  • src/song-fit.js
  • src/track-session.js
  • tests/tempo_bootstrap_seed.test.mjs
  • tests/test_filename_bpm.py

Comment thread src/input.js Outdated
Comment thread src/input.js
@ChrisBeWithYou ChrisBeWithYou changed the title Metronome bootstrap for G + filename BPM seeding feat(editor): bootstrap tempo from click stems and filename BPM Jul 18, 2026
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-metronome-bootstrap branch from f77d3f4 to 9a35e85 Compare July 19, 2026 05:29
ChrisBeWithYou and others added 2 commits July 19, 2026 00:34
…seeding

TEMPO-ASSIST charrette, the opportunistic slice (A's name-based easy win + B):

- With no tempo guide locked, Suggest (G) auto-detects a stem NAMED like a
  click track ("Click"/"Metronome", word-bounded, never the master) and
  analyzes it with the metronome engine instead of the mix, announcing the
  choice in the status bar. A LOCKED guide stays supreme (locking the master
  as plain audio is the opt-out); nothing persists — the bootstrap is an
  analysis default, not a session edit. An undecodable click falls back to
  ordinary mix analysis. Post-await revalidation covers the bootstrap route
  (lock arriving elsewhere / stem gone / url or offset changed all discard).

- Filename BPM priors: "Song-147bpm.mp3"-style names parse as a soft hint
  (standalone 2-3 digit values by "bpm" in either order, 40-300 window).
  The create flow's starter grid is spaced at that BPM instead of the 120
  literal (uploaded file first, then the create modal's rows, then the
  audio_url basename), and Song Fit's Set-constant-tempo dialog pre-fills
  it — scanning source name, id, then the URL basename, because a live
  create/import session's upload-cache path keeps the original file name
  after display names collapse. No hint anywhere → no behavior change.

Verified end-to-end on the testbed: a pack created from "Test Song-147bpm.wav"
+ "Metronome.wav" seeds song_timeline.json at 147 (0.408163s spacing) through
both the API and the Create New modal; G on the loaded pack reports
"Analyzing the click stem "metronome" (auto-detected)" and proposes from the
click; Set constant tempo pre-fills 147 after Replace Audio.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01929LgKdJMyPGLf8N1WpEVW
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-metronome-bootstrap branch from 9a35e85 to 0fe848f Compare July 19, 2026 05:35
@ChrisBeWithYou
ChrisBeWithYou merged commit e39a16d into main Jul 19, 2026
4 checks passed
@ChrisBeWithYou
ChrisBeWithYou deleted the feat/editor-metronome-bootstrap branch July 19, 2026 05:35
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.

1 participant