Fixed acoustic
ci / checks (push) Successful in 48m31s

This commit is contained in:
khodowany
2026-09-30 12:14:49 -05:00
parent 7683793052
commit 10d295abfe
5 changed files with 173 additions and 26 deletions
+33 -13
View File
@@ -82,22 +82,22 @@ class VoicingTests(unittest.TestCase):
self.assertEqual(generator.guitar.voicing_cost(grip((fret,) * 4)).effort, 2 * max(0, fret - 12))
crossing = grip((11, 12, 13, 14))
self.assertEqual(generator.guitar.voicing_cost(crossing).effort, 7)
for profile in ('electric', 'acoustic'):
self.assertEqual(generator.guitar.voicing_cost(crossing, profile).effort, 33)
self.assertEqual(generator.guitar.voicing_cost(crossing, 'electric').effort, 33)
self.assertEqual(generator.guitar.voicing_cost(crossing, 'acoustic').effort, 24)
def test_chord_shape_and_height_share_one_effort_cost(self):
shape = grip((1, 3, 4, 5))
self.assertEqual(generator.guitar.voicing_cost(shape).effort, 4)
for profile in ('electric', 'acoustic'):
self.assertEqual(generator.guitar.voicing_cost(shape, profile).effort, 23)
self.assertEqual(generator.guitar.voicing_cost(shape, 'electric').effort, 23)
self.assertEqual(generator.guitar.voicing_cost(shape, 'acoustic').effort, 11)
for profile in ('', 'electric', 'acoustic'):
self.assertLess(generator.guitar.voicing_cost(grip((4, 4, 4, 4)), profile), generator.guitar.voicing_cost(grip((2, 4, 1, 3)), profile))
def test_distinct_frets_add_difficulty_even_with_the_same_span(self):
simple, varied = grip((3, 3, 5, 5)), grip((3, 4, 5, 5))
for profile in ('electric', 'acoustic'):
self.assertEqual(generator.guitar.voicing_cost(simple, profile).effort, 13)
self.assertEqual(generator.guitar.voicing_cost(varied, profile).effort, 15)
for profile, expected in (('electric', 13), ('acoustic', 7)):
self.assertEqual(generator.guitar.voicing_cost(simple, profile).effort, expected)
self.assertEqual(generator.guitar.voicing_cost(varied, profile).effort, expected + 2)
self.assertEqual(generator.guitar.voicing_cost(simple).effort, generator.guitar.voicing_cost(varied).effort)
def test_compact_mid_neck_acoustic_pair_beats_high_and_stretched_low_pairs(self):
def test_acoustic_reach_excludes_stretched_low_pair_before_scoring(self):
high = [grip((12, 12, 12, 11), (6, 4, 3, 2)), grip((12, 11, 12, 11), (6, 4, 3, 2))]
requested = [grip((7, 8, 7, 8), (5, 4, 3, 2)), grip((7, 8, 6, 8), (5, 4, 3, 2))]
low = [grip((6, 5, 3, 3), (6, 4, 3, 2)), grip((5, 5, 3, 2), (6, 4, 3, 2))]
@@ -108,20 +108,40 @@ class VoicingTests(unittest.TestCase):
self.assertIn(shape, candidates)
self.assertEqual({p.midi % 12 for p in upper}, {p.midi % 12 for p in middle})
self.assertEqual(min(p.midi for p in upper), min(p.midi for p in middle))
preferred = generator.guitar.acoustic_positions(requirements)
self.assertIn(middle, preferred)
self.assertIn(upper, preferred)
self.assertNotIn(lower, preferred)
costs = [sum(generator.guitar.voicing_cost(s, 'acoustic').effort for s in pair) + 2 * generator.guitar.acoustic_movement(*pair) for pair in (requested, high, low)]
self.assertEqual(costs, [33, 36, 41])
def test_real_autumn_leaves_lead_in_uses_requested_compact_grips(self):
self.assertEqual(costs, [24, 30, 23])
def test_real_autumn_leaves_cadences_use_comfortable_inversions_and_complete_triads(self):
source = generator.musicxml.load_musicxml(pathlib.Path('data/songs/autumn-leaves.xml'))
before = copy.deepcopy(source)
result = generator.arrange.generate.arrange_document(source, 'acoustic', rhythms=generator.rhythm.load_library(pathlib.Path('data/rhythms')))
generator.model.validate_document(result)
measures = result['staves'][0]['measures'][:2]
measures = result['staves'][0]['measures']
self.assertEqual(measures[0]['section_label'], 'Rhythm Lead-in')
for measure, symbol, expected in zip(measures, ('Em7b5', 'A7b9'), (((5, 7), (4, 8), (3, 7), (2, 8)), ((5, 7), (4, 8), (3, 6), (2, 8)))):
with self.subTest(chord=symbol):
for index, symbol, expected in ((0, 'Em7b5', ((6, 3), (4, 2), (3, 3), (2, 3))), (1, 'A7b9', ((6, 3), (4, 2), (3, 3), (2, 2))), (4, 'Em7b5', ((6, 3), (4, 2), (3, 3), (2, 3))), (5, 'A7b9', ((6, 3), (4, 2), (3, 3), (2, 2)))):
measure = measures[index]
with self.subTest(measure=measure['number'], chord=symbol):
self.assertEqual(generator.harmony.symbol(measure['harmonies'][0]), symbol)
positions = {(p['tablature']['string'], p['tablature']['fret']) for v in measure['voices'] for n in v['notes'] for p in n['pitches']}
self.assertEqual(tuple(sorted(positions, reverse=True)), expected)
checked = set()
for measure in measures:
if measure['number'] not in ('26', '27', '30', '31', '32', '33'):
continue
pitches = [p for v in measure['voices'] for n in v['notes'] for p in n['pitches']]
if not pitches:
continue
positions = tuple(generator.guitar.TabPosition(p['tablature']['string'], p['tablature']['fret'], int(generator.model.pitch_number(p)) - 12) for p in pitches)
self.assertLessEqual(generator.guitar.physical_reach(positions), generator.guitar.PREFERRED_ACOUSTIC_REACH, measure['id'])
self.assertTrue(all('tie_from' not in p and 'tie_to' not in p for p in pitches))
if measure['number'] in ('32', '33'):
self.assertEqual({p.midi % 12 for p in positions}, {2, 5, 9})
checked.add(measure['number'])
self.assertEqual(checked, {'26', '27', '30', '31', '32', '33'})
self.assertEqual(result, generator.arrange.generate.arrange_document(source, 'acoustic', rhythms=generator.rhythm.load_library(pathlib.Path('data/rhythms'))))
self.assertEqual(source, before)
def test_acoustic_simpler_three_note_shape_can_beat_four_notes(self):
candidates = ranked('Csus', profile='acoustic')