mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-10-02 03:02:01 +00:00
Restore GP6/7/8 tremolo picking on import (#572)
* Map GP7/8 tremolo picking on import GP7/8 (GPIF) encodes tremolo picking as a beat-level <Tremolo> element, which the importer ignored — so tremolo was silently dropped on .gp import, while note vibrato and the GP3-5 path were unaffected. Read the beat-level <Tremolo> and set the note tremolo flag across the beat, independent of vibrato (a note can carry both). Signed-off-by: Sin <deathlysin@outlook.com> * test: cover GP6/7/8 tremolo-picking import Extract the beat-level <Tremolo> detection into a pure _beat_has_tremolo helper (mirroring the tested _note_has_vibrato) so it's unit-testable in this suite's fixture-free style, then add: - 4 unit tests on _beat_has_tremolo: direct <Tremolo> child detected (rate-agnostic), absent -> False, direct-child-only (nested Tremolo ignored), independent of the VibratoWTremBar whammy property. - 1 end-to-end test driving convert_file via a crafted GPIF (monkeypatched _load_gpif): a tremolo-picked beat's note serializes tremolo="1" while a plain beat stays "0". Both the detection and integration tests fail without the fix; full GP suite 238 passed. Refactor is behavior-identical. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Signed-off-by: Sin <deathlysin@outlook.com> Co-authored-by: Byron Gamatos <xasiklas@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
Byron Gamatos
parent
32127bc70b
commit
37aedd4251
@@ -722,6 +722,20 @@ def _note_has_vibrato(note_el: ET.Element, prop_map: dict) -> bool:
|
||||
return 'Vibrato' in prop_map or note_el.find('Vibrato') is not None
|
||||
|
||||
|
||||
def _beat_has_tremolo(beat_el: ET.Element) -> bool:
|
||||
"""True if a GP7/GP8 beat carries tremolo picking.
|
||||
|
||||
GPIF encodes tremolo picking as a DIRECT beat-level
|
||||
``<Tremolo>1/8</Tremolo>`` child of ``<Beat>`` (the value is the rate). The
|
||||
RS note model has a single boolean tremolo flag with no rate, so the rate is
|
||||
intentionally ignored — any tremolo-picked beat maps to note tremolo across
|
||||
it. Matched as a direct child (not ``.//``) so it is never confused with the
|
||||
whammy-bar ``VibratoWTremBar`` Property, a separate beat-level effect
|
||||
handled elsewhere.
|
||||
"""
|
||||
return beat_el.find('Tremolo') is not None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# list_tracks — mirrors gp2rs.list_tracks interface
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1696,6 +1710,17 @@ def convert_file(
|
||||
for _bn in beat_rs_notes:
|
||||
_bn.vibrato = True
|
||||
|
||||
# Tremolo picking: GP7/GP8 encodes the rate as a
|
||||
# beat-level <Tremolo>1/8</Tremolo> child. The note
|
||||
# model has a single tremolo flag (no rate), so map
|
||||
# any tremolo-picked beat to note tremolo across it.
|
||||
# Independent of vibrato above — a note can carry
|
||||
# both. (Beat-level <Tremolo>, not the whammy
|
||||
# VibratoWTremBar Property, which is handled above.)
|
||||
if _beat_has_tremolo(beat_el):
|
||||
for _bn in beat_rs_notes:
|
||||
_bn.tremolo = True
|
||||
|
||||
if len(beat_rs_notes) == 1:
|
||||
rs_notes.append(beat_rs_notes[0])
|
||||
elif len(beat_rs_notes) > 1:
|
||||
|
||||
Reference in New Issue
Block a user