Compare commits

...
Author SHA1 Message Date
zagatozeeandBret Mogilefsky 2d79e216b3 Change staff parameter to use _wire_int_optional
(cherry picked from commit 64656bda1edc3fe03b6ad5e747c7afa3abca31d6)
2026-06-17 23:42:08 -07:00
zagatozeeandBret Mogilefsky bd22951874 feed the rabbit
Updated the docstring for _load_gpif to clarify supported file formats and improved error messages for unrecognized containers.L

(cherry picked from commit 1866c72e53339a10d6f184d21c2261e9f0b21395)
2026-06-17 23:42:08 -07:00
zagatozeeandBret Mogilefsky eb2121b4bc Piano LH/RH seperation work
Add LH/RH piano chart seperation ground work

(cherry picked from commit 4c227f30347d7f0cf2f3a8c4f81f9ba7ec2dee68)
2026-06-17 23:42:07 -07:00
zagatozeeandBret Mogilefsky 297778864c Add staff attribute to note representation
Par tof the LH/RH seperation future schema

(cherry picked from commit 56ad235a1ad4875527abb69836bcace79fdadfdc)
2026-06-17 23:42:07 -07:00
zagatozeeandBret Mogilefsky 3ab57977c4 Update song.py
Groundwork for piano charts keeping LH and RH designations intact, but seperated, so they can be toggled via menu options etc in future

(cherry picked from commit 606b69a75eb5202c6fb2978f496bf97db0ec65e4)
2026-06-17 23:42:07 -07:00
3 changed files with 28 additions and 30 deletions
+3
View File
@@ -69,6 +69,7 @@ class RsNote:
tremolo: bool = False
tap: bool = False
link_next: bool = False
staff: int = -1 # Piano staff: -1=absent, 0=LH/bass clef, 1=RH/treble clef
@dataclass
@@ -1021,6 +1022,8 @@ def _build_xml(
"tap": "1" if n.tap else "0",
"ignore": "0",
}
if getattr(n, 'staff', -1) != -1:
attrs["staff"] = str(n.staff)
ET.SubElement(notes_el, "note", **attrs)
# Chords
+20 -30
View File
@@ -279,7 +279,6 @@ def _gpif_tracks(root: ET.Element) -> list[dict]:
midi_channel = 0
is_drums = False
if gm is not None:
# GP6 (.gpx): <GeneralMidi table="Percussion"><Program>...</Program>
try: midi_program = int(gm.findtext('Program') or 0)
except (ValueError, TypeError): pass
try:
@@ -289,25 +288,6 @@ def _gpif_tracks(root: ET.Element) -> list[dict]:
except (ValueError, TypeError): pass
if gm.get('table') == 'Percussion':
is_drums = True
else:
# GP7/GP8 (.gp): <InstrumentSet><Type>drumKit</Type>
# and <MidiConnection><PrimaryChannel>9</PrimaryChannel>
inst_set = t.find('InstrumentSet')
if inst_set is not None:
inst_type = (inst_set.findtext('Type') or '').lower()
if inst_type == 'drumkit':
is_drums = True
midi_conn = t.find('MidiConnection')
if midi_conn is not None:
try:
ch_text = (midi_conn.findtext('PrimaryChannel') or '').strip()
if ch_text:
ch = int(ch_text)
midi_channel = ch
if ch == 9:
is_drums = True
except (ValueError, TypeError):
pass
# String tuning
string_pitches: list[int] = []
@@ -624,15 +604,14 @@ def list_tracks(gp_path: str) -> list[dict]:
result = []
for i, t in enumerate(tracks):
is_bass = bool(
not t['is_drums'] # drums always have low/zero string pitches — must exclude
and (
(
not t['string_pitches'] # no string tuning = not guitar-family
and 32 <= t['midi_program'] <= 39
) or (
t['string_pitches']
and max(t['string_pitches']) <= 48 # bass top string ≤ C3
)
(
not t['is_drums']
and not t['string_pitches'] # no string tuning = not guitar-family
and 32 <= t['midi_program'] <= 39
) or (
not t['is_drums'] # explicit guard: drums can have low string pitches
and t['string_pitches']
and max(t['string_pitches']) <= 48 # bass top string ≤ C3
)
)
is_piano = (
@@ -1339,7 +1318,7 @@ def convert_file(
if bid != '-1' and bid:
bar = bars_by_id.get(bid)
if bar is not None:
for vid in bar.findtext('Voices', '').split():
for voice_pos, vid in enumerate(bar.findtext('Voices', '').split()):
if vid == '-1':
continue
voice = voices_dict.get(vid)
@@ -1440,6 +1419,13 @@ def convert_file(
fret=rs_fret,
sustain=sustain,
)
# Staff assignment for keys/piano (slopsmith staff schema).
# Derived from voice position as authored in the GP tab —
# not inferred from pitch — preserving hand crossings.
# Voice 0 = treble/RH staff (staff=1),
# Voice 1+ = bass/LH staff (staff=0).
if is_keys:
rn.staff = 1 if voice_pos == 0 else 0
# Techniques — GPIF stores these as <Property>
# elements (NOT child tags), so the old
@@ -1674,6 +1660,10 @@ def convert_file(
fret=_lh_midi % 24,
sustain=_lh_dur if _lh_dur > 0.2 else 0.0,
)
# LH track notes are always bass clef (staff=0).
# The LH track as a whole IS the left hand —
# voice position within the LH track is irrelevant.
_lh_rn.staff = 0
_lh_notes.append(_lh_rn)
_lh_last_per_key[_lh_midi] = _lh_rn
_lh_vt += _lh_dur
+5
View File
@@ -37,6 +37,7 @@ class Note:
right_hand: int = -1
pick_direction: int = -1
ignore: bool = False
staff: int = -1 # Piano staff: -1=absent, 0=LH/bass clef, 1=RH/treble clef
@dataclass
@@ -217,6 +218,8 @@ def note_to_wire(n: Note) -> dict:
out["pkd"] = n.pick_direction
if n.ignore:
out["ig"] = True
if n.staff != -1:
out["stf"] = n.staff
return out
@@ -307,6 +310,7 @@ def note_from_wire(d: dict, time: float | None = None) -> Note:
right_hand=_wire_int_optional(d.get("rh"), -1),
pick_direction=_wire_int_optional(d.get("pkd"), -1),
ignore=bool(d.get("ig", False)),
staff=_wire_int_optional(d.get("stf"), -1),
)
@@ -762,6 +766,7 @@ def _parse_note(n) -> Note:
right_hand=_int_optional(n, "rightHand", -1),
pick_direction=_int_optional(n, "pickDirection", -1),
ignore=_bool(n, "ignore"),
staff=_int_optional(n, "staff", -1),
)