Rhythm Comping Pairs
ci / checks (push) Successful in 56m36s

This commit is contained in:
khodowany
2026-10-07 20:57:23 -05:00
parent a0e13434e4
commit 3a01b4b6bd
10 changed files with 849 additions and 555 deletions
+9 -8
View File
@@ -31,6 +31,8 @@ def _load(contents):
policy = value['instruments'][part]
if part == 'acoustic' and policy.get('first_attack') != 'bass_per_harmony':
raise ValueError('acoustic patterns require bass at the first attack of each harmony')
if part == 'acoustic' and policy.get('attack_assignment') != {'version': 1, 'mode': 'combined_except_eighth_pairs'}:
raise ValueError('acoustic patterns require automatic eighth-pair assignment')
groove = policy['groove']
if any(type(groove[k]) is not int for k in ('cycle_patterns', 'slot_candidates', 'fill_spacing_measures')) or not 1 <= groove['cycle_patterns'] <= 4 or not 4 <= groove['slot_candidates'] <= 8 or groove['fill_spacing_measures'] < 8:
raise ValueError('invalid bounded accompaniment groove policy')
@@ -62,16 +64,15 @@ def _load(contents):
if onset.denominator > 2 or duration.denominator > 2:
raise ValueError('comping vocabulary uses a quarter/eighth grid')
if part == 'acoustic':
if row['layer'] == 'combined':
if duration < 1 and duration != fractions.Fraction(1, 2):
raise ValueError('combined acoustic attacks require quarter/eighth notes or sustains')
elif row['layer'] not in ('bass', 'upper') or duration not in (fractions.Fraction(1, 2), 1):
raise ValueError('acoustic short attacks require bass/upper quarter/eighth notes')
if row['layer'] != 'acoustic':
raise ValueError('acoustic pattern layers are assigned from the complete rhythm')
elif row['layer'] != 'electric':
raise ValueError('electric pattern must contain electric chord attacks')
if row.get('anticipate') and row['layer'] not in ('upper', 'electric'):
raise ValueError('only upper/electric chords can anticipate')
lanes = ('bass', 'upper') if row['layer'] == 'combined' else (row['layer'],)
if row.get('anticipate') and row['layer'] not in ('acoustic', 'electric'):
raise ValueError('only acoustic/electric chords can anticipate')
# Acoustic overlaps are checked after deterministic string-lane
# assignment, including neighboring figures in the source form.
lanes = () if part == 'acoustic' else (row['layer'],)
if any(releases.get(lane, fractions.Fraction()) > onset for lane in lanes):
raise ValueError('overlapping accompaniment attacks on the same strings')
releases.update({lane: onset + duration for lane in lanes})