Cleanup
This commit is contained in:
+11
-24
@@ -15,7 +15,7 @@ ACTIVE = contextvars.ContextVar('composition_vocabulary', default=None)
|
||||
def load(root=ROOT):
|
||||
# Content, not just the directory, keys memoization. Long-lived individual
|
||||
# role builds must observe edited production data without restarting Python.
|
||||
names = ('roles', 'grammar', 'harmony', 'lead_ins', 'rhythms', 'melodic_vocabulary')
|
||||
names = ('roles', 'solo', 'harmony', 'lead_ins', 'rhythms')
|
||||
return _load(tuple((name, (Path(root)/(name+'.json')).read_text()) for name in names))
|
||||
|
||||
@functools.lru_cache(maxsize=16)
|
||||
@@ -29,28 +29,15 @@ def _load(contents):
|
||||
if len({v['id'] for v in entries}) != len(entries):
|
||||
raise ValueError(f'{name}: duplicate vocabulary ID')
|
||||
result[name] = value
|
||||
cells=result['rhythms']['cells']+result['melodic_vocabulary']['cells']
|
||||
if len({c['id'] for c in cells})!=len(cells):
|
||||
raise ValueError('Duplicate rhythmic identity across production vocabularies')
|
||||
for c in cells:
|
||||
if c['purpose'] not in ('entrance','continue','connect','answer','arrival'):
|
||||
raise ValueError(c['id']+': unknown rhythmic purpose')
|
||||
paths=result['melodic_vocabulary']['chord_paths']
|
||||
if len({p['id'] for p in paths})!=len(paths):raise ValueError('Duplicate chord-path operator')
|
||||
for p in paths:
|
||||
if p['direction'] not in ('up','down','toward') or p['members'] not in ('basic','defining'):
|
||||
raise ValueError(p['id']+': invalid chord traversal')
|
||||
if not p['member_steps'] or any(type(s)!=int or not 1<=abs(s)<=3 for s in p['member_steps']):
|
||||
raise ValueError(p['id']+': invalid chord-member step')
|
||||
if not p['prefix_attacks'] or any(n not in (2,3,4) for n in p['prefix_attacks']) or not 1<=p['max_interval']<=12:
|
||||
raise ValueError(p['id']+': unsupported bounded chord prefix')
|
||||
gestures=result['melodic_vocabulary']['gesture_rules']
|
||||
if len({g['id'] for g in gestures})!=len(gestures):raise ValueError('Duplicate joint gesture rule')
|
||||
for g in gestures:
|
||||
if g['kind'] not in ('harmonic_arrival','sustained_color'):raise ValueError(g['id']+': unsupported joint gesture')
|
||||
if g['kind']=='harmonic_arrival':
|
||||
if not g['arrival_offsets'] or any(abs(F(v))>F(result['harmony']['anticipation_max']) for v in g['arrival_offsets']):raise ValueError(g['id']+': arrival outside bounded policy')
|
||||
if not g['holds'] or any(F(v)<=0 for v in g['holds']) or F(g['release'])<0:raise ValueError(g['id']+': invalid sounding interval')
|
||||
cells=result['rhythms']['cells']+result['solo']['cells']
|
||||
if len({c['id'] for c in cells})!=len(cells):raise ValueError('Duplicate rhythm identity')
|
||||
rules=result['solo']
|
||||
if set(rules['actions'])!={'arpeggio','scalar','neighbor','develop','answer','return','head'}:raise ValueError('Unknown/missing phrase action')
|
||||
if set(rules['weights'])!={'purpose','continuity','repetition','register'}:raise ValueError('Unknown/missing musical contribution')
|
||||
if any(v<=0 for v in rules['search'].values()):raise ValueError('Invalid deterministic search budget')
|
||||
for group in rules['groups']:
|
||||
if not group['values'] or any(F(v.removeprefix('r:'))<=0 for v in group['values']):raise ValueError('Invalid rhythm group')
|
||||
if set(group['purpose'])-set(rules['actions']):raise ValueError('Unreachable rhythm purpose')
|
||||
return result
|
||||
|
||||
@functools.lru_cache(maxsize=1)
|
||||
@@ -93,7 +80,7 @@ class RhythmCell:
|
||||
|
||||
def rhythm_cells(tempo, purpose=None):
|
||||
limits = data('roles')['difficulty']
|
||||
for cell in (*data('rhythms')['cells'],*data('melodic_vocabulary')['cells']):
|
||||
for cell in (*data('rhythms')['cells'],*data('solo')['cells']):
|
||||
if purpose is not None and cell['purpose'] != purpose:
|
||||
continue
|
||||
difficulty = cell.get('difficulty')
|
||||
|
||||
Reference in New Issue
Block a user