feat(editor): onset detection strip — blocky attack markers over the waveform - #95
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis PR adds an onset detection strip to the waveform editor, with pure RMS-based onset detection, independent strip rendering, a localStorage-backed toggle and shortcut, tests for the detector, and changelog documentation. ChangesOnset detection strip
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant editorToggleOnsetStrip
participant DrawWaveform
participant OnsetCache
User->>editorToggleOnsetStrip: Click Onsets / Shift+W
editorToggleOnsetStrip->>editorToggleOnsetStrip: persist toggle state
editorToggleOnsetStrip->>DrawWaveform: trigger draw()
DrawWaveform->>OnsetCache: request onset data
OnsetCache->>OnsetCache: compute via _onsetTimesFromPeaksPure if needed
OnsetCache-->>DrawWaveform: return onset times and strengths
DrawWaveform->>DrawWaveform: render onset blocks
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.44.1)screen.jsast-grep timed out on this file Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
screen.js (1)
1359-1365: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winOnset overlay silently skipped when the visible pixel span collapses.
drawOnsets()is correctly called on the "waveform hidden" and "no peaks" early-return paths (lines 1352, 1357), and at the end of the function (line 1411), but the pre-existingif (xHi <= xLo) return;at line 1365 returns without callingdrawOnsets(). In that edge case (e.g. degenerate zoom/pixel range) the onset overlay won't render even though the waveform is visible and peaks exist — undermining the PR's stated goal of the strip working as an overlay independent of the rest of the waveform draw.🐛 Proposed fix
- if (xHi <= xLo) return; + if (xHi <= xLo) { drawOnsets(); return; }🤖 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 `@screen.js` around lines 1359 - 1365, The `drawWaveform` path currently exits early on the `if (xHi <= xLo) return;` check without ever reaching `drawOnsets()`, so the onset overlay is skipped in collapsed-span edge cases. Update this branch so the onset overlay still renders when the waveform is otherwise visible and peaks exist, by calling `drawOnsets()` before returning or by restructuring the early-return handling in `drawWaveform` around the `xLo`/`xHi` clamp logic.
🧹 Nitpick comments (1)
tests/onset_strip.test.js (1)
17-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
new Function()extraction is test-only and low-risk here, but fragile.Static analysis flags dynamic code execution and non-literal fs path, but both operate on a fixed, repo-owned path (
__dirname-derived) and the repository's own trustedscreen.js— not attacker-controlled input, so there's no real injection vector. The bigger practical risk is fragility: any edit to the@pure:onset-stripmarkers or surrounding syntax inscreen.jssilently breaks this extraction. Consider exposing_onsetTimesFromPeaksPurevia a smalltypeof module !== 'undefined' && (module.exports = { _onsetTimesFromPeaksPure })guard inscreen.js(mirroring thetypeofguard already added fordrawWaveformper the PR summary) instead of regex+Functionextraction.🤖 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 `@tests/onset_strip.test.js` around lines 17 - 26, The onset-strip test is relying on brittle regex-plus-Function extraction from screen.js to access _onsetTimesFromPeaksPure. Expose that helper directly from screen.js using the existing typeof module guard pattern already used for drawWaveform, so the test can import it without parsing source text. Update tests/onset_strip.test.js to require the exported symbol instead of matching the `@pure`:onset-strip markers and constructing a Function.Source: Linters/SAST tools
🤖 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.
Outside diff comments:
In `@screen.js`:
- Around line 1359-1365: The `drawWaveform` path currently exits early on the
`if (xHi <= xLo) return;` check without ever reaching `drawOnsets()`, so the
onset overlay is skipped in collapsed-span edge cases. Update this branch so the
onset overlay still renders when the waveform is otherwise visible and peaks
exist, by calling `drawOnsets()` before returning or by restructuring the
early-return handling in `drawWaveform` around the `xLo`/`xHi` clamp logic.
---
Nitpick comments:
In `@tests/onset_strip.test.js`:
- Around line 17-26: The onset-strip test is relying on brittle
regex-plus-Function extraction from screen.js to access
_onsetTimesFromPeaksPure. Expose that helper directly from screen.js using the
existing typeof module guard pattern already used for drawWaveform, so the test
can import it without parsing source text. Update tests/onset_strip.test.js to
require the exported symbol instead of matching the `@pure`:onset-strip markers
and constructing a Function.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d56aa8b0-353d-4eee-9484-002d2f9d2027
📒 Files selected for processing (4)
CHANGELOG.mdscreen.htmlscreen.jstests/onset_strip.test.js
…waveform Adds an "Onsets" toolbar toggle (Shift+W in both shortcut profiles): amber blocks over the waveform band mark where sharp attacks are detected in the recording — a visual hint of where notes/beats likely live while charting by eye. - Detection is client-side from the existing waveform RMS cache (no server round-trip, no new deps): an onset fires where loudness rises sharply above the local sliding baseline, gated by an absolute noise floor and a ~50 ms refractory gap so one attack registers exactly once. Block brightness/height scale with attack strength. - Independent of the waveform toggle: overlay with the waveform on, or a pure "blocky" view with the waveform hidden (W off + Onsets on). - Display only — the strip never places notes (design D22). Analysis cached per audio load, invalidated by computeWaveform on replace. - typeof guards keep drawWaveform extractable by the existing waveform_render geometry test. Tests: tests/onset_strip.test.js (7 cases: isolated attacks, refractory single-fire, flam spacing, silence/noise floor, slow-swell rejection, strength ordering, degenerate inputs). node --check clean; all 26 JS test files pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu
Two per-frame hot-path fixes in _drawOnsetStrip (both reviewer-confirmed LOW perf, behavior unchanged): 1. _onsetStripEnabled() no longer reads localStorage on every draw()/frame. The flag is cached in a module-scope var, seeded once from storage and kept in sync by _editorToggleOnsetStrip. 2. _drawOnsetStrip no longer scans the whole time-sorted onsets array each frame. Since timeToX is monotonic, binary-search the first visible onset and break past the visible window. Rendered output is identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0387aa1 to
fd5d7b6
Compare
Summary
A new Onsets toolbar toggle (Shift+W, both shortcut profiles) draws amber blocks over the waveform band where sharp attacks are detected in the recording — a visual hint of where notes/beats likely live while placing notes by eye.
typeofguards keepdrawWaveformextractable by the existingwaveform_rendergeometry test (same pattern as theeditorWaveformVisibleguard).Verification
node --check screen.jscleantests/onset_strip.test.js— 7 cases via the@pure:onset-stripblock (isolated attacks detected once each, refractory single-fire within one burst, two hits at flam spacing both fire, silence/noise-floor rejection, slow swells are not onsets, strength ordering, degenerate inputs)waveform_rendergeometry test against the modifieddrawWaveform🤖 Generated with Claude Code
https://claude.ai/code/session_01JEoFeTPSnz4NpwwCG52hnu
Summary by CodeRabbit
New Features
Tests