Skip to content

feat(editor): drum velocity authoring — Alt-drag, nudge keys, ghost pull, import velocity carry-through - #111

Merged
byrongamatos merged 3 commits into
mainfrom
feat/editor-drum-velocity
Jul 7, 2026
Merged

feat(editor): drum velocity authoring — Alt-drag, nudge keys, ghost pull, import velocity carry-through#111
byrongamatos merged 3 commits into
mainfrom
feat/editor-drum-velocity

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Roadmap 4.5 (Phase 4 — drums). Velocity always rendered (brightness/height) and imported from MIDI, but authoring wrote a hardcoded v:100 with no way to change it. Drum undo (#89) unblocked this.

What

  • Alt+vertical-drag on selected hits edits velocity live (piano-roll idiom: up = louder, 1 step/px off each hit's original value so the drag never accumulates). The velocity-scaled render is the live feedback; release commits one undoable SetDrumVelocityCmd via the same revert-then-exec finalize pattern as MoveDrumHitsCmd. Mode switches mid-drag finalize through _finalizeActiveDrag.
  • Shift+↑/↓ nudges the selection ±10; A / N quick-set accent (115) / normal (100). Both claimed only inside drum-edit mode with a drum selection, where those keys otherwise no-op against the empty note selection.
  • SetDrumVelocityCmd restores authored dynamics verbatim on undo — a hit that had no explicit v gets the field deleted again, not set to 100. Inputs clamp to MIDI 1–127 at the command boundary.
  • Ghost pull: the G toggle now quiets a normal-strength hit to the ghost velocity (35 — import derives g from v < 40, so it round-trips as a ghost) instead of authoring a contradictory ghost at v:100. Already-quiet hits keep their authored dynamics; un-ghosting leaves velocity untouched; ToggleDrumArticulationCmd(''g'') snapshots {had, v} per hit and restores both exactly on undo. f/k stay involutive.
  • XS fix — import dialog velocity discard: hand-mapping an unmapped percussion note pushed v:100, flattening real dynamics. routes.py now passes an optional velocities array (index-aligned with times, filtered in lockstep so a dropped bad time drops its velocity) and the modal carries source velocities through. Older cores that don''t capture velocities simply omit the field — the 100 default is unchanged. A tiny additive core PR can light this up end-to-end (the capture site in lib/midi_import.py has msg.velocity in hand).

Tests

tests/drum_velocity.test.js (11 cases) drives the real command classes over the extracted @pure:drum-cmds + @pure:edit-history blocks: clamp/drag pure math, exact-restore undo (including delete-not-100), scalar broadcast, ghost pull + mixed-state round-trips, and f/k involution regression. The authoring and ghost-pull assertions fail on main. Full JS suite: 42 files green; pytest: 191 passed; node --check clean.

Fresh region vs the open queue (#101#108) — drum editor interaction + the unmapped-import shaping only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu

Summary by CodeRabbit

  • New Features

    • Added authorable drum velocity editing in the drum editor with Alt+drag, plus keyboard shortcuts (A/N for accent/normal, Shift+↑/↓ nudges).
    • Enhanced ghost-note toggling to map between normal and ghost velocities more consistently.
  • Bug Fixes

    • Fixed unmapped drum-hit import so source (hand-mapped) dynamics are preserved instead of being flattened.
    • Improved undo/redo to restore drum velocity edits exactly, including when velocity was previously unset.
  • Tests

    • Added automated tests covering velocity editing, clamping, undo/redo, and ghost behavior.

@coderabbitai

coderabbitai Bot commented Jul 7, 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: 22 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: 5cb4873c-04ad-4d42-91e8-c55879f2f523

📥 Commits

Reviewing files that changed from the base of the PR and between 0cd2bdb and 813964f.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • routes.py
  • screen.js
  • tests/drum_velocity.test.js
📝 Walkthrough

Walkthrough

Adds drum velocity authoring in the editor, preserves imported source velocities, updates ghost-toggle undo semantics, and adds tests plus changelog documentation.

Changes

Drum velocity authoring

Layer / File(s) Summary
Unmapped velocity coercion
routes.py
_safe_unmapped_entry now accepts optional per-hit velocities alongside times, filters invalid times, coerces aligned velocity values, and omits the field when the converter did not provide it.
Unmapped hit velocities in the editor
screen.js
The drum editor now stores per-row source velocities from unmapped notes and uses them, with clamping, when building drum hits instead of defaulting to 100.
Ghost and velocity commands
screen.js
ToggleDrumArticulationCmd snapshots per-hit ghost state for g, applies custom ghost velocity handling, and SetDrumVelocityCmd plus the clamp helper restore or delete authored v exactly on undo.
Velocity drag and keyboard editing
screen.js
Alt+click starts a drum-velocity drag, drag move and end routing call the dedicated handlers, and drum keyboard shortcuts add velocity quick-sets and nudges with redraw and early return.
Tests and changelog
tests/drum_velocity.test.js, CHANGELOG.md
The new Node.js test suite covers pure velocity helpers, command undo/redo behavior, ghost toggling, and flam/choke regressions, and the changelog records the new drum velocity authoring flow.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant screen_js as screen.js
  participant SetDrumVelocityCmd
  participant EditHistory

  User->>screen_js: Alt+drag on drum hit
  screen_js->>screen_js: _drumEditorOnVelocityDragMove(y)
  screen_js->>screen_js: preview h.v update
  User->>screen_js: release drag
  screen_js->>screen_js: _drumEditorOnVelocityDragEnd()
  screen_js->>SetDrumVelocityCmd: commit velocity change
  SetDrumVelocityCmd->>EditHistory: push undoable command
  User->>screen_js: undo
  EditHistory->>SetDrumVelocityCmd: rollback()
  SetDrumVelocityCmd->>screen_js: restore original v or delete v
Loading

Possibly related PRs

🚥 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 accurately summarizes the main editor drum-velocity authoring changes, including drag, nudges, ghost behavior, and import carry-through.
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-drum-velocity

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: 1

🤖 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 `@screen.js`:
- Around line 14847-14853: Update _drumEditorSetVelocity so that when the
selected hit velocity is raised above the ghost threshold, any existing g:true
flag is cleared as part of the same edit. This likely belongs in
SetDrumVelocityCmd (or the command path it uses) so v and g stay in sync for
selected refs, preserving the ghost/quiet invariant used by the editor and
importer.
🪄 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: 7a28ca24-e46d-4c59-9b18-fe028fa6f796

📥 Commits

Reviewing files that changed from the base of the PR and between 2222c59 and 3b81d21.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • routes.py
  • screen.js
  • tests/drum_velocity.test.js

Comment thread screen.js
…ull, import carry-through

Roadmap 4.5. Velocity always rendered and imported but authoring wrote a
hardcoded v:100 with no way to change it.

- Alt+vertical-drag on selected hits edits velocity live (up = louder,
  1 step/px off each hit's ORIGINAL value), committing ONE undoable
  SetDrumVelocityCmd on release via the revert-then-exec finalize pattern.
- Shift+Up/Down nudges the selection +/-10; A / N quick-set accent (115) /
  normal (100). Claimed inside drum-edit mode only, where those keys
  otherwise no-op against the empty note selection.
- SetDrumVelocityCmd restores authored dynamics verbatim on undo — a hit
  with no explicit v gets the field DELETED again, not set to 100.
- The G ghost toggle pulls a normal-strength hit to the ghost velocity
  (35; import derives g from v<40) instead of authoring a contradictory
  ghost at v:100. Already-quiet hits keep their dynamics; un-ghost leaves
  v untouched; ToggleDrumArticulationCmd('g') snapshots {had, v} and
  restores both exactly (f/k stay involutive).
- XS fix: the unmapped-notes import dialog no longer flattens dynamics —
  routes.py passes an optional velocities array (index-aligned with times,
  filtered in lockstep) and the modal carries real source velocities
  through instead of pushing v:100. Older cores omit the field cleanly.

Tests: tests/drum_velocity.test.js (11 cases, real commands over the
extracted blocks — the ghost-pull and authoring assertions fail on main).
Full JS suite 42 files + pytest 191 passed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu
@ChrisBeWithYou
ChrisBeWithYou force-pushed the feat/editor-drum-velocity branch from 3b81d21 to 0cd2bdb Compare July 7, 2026 21:31
@byrongamatos

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 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.

- SetDrumVelocityCmd: clear a stale g (ghost) flag when velocity is
  raised above the ghost band (Accent / Shift+Up / Alt-drag up), so an
  edit can no longer author a contradictory loud ghost; snapshot + restore
  the {g, v} pair symmetrically on undo/redo (CodeRabbit).
- Import: hand-mapped unmapped notes now derive g from carried velocity
  (g <= v < 40) via a new pure _drumImportHitPure helper, matching the
  MIDI importer so a quiet hand-mapped note renders/round-trips as a ghost
  identically to the normal path (Codex).
- Regression tests in tests/drum_velocity.test.js for both paths (fail
  pre-fix). Full JS suite 48/48 green.

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 7, 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.

@byrongamatos
byrongamatos merged commit 2a84626 into main Jul 7, 2026
@byrongamatos
byrongamatos deleted the feat/editor-drum-velocity branch July 8, 2026 06:07
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