Soloing
This commit is contained in:
@@ -123,10 +123,10 @@ def endings(source, incoming, part, harmony, bar, *, limit=8):
|
||||
harmony.members(arrival,arrival+hold),(41,76),hold,'terminal',harmony.at(arrival).chord,
|
||||
hold_until=arrival+hold)
|
||||
cadence=sum(h.ending==(pitch.number()%12,arrival%4,hold) for h in incoming.history)
|
||||
rank=abs(pitch.number()-goal)/24+.16*min(2,cadence)+.035*abs(float(hold)-3)+.015*float(release)
|
||||
rank=destination_cost(pitch,arrival,arrival+hold,harmony,incoming,goal,True)+.16*min(2,cadence)+.035*abs(float(hold)-3)+.015*float(release)
|
||||
values.append((rank,anchor))
|
||||
selected=sequences.diverse(values,lambda x:(x[0],policy.stable_rank(source.id,x[1])),
|
||||
lambda x:(x[1].duration,x[1].earliest%bar,x[1].pitch.number()>goal),limit)
|
||||
selected=destination_options(values,lambda x:(x[0],policy.stable_rank(source.id,x[1])),
|
||||
lambda x:x[1].pitch,lambda x:x[1].earliest,harmony,limit)
|
||||
return tuple(a for _,a in selected)
|
||||
|
||||
|
||||
@@ -159,3 +159,61 @@ def evaluate(notes, incoming, harmony, start, end, bar):
|
||||
leaps=sum(max(0,abs(b.pitch.number()-a.pitch.number())-5)/12 for a,b in zip(notes,notes[1:]))/max(1,len(notes)-1)
|
||||
history_cost=repetition(heard(notes,'develop'),incoming.history,bar)
|
||||
return 2*checked.exposure+entry+leaps+.35*max(0,4-density,density-6)+.8*history_cost
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=512)
|
||||
def defining_members(symbol):
|
||||
"""Written chord identity, without inventing omitted thirds or sevenths."""
|
||||
chord=generator.harmony.parse_symbol(symbol)
|
||||
degrees=generator.harmony.degrees(chord)
|
||||
root=generator.model.NATURAL[chord['root']['letter']]+generator.model.alteration(chord['root']['alter'])
|
||||
selected={d for d in (3,6,7) if d in degrees}
|
||||
if chord['kind'] in ('suspended-second','suspended-fourth'):
|
||||
selected.update(d for d in (2,4) if d in degrees)
|
||||
if degrees.get(5,0):selected.add(5)
|
||||
return frozenset(int(root+generator.harmony.degree_semitones(d,degrees[d]))%12 for d in selected)
|
||||
|
||||
|
||||
def destination_cost(pitch, start, end, harmony, incoming=None, goal=None, terminal=False):
|
||||
"""Shared bounded function/trajectory cost at retrieval and final selection."""
|
||||
span=harmony.at(start)
|
||||
if not span or not span.scale:return float('inf')
|
||||
number=pitch.number(); pc=number%12
|
||||
if pc not in harmony.members(start,end):return float('inf')
|
||||
tonic=generator.model.parse_pitch_name(span.scale.root)
|
||||
tonic_pc=int(generator.model.NATURAL[tonic['letter']]+generator.model.alteration(tonic['alter']))%12
|
||||
function=0. if pc in defining_members(span.chord) or terminal and pc==tonic_pc else .25
|
||||
register=min(.15,abs(number-goal)/80) if goal is not None else 0.
|
||||
entry=min(.15,max(0,abs(number-incoming.last.pitch.number())-7)/80) if incoming and incoming.last else 0.
|
||||
previous=next((h.ending[0] for h in reversed(incoming.history) if h.ending),None) if incoming else None
|
||||
recurrence=.04 if previous==pc else 0.
|
||||
following=next((s for s in harmony.spans if s.start>=end and s.scale and s.roles.basic),None)
|
||||
lookahead=0.
|
||||
if following:
|
||||
future=[p for p in range(max(41,number-12),min(76,number+12)+1) if p%12 in following.roles.basic and p%12 in following.scale.pitches]
|
||||
if future:lookahead=.04*min(1,min(abs(p-number) for p in future)/5)
|
||||
# Contour comes from the heard theme, not a universal upward/nearest rule.
|
||||
direction=0.
|
||||
if incoming and incoming.home and len(incoming.home.notes)>1 and incoming.last:
|
||||
motion=incoming.home.notes[-1].pitch.number()-incoming.home.notes[0].pitch.number()
|
||||
direction=.025 if (number-incoming.last.pitch.number())*motion<0 else 0.
|
||||
return function+register+entry+recurrence+lookahead+direction
|
||||
|
||||
|
||||
def destination_family(pitch, time, harmony):
|
||||
return next((degrees for pc,degrees in harmony.at(time).roles.degrees if pc==pitch.number()%12),())
|
||||
|
||||
|
||||
def destination_options(values, rank, pitch, time, harmony, limit):
|
||||
"""Keep chord-degree representatives before register/timing variants."""
|
||||
ordered=sorted(values,key=rank)
|
||||
selected=[];seen=set()
|
||||
for value in ordered:
|
||||
family=destination_family(pitch(value),time(value),harmony)
|
||||
if family not in seen:
|
||||
selected.append(value);seen.add(family)
|
||||
if len(selected)==limit:return tuple(selected)
|
||||
for value in ordered:
|
||||
if value not in selected:selected.append(value)
|
||||
if len(selected)==limit:break
|
||||
return tuple(selected)
|
||||
|
||||
Reference in New Issue
Block a user