@@ -1,3 +1,4 @@
|
||||
import generator.hand_position
|
||||
import generator.validation
|
||||
import copy
|
||||
import fractions
|
||||
@@ -13,6 +14,7 @@ import generator.harmony
|
||||
import generator.model
|
||||
import tests.test_acoustic
|
||||
import tests.test_accompaniment
|
||||
import tests.position_fixtures
|
||||
|
||||
def planned_document(harmonies, attacks=None):
|
||||
if attacks is None:
|
||||
@@ -173,19 +175,15 @@ class AcousticVoicingTests(unittest.TestCase):
|
||||
dense = planned_document(harmonies, [([(i, 1) for i in range(4)], [(i, 1) for i in range(4)])] * 3)
|
||||
selected = [tuple(tuple(sorted({p for n in span.notes for p in shape(n)})) for span in self.place(document)) for document in (sparse, dense)]
|
||||
self.assertEqual(*selected)
|
||||
def test_bass_switch_counts_both_string_and_fret_travel(self):
|
||||
def test_bass_switch_and_finger_changes_do_not_move_the_hand(self):
|
||||
previous = voicing((7, 8, 7, 8))
|
||||
for current, expected in ((voicing((7, 8, 7, 8), 6), 1), (voicing((8, 8, 7, 8), 6), 2), (voicing((9, 8, 7, 8), 6), 3), (voicing((6, 8, 7, 8)), 1)):
|
||||
self.assertEqual(generator.acoustic_voicing.movement(previous, current), expected)
|
||||
self.assertEqual(generator.acoustic_voicing.movement(current, previous), expected)
|
||||
self.assertEqual(generator.acoustic_voicing.movement(previous, previous), 0)
|
||||
def test_each_upper_string_is_tracked_not_just_the_grip_center(self):
|
||||
for current in (voicing((7, 8, 7, 8), 6), voicing((8, 8, 7, 8), 6), voicing((9, 8, 7, 8), 6), voicing((6, 8, 7, 8))):
|
||||
self.assertEqual(generator.acoustic_voicing.movement(previous, current, 6, 6), 0)
|
||||
self.assertEqual(generator.acoustic_voicing.movement(previous, previous, 5, 7), 2)
|
||||
def test_each_upper_string_is_tracked_as_secondary_finger_effort(self):
|
||||
before, after = voicing((5, 7, 5, 7)), voicing((7, 5, 7, 5))
|
||||
self.assertEqual(generator.acoustic_voicing.movement(before, after), 8)
|
||||
for index in (1, 2, 3):
|
||||
frets = [5, 7, 5, 7]
|
||||
frets[index] += 1
|
||||
self.assertEqual(generator.acoustic_voicing.movement(before, voicing(frets)), 1)
|
||||
self.assertEqual(generator.acoustic_voicing.movement(before, after, 5, 5), 0)
|
||||
self.assertEqual(generator.hand_position.motion(before, after, 5, 5, True).fingers, 8)
|
||||
def test_fret_and_register_limits_are_hard_constraints(self):
|
||||
for frets, valid in (((1, 1, 1, 1), True), ((1, 3, 1, 3), True), ((1, 4, 1, 4), True), ((1, 5, 1, 4), False), ((0, 1, 1, 1), False), ((12, 14, 14, 12), True), ((12, 15, 14, 12), True), ((13, 13, 13, 13), False), ((12, 16, 14, 12), False), ((12, 21, 12, 12), False)):
|
||||
with self.subTest(frets=frets):
|
||||
@@ -205,7 +203,8 @@ class AcousticVoicingTests(unittest.TestCase):
|
||||
options = (voicing((1, 1, 1, 1)), voicing((1, 3, 2, 3)), voicing((12, 12, 12, 12)), voicing((12, 14, 13, 14)))
|
||||
states = [generator.acoustic_voicing.Placement(generator.acoustic_voicing.Cost())]
|
||||
result = generator.acoustic_voicing.advance(options, generator.fretboard.ChordRequirements(), states)
|
||||
self.assertEqual([p.cost for p in result], [generator.acoustic_voicing.Cost()] * len(options))
|
||||
self.assertTrue(all(p.cost.travel == 0 for p in result))
|
||||
self.assertEqual({(p.voicing, p.hand, p.cost.peak_position) for p in result}, {(v, h, h) for v in options for h in tests.position_fixtures.regions(v)})
|
||||
def test_optional_tones_break_equal_movement_costs_only(self):
|
||||
requirements = generator.fretboard.chord_requirements(generator.harmony.parse_symbol('C7'))
|
||||
states = [generator.acoustic_voicing.Placement(generator.acoustic_voicing.Cost())]
|
||||
@@ -217,13 +216,16 @@ class AcousticVoicingTests(unittest.TestCase):
|
||||
def test_dynamic_program_matches_exhaustive_paths_and_is_deterministic(self):
|
||||
harmonies = ['FM7', 'Gm7', 'C7']
|
||||
spans = generator.acoustic.harmony_spans(planned_document([[(0, s)] for s in harmonies]))
|
||||
options = [candidates(s) for s in harmonies]
|
||||
options = [candidates(s)[:4] for s in harmonies]
|
||||
requirements = [generator.fretboard.chord_requirements(span.harmony) for span in spans]
|
||||
costs = [{v: generator.acoustic_voicing.Cost(extended_grips=generator.fretboard.span_penalty(v), missing_tones=len(set(r.allowed) - {p.midi % 12 for p in v})) for v in layer} for r, layer in zip(requirements, options)]
|
||||
def cost(path):
|
||||
return generator.acoustic_voicing.Cost(sum(generator.fretboard.span_penalty(v) for v in path), sum(generator.acoustic_voicing.movement(a, b) for a, b in zip(path, path[1:])), sum(len(set(r.allowed) - {p.midi % 12 for p in v}) for r, v in zip(requirements, path)))
|
||||
selected = generator.acoustic_voicing.select(spans)
|
||||
self.assertEqual(cost(selected), min(cost(path) for path in itertools.product(*options)))
|
||||
self.assertEqual(selected, generator.acoustic_voicing.select(spans))
|
||||
return tests.position_fixtures.cost(path, costs, (1, 2, 3), acoustic=True)
|
||||
states = [generator.acoustic_voicing.Placement(generator.acoustic_voicing.Cost())]
|
||||
for layer, r in zip(options, requirements):
|
||||
states = generator.acoustic_voicing.advance(layer, r, states)
|
||||
self.assertEqual(min(s.cost for s in states), min(cost(path) for path in itertools.product(*options)))
|
||||
self.assertEqual(generator.acoustic_voicing.select(spans), generator.acoustic_voicing.select(spans))
|
||||
def test_complete_triads_and_doubling_use_only_authored_chord_tones(self):
|
||||
for symbol in ('CM', 'Dm', 'Cdim', 'Caug', 'Dm/F', 'Dm7no7'):
|
||||
with self.subTest(chord=symbol):
|
||||
|
||||
Reference in New Issue
Block a user