Skip to content

refactor(editor): extract beatOf/timeOf tempo-map converter (Phase A1) - #133

Merged
byrongamatos merged 3 commits into
mainfrom
feat/editor-beat-converter
Jul 8, 2026
Merged

refactor(editor): extract beatOf/timeOf tempo-map converter (Phase A1)#133
byrongamatos merged 3 commits into
mainfrom
feat/editor-beat-converter

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Workspace-shell time-model foundation — Phase A1 (charrette §1.1/§1.10). First step of the beat-primary time model; a pure refactor with no behaviour change, parallel-safe with the open feature-PR backlog (barely touches chrome).

What

The musical-beat ⇄ seconds math that _makeTimeRemap and snapTime each computed inline is now ONE named, shared @pure:beat-converter pair, so every musical-time quantity (ruler, snap, loop, playhead, and — from A2 on — notes) reads a single implementation.

  • beatOf(beats, t) — seconds → fractional beat. Exact inverse of timeOf within a grid gap; before the first / after the last beat it extrapolates along that gap's local tempo, so the pair stays invertible outside the grid too.
  • timeOf(beats, β) — fractional beat → seconds, the mirror.
  • < 2 beats ⇒ both are the identity (no tempo map → notes stay seconds-primary, §1.3.4). Non-finite input and zero-width gaps guarded.

_makeTimeRemap now routes interior times through the converter — remap is timeOf(new, beatOf(old, t)) within the grid — while keeping its legacy constant-shift on the tails, so the extraction is behaviour-identical across the whole domain. snapTime snaps in the beat domain: timeOf(round(beatOf(t)·subs)/subs).

No note-model change — that is A2. This PR only extracts + names the converter and rewires the two existing callers to it.

Tests — tests/beat_converter.test.js (13)

  • inverse both directions (exact, 1e-9)
  • the converter reproduces the pre-refactor _makeTimeRemap in the interior (the charrette identity)
  • the shipped _makeTimeRemap and snap math match the pre-refactor code across the whole domain — the no-behaviour-change proof
  • both-tail extrapolation, the <2-beats identity, adversarial guards (empty / null / NaN / Inf / zero-width)

beatOf/timeOf do not exist on main, so the suite fails on main (would-fail-on-main).

tests/tempo_ride_parts.test.js (+3): its hand-assembled sandbox extracts the real _makeTimeRemap, which now needs the converter block in scope. Full JS suite green.

🤖 Generated with Claude Code

https://claude.ai/code/session_013kpQfrmvrUHsZ8asuPnuW4

Summary by CodeRabbit

  • Bug Fixes

    • Improved time snapping and tempo-map remapping consistency by standardizing conversions through a shared beat/seconds logic.
    • Preserves legacy tail/endpoint behavior while improving accuracy across interior gaps and boundary cases (including degenerate or irregular timing inputs).
  • Tests

    • Added/expanded coverage for converter inverse properties, remap equivalence, and snapping invariants (ties, idempotence, subdivision limits, extrapolation).
    • Updated TempoMap pipeline test setup to include the new beat-converter logic in scope.

Workspace-shell time-model foundation (charrette §1.1/§1.10, Phase A1).
The musical-beat <-> seconds math that _makeTimeRemap and snapTime each
computed inline is now ONE named, shared @pure:beat-converter pair, so
every musical-time quantity (ruler, snap, loop, playhead, and — from A2
on — notes) reads a single implementation.

- beatOf(beats, t): seconds -> fractional beat. Exact inverse of timeOf
  within a grid gap; before the first / after the last beat it
  extrapolates along that gap's local tempo, so the pair stays invertible
  outside the grid too.
- timeOf(beats, beat): fractional beat -> seconds, the mirror.
- < 2 beats => both are the identity (no tempo map => notes stay
  seconds-primary, §1.3.4). Non-finite input and zero-width gaps guarded.

_makeTimeRemap now routes interior times through the converter — remap IS
timeOf(new, beatOf(old, t)) within the grid — while KEEPING its legacy
constant-shift on the tails, so the extraction is behaviour-identical
across the whole domain. snapTime snaps in the beat domain:
timeOf(round(beatOf(t)·subs)/subs). No note-model change (that is A2).

Tests: tests/beat_converter.test.js (13) — inverse both directions
(exact); the converter reproduces the pre-refactor _makeTimeRemap in the
interior (the charrette identity); the SHIPPED _makeTimeRemap and snap
math match the pre-refactor code across the whole domain (the
no-behaviour-change proof); both-tail extrapolation; the <2-beats
identity; and adversarial guards (empty/null/NaN/Inf/zero-width). These
reference beatOf/timeOf, absent on main, so the suite fails on main.
tests/tempo_ride_parts.test.js: +3 — its hand-assembled sandbox extracts
the real _makeTimeRemap, which now needs the converter block in scope.
Full JS suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013kpQfrmvrUHsZ8asuPnuW4
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 32 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: 540c13c7-7c97-49ab-9042-e8906def08af

📥 Commits

Reviewing files that changed from the base of the PR and between 7511056 and 21934e7.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • screen.js
📝 Walkthrough

Walkthrough

This PR extracts beat/time conversion into shared pure helpers in screen.js, refactors remapping and snapping to use them, adds a dedicated converter test suite, updates one pipeline test scope, and records the change in the changelog.

Changes

Beat converter extraction

Layer / File(s) Summary
Pure converter implementation
screen.js
Adds beatOf(beats, t) and timeOf(beats, beat) for seconds↔fractional-beat conversion with extrapolation and degenerate/non-finite input guards.
Remap and snap refactor
screen.js
Rewrites _makeTimeRemap to route interior conversions through timeOf(newBeats, beatOf(oldBeats, t)) while preserving tail constant-shift behavior; refactors snapTime to round in beat-domain via the converter.
New beat converter test suite
tests/beat_converter.test.js
Adds a runner extracting the converter/remap logic from screen.js, legacy reference implementations, and tests for inverse properties, remap equivalence, snap equivalence, extrapolation, degenerate grids, and robustness edge cases.
Existing test update and changelog
tests/tempo_ride_parts.test.js, CHANGELOG.md
Adds the beat-converter block to the pipeline test's evaluation scope and documents the refactor in the changelog.

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

Possibly related PRs

  • got-feedBack/feedBack-plugin-editor#110: Updates the same tempo-ride pipeline/test to include the beat-converter pure block so the tempo remap path works with the refactored beatOf/timeOf conversions.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main refactor: extracting the beatOf/timeOf tempo-map converter.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/editor-beat-converter

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

Strengthen tests/beat_converter.test.js after review: pin the
converter's exact-inverse at the reported exact-tie input, prove the
snapTime tie-break divergence is a measure-zero float ULP (agrees one
ULP either side), and add snap idempotency + one-subdivision-bound
invariants. No product-code change: the extracted converter is a
provably exact inverse with no mapping drift.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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