Sepearate Generation

This commit is contained in:
khodowany
2026-10-04 09:05:25 -05:00
parent e8ac7b5edd
commit 8fdd736e13
56 changed files with 1739 additions and 341 deletions
+11 -33
View File
@@ -20,6 +20,8 @@ import generator.solo.realization
import generator.solo.types
def compose(analysis, measures, part, end, *, scales, sequences, rhythms, initial=None, revise=True, context=None, references=(), stage='elaborated', mode='thematic', enclosures_enabled=True):
if references or context is not None and getattr(context,'references',()):
raise ValueError('Audition references are not production composition inputs')
started = time.monotonic()
timeline = generator.scales.timeline.read({'measures': measures}, initial=initial)
region = generator.scales.timeline.Solo(fractions.Fraction(), end, part.capitalize() + ' Solo')
@@ -32,43 +34,18 @@ def compose(analysis, measures, part, end, *, scales, sequences, rhythms, initia
options = leads.solo_options(analysis, measures, part, end, choices, sequences, rhythms, context, stage, mode)
result = options[0][1]
result.diagnostics['strict_composition_seconds'] = time.monotonic()-started
result = generator.solo.enclosures.refine(result,sequences,enclosures_enabled and stage=='elaborated' and mode=='thematic')
from generator.solo import expressions
result = expressions.refine(result,enclosures_enabled and stage=='elaborated' and mode=='thematic')
validate(result)
return result
def compose_pair(analysis, requests, *, scales, sequences, rhythms, contexts=None, stage='elaborated', mode='thematic', enclosures_enabled=True):
"""Select two recognizable interpretations together, before arranging backing."""
from generator.solo import leads
started = time.monotonic()
pools = {}
for part, (measures,end,initial) in requests.items():
timeline = generator.scales.timeline.read({'measures': measures},initial=initial)
choices = generator.scales.selection.select(timeline,generator.scales.timeline.Solo(0,end,part),scales)
for c in choices:
if c.span.chord and c.scale is None:
raise ValueError(f'{part} solo, source measure {c.span.source_measure}: No exact match within the permitted catalog')
context = (contexts or {}).get(part) or generator.solo.realization.SearchContext()
pools[part] = leads.solo_options(analysis,measures,part,end,choices,sequences,rhythms,context,stage,mode)
def theme_key(solo):
home=next((p.state.home for p in solo.phrases if p.state and p.state.home),None)
if not home or not home.notes:return ()
return tuple((n.start-home.notes[0].start,n.end-n.start,n.pitch.number()-home.notes[0].pitch.number()) for n in home.notes)
pairs = [(a_cost+e_cost+2*leads.similarity(a,e),a,e) for a_cost,a in pools['acoustic'] for e_cost,e in pools['electric']
if mode!='thematic' or not theme_key(a) or theme_key(a)!=theme_key(e)]
if not pairs:
raise generator.solo.realization.SearchExhausted('Distinct acoustic/electric themes not found within the bounded pair search')
_,acoustic,electric = min(pairs,key=lambda v:(v[0],generator.solo.policy.stable_rank('solo-pair',(v[1].notes(),v[2].notes()))))
overlap = leads.similarity(acoustic,electric)
strict_seconds=time.monotonic()-started
selected=[]
for solo in (acoustic,electric):
solo.diagnostics.update(pair_similarity=overlap,pairs_compared=len(pairs),solo_alternatives=len(pools[solo.part]))
solo.diagnostics['strict_pair_seconds']=strict_seconds
solo=generator.solo.enclosures.refine(solo,sequences,enclosures_enabled and stage=='elaborated' and mode=='thematic')
validate(solo)
selected.append(solo)
return tuple(selected)
"""Compatibility batch helper: each winner is selected independently."""
return tuple(compose(analysis, measures, part, end, scales=scales, sequences=sequences,
rhythms=rhythms, initial=initial, context=(contexts or {}).get(part), stage=stage,
mode=mode, enclosures_enabled=enclosures_enabled)
for part, (measures, end, initial) in requests.items())
def phrase_notes(phrase):
@@ -144,7 +121,8 @@ def validate(solo):
harmony = generator.solo.harmonic.Harmony(solo.choices)
contracts = tuple(dict.fromkeys(t for phrase in solo.phrases if phrase.state for t in phrase.state.treatments))
enclosures = tuple(e for p in solo.phrases if p.draft for e in p.draft.enclosures)
checked = harmony.evaluate(solo.notes(), contracts, until=solo.end, complete=True,enclosures=enclosures)
expressions = tuple(e for p in solo.phrases if p.draft for e in p.draft.expressions)
checked = harmony.evaluate(solo.notes(), contracts, until=solo.end, complete=True,enclosures=enclosures,expressions=expressions)
if not checked.valid:
raise ValueError(f'{solo.part} solo: {checked.error}')
for phrase in solo.phrases: