Tab Generation
ci / checks (push) Successful in 1h19m14s

This commit is contained in:
khodowany
2026-09-20 16:20:43 -05:00
parent 68ded4d8c8
commit cf7c0c010c
423 changed files with 1422 additions and 1332 deletions
+45 -2
View File
@@ -9,6 +9,7 @@ from generator.engraving.audit import audit_layout
from generator.engraving.notation import ScoreIndex
from generator.model import NOTE_TYPES, TYPE_LENGTHS, fraction, rational
from generator.musicxml import load_musicxml
from generator.practice.generate import practice_document
from tests.test_musicxml import notes, parse, xml_note, xml_score
class EngravingTests(unittest.TestCase):
@@ -110,8 +111,50 @@ class EngravingTests(unittest.TestCase):
percussion = '<note><unpitched><display-step>C</display-step><display-octave>5</display-octave></unpitched><duration>4</duration><type>whole</type><notehead>x</notehead></note>'
source = parse(xml_score(percussion, attributes='<clef><sign>percussion</sign></clef>'))
self.assertEqual(self.primitives(self.layout(source), 'head')[0].value, 'noteheadXWhole')
tab = parse(xml_score(xml_note(extra='<notations><technical><string>2</string><fret>1</fret></technical></notations>'), attributes='<clef><sign>TAB</sign><line>5</line></clef><staff-details><staff-lines>6</staff-lines></staff-details>'))
self.assertEqual(self.primitives(self.layout(tab), 'tab')[0].value, '1')
tab = practice_document(parse(xml_score(xml_note('C', octave=7))), 'electric')
self.assertEqual(self.primitives(self.layout(tab), 'tab')[0].value, '20')
def test_generated_tab_shares_columns_and_systems_without_duplicating_music(self):
content = ''.join(xml_note('G', 1, '64th') for _ in range(64))
source = practice_document(parse(xml_score(content, divisions=16)), 'electric')
layout = self.layout(source)
systems = [s for page in layout.pages for s in page.systems]
self.assertGreater(len(systems), 1)
self.assertEqual(sum(len(s.fragments) for s in systems), 64)
for system in systems:
heads = [p for p in system.primitives if p.role == 'head']
tabs = [p for p in system.primitives if p.role == 'tab']
self.assertEqual(len(heads), len(tabs))
self.assertEqual(len({p.points[0][1] for p in system.primitives if p.role == 'tab-staff'}), 6)
self.assertLess(max(p.box.bottom for p in system.primitives if not p.role.startswith('tab')), min(p.box.top for p in system.primitives if p.role.startswith('tab')))
self.assertEqual({p.sources[-1] for p in heads}, {p.sources[-1] for p in tabs})
self.assertTrue(all(any(abs(head.points[0][0] - tab.points[0][0]) < 0.01 for head in heads if head.sources[-1] == tab.sources[-1]) for tab in tabs))
from generator.playback import compile_timeline
self.assertEqual(len(compile_timeline(source)['events']), 64)
def test_generated_tab_ties_and_barline_styles(self):
content = xml_note('E', 2, 'half', extra='<tie type="start"/>') + xml_note('E', 2, 'half', extra='<tie type="stop"/>') + '<barline location="right"><bar-style>dashed</bar-style></barline>'
source = practice_document(parse(xml_score(content)), 'electric')
layout = self.layout(source)
self.assertEqual(len(self.primitives(layout, 'tab-tie')), 1)
self.assertEqual(len(self.primitives(layout, 'barline')), len(self.primitives(layout, 'tab-barline')))
for system in (s for page in layout.pages for s in page.systems):
baselines = {role: max(p.points[0][1] for p in system.primitives if p.role == role) for role in ('staff', 'tab-staff')}
notation = [p for p in system.primitives if p.role == 'barline']
tabs = [p for p in system.primitives if p.role == 'tab-barline']
for bar, tab in zip(notation, tabs):
for (x1, y1), (x2, y2) in zip(bar.points, tab.points):
self.assertAlmostEqual(x1, x2)
self.assertAlmostEqual((y1 - baselines['staff']) * 5 / 4, y2 - baselines['tab-staff'])
def test_adjacent_string_tab_numerals_do_not_overlap(self):
from generator.guitar import place_guitar
content = ''.join(xml_note(step, octave=octave, prefix='<chord/>' if index else '') for index, (step, octave) in enumerate((('G', 3), ('C', 4), ('E', 4), ('G', 4), ('C', 5), ('E', 5))))
source = parse(xml_score(content))
place_guitar(source)
layout = self.layout(source)
tabs = self.primitives(layout, 'tab')
self.assertEqual(len(tabs), 6)
for i, first in enumerate(tabs):
for second in tabs[i + 1:]:
self.assertFalse(first.box.overlaps(second.box, 0.1))
def test_lyrics_hyphens_and_extenders(self):
begin = '<lyric number="1"><syllabic>begin</syllabic><text>hel</text></lyric>'
end = '<lyric number="1"><syllabic>end</syllabic><text>lo</text><extend type="start"/></lyric>'