Soloing
This commit is contained in:
@@ -34,7 +34,7 @@ def approach(target, harmony):
|
||||
letter=letters[(index-1)%7]; octave=target.pitch.octave-(index==0)
|
||||
alter=number-1-generator.model.NATURAL[letter]-12*(octave+1)
|
||||
lower=types.Pitch(letter,F(alter),octave)
|
||||
if not all(placement.positions(p.number()) for p in (upper,lower)):return None
|
||||
if not all(placement.positions(p.number(),harmony.profile) for p in (upper,lower)):return None
|
||||
identity=target.event_id+':enclosure'
|
||||
return (types.Note(t-1,t-F(1,2),upper,target.sources,'enclosure_upper',event_id=identity+':upper'),
|
||||
types.Note(t-F(1,2),t,lower,target.sources,'enclosure_lower',event_id=identity+':lower'))
|
||||
@@ -113,28 +113,40 @@ def candidates(phrase,harmony,library,tempo,rejections):
|
||||
for begin,replacement,route in proposals[:1+policy.DEFAULT.enclosure_routes]:
|
||||
actual=replace_window(notes,begin,target.start,replacement)
|
||||
if actual is None:continue
|
||||
valid,error=drafts.contract_valid(draft,actual)
|
||||
if not valid:continue
|
||||
checked=harmony.evaluate(actual,phrase.state.treatments if phrase.state else (),until=phrase.plan.end,complete=True,enclosures=(contract,))
|
||||
if not checked.valid:continue
|
||||
valid,error=harmonic.anchors_valid(phrase.anchors,actual,phrase.plan.end,complete=True,closure=phrase.plan.closure)
|
||||
if not valid:continue
|
||||
placed=placement.place(actual,tempo)
|
||||
if placed is None:continue
|
||||
old_travel=placement.place(notes,tempo)[0]
|
||||
delta=.6*(movement(actual)-movement(notes))+.025*(placed[0]-old_travel)/max(1,len(notes))/2000
|
||||
density=4*len(actual)/float(phrase.plan.end-phrase.plan.start)
|
||||
before_density=4*len(notes)/float(phrase.plan.end-phrase.plan.start)
|
||||
delta+=.1*(max(0,density-6)-max(0,before_density-6))
|
||||
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,notes=actual,transformations=transforms,enclosures=(contract,))
|
||||
decisions=tuple(dataclasses.replace(d,notes=tuple(n for n in actual if d.start<=n.start<d.end)) for d in phrase.decisions)
|
||||
if tuple(n for d in decisions for n in d.notes)!=actual:continue
|
||||
retained=tuple(r for r in phrase.routes if all(any((n.event_id,n.start,n.end,n.pitch)==(a.event_id,a.start,a.end,a.pitch) for a in actual) for n in r.notes))
|
||||
result.append((delta,dataclasses.replace(phrase,draft=changed,decisions=decisions,before_enclosure=draft,
|
||||
routes=(*retained,*((route,) if route else ())))))
|
||||
accepted+=1
|
||||
variants=[(actual,contract)]
|
||||
upper_index=next((i for i,n in enumerate(actual) if n.event_id==upper.event_id),0)
|
||||
if upper_index:
|
||||
prior=actual[upper_index-1]
|
||||
if prior.end==upper.start and prior.pitch==upper.pitch and editable(draft,prior.start,upper.start):
|
||||
shared=dataclasses.replace(prior,end=upper.end)
|
||||
combined=(*actual[:upper_index-1],shared,*actual[upper_index+1:])
|
||||
variants.append((combined,dataclasses.replace(contract,upper=shared,approach_start=upper.start)))
|
||||
for actual,actual_contract in variants:
|
||||
valid,error=drafts.contract_valid(draft,actual)
|
||||
if not valid:continue
|
||||
checked=harmony.evaluate(actual,phrase.state.treatments if phrase.state else (),until=phrase.plan.end,complete=True,enclosures=(actual_contract,))
|
||||
if not checked.valid:continue
|
||||
valid,error=harmonic.anchors_valid(phrase.anchors,actual,phrase.plan.end,complete=True,closure=phrase.plan.closure)
|
||||
if not valid:continue
|
||||
placed=placement.place(actual,tempo,profile=harmony.profile)
|
||||
if placed is None:continue
|
||||
old_travel=placement.place(notes,tempo,profile=harmony.profile)[0]
|
||||
delta=.6*(movement(actual)-movement(notes))+.025*(placed[0]-old_travel)/max(1,len(notes))/2000
|
||||
density=4*len(actual)/float(phrase.plan.end-phrase.plan.start)
|
||||
before_density=4*len(notes)/float(phrase.plan.end-phrase.plan.start)
|
||||
repeated=sum(a.pitch.number()==b.pitch.number() and a.end==b.start for a,b in zip(actual,actual[1:]))
|
||||
old_repeated=sum(a.pitch.number()==b.pitch.number() and a.end==b.start for a,b in zip(notes,notes[1:]))
|
||||
delta+=.15*max(0,repeated-old_repeated)
|
||||
delta+=.1*(max(0,density-6)-max(0,before_density-6))
|
||||
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,notes=actual,transformations=transforms,enclosures=(actual_contract,))
|
||||
decisions=tuple(dataclasses.replace(d,notes=tuple(n for n in actual if d.start<=n.start<d.end)) for d in phrase.decisions)
|
||||
if tuple(n for d in decisions for n in d.notes)!=actual:continue
|
||||
retained=tuple(r for r in phrase.routes if all(any((n.event_id,n.start,n.end,n.pitch)==(a.event_id,a.start,a.end,a.pitch) for a in actual) for n in r.notes))
|
||||
result.append((delta,dataclasses.replace(phrase,draft=changed,decisions=decisions,before_enclosure=draft,
|
||||
routes=(*retained,*((route,) if route else ())))))
|
||||
accepted+=1
|
||||
if not accepted:rejections.append({'target':target.event_id,'reason':'no valid complete enclosure within local route budget'})
|
||||
return tuple(result)
|
||||
|
||||
@@ -152,7 +164,7 @@ def strict(solo):
|
||||
|
||||
|
||||
def refine(solo,library,enabled=True):
|
||||
started=time.monotonic();harmony=harmonic.Harmony(solo.choices);rejections=[]
|
||||
started=time.monotonic();harmony=harmonic.Harmony(solo.choices,solo.profile);rejections=[]
|
||||
baseline=[]
|
||||
for phrase in solo.phrases:
|
||||
draft=dataclasses.replace(phrase.draft,notes=tuple(n for d in phrase.decisions for n in d.notes)) if phrase.draft else None
|
||||
@@ -179,7 +191,7 @@ def refine(solo,library,enabled=True):
|
||||
_,phrases,arrivals=min(states,key=lambda x:(x[0]+sparse(len(x[2])),policy.stable_rank('enclosure-selection',x[2])))
|
||||
# Resolve the complete guitar path once, including both sides of phrase joins.
|
||||
raw=tuple(n for p in phrases for d in p.decisions for n in d.notes)
|
||||
placed=placement.place(raw,solo.tempo)
|
||||
placed=placement.place(raw,solo.tempo,profile=harmony.profile)
|
||||
if placed is None:raise ValueError('selected enclosure solo has no complete guitar path')
|
||||
by_id={n.event_id:n for n in placed[1]};final=[]
|
||||
for phrase in phrases:
|
||||
|
||||
Reference in New Issue
Block a user