Skip to content

[WIP, has hash changes] Vocal phrase refactor with supporting parser improvements - #88

Closed
elicwhite wants to merge 6 commits into
Geomitron:masterfrom
elicwhite:parser-improvements
Closed

[WIP, has hash changes] Vocal phrase refactor with supporting parser improvements#88
elicwhite wants to merge 6 commits into
Geomitron:masterfrom
elicwhite:parser-improvements

Conversation

@elicwhite

Copy link
Copy Markdown
Contributor

Summary

Core change: Eliminate top-level notes[] and lyrics[] from NormalizedVocalPart. All vocal content is now accessed through phrase grouping (notePhrases/staticLyricPhrases).

Vocal phrase refactor

  • NormalizedVocalPart: removed notes[] and lyrics[]
  • NormalizedVocalPhrase: added player?: 1 | 2 for PART VOCALS versus mode
  • NormalizedLyricEvent.text: stores original unstripped markup symbols
  • NormalizedVocalNote.pitch: nonPitched notes keep original MIDI pitch (not -1)
  • PART VOCALS merges note 105+106 phrases with player tags
  • Harmonies keep separate 105/106 for lossless round-trip
  • Pre-collect lyrics per phrase (lyricIdx advances to phraseEnd regardless of notes, preventing divergence on pitch slide round-trip)
  • Pitch slide notes only skipped when controlling lyric survives emptiness filter
  • HARM3 CopyDown: clone staticLyricPhrases from HARM2

Supporting changes (same files, interleaved)

  • Beat track: extract MIDI BEAT track for round-trip fidelity
  • Chart parser: resolveChartTrackName, parseChartSectionEventText
  • orangeDrum noteType for 5-lane drums, _endSeq modifier ordering
  • extractMidiVocalTextEvents for stance/facial anim events
  • VenueEvent: 'unknown' type and length field
  • New tests: drum pads (4/5-lane) and guitar force modifiers

Validation

  • Hash validation: 13,338 matched, 0 mismatched
  • Round-trip: 0 vocal/harmony diffs across 15,525 charts
  • Unit tests: 46/46 pass

@elicwhite
elicwhite force-pushed the parser-improvements branch 8 times, most recently from 8394b43 to 6116e21 Compare April 17, 2026 06:33
@elicwhite elicwhite changed the title Vocal phrase refactor with supporting parser improvements [WIP, has hash changes] Vocal phrase refactor with supporting parser improvements Apr 17, 2026
@elicwhite
elicwhite force-pushed the parser-improvements branch 7 times, most recently from 7fb7ddb to 29943f5 Compare April 18, 2026 03:42
- unrecognizedEvents on RawChartData: text events from EVENTS track not already
  extracted into sections/endEvents/vocalTracks/coda (crowd events, music_start/end,
  practice mode, time signatures as text, etc.)
- Read MIDI sections, end events, coda, and unrecognized events from all
  text-like event types (text, lyrics, marker, cuePoint), matching YARG.Core's
  MoonText behavior
- Coda events are surfaced both in `codaEvents` AND `unrecognizedEvents` for
  consumer visibility (mirrors YARG/MoonSong behavior where coda appears in
  globalEvents)
- New parse issues for stray vocal events on the EVENTS track. Game engines
  silently drop these; we record them so consumers can surface the misplacement:
  - `invalidLyric` — lyric text on EVENTS instead of PART VOCALS
  - `invalidPhraseStart` — phrase_start on EVENTS (vocal phrases use MIDI 105/106 on PART VOCALS)
  - `invalidPhraseEnd` — phrase_end on EVENTS

Validated: 0 hash regressions across 78,046 charts.
…t Real Drums

New instruments: proguitar, proguitar22, probass, probass22, prokeys,
elitedrums, keysghl. New instrument types: proGuitar, proKeys, eliteDrums.
New game modes: proGuitar, proKeys, eliteDrums.

MIDI tracks recognized:
- PART REAL_GUITAR, PART REAL_GUITAR_22, PART REAL_BASS, PART REAL_BASS_22
- PART REAL_KEYS_X/H/M/E (one track per difficulty, CopyDown from expert)
- PART ELITE_DRUMS
- PART REAL_DRUMS_PS (parsed as standard drums fallback)
- PART KEYS GHL (6-fret keys, same layout as other GHL instruments)

Instrument-wide data extracted (star power, solo via 116/115, text events,
versus phrases, animations, Pro Keys range shifts). Per-difficulty raw notes
stored in rawNotes with noteNumber/velocity/channel.

Pro instrument parsing (raw notes + Pro Keys range shifts) is integrated into
the existing single-pass scanInstrumentTrack flow — no separate extractors.
\`storesRawNotes(instrumentType)\` and \`getRawNoteDifficulty()\` replace the
"isNewInstrument" flag with meaningful, instrumentType-derived checks.

PART REAL_DRUMS_PS / PART DRUMS dedup
-------------------------------------
Matches YARG/Moonscraper behavior: PART DRUMS is canonical, PART REAL_DRUMS_PS
is a fallback. When both exist, the fallback is dropped during parsing (YARG
TrackOverrides: DRUMS_REAL_TRACK has overwrite=false). When only the fallback
is present, it is parsed as drums.

- New \`duplicateDrumsTrack\` ChartIssueType, emitted into the parseIssues
  array (added in the prior PR) when the dedup triggers
- 19 charts in the 78,046-chart corpus have both PART DRUMS and
  PART REAL_DRUMS_PS; all now hash deterministically regardless of
  MIDI track order

Validated: 78,046 charts, 0 hash diffs vs fresh master baseline.
…e, glissando

Semantic labels on rawNotes:
- Pro Guitar/Bass: string (0-5), fret (0-17/22, capped), noteModifier
  (normal/muted/ghost/bend/tapped/harmonics/pinchHarmonics from MIDI channel)
- Pro Keys: key (0-24)
- Elite Drums: pad name (hatPedal/kick/snare/hiHat/leftCrash/tom1-3/ride/rightCrash)
- Sustain trimming: resolution/3 with strict < (matching YARG Pro behavior)

Typed animations (name field on animations array):
- Drum: 28 named pad hits (kick, snareLHHard, hiHatRight, etc.)
- Guitar/Bass/Keys: leftHandPosition1-20

New trackData fields:
- handMaps: typed HandMap events (10 types from "map HandMap_*" text events)
- strumMaps: typed StrumMap events (3 types from "map StrumMap_*" text events)
- characterStates: typed character states (7 types from [idle]/[play]/etc.)
- glissandoSections: Pro Keys glissando (note 126, distinct from flexLane)

Coda events now included in globalEvents (not filtered).

Validated: 78,046 charts 0 hash diffs.
Parse the complete VENUE MIDI track into typed events:
- Lighting: keyframes (notes 48-50) + presets (24 text patterns via
  "lighting (TYPE)")
- PostProcessing: notes 96-110 (15 effects) + text .pp patterns (30 effects)
- Camera Cuts: notes 60-64 + directed text (30+ patterns) + coop text
  (35+ patterns)
- Camera Constraints: notes 70-73 (no_behind/only_far/only_close/no_close)
- Spotlights: notes 37-41 (bass/drums/guitar/vocals/keys)
- Singalongs: notes 85-87 (bass/drums/guitar)
- Stage Effects: text events (bonus_fx, fog_on/off)

New types: VenueEvent, VenueEventType. New field: venue[] on
RawChartData/ParsedChart.

Validated: 78,046 charts 0 hash diffs. Venue matches YARG for all charts
with venue data (0 diffs).
scanIni() now returns unknownIniValues: a Record<string, string> of all
key-value pairs from the [Song] section that aren't in the recognized
property set (36 known keys + 4 legacy aliases). This enables writers to
reconstruct the full song.ini without data loss.

Also exports scanIni() from the package entry point.

Found 206 unique unrecognized keys across 78K charts, including:
- Pro instrument diffs (diff_vocals_harm, diff_guitar_real, etc.)
- Phase Shift SysEx flags (sysex_slider, sysex_open_bass, etc.)
- Tuning/type metadata (real_guitar_tuning, kit_type, etc.)
- Various typos and custom metadata

Validated: 78,046 charts 0 hash diffs.
Core change: eliminate top-level notes[]/lyrics[] from NormalizedVocalPart.
All vocal content is now accessed through phrase grouping.

Vocal phrase refactor:
- NormalizedVocalPart: removed notes[] and lyrics[]
- NormalizedVocalPhrase: added player?: 1 | 2 for versus mode
- NormalizedLyricEvent.text: stores original unstripped markup symbols
- NormalizedVocalNote.pitch: nonPitched keeps original MIDI pitch
- PART VOCALS merges note 105+106 phrases with player tags
- Harmonies keep separate 105/106 for lossless round-trip
- Pre-collect lyrics per phrase (lyricIdx advances to phraseEnd
  regardless of notes, preventing divergence on pitch slide round-trip)
- Pitch slide notes only skipped when lyric survives emptiness filter
- HARM3 CopyDown: clone staticLyricPhrases from HARM2
- Writer emits union of notes/lyrics from both phrase sets

Supporting changes (same files, interleaved):
- Beat track: extract MIDI BEAT track for round-trip fidelity
- Chart parser: resolveChartTrackName, parseChartSectionEventText
- orangeDrum noteType for 5-lane drums, _endSeq modifier ordering
- extractMidiVocalTextEvents for stance/facial anim events
- VenueEvent: 'unknown' type and length field
- New tests: drum pads (4/5-lane) and guitar force modifiers

Validated: 0 vocal diffs across 15,525 charts on round-trip.
Hash validation: 13,338 matched, 0 mismatched.
46/46 unit tests pass.
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.

1 participant