Improve wording in terminology cleanup

Replace the placeholder noun left by the previous pass with context-fit
phrasing (arrangement XML, chart, custom songs, etc.).
This commit is contained in:
Sin
2026-06-16 19:43:45 +01:00
parent 4148b0e72e
commit c1870a0597
11 changed files with 38 additions and 38 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
"""Audio extraction and conversion for the source game custom song.""" """Audio extraction and conversion for custom songs."""
import logging import logging
import os import os
+13 -13
View File
@@ -1,4 +1,4 @@
"""Convert Guitar Pro files (.gp5/.gp4/.gp3) to the source game arrangement XML.""" """Convert Guitar Pro files (.gp5/.gp4/.gp3) to arrangement XML."""
import logging import logging
import re import re
@@ -544,7 +544,7 @@ def convert_track(
*, *,
expand_repeats: bool = True, expand_repeats: bool = True,
) -> str: ) -> str:
"""Convert a GP track to the source game arrangement XML string. """Convert a GP track to arrangement XML string.
Args: Args:
song: Parsed Guitar Pro song song: Parsed Guitar Pro song
@@ -558,7 +558,7 @@ def convert_track(
once in authored order — equivalent to the pre-expansion behavior. once in authored order — equivalent to the pre-expansion behavior.
Returns: Returns:
XML string of the the source game arrangement XML string of the chart arrangement
""" """
track = song.tracks[track_index] track = song.tracks[track_index]
num_strings = len(track.strings) num_strings = len(track.strings)
@@ -1149,7 +1149,7 @@ def list_tracks(gp_path: str) -> list[dict]:
def auto_select_tracks(gp_path: str) -> tuple[list[int], dict[int, str]]: def auto_select_tracks(gp_path: str) -> tuple[list[int], dict[int, str]]:
"""Auto-select guitar/bass/keys tracks and assign the source game arrangement names. """Auto-select guitar/bass/keys tracks and assign the standard arrangement names.
Includes piano/keyboard tracks as "Keys" arrangements alongside Includes piano/keyboard tracks as "Keys" arrangements alongside
guitar and bass tracks. guitar and bass tracks.
@@ -1205,7 +1205,7 @@ def auto_select_tracks(gp_path: str) -> tuple[list[int], dict[int, str]]:
role = "bass" if t["is_bass"] else "guitar" role = "bass" if t["is_bass"] else "guitar"
selected.append((t["index"], role)) selected.append((t["index"], role))
# Assign the source game names: Lead, Rhythm, Combo, Bass, Keys, Drums # Assign the standard arrangement names: Lead, Rhythm, Combo, Bass, Keys, Drums
track_indices = [] track_indices = []
name_map = {} name_map = {}
lead_count = 0 lead_count = 0
@@ -1243,14 +1243,14 @@ def convert_piano_track(
*, *,
expand_repeats: bool = True, expand_repeats: bool = True,
) -> str: ) -> str:
"""Convert a GP piano/keyboard track to the source game XML using MIDI encoding. """Convert a GP piano/keyboard track to arrangement XML using MIDI encoding.
Encodes MIDI notes into the source game's string+fret format: Encodes MIDI notes into the string+fret format:
string = midi_note // 24 string = midi_note // 24
fret = midi_note % 24 fret = midi_note % 24
This gives a range of 0-143, covering the full piano range within This gives a range of 0-143, covering the full piano range within
the source game's 6-string x 24-fret structure. The piano highway plugin the 6-string x 24-fret structure. The piano highway plugin
decodes back via: midi = string * 24 + fret. decodes back via: midi = string * 24 + fret.
Honors GP repeat brackets and D.S./D.C./Coda/Fine jumps when Honors GP repeat brackets and D.S./D.C./Coda/Fine jumps when
@@ -1342,7 +1342,7 @@ def convert_piano_track(
base_midi = 60 # fallback to middle C base_midi = 60 # fallback to middle C
midi_note = base_midi + note.value midi_note = base_midi + note.value
# Encode into the source game string+fret # Encode into the string+fret
rs_string = midi_note // 24 rs_string = midi_note // 24
rs_fret = midi_note % 24 rs_fret = midi_note % 24
@@ -1449,9 +1449,9 @@ def convert_drum_track(
*, *,
expand_repeats: bool = True, expand_repeats: bool = True,
) -> str: ) -> str:
"""Convert a GP drum/percussion track to the source game XML using MIDI encoding. """Convert a GP drum/percussion track to arrangement XML using MIDI encoding.
Encodes MIDI drum note numbers into the source game's string+fret format: Encodes MIDI drum note numbers into the string+fret format:
string = midi_note // 24 string = midi_note // 24
fret = midi_note % 24 fret = midi_note % 24
@@ -1534,7 +1534,7 @@ def convert_drum_track(
if midi_note not in GM_DRUM_MAP: if midi_note not in GM_DRUM_MAP:
continue # Skip unknown percussion sounds continue # Skip unknown percussion sounds
# Encode into the source game string+fret # Encode into the string+fret
rs_string = midi_note // 24 rs_string = midi_note // 24
rs_fret = midi_note % 24 rs_fret = midi_note % 24
@@ -1778,7 +1778,7 @@ def convert_file(
*, *,
expand_repeats: bool = True, expand_repeats: bool = True,
) -> list[str]: ) -> list[str]:
"""Convert a GP file to the source game XMLs. """Convert a GP file to arrangement XMLs.
Args: Args:
gp_path: Path to .gp5/.gp4/.gp3 file gp_path: Path to .gp5/.gp4/.gp3 file
+9 -9
View File
@@ -663,7 +663,7 @@ def list_tracks(gp_path: str) -> list[dict]:
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# convert_file — mirrors gp2rs.convert_file interface # convert_file — mirrors gp2rs.convert_file interface
# Converts GPX tracks directly to the source game XML, reusing gp2rs._build_xml # Converts GPX tracks directly to arrangement XML, reusing gp2rs._build_xml
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -816,7 +816,7 @@ def _collect_tone_events(
def _inject_tones(xml_str: str, tone_events: list[tuple[float, str]]) -> str: def _inject_tones(xml_str: str, tone_events: list[tuple[float, str]]) -> str:
""" """
Inject a <tones> element into a the source game arrangement XML string. Inject a <tones> element into a chart arrangement XML string.
Parses the prettified XML returned by _build_xml, inserts the tones Parses the prettified XML returned by _build_xml, inserts the tones
block before </song>, and re-serialises. Noop if tone_events is empty. block before </song>, and re-serialises. Noop if tone_events is empty.
@@ -1124,7 +1124,7 @@ def convert_file(
*, *,
expand_repeats: bool = True, expand_repeats: bool = True,
) -> list[str]: ) -> list[str]:
"""Convert a .gpx file to the source game XML arrangement files. """Convert a .gpx file to arrangement XML arrangement files.
Mirrors gp2rs.convert_file so the editor plugin can call it transparently. Mirrors gp2rs.convert_file so the editor plugin can call it transparently.
expand_repeats is accepted for API compatibility but repeat expansion from expand_repeats is accepted for API compatibility but repeat expansion from
@@ -1809,10 +1809,10 @@ def _is_vocal_track(track: dict) -> bool:
def _gpx_lyric_to_rs(raw: str) -> str: def _gpx_lyric_to_rs(raw: str) -> str:
""" """
Convert a GPX lyric token to the source game vocal lyric format. Convert a GPX lyric token to the vocal lyric format.
GPX encodes syllable continuation with a trailing hyphen (e.g. "in-", "t-"). GPX encodes syllable continuation with a trailing hyphen (e.g. "in-", "t-").
the source game uses the same convention for mid-word syllables. For word-final charts use the same convention for mid-word syllables. For word-final
syllables with no hyphen, RS requires a "+" suffix to signal "connect to syllables with no hyphen, RS requires a "+" suffix to signal "connect to
next syllable without a space" — but only when the next beat is a next syllable without a space" — but only when the next beat is a
continuation of the same word. We handle this at the sequence level in continuation of the same word. We handle this at the sequence level in
@@ -1849,7 +1849,7 @@ def convert_vocal_track(
arr_name: str = 'Vocals', arr_name: str = 'Vocals',
) -> str: ) -> str:
""" """
Convert a GPX vocal track to a the source game vocals arrangement XML. Convert a GPX vocal track to a chart vocals arrangement XML.
Each beat with a lyric and a note becomes a <vocal> element: Each beat with a lyric and a note becomes a <vocal> element:
time — seconds from song start + audio_offset time — seconds from song start + audio_offset
@@ -1864,7 +1864,7 @@ def convert_vocal_track(
Beats with a lyric but no pitch note are included as pitch-0 rests so the Beats with a lyric but no pitch note are included as pitch-0 rests so the
display timeline stays intact. Beats with no lyric are skipped entirely. display timeline stays intact. Beats with no lyric are skipped entirely.
The output is a minimal but valid the source game vocals XML. It does not include The output is a minimal but valid vocals XML. It does not include
ebeats or phrases (RS parses vocal XMLs without them). ebeats or phrases (RS parses vocal XMLs without them).
""" """
string_pitches = track['string_pitches'] # high→low, standard guitar if vocal string_pitches = track['string_pitches'] # high→low, standard guitar if vocal
@@ -2018,10 +2018,10 @@ def _build_vocals_xml(
vocals: list[dict], vocals: list[dict],
tempo: int, tempo: int,
) -> str: ) -> str:
"""Build a the source game vocals arrangement XML string.""" """Build a chart vocals arrangement XML string."""
from xml.dom import minidom from xml.dom import minidom
# the source game vocals arrangement is a flat <vocals> document — NOT a # vocals arrangement is a flat <vocals> document — NOT a
# <song> wrapper. Every lyric consumer in the codebase keys off the root # <song> wrapper. Every lyric consumer in the codebase keys off the root
# tag being literally "vocals" (lib/loosefolder.py, server.py highway # tag being literally "vocals" (lib/loosefolder.py, server.py highway
# loader), so a <song> root would be silently skipped and the generated # loader), so a <song> root would be silently skipped and the generated
+1 -1
View File
@@ -128,7 +128,7 @@ def _resolve_audio_asset(zf, root=None) -> tuple[str, str | None]:
# GP8 uses 44100 Hz internally for FrameOffset values regardless of the # GP8 uses 44100 Hz internally for FrameOffset values regardless of the
# OGG file's own sample rate. The embedded OGG is typically 48000 Hz # OGG file's own sample rate. The embedded OGG is typically 48000 Hz
# (the source game's preferred rate) and should be passed through as-is — # (the preferred rate) and should be passed through as-is —
# do NOT resample it. The 44100 constant is only used here to convert # do NOT resample it. The 44100 constant is only used here to convert
# FrameOffset integers to seconds for timing math; it never touches audio. # FrameOffset integers to seconds for timing math; it never touches audio.
# Verified: 44100 gives <10ms sync error; 48000 gives ~530ms error. # Verified: 44100 gives <10ms sync error; 48000 gives ~530ms error.
+1 -1
View File
@@ -176,7 +176,7 @@ def _arr_type_from_filename(stem: str) -> tuple:
def _parse_xml_meta(xml_path: Path) -> dict: def _parse_xml_meta(xml_path: Path) -> dict:
"""Parse a the source game arrangement XML and return song-level metadata.""" """Parse a chart arrangement XML and return song-level metadata."""
try: try:
root = ET.parse(str(xml_path)).getroot() root = ET.parse(str(xml_path)).getroot()
if root.tag != "song": if root.tag != "song":
+1 -1
View File
@@ -349,7 +349,7 @@ def instrument_for_arrangement(arr_entry) -> str:
# arrangements often carry a generic type (lead/rhythm/combo) but have a # arrangements often carry a generic type (lead/rhythm/combo) but have a
# name like "Keys" or "Piano". Name overrides the generic type for all # name like "Keys" or "Piano". Name overrides the generic type for all
# well-known non-guitar instruments so that scored keys runs advance the # well-known non-guitar instruments so that scored keys runs advance the
# keys path and quests even when the the source game XML type was not updated. # keys path and quests even when the chart XML type was not updated.
if "bass" in name: if "bass" in name:
return "bass" return "bass"
if "drum" in name or "percussion" in name: if "drum" in name or "percussion" in name:
+8 -8
View File
@@ -1,4 +1,4 @@
"""the source game arrangement XML parser and song data models.""" """arrangement XML parser and song data models."""
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
@@ -88,7 +88,7 @@ class HandShape:
@dataclass @dataclass
class PhraseLevel: class PhraseLevel:
"""One difficulty tier's worth of note/chord/anchor/hand-shape data for a """One difficulty tier's worth of note/chord/anchor/hand-shape data for a
single phrase iteration. the source game's XML stores these as `<level single phrase iteration. the arrangement XML stores these as `<level
difficulty="N">` blocks that repeat for every difficulty tier the chart difficulty="N">` blocks that repeat for every difficulty tier the chart
author wrote; slopsmith used to collapse them to the phrase's author wrote; slopsmith used to collapse them to the phrase's
maxDifficulty and throw the rest away. Keeping them around lets the maxDifficulty and throw the rest away. Keeping them around lets the
@@ -129,7 +129,7 @@ class Arrangement:
chord_templates: list[ChordTemplate] = field(default_factory=list) chord_templates: list[ChordTemplate] = field(default_factory=list)
# None for single-level sources (GP converter, old sloppaks) — frontends # None for single-level sources (GP converter, old sloppaks) — frontends
# should treat a missing `phrases` as "no per-phrase difficulty data # should treat a missing `phrases` as "no per-phrase difficulty data
# available, disable the slider". Populated from the source game XML when # available, disable the slider". Populated from arrangement XML when
# multiple `<level>` tiers exist. # multiple `<level>` tiers exist.
phrases: list[Phrase] | None = None phrases: list[Phrase] | None = None
# Tone data lifted from the source archive by the sloppak converter and # Tone data lifted from the source archive by the sloppak converter and
@@ -681,7 +681,7 @@ def _int_optional(elem, attr, default=-1):
Use for fields that are merely metadata hints (right-hand fingering, Use for fields that are merely metadata hints (right-hand fingering,
pick direction, etc.) where a malformed value from a third-party pick direction, etc.) where a malformed value from a third-party
the source game XML emitter shouldn't abort the whole arrangement parse. arrangement XML emitter shouldn't abort the whole arrangement parse.
Required-field readers (`string`, `fret`, `chordId`, …) keep using Required-field readers (`string`, `fret`, `chordId`, …) keep using
`_int` so a corrupted required attribute still fails fast at parse `_int` so a corrupted required attribute still fails fast at parse
@@ -708,7 +708,7 @@ def _bool(elem, attr):
def _hand_shape_arpeggio_flag(elem) -> bool: def _hand_shape_arpeggio_flag(elem) -> bool:
"""the source game / EOF may mark arpeggio on ``<handShape>`` (various casings).""" """charts / EOF may mark arpeggio on ``<handShape>`` (various casings)."""
for attr in ("arpeggio", "Arpeggio", "arp", "Arp"): for attr in ("arpeggio", "Arpeggio", "arp", "Arp"):
if _bool(elem, attr): if _bool(elem, attr):
return True return True
@@ -716,7 +716,7 @@ def _hand_shape_arpeggio_flag(elem) -> bool:
def _chord_template_arpeggio_flag(elem) -> bool: def _chord_template_arpeggio_flag(elem) -> bool:
"""the source game commonly tags arpeggio templates in ``displayName`` via ``-arp``.""" """charts commonly tag arpeggio templates in ``displayName`` via ``-arp``."""
for attr in ("arpeggio", "Arpeggio", "arp", "Arp"): for attr in ("arpeggio", "Arpeggio", "arp", "Arp"):
if _bool(elem, attr): if _bool(elem, attr):
return True return True
@@ -766,7 +766,7 @@ def _parse_note(n) -> Note:
def parse_arrangement(xml_path: str) -> Arrangement: def parse_arrangement(xml_path: str) -> Arrangement:
"""Parse a the source game arrangement XML file.""" """Parse a chart arrangement XML file."""
tree = ET.parse(xml_path) tree = ET.parse(xml_path)
root = tree.getroot() root = tree.getroot()
@@ -1049,7 +1049,7 @@ def parse_arrangement(xml_path: str) -> Arrangement:
# wrote at or below this phrase's max — these are what the # wrote at or below this phrase's max — these are what the
# master-difficulty slider selects between at render time. # master-difficulty slider selects between at render time.
# Tiers above max_diff exist in some XMLs (authoring leftovers) # Tiers above max_diff exist in some XMLs (authoring leftovers)
# and are skipped to match the source game's in-game behaviour. # and are skipped to match the reference player's behaviour.
# Capture the extracted slices so the flat max-mastery merge # Capture the extracted slices so the flat max-mastery merge
# below can reuse one of them. # below can reuse one of them.
phrase_levels: list[PhraseLevel] = [] phrase_levels: list[PhraseLevel] = []
+1 -1
View File
@@ -58,7 +58,7 @@ def _convert_riff_wem(data: bytes, output_path: str) -> bool:
if codec == 0xFFFF or codec == 0x0069: if codec == 0xFFFF or codec == 0x0069:
# Wwise Vorbis — audio_data contains raw Ogg pages or encoded Vorbis # Wwise Vorbis — audio_data contains raw Ogg pages or encoded Vorbis
# For the source game custom song, the data is typically packed Vorbis # For custom songs, the data is typically packed Vorbis
# Try writing raw data as OGG (some WEM files have valid OGG inside) # Try writing raw data as OGG (some WEM files have valid OGG inside)
if _try_extract_ogg_pages(audio_data, output_path): if _try_extract_ogg_pages(audio_data, output_path):
return True return True
+1 -1
View File
@@ -1,5 +1,5 @@
// Pins the fret-spacing setting in plugins/highway_3d/screen.js (PR #329). // Pins the fret-spacing setting in plugins/highway_3d/screen.js (PR #329).
// The board can render fret columns either Uniform (equal width, the source game // The board can render fret columns either Uniform (equal width, the chart
// Remastered style) or Logarithmic (real instrument geometry), switchable at // Remastered style) or Logarithmic (real instrument geometry), switchable at
// runtime via window.h3dSetFretSpacing and persisted in localStorage. A // runtime via window.h3dSetFretSpacing and persisted in localStorage. A
// refactor that renames the storage key, drops the uniform/log branch in // refactor that renames the storage key, drops the uniform/log branch in
+1 -1
View File
@@ -294,7 +294,7 @@ def test_year_sort_asc_oldest_first(client, seeded):
def test_tuning_sort_down_tuned_before_up_tuned_at_same_distance(client, server_mod): def test_tuning_sort_down_tuned_before_up_tuned_at_same_distance(client, server_mod):
"""Within an ABS(tuning_sort_key) tier, the down-tuned variant """Within an ABS(tuning_sort_key) tier, the down-tuned variant
must come before the up-tuned one so the order matches the source game's must come before the up-tuned one so the order matches the chart's
grouping (Eb Standard before F Standard at distance 6, etc.). grouping (Eb Standard before F Standard at distance 6, etc.).
Earlier code used signed-key DESC for the tiebreaker, which put Earlier code used signed-key DESC for the tiebreaker, which put
+6 before -6 — the opposite of intent. Regression for Copilot +6 before -6 — the opposite of intent. Regression for Copilot
+1 -1
View File
@@ -133,7 +133,7 @@ def test_int_optional_falls_back_on_overflow():
def test_parse_note_falls_back_to_default_on_malformed_numeric_attrs(): def test_parse_note_falls_back_to_default_on_malformed_numeric_attrs():
"""Malformed numeric XML attributes degrade gracefully. """Malformed numeric XML attributes degrade gracefully.
Third-party the source game XML occasionally emits empty / non-numeric Third-party arrangement XML occasionally emits empty / non-numeric
values for fields like `rightHand`. `_int_optional` (used for values for fields like `rightHand`. `_int_optional` (used for
optional metadata fields like `rightHand` and `pickDirection`) optional metadata fields like `rightHand` and `pickDirection`)
falls back to the caller's default instead of raising, so a falls back to the caller's default instead of raising, so a