Comp
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"""Instantiate complete authored routes; a failed path is never repaired note by note."""
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import fractions
|
||||
|
||||
import generator.fretboard
|
||||
import generator.solo.policy
|
||||
import generator.solo.sequences
|
||||
import generator.solo.types as types
|
||||
|
||||
def advance(scale, previous, step):
|
||||
try:
|
||||
degree = types.scale_index(scale, previous)
|
||||
return tuple([types.scale_pitch(scale, degree + step)])
|
||||
except ValueError:
|
||||
# At a changed collection count actual intervening scale tones from the
|
||||
# incoming sound. No old degree number is transferred to the new scale.
|
||||
if not step:
|
||||
return ()
|
||||
nearby = sorted((types.scale_pitch(scale, i) for i in range(-28, 35)), key=lambda p: p.number())
|
||||
outgoing = [p for p in nearby if (p.number() - previous) * step > 0]
|
||||
if step < 0:
|
||||
outgoing.reverse()
|
||||
return tuple(outgoing[abs(step) - 1:abs(step)])
|
||||
|
||||
def candidates(notes, fixed, harmony, library, register, relationship='connect theme'):
|
||||
"""Every returned candidate includes its entry, complete rhythm and destination.
|
||||
|
||||
Callers validate the whole surrounding sounding context and guitar path.
|
||||
This function applies exact scale paths, fixed events and range constraints.
|
||||
"""
|
||||
if not 3 <= len(notes) <= 16:
|
||||
return ()
|
||||
first, destination = notes[0], notes[-1]
|
||||
span = harmony.at(first.start)
|
||||
if not span or not span.scale:
|
||||
return ()
|
||||
try:
|
||||
displacement = types.scale_index(span.scale, destination.pitch.number()) - types.scale_index(span.scale, first.pitch.number())
|
||||
displacements = tuple([displacement])
|
||||
except ValueError:
|
||||
# Enumerate possible degree routes when the destination changes collection;
|
||||
# actual endpoint equality below decides compatibility, not a pitch estimate.
|
||||
displacements = tuple(range(-7, 8))
|
||||
direction = (destination.pitch.number() > first.pitch.number()) - (destination.pitch.number() < first.pitch.number())
|
||||
paths = dict.fromkeys(path for d in displacements for path in library.retrieve(len(notes), d, direction))
|
||||
result = []
|
||||
low, high = generator.fretboard.single_register((register[0] - 7, register[1] + 5))
|
||||
allowed = {n.start: types.permitted(harmony.choices, n.start, n.end) for n in notes}
|
||||
for path in paths:
|
||||
realized = [first]
|
||||
for template, step in zip(notes[1:], path.steps):
|
||||
current = harmony.at(template.start)
|
||||
if not current or not current.scale:
|
||||
break
|
||||
options = advance(current.scale, realized[-1].pitch.number(), step)
|
||||
if not options:
|
||||
break
|
||||
pitch = options[0]
|
||||
if not low <= pitch.number() <= high or pitch.number() % 12 not in allowed[template.start]:
|
||||
break
|
||||
if (template.event_id in fixed or template is destination) and pitch.number() != template.pitch.number():
|
||||
break
|
||||
if not generator.fretboard.single_positions_for_pitch(fractions.Fraction(pitch.number())):
|
||||
break
|
||||
realized.append(dataclasses.replace(template, pitch=template.pitch if pitch.number() == template.pitch.number() else pitch, position=None))
|
||||
if len(realized) != len(notes):
|
||||
continue
|
||||
route_spans = tuple((max(first.start, c.span.start), min(destination.end, c.span.end), c.scale.label()) for c in harmony.choices if c.scale and c.span.start < destination.end and c.span.end > first.start)
|
||||
identity = generator.solo.policy.stable_rank('complete-route', (tuple(realized), path.sources))[:16]
|
||||
result.append(types.RouteCandidate(identity, tuple(realized), destination, route_spans, relationship, path.sources))
|
||||
return generator.solo.sequences.diverse(result,
|
||||
lambda r: (sum(abs(b.pitch.number() - a.pitch.number()) for a, b in zip(r.notes, r.notes[1:])), r.id),
|
||||
lambda r: generator.solo.sequences.family(tuple(b.pitch.number() - a.pitch.number() for a, b in zip(r.notes, r.notes[1:]))),
|
||||
generator.solo.policy.DEFAULT.join_alternatives)
|
||||
Reference in New Issue
Block a user