Skip to content

feat(editor): detect key from a part's notes (Krumhansl–Schmuckler) - #132

Merged
byrongamatos merged 2 commits into
mainfrom
feat/editor-detect-key
Jul 8, 2026
Merged

byrongamatos merged 2 commits into
mainfrom
feat/editor-detect-key

Conversation

@ChrisBeWithYou

@ChrisBeWithYou ChrisBeWithYou commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

DAW roadmap 4.17 — the passive-key third of the item, sibling to the chord-at-cursor readout (#130). Editor-only.

What

A new Detect button in the key controls guesses the current part's key from its pitch-class content and sets it, turning the in-key highlight on so you see the result immediately.

  • @pure:key-detect (_detectKeyPure): the Krumhansl–Schmuckler algorithm — Pearson correlation of a 12-bin pitch-class histogram against the standard major/minor key profiles, across all 24 keys. Correlation (not a raw dot product) is the load-bearing choice: the two profiles have different magnitudes, so a dot product would systematically favour one mode, and correlation makes a profile score a perfect 1.0 against its own key. Returns null for an empty or perfectly flat histogram (no tonal centre) so the caller shows nothing.
  • editorDetectKey: builds the histogram from the active arrangement — fretted parts resolved to sounding pitch (capo/tuning-aware via _rollMidiForNote + _rollPitchCtx), keys parts to their packed pitch — duration-weighted (a held note counts more than a passing one, with a small floor so staccato still registers). Sets S.editorKey, enables the highlight, and reports the guess. A suggestion — the tonic/scale pickers stay editable.

Tests — tests/key_detect.test.js (8, all fail on main)

Exact profiles detect their own key (C major, G major, A minor via rotation); a textbook C-major distribution; uses-all-weights (shifting the histogram shifts the detected tonic by exactly the same amount); degenerate inputs → null (empty / all-zero / short / non-array / flat); a single pitch class; and the NaN/negative-bin guard. Full JS suite green.

Scope / fresh region

New @pure:key-detect block by @pure:scale, an editorDetectKey handler by the other key handlers, and one HTML button. No core, no spec; independent of the chord-readout PR (#130) — different block, different control.

Summary by CodeRabbit

  • New Features
    • Added a Detect button in the key controls to suggest the current part’s key from the notes.
    • Key suggestions can now enable in-key highlighting and update the on-screen key display.
  • Bug Fixes
    • Improved handling for passages without a clear tonal center, returning no suggestion instead of a misleading result.
    • Key detection now accounts for note duration, capo, and tuning so results better match the played material.

DAW roadmap 4.17 (the passive-key third; sibling to the chord-at-cursor readout).
A "Detect" button in the key controls guesses the current part's key and sets it.

- @pure:key-detect: _detectKeyPure runs the Krumhansl–Schmuckler algorithm —
  PEARSON CORRELATION of a 12-bin pitch-class histogram against the standard
  major/minor key profiles, over all 24 keys. Correlation (not a raw dot
  product) is what makes the cross-mode comparison fair — the two profiles have
  different magnitudes — and makes a profile score 1.0 against its own key.
  Returns null for an empty or perfectly flat histogram (no tonal centre).
- window.editorDetectKey builds the histogram from the active arrangement,
  resolving fretted parts to sounding pitch (capo/tuning via _rollMidiForNote +
  _rollPitchCtx) and keys parts to their packed pitch, duration-weighted with a
  small floor so staccato notes still count. Sets S.editorKey, turns the in-key
  highlight on, and reports the guess. A suggestion — the pickers stay editable.
  New Detect button in the key group.

Tests: tests/key_detect.test.js (8) — exact profiles detect their own key
(C major, G major, A minor via rotation), a textbook C-major distribution,
uses-all-weights (shifting the histogram shifts the tonic by the same amount),
degenerate → null (empty/all-zero/short/non-array/flat), single-pc, and the
NaN/negative-bin guard. All fail on main. Full JS suite green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HzMBtxWnLGHYkMMXtK38Bg
@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: 29 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: f05b37ca-d5e5-4cd0-9c2f-2de8d2573059

📥 Commits

Reviewing files that changed from the base of the PR and between 09ae61e and 47b3066.

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

Walkthrough

This PR adds a key-detection feature: a pure Krumhansl–Kessler/Pearson-correlation algorithm (_detectKeyPure) in screen.js, a global window.editorDetectKey function that builds a duration-weighted pitch-class histogram from arrangement notes and updates editor state/UI, a new "Detect" button in screen.html, a corresponding test suite, and a changelog entry.

Changes

Key Detection

Layer / File(s) Summary
Key-detection algorithm and editor integration
screen.js
Adds Krumhansl-Kessler profile constants and _detectKeyPure (Pearson correlation over rotated profiles, returns tonic/scale/score or null), and window.editorDetectKey, which builds a duration-weighted 12-bin pitch-class histogram from arrangement notes, runs detection, updates S.editorKey, enables key highlighting, persists state, refreshes controls, redraws, and sets a status message.
Detect button UI wiring
screen.html
Adds a "Detect" button (editor-key-detect-btn) in the key/scale controls that calls editorDetectKey() on click.
Key detection test suite
tests/key_detect.test.js
Extracts the @pure:key-detect block from screen.js, evaluates it in a sandbox, and tests exact/rotated profile matching plus degenerate/invalid input handling, reporting pass/fail counts.
Changelog entry
CHANGELOG.md
Documents the new "Detect" key-control feature under Unreleased → Added, referencing the algorithm and test file.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant DetectButton as Detect Button
  participant editorDetectKey
  participant _detectKeyPure
  participant EditorState as Editor State/UI

  User->>DetectButton: click
  DetectButton->>editorDetectKey: editorDetectKey()
  editorDetectKey->>editorDetectKey: build weighted pitch-class histogram from notes
  editorDetectKey->>_detectKeyPure: pcWeights
  _detectKeyPure-->>editorDetectKey: {tonic, scale, score} or null
  editorDetectKey->>EditorState: update S.editorKey, enable highlighting, persist key
  editorDetectKey->>EditorState: refresh key controls, redraw, set status message
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 clearly summarizes the main editor change: detecting a part's key from notes using the Krumhansl–Schmuckler method.
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-detect-key

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

@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