This commit is contained in:
khodowany
2026-10-04 17:14:13 -05:00
parent f234dd6d51
commit 68df7292ee
33 changed files with 990 additions and 1872 deletions
+6 -3
View File
@@ -16,6 +16,7 @@ def join_variants(draft,harmony,library):
"""
from generator.solo import routes, policy
fixed=set(draft.protected)|set(draft.structural)|{e for f in draft.features for e in f.events}
fixed.update(n.event_id for c in draft.expressions for n in (*c.events,c.target,*((c.preparation,) if c.preparation else ())))
produced=0
for join in (t.start for t in draft.transformations[1:]):
inside=tuple(n for n in draft.notes if join-2<=n.start and n.end<=join+2)
@@ -34,7 +35,7 @@ def join_variants(draft,harmony,library):
for route in routes.candidates(template,{template[-1].event_id},harmony,library,harmony.profile.register,'two-sided join'):
if route.notes==inside:continue
actual=tuple(n for n in draft.notes if n.end<=inside[0].start)+route.notes+tuple(n for n in draft.notes if n.start>=inside[-1].end)
if not drafts.contract_valid(draft,actual)[0] or not harmony.evaluate(actual,until=draft.end,complete=True).valid:continue
if not drafts.contract_valid(draft,actual)[0] or not harmony.evaluate(actual,until=draft.end,complete=True,expressions=draft.expressions).valid:continue
transforms=tuple(dataclasses.replace(t,events=tuple(n.event_id for n in actual if t.start<=n.start<t.end),
rhythm=tuple((n.start-t.start,n.end-t.start) for n in actual if t.start<=n.start<t.end)) for t in draft.transformations)
changed=dataclasses.replace(draft,id=draft.id+':join:'+route.id,notes=actual,transformations=transforms)
@@ -61,6 +62,7 @@ def shared_arrival(draft,harmony,treatments=()):
joins={t.start for t in draft.transformations[1:] if t.operation=='continue'}
fixed=set(draft.protected)|set(draft.structural)
fixed.update(n.event_id for n in notes if n.role=='quote')
fixed.update(n.event_id for c in draft.expressions for n in (*c.events,c.target,*((c.preparation,) if c.preparation else ())))
fixed.update(e for f in draft.features if 'rhythm' in f.preserves for e in f.events)
edits=[]
for join in sorted(joins):
@@ -69,7 +71,7 @@ def shared_arrival(draft,harmony,treatments=()):
if a.event_id in fixed or b.event_id in fixed:continue
actual=(*notes[:i],dataclasses.replace(b,start=a.start),*notes[i+2:])
if not drafts.contract_valid(draft,actual)[0]:continue
if not harmony.evaluate(actual,treatments,until=draft.end,complete=True).valid:continue
if not harmony.evaluate(actual,treatments,until=draft.end,complete=True,expressions=draft.expressions).valid:continue
if not harmonic.anchors_valid(draft.anchors,actual,draft.end,complete=True,closure='closed')[0]:continue
edits.append(types.DraftEdit('shared_arrival',a.start,b.end,(a.event_id,b.event_id),(b.event_id,),
'reopen provisional endpoint as the same sounding continuation; preserve feature and arrival contracts'))
@@ -92,6 +94,7 @@ def revise(draft, harmony, treatments=()):
if len(notes)<3: return draft, tuple(edits)
protected=set(draft.protected)|set(draft.structural)|{n.event_id for n in notes if n.role=='quote'}
# An identifying rhythm also protects its internal rests.
protected.update(n.event_id for c in draft.expressions for n in (*c.events,c.target,*((c.preparation,) if c.preparation else ())))
protected.update(e for feature in draft.features if 'rhythm' in feature.preserves for e in feature.events)
for _ in range(2):
proposals=[]
@@ -111,7 +114,7 @@ def revise(draft, harmony, treatments=()):
if not groups.valid(tuple((n.start-draft.start,n.end-draft.start) for n in actual)):continue
if fragmentation(actual)>=fragmentation(notes): continue
if not drafts.contract_valid(draft,actual)[0]: continue
if not harmony.evaluate(actual,treatments,until=draft.end,complete=True).valid: continue
if not harmony.evaluate(actual,treatments,until=draft.end,complete=True,expressions=draft.expressions).valid: continue
if not harmonic.anchors_valid(draft.anchors,actual,draft.end,complete=True,closure='closed')[0]: continue
valid.append(actual)
if not valid: break