Chord Families

This commit is contained in:
khodowany
2026-10-06 03:18:02 -05:00
parent e71385dad1
commit 8423f3d788
14 changed files with 196 additions and 57 deletions
+6 -6
View File
@@ -21,9 +21,9 @@ def options(symbol, part):
return generator.comping.voicings(generator.comping.semantic_key(generator.harmony.parse_symbol(symbol)), part)
def path_cost(path, costs, part, left=(), right=(), close=False):
total = [sum(costs[i][v][j] for i, v in enumerate(path)) for j in range(4)]
total[1] += sum(generator.comping.movement(part, a, b) for a, b in zip((left, *path), path))
total[1] += generator.comping.movement(part, path[-1], path[0] if close else right)
total = [sum(costs[i][v][j] for i, v in enumerate(path)) for j in range(5)]
total[2] += sum(generator.comping.movement(part, a, b) for a, b in zip((left, *path), path))
total[2] += generator.comping.movement(part, path[-1], path[0] if close else right)
return tuple(total)
class CompingVoicings(unittest.TestCase):
@@ -69,9 +69,9 @@ class CompingVoicings(unittest.TestCase):
# Bass responsibility belongs to the acoustic voicing, as in electric melody.
self.assertEqual(options('C/E', 'electric'), options('C', 'electric'))
def test_impossible_grip_reports_source_location_without_relaxation(self):
source = tests.solo_fixtures.song(count=1, chords=('C5',))
source = tests.solo_fixtures.song(count=1, chords=('C7b9#11',))
for part in ('acoustic', 'electric'):
with self.subTest(part=part), self.assertRaisesRegex(ValueError, 'source measure 1, beat 1: C5: no legal'):
with self.subTest(part=part), self.assertRaisesRegex(ValueError, 'source measure 1, beat 1: C7b9#11: no legal'):
generator.comping.Canonical(source, part, 'test')
def test_complete_path_beats_greedy_and_matches_exhaustive_search(self):
for part in ('acoustic', 'electric'):
@@ -90,7 +90,7 @@ class CompingVoicings(unittest.TestCase):
def test_repeat_join_and_neighbor_boundaries_participate_in_selection(self):
for part in ('acoustic', 'electric'):
layers = tuple(options(s, part) for s in ('C', 'Dm7', 'G7'))
costs = tuple({v: (0, 0, 0, 0) for v in layer} for layer in layers)
costs = tuple({v: generator.comping.VoicingCost(generator.fretboard.span_penalty(v)) for v in layer} for layer in layers)
for endpoints in ({'close': True}, {'left': options('Am7', part)[0], 'right': options('FM7', part)[-1]}):
with self.subTest(part=part, endpoints=endpoints):
path, cost = generator.comping.select_grips(layers, costs, part, 'test', **endpoints)