Skip to content

feat(core): teaching marks fg/ch/sd — wire + GP import + sd derivation (§6.2.2) - #536

Merged
byrongamatos merged 1 commit into
mainfrom
feat/teaching-marks-wire
Jun 21, 2026
Merged

feat(core): teaching marks fg/ch/sd — wire + GP import + sd derivation (§6.2.2)#536
byrongamatos merged 1 commit into
mainfrom
feat/teaching-marks-wire

Conversation

@byrongamatos

Copy link
Copy Markdown
Contributor

Part of #334

PR 1 of 3 for the teaching marks epic (feedpak 1.5.0 §6.2.2). Mirrors the bend-shape precedent (#531/#532). The three marks are display/teaching only — nothing in the scoring / NoteVerifier path reads fg/ch/sd.

What this adds

Three OPTIONAL per-note fields, all default-omitted on the wire:

  • fg fret-hand finger (-1 unset, 0 thumb … 4 pinky)
  • ch strum-group key (>= -1; notes sharing a value >= 0 are one gesture, pkd gives direction)
  • sd scale degree (0..11 chromatic offset above the active key's tonic; author value wins, otherwise derived)

Core wire (lib/song.py)

  • Note.fret_finger / strum_group / scale_degree; emit (fg/ch/sd) and decode via the existing _wire_int_optional helper. Lossless round-trip.
  • _parse_note reads the GP-written fretFinger chart-XML attribute.
  • Pure helpers: key_to_tonic_pc (parse a keys.json key name like "Em" → tonic pitch class), scale_degree_for_pitch, and base_open_string_midis / pitch_from_base / note_pitch_midi (tuning offsets + capo + fret → MIDI, mirroring app.js _TUNING_BASE_MIDI).

GP import → fg

  • GP5 (lib/gp2rs.py): note.effect.leftHandFinger (Fingering enum) → fg, reusing the chord-path value convention; carried as a fretFinger XML attr.
  • GP8/GPIF (lib/gp2rs_gpx.py): per-note <LeftFingering> (classical p-i-m-a-c letter codes, verified against real GP8 exports) → fg.

sd derivation (server.py highway_ws)

Where notes, tuning and the keys.json timeline converge: for each note/chord-note, if the author didn't set sd and a key is active at the note's time, derive it from the sounding pitch. Base hoisted out of the per-note loop.

Tested locally

  • pytest tests/test_song.py tests/test_gp2rs.py tests/test_gp2rs_gpx.py292 passed. (Pre-existing unrelated test_diagnostics_redact.py failure on clean main is untouched / not in this run.)
  • GP8 end-to-end: converting a real GP8_Test.gp emits fretFinger="2"/"1" for the M/I notes and omits the Open note.
  • Independent Codex review (read-only): caught a real bug in v1 — arr.tuning is offsets from standard, not absolute MIDI — now fixed via base_open_string_midis/pitch_from_base; re-review clean.

Notes / follow-ups

  • sd derivation only fires when a keys.json is present and the key name parses; missing key / unparseable name / no tuning entry → sd left unset (never fabricated).
  • Renders in PR 2; authored in the editor in PR 3.

🤖 Generated with Claude Code

…n (§6.2.2)

Add the three OPTIONAL per-note feedpak 1.5.0 teaching marks — fg (fret-hand
finger), ch (strum-group key), sd (scale degree) — to the Note model and wire
format, mirroring the bend-shape work (#531). These are DISPLAY/TEACHING ONLY:
nothing in the scoring / note-verification path reads them.

- lib/song.py: Note.fret_finger / strum_group / scale_degree, default-omitted
  on the wire (fg/ch/sd) and decoded via _wire_int_optional; _parse_note reads
  the GP-written fretFinger XML attr. Pure helpers key_to_tonic_pc (§7.7 key
  name -> tonic pitch class) + scale_degree_for_pitch, plus base_open_string_midis
  / pitch_from_base / note_pitch_midi (tuning offsets + capo + fret -> MIDI,
  mirroring app.js _TUNING_BASE_MIDI).
- lib/gp2rs.py: GP5 note.effect.leftHandFinger -> fg (RsNote field + fretFinger
  XML attr), reusing the chord Fingering value convention.
- lib/gp2rs_gpx.py: GP8/GPIF per-note <LeftFingering> (p-i-m-a-c letter codes,
  verified against real GP8 exports) -> fg.
- server.py highway_ws: derive sd for notes + chord notes from the active
  keys.json key + sounding pitch when the author didn't author one (author value
  wins); base hoisted out of the per-note loop.

Tests: round-trip + omit-when-default + malformed-tolerance for fg/ch/sd;
key_to_tonic_pc + scale_degree_for_pitch + note_pitch_midi (standard/drop-D/
capo/bass) units; GP5 leftHandFinger and GP8 <LeftFingering> import.

Part of #334

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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