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
+15 -5
View File
@@ -148,8 +148,12 @@ class Harmony:
intervals.append((upper.start,target.start))
return accepted, ''
def evaluate(self, notes, treatments=(), *, until=None, complete=False, enclosures=()):
def evaluate(self, notes, treatments=(), *, until=None, complete=False, enclosures=(), expressions=()):
notes = generator.solo.types.sounding_notes(notes)
from generator.solo.expressions import validate
permissions, expression_error = validate(notes, expressions, self)
if expression_error:
return Result(False, 0, error=expression_error)
until = until if until is not None else max((n.end for n in notes), default=fractions.Fraction())
ornaments, error = self.enclosure_events(notes, enclosures, until) if enclosures else ({}, '')
if error:
@@ -190,13 +194,14 @@ class Harmony:
for span, start, end in self.segments(note):
contract = next((t for t in valid_contracts if t.pitch == number and t.start <= start and t.end >= end), None)
enclosure = ornaments.get(note.event_id)
exception = enclosure is not None and note.event_id == enclosure.lower.event_id
expression = permissions.get(note.event_id)
exception = enclosure is not None and note.event_id == enclosure.lower.event_id or expression is not None and (expression.kind != 'anticipation' or end <= expression.boundary)
if start > covered or not span.scale or number % 12 not in span.scale.pitches and not exception:
return Result(False, 0, error=f'{note.pitch.label()} at {start}-{end}: outside mapped collection or missing harmony')
covered = end
function = self.function(number, span)
brief_enclosure = enclosure and (exception or enclosure.approach_start is None or enclosure.approach_start == note.start)
if function == 'basic' or note.role == 'quote' or brief_enclosure:
if function == 'basic' or note.role == 'quote' or brief_enclosure or expression:
continue
if duration >= generator.solo.policy.DEFAULT.long_note and contract is None:
return Result(False, 0, error=f'{note.pitch.label()} at {start}-{end} over {span.chord}: sustained {function} without a planned treatment')
@@ -206,8 +211,12 @@ class Harmony:
if covered < note.end:
return Result(False, 0, error=f'{note.pitch.label()} at {covered}: missing harmonic coverage')
return Result(True, exposed / max(sounding, 0.001), tuple(pending))
def describe(self, notes, treatments=(), enclosures=()):
def describe(self, notes, treatments=(), enclosures=(), expressions=()):
notes = generator.solo.types.sounding_notes(notes)
from generator.solo.expressions import validate
permissions, error = validate(notes, expressions, self)
if error:
raise ValueError(error)
ornaments, error = self.enclosure_events(notes,enclosures) if enclosures else ({}, '')
if error:
raise ValueError(error)
@@ -215,7 +224,8 @@ class Harmony:
for index, note in enumerate(notes):
for span, start, end in self.segments(note):
contracts = [t for t in treatments if t.pitch == note.pitch.number() and t.start <= start and t.end >= end]
result.append({'start': str(start), 'end': str(end), 'attack': str(note.start), 'sounding_duration': str(note.end - note.start), 'pitch': note.pitch.label(), 'chord': span.chord, 'function': self.function(note.pitch.number(), span), 'motion': self.connected_motion(notes, index), 'enclosure': ornaments[note.event_id].id if note.event_id in ornaments else '', 'scale_exception': note.event_id in ornaments and self.function(note.pitch.number(), span) == 'outside_collection', 'protected': note.role == 'quote', 'treatments': [t.id for t in contracts]})
expression = permissions.get(note.event_id)
result.append({'start': str(start), 'end': str(end), 'attack': str(note.start), 'sounding_duration': str(note.end - note.start), 'pitch': note.pitch.label(), 'chord': span.chord, 'function': self.function(note.pitch.number(), span), 'motion': self.connected_motion(notes, index), 'enclosure': ornaments[note.event_id].id if note.event_id in ornaments else '', 'expression': expression.kind if expression else '', 'expression_rule': expression.rule if expression else '', 'scale_exception': bool(note.event_id in ornaments or expression) and self.function(note.pitch.number(), span) == 'outside_collection', 'protected': note.role == 'quote', 'treatments': [t.id for t in contracts]})
return result
def anchor_note(anchor, notes, *, until=None):