Save
This commit is contained in:
@@ -510,39 +510,45 @@ def checked_phrase(plan, draft, notes, contracts, incoming, harmony, tempo, edit
|
||||
except ValueError:
|
||||
return None
|
||||
complete = plan.end == (plan.passage_end or plan.end)
|
||||
checked = harmony.evaluate(canonical, contracts, until=plan.end, complete=complete, enclosures=draft.enclosures)
|
||||
checked = harmony.evaluate(canonical, contracts, until=plan.end, complete=complete, enclosures=draft.enclosures, expressions=draft.expressions)
|
||||
valid, _ = generator.solo.harmonic.anchors_valid(draft.anchors, canonical, plan.end, complete=complete, closure=plan.closure)
|
||||
if not checked.valid or not valid or not target_ok(notes, plan.target):
|
||||
return None
|
||||
shared = bool(plan.passage_id and incoming.passage_id == plan.passage_id)
|
||||
score_notes = canonical if shared or notes and notes[0].continuation else generator.solo.drafts.local_sounding(notes)
|
||||
evaluation_key = (plan, draft.anchors)
|
||||
if evaluation_key not in harmony.evaluations:
|
||||
harmony.evaluations[evaluation_key] = generator.solo.scoring.Evaluation(evaluation_plan(plan, draft.anchors), harmony.choices, plan.end)
|
||||
evaluation = harmony.evaluations[evaluation_key]
|
||||
if not evaluation.feasible(score_notes):
|
||||
return None
|
||||
features = evaluation.features(score_notes, previous=incoming.last if not shared else None, movement=travel + (incoming.travel if shared else 0))
|
||||
features.update(generator.solo.drafts.relationship_features(draft, notes))
|
||||
if draft.lead_identity or draft.source_phrase:
|
||||
supported = {e for feature in draft.features for e in feature.events}
|
||||
motions = [(a, b) for a, b in zip(notes, notes[1:]) if not (a.event_id in supported and b.event_id in supported)]
|
||||
features['contour'] = sum(min(1, max(0, abs(b.pitch.number() - a.pitch.number()) - 5) / 12) for a, b in motions) / max(1, len(motions))
|
||||
# Fidelity is checked against explicit transformed identities, not against
|
||||
# the source head as one undifferentiated phrase-long quotation.
|
||||
features.pop('fidelity', None)
|
||||
features['harmonic_exposure'] = checked.exposure
|
||||
intended_repeats = 0
|
||||
if draft.home:
|
||||
reference = draft.home.notes
|
||||
for transform in draft.transformations:
|
||||
actual = tuple(n for n in notes if n.event_id in transform.events)
|
||||
for i, (a, b, c) in enumerate(zip(actual, actual[1:], actual[2:])):
|
||||
expected = tuple(reference[j % len(reference)].pitch for j in (i, i + 1, i + 2)) if reference else ()
|
||||
if a.pitch == b.pitch == c.pitch and len(expected) == 3 and expected[0] == expected[1] == expected[2]:
|
||||
intended_repeats += 1
|
||||
features['repetition'] = max(0, features['repetition'] - intended_repeats / max(1, len(score_notes) - 2))
|
||||
score = generator.solo.scoring.value(features)
|
||||
musical = None
|
||||
if plan.treatment == 'thematic_recomposition':
|
||||
musical = generator.solo.scoring.musical(plan,draft,notes,incoming,harmony,travel=travel)
|
||||
if not musical.valid:return None
|
||||
score = musical.total
|
||||
else:
|
||||
evaluation_key = (plan, draft.anchors)
|
||||
if evaluation_key not in harmony.evaluations:
|
||||
harmony.evaluations[evaluation_key] = generator.solo.scoring.Evaluation(evaluation_plan(plan, draft.anchors), harmony.choices, plan.end)
|
||||
evaluation = harmony.evaluations[evaluation_key]
|
||||
if not evaluation.feasible(score_notes):
|
||||
return None
|
||||
features = evaluation.features(score_notes, previous=incoming.last if not shared else None, movement=travel + (incoming.travel if shared else 0))
|
||||
features.update(generator.solo.drafts.relationship_features(draft, notes))
|
||||
if draft.lead_identity or draft.source_phrase:
|
||||
supported = {e for feature in draft.features for e in feature.events}
|
||||
motions = [(a, b) for a, b in zip(notes, notes[1:]) if not (a.event_id in supported and b.event_id in supported)]
|
||||
features['contour'] = sum(min(1, max(0, abs(b.pitch.number() - a.pitch.number()) - 5) / 12) for a, b in motions) / max(1, len(motions))
|
||||
# Fidelity is checked against explicit transformed identities, not against
|
||||
# the source head as one undifferentiated phrase-long quotation.
|
||||
features.pop('fidelity', None)
|
||||
features['harmonic_exposure'] = checked.exposure
|
||||
intended_repeats = 0
|
||||
if draft.home:
|
||||
reference = draft.home.notes
|
||||
for transform in draft.transformations:
|
||||
actual = tuple(n for n in notes if n.event_id in transform.events)
|
||||
for i, (a, b, c) in enumerate(zip(actual, actual[1:], actual[2:])):
|
||||
expected = tuple(reference[j % len(reference)].pitch for j in (i, i + 1, i + 2)) if reference else ()
|
||||
if a.pitch == b.pitch == c.pitch and len(expected) == 3 and expected[0] == expected[1] == expected[2]:
|
||||
intended_repeats += 1
|
||||
features['repetition'] = max(0, features['repetition'] - intended_repeats / max(1, len(score_notes) - 2))
|
||||
score = generator.solo.scoring.value(features)
|
||||
motifs = list(incoming.motifs) if shared else []
|
||||
decisions = []
|
||||
for transform in draft.transformations:
|
||||
@@ -568,7 +574,7 @@ def checked_phrase(plan, draft, notes, contracts, incoming, harmony, tempo, edit
|
||||
(notes[-1].pitch.number() > notes[0].pitch.number()) - (notes[-1].pitch.number() < notes[0].pitch.number()) if notes else incoming.direction,
|
||||
last, 0, (), travel + (incoming.travel if shared else 0), home, motifs[-1] if motifs else incoming.development, themes, draft.anchors, plan.ending)
|
||||
actual_plan = dataclasses.replace(plan, relationship=draft.relationship)
|
||||
return generator.solo.types.Phrase(actual_plan, tuple(decisions), score, state, draft.anchors, draft, edits)
|
||||
return generator.solo.types.Phrase(actual_plan, tuple(decisions), score, state, draft.anchors, draft, edits, evaluation=musical)
|
||||
|
||||
def legacy_target_plan(plan, choices):
|
||||
if not plan.target or plan.anchor_options:
|
||||
|
||||
Reference in New Issue
Block a user