diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..67a8c1a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +## [Unreleased] - 1.33.0 + +Guitar scoring accuracy: notes the player got right no longer score as misses, and stats plugins +get the per-string detail they used to patch `screen.js` for. + +### Fixed + +- **Capo charts** (#73): fretted notes are read from the nut, the Rocksmith way (a capo-4 E chord + is `A7 D6 G0 B5`); only fret 0 sounds at the capo. Expected pitch used to add the capo to every + fret, so capo charts barely scored. Covers the browser detector, the desktop engine (both the + pushed chart and `scoreChord`: capo 0, open notes moved to the capo fret) and the detected-fret + display fallback. +- **Fast chord runs** (#75): a ringing chord in a fast run of one shape no longer scores as an + EARLY miss. The engine verifier stamps a chord with the member detected closest to the chart + time, and a stamp within one poll of the window edge counts as on time; on the `scoreChord` + path one early strummed frame no longer locks the chord as a miss. +- **Retuned / transformed charts** (#24): expected pitches follow the host's effective + `getTuning()` / `getCapo()` / `getCentOffset()` (core chart-transform providers) instead of the + original song metadata, and resync within a tick when a provider restages mid-song, on every + detect path including desktop addons without the engine verifier. The desktop engine gets + whole-semitone offsets with its pitch gate widened by the rounding error (up to 50 cents), so a + +45-cent record still scores. +- **Technique flags** (#79): bend / slide are read off core's wire format (`sl`/`slu` = -1 means + no slide, `bn` is the bend amount). Every plain note used to look like a slide and get the + widened pitch window, and real bends were never seen. +- **Section stats on desktop** (#81): per-section hits / misses are tracked on the engine-verifier + path (they were always empty there), and each judgment is binned by the section its note sits in. + +### Added + +- **Richer `notedetect:hit` / `notedetect:miss` payloads** (#77), all additive: `key`, per-string + `strings` verdicts (`{ s, f, hit, centsError, snr?, detectedAt? }`), named `techniques`, + `latencyMs`, `avOffsetMs`, `detector`, `section`, `counted`, `points`, `multiplier`, `streak`, + `isDefault`, and `verdictAt` / `noteEnd` so a renderer can still flash a hit whose verdict landed + after the gem was culled. See README "Events". diff --git a/CLAUDE.md b/CLAUDE.md index 9229fa0..71823ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -67,7 +67,7 @@ defer the window copy a task and drop it if the element copy arrived | Event | Bus too? | Payload (additive-only contract) | |---|---|---| -| `notedetect:hit` / `notedetect:miss` | as `note:hit`/`note:miss` | the full judgment (`{ hit, chord, note, notes, noteTime, timingState, timingError, pitchState, pitchError, ... }`) | +| `notedetect:hit` / `notedetect:miss` | as `note:hit`/`note:miss` | the full judgment (`{ hit, chord, note, notes, noteTime, timingState, timingError, pitchState, pitchError, ... }`), plus from v1.33 `{ key, strings, techniques, latencyMs, avOffsetMs, detector, section, counted, points, multiplier, streak, isDefault, verdictAt, noteEnd }` (field reference in README "Events"; stamped by `_ndEnrichJudgment` at emit) | | `notedetect:fx` | yes | `{ fxType: 'multiplier', mult, prevMult, streak }` on tier change; `{ fxType: 'milestone', streak, mult }` at 25/50/every 100; `{ fxType: 'streakBreak', lostStreak, prevMult }` after a ≥10 run dies. All carry `{ isDefault, ts }` | | `notedetect:session` | no | end-of-song summary: `{ title, artist, arrangement, accuracy, hits, misses, bestStreak, score, maxMultiplier, grade, fullCombo, sections, timestamp }` (consumed by the practice journal — never rename/remove keys) | | `notedetect:skin` | yes | `{ skin }` — re-resolve any cached palette on this | diff --git a/README.md b/README.md index d921626..79c8d9b 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ git clone https://github.com/got-feedback/feedback-plugin-notedetect.git note_de 5. The note's **gem lights up brightly** on the highway when you hit it cleanly — and a sustained note keeps glowing for as long as you keep playing it on-pitch; a miss is flagged on the note too 6. Running accuracy and streak shown in the HUD +**Capo charts** are read the Rocksmith way: a fretted note's fret counts from the nut (it already includes the capo), and only fret 0 means an open string, which sounds at the capo. A capo-4 E chord is charted `A7 D6 G0 B5`. The expected pitch is `open + fret` for fretted notes and `open + capo` for open ones. A `setVerifyTarget` context that passes `tuning` + `capo` is read the same way. + > **How step 5 renders depends on the active highway renderer.** The plugin publishes a per-note judgment via Slopsmith core's `highway.setNoteStateProvider` hook (slopsmith#254 — honored by the bundled 2D and 3D highways; other renderers can read the same data from the core renderer `bundle`'s `getNoteState(note, chartTime)` method). On the **default 2D highway** the plugin *also* draws its own canvas overlay — the slide-down red ✕ miss markers below the now-line, the EARLY/LATE/SHARP/FLAT diagnostic labels, and the cyan "currently detected" indicator. On a **custom renderer** (e.g. the 3D highway) that canvas overlay is suppressed — the renderer owns the per-note feedback there (the 3D highway shows hit/active glow and a red miss outline + the diagnostic labels itself). The HUD (accuracy/streak), the end-of-song summary, and the optional screen-edge flash are DOM, not canvas, so they show under any renderer. On an **older core** without the hook, the plugin falls back to the 2D-canvas green hit ring / red miss marker overlay near the note regardless of renderer. ## Audio Input Channel Selection @@ -123,6 +125,19 @@ Field reference for the per-note events: | `hit` | `true` only when timing and pitch are both clean | all events (`false` for misses) | | `timingState` / `pitchState` | Independent diagnostic axes: `OK`, `EARLY`, `LATE`, `SHARP`, or `FLAT` | clean hits and matched diagnostic misses; `null` for pure misses | | `timingError` / `pitchError` | Signed timing error in milliseconds and signed pitch error in cents; `pitchError` is octave-folded to the nearest octave (so it is not necessarily equal to `(detectedMidi - expectedMidi) * 100`) | clean hits and matched diagnostic misses; `null` for pure misses | +| `chord` / `notes` | `true` for a chord-level judgment; `notes` lists the chord's `{ s, f }` (`note` is then the lead string) | all events | +| `hitStrings` / `totalStrings` / `score` | How many of the chord's strings rang, out of how many, and the ratio the Chord Leniency setting is compared with | chord judgments where the scorer ran | +| `key` | Stable id of the judged chart note, the same key the highway's `getNoteState` hands back as `popKey`: `"