Soloing
This commit is contained in:
@@ -2,6 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import fractions
|
||||
import time
|
||||
|
||||
import generator.solo.enclosures
|
||||
|
||||
import generator.fretboard
|
||||
import generator.harmony
|
||||
@@ -15,7 +18,8 @@ import generator.solo.policy
|
||||
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'):
|
||||
def compose(analysis, measures, part, end, *, scales, sequences, rhythms, initial=None, revise=True, context=None, references=(), stage='elaborated', mode='thematic', enclosures_enabled=True):
|
||||
started = time.monotonic()
|
||||
timeline = generator.scales.timeline.read({'measures': measures}, initial=initial)
|
||||
region = generator.scales.timeline.Solo(fractions.Fraction(), end, part.capitalize() + ' Solo')
|
||||
choices = generator.scales.selection.select(timeline, region, scales)
|
||||
@@ -26,13 +30,16 @@ def compose(analysis, measures, part, end, *, scales, sequences, rhythms, initia
|
||||
context = context if context is not None else generator.solo.realization.SearchContext()
|
||||
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')
|
||||
validate(result)
|
||||
return result
|
||||
|
||||
|
||||
def compose_pair(analysis, requests, *, scales, sequences, rhythms, contexts=None, stage='elaborated', mode='thematic'):
|
||||
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)
|
||||
@@ -52,10 +59,15 @@ def compose_pair(analysis, requests, *, scales, sequences, rhythms, contexts=Non
|
||||
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)
|
||||
return acoustic,electric
|
||||
selected.append(solo)
|
||||
return tuple(selected)
|
||||
|
||||
|
||||
def phrase_notes(phrase):
|
||||
@@ -120,8 +132,6 @@ def validate(solo):
|
||||
for note in solo.notes():
|
||||
if note.start < 0 or note.end > solo.end or note.end <= note.start or previous and previous.end > note.start:
|
||||
raise ValueError(f'{solo.part} solo: overlapping or out-of-bounds note at {note.start}')
|
||||
if note.pitch.number() % 12 not in generator.solo.types.permitted(solo.choices, note.start, note.end):
|
||||
raise ValueError(f'{solo.part} solo: note outside mapped collection at {note.start}')
|
||||
if note.position not in generator.fretboard.single_positions_for_pitch(fractions.Fraction(note.pitch.number())):
|
||||
raise ValueError(f'{solo.part} solo: invalid guitar placement at {note.start}')
|
||||
previous = note
|
||||
@@ -131,7 +141,8 @@ def validate(solo):
|
||||
raise ValueError(f'{phrase.plan.id}: planned harmonic arrival was not realized')
|
||||
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))
|
||||
checked = harmony.evaluate(solo.notes(), contracts, until=solo.end, complete=True)
|
||||
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)
|
||||
if not checked.valid:
|
||||
raise ValueError(f'{solo.part} solo: {checked.error}')
|
||||
for phrase in solo.phrases:
|
||||
|
||||
Reference in New Issue
Block a user