Skip to content

fix(core): GPIF imports use string 1 = highest and read staff tunings - #198

Merged
PhysShell merged 3 commits into
mainfrom
claude/gp6-string-orientation
Sep 17, 2026
Merged

PhysShell merged 3 commits into
mainfrom
claude/gp6-string-orientation

Conversation

@PhysShell

Copy link
Copy Markdown
Owner

What

GPIF imports (GP6 .gpx, GP7/8 .gp) now follow griff's string convention — string 1 = highest (ADR-0018) — and GP7 imports get the right pitch.

GPIF lists a tuning lowest string first and numbers a note's string from 0 = lowest. Two defects followed:

before after
GP6, tuning in track properties (177 guitar tracks in a 410-file corpus) pitches right, tuning low-first, every position mirrored (string 1 = lowest) tuning highest first, positions renumbered
GP7, tuning only in <Staves><Staff> (314 tracks) guitarpro 0.4.2 never reads the staff tuning, falls back to a high-first Standard E while notes stay numbered from the low string — 0.2% of notes had the pitch their GPIF Midi property states tuning read from the staff; (string, fret, pitch) = tuning + fret for 100% of 329,501 non-tie notes, = Midi for 98.1%

The remaining 1.9% Midi mismatches are notes where GPIF's Midi is not tuning + fret at all (octave-transposed and percussion parts) — out of scope here.

How

import_gp_score reads the GPIF document itself (guitarpro::io::gpx::read_gpx / read_gp), runs the crate's read_gpif, then renumbers each track once at the boundary:

  • tuning from the GPIF track's own properties, else the first staff carrying one;
  • track.strings highest first; a note's raw string r of n becomes n − r; out-of-range → 0, refused like any invalid GP string;
  • a track without a GPIF tuning (drums, keys) keeps the crate's fallback, read in GPIF order.

Every source now shares the GP3/4/5 convention, so the per-format zero_indexed plumbing is gone. The unit tests that pinned the mirrored numbering (GP6 raw string 0 → griff string 1) are replaced by tests of the renumbering.

Tests

  • Red first (1af6df3), then green (8d1dcb2).
  • core/tests/gp_gpif_strings.rs: authored GPIF text (no copyrighted tab) packed with the crate's public BCFS/BCFZ writer — track-level tuning, staff-only tuning, staff tuning beside a tuning-less track property block, seven strings. Each asserts tuning order, positions, pitches, and pitch_at(position) == pitch.
  • Unit tests for mirror_gpif_string and normalise_track_strings (GPIF tuning, crate fallback, out-of-range strings).
  • Corpus check against the GPIF Midi property as above (local, corpus not committed).
  • cargo test --workspace: green locally (1,692 tests). missing_file_golden was skipped locally only: its golden holds the English OS error text and the test host reports it in Russian — unrelated to this change. cargo clippy -p griff-core --all-targets -- -D warnings and cargo fmt --check clean.

Notes

🤖 Generated with Claude Code

…pitch

GPIF (GP6 .gpx, GP7 .gp) lists a tuning low string first and numbers a
note's string from 0 = lowest; griff's model is string 1 = highest. Two
defects, measured on a 410-file Guitar Pro corpus against the GPIF `Midi`
note property:

- track-level tuning (the GP6 shape) imports low string first and numbers
  positions from the low string: pitches right, every position mirrored
  (177 GP6 tracks);
- staff-level tuning only (the GP7 shape) is never reached by the
  guitarpro crate, which falls back to a high-first Standard E while notes
  stay numbered from the low string: 739 of 344,274 GP7 notes carried the
  pitch their GPIF `Midi` value states (314 GP7 tracks).

Four failing tests over authored GPIF text (no copyrighted tab) packed
with the crate's public BCFS/BCFZ writer: track-level tuning, staff-only
tuning, staff tuning beside a tuning-less track <Properties>, and a
seven-string tuning. Each pins tuning high-first, positions renumbered,
and pitch_at(position) == pitch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GPIF (GP6 .gpx, GP7/8 .gp) lists a tuning lowest string first and numbers a
note's string from 0 = lowest; GP3/4/5 and griff (ADR-0018) number from
1 = highest. The adapter now reads the GPIF document itself
(`guitarpro::io::gpx::read_gpx` / `read_gp`), runs the crate's
`read_gpif` on it, and renumbers every track once at the boundary:

- the tuning comes from the GPIF track — its own properties (GP6) or else
  the first staff carrying one (GP7/8). The guitarpro crate reaches the
  staff tuning only when a track has an empty property block, so GP7 tracks
  without track properties silently fell back to a high-first Standard E
  while their notes stayed numbered from the low string: wrong pitch on
  nearly every GP7 note;
- `track.strings` becomes highest string first and each note's raw string
  r of n becomes n − r; a raw string outside the tuning becomes 0 and is
  refused like any invalid GP string. A track with no GPIF tuning (drums,
  keys) keeps the crate's fallback, read in GPIF order.

With every source sharing one convention, the per-format `zero_indexed`
plumbing (gp_one_indexed_string, StringCtx::zero_indexed, the extra
parameters) is removed. The unit tests that pinned the mirrored numbering
(GP6 raw string 0 → griff string 1) are replaced by tests of the
renumbering, including the fallback and out-of-range cases.

Verified against the GPIF `Midi` note property over a 410-file corpus
(314 tuned GP7 tracks, 329,501 non-tie notes): (string, fret, pitch)
now matches tuning + fret for 100% of notes and the stated `Midi` for 98.1%
(the rest are transposed or percussion parts whose `Midi` is not
tuning + fret); before, 0.2% of GP7 notes had their `Midi` pitch. All 177
tuned GP6 tracks now import highest string first; GP6 notes carry no `Midi`
property to check against.

cargo test --workspace green (1,692 tests; `missing_file_golden` skipped
locally only — its golden holds the English OS error text and this host
reports it in Russian); clippy -D warnings clean for griff-core.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 576bb175-2db2-4d0c-9d0f-e385a5af213b


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

PhysShell pushed a commit that referenced this pull request Sep 16, 2026
The audit's corpus import gave nearly every GP7 note the wrong pitch
(fallback Standard E tuning, notes numbered from the low string; fixed in
#198). Adds a dated Correction section with the re-measured numbers on the
fixed importer — refitted weights, holdout agreement for all four models,
human-optimal rates, and CP-SAT-verified ceilings for v1 (36.2% vs DP
35.8%) and v1-fit (55.5% vs 44.1%) with zero solver gap on all 1,954
holdout lines — and states which conclusions hold (all), which got
stronger (v1-fit is too flat), which shifted (absolute agreement, fitted
weights; the hand model no longer beats fitted v1 on agreement), and which
results were not re-run (repeat consistency, hand-model oracle).

The side-finding section now points at the GP7 half, and the decision
record flags its numbers as pre-fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@PhysShell
PhysShell merged commit ac74e7f into main Sep 17, 2026
16 checks passed
PhysShell pushed a commit that referenced this pull request Sep 17, 2026
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