Cleanup
ci / checks (push) Failing after 23m22s

This commit is contained in:
khodowany
2026-10-08 02:13:45 -05:00
parent 300097a37e
commit bc7baea7c4
88 changed files with 18844 additions and 1335 deletions
+19 -17
View File
@@ -8,17 +8,18 @@ legacy fallback. A bounded restart handles an exhausted/entirely silent sample.
from __future__ import annotations
import collections
import dataclasses
import hashlib
import random
import math
import fractions
import hashlib
import math
import random
import generator.harmony
import generator.model
import generator.vocabulary
import generator.solo.expressions
import generator.solo.groups
import generator.solo.learned
import generator.solo.types
import generator.harmony
import generator.vocabulary
class SearchExhausted(ValueError):
pass
@@ -35,7 +36,8 @@ class State:
def event_key(events): return tuple((a, b, -1 if p is None else p) for a, b, p in events)
class Decoder:
def __init__(self, analysis, harmony, model, role, start, end, *, expressions_enabled=True, attempts=4, seed=''):
def __init__(self, analysis, harmony, model, role, start, end, *, expressions_enabled=True, attempts=4, seed='', composition=None):
self.composition = composition or generator.vocabulary.current()
if type(attempts) is not int or not 1 <= attempts <= 16:
raise ValueError('attempts must be between 1 and 16')
self.analysis, self.harmony, self.model, self.role = analysis, harmony, model, role
@@ -45,7 +47,7 @@ class Decoder:
self.seed = seed or hashlib.sha256(repr((source, role, generator.solo.learned.VERSION)).encode()).hexdigest()
self.expressions_enabled = expressions_enabled
self.low, self.high = harmony.profile.register
limits = generator.vocabulary.data('roles')['difficulty']
limits = self.composition.data('roles')['difficulty']
self.durations = tuple(d for d in model.durations if 0 < d <= 4 and d.denominator in (1, 2, 4) and
(d.denominator != 4 or analysis.tempo <= limits['sixteenth_max_bpm']))
self.allow_triplets = analysis.tempo <= limits['triplet_max_bpm']
@@ -92,12 +94,12 @@ class Decoder:
bases = []
regions = [(None, n)]
regions.extend((s.start, dataclasses.replace(n, start=s.start)) for s in self.harmony.spans
if n.start < s.start < n.end and s.start - n.start <= fractions.Fraction(generator.vocabulary.data('harmony')['anticipation_max']))
if n.start < s.start < n.end and s.start - n.start <= fractions.Fraction(self.composition.data('harmony')['anticipation_max']))
for boundary, arrived in regions:
exposed = [s for s, _, _ in self.harmony.segments(arrived) if n.pitch.number() % 12 not in s.roles.basic | s.roles.specified]
if not exposed:
continue
for rule in generator.vocabulary.data('harmony')['colors']:
for rule in self.composition.data('harmony')['colors']:
if arrived.end - arrived.start > fractions.Fraction(rule['max_beats']):
continue
if any(s.chord == 'N.C.' or not generator.solo.expressions.eligible_color(rule, s, self.harmony) or
@@ -139,7 +141,7 @@ class Decoder:
if self.harmony.evaluate((n,), complete=True, expressions=(c,), infer=False).valid:
return tuple([c])
for arriving in self.harmony.spans:
if n.start < arriving.start < n.end and arriving.start - n.start <= fractions.Fraction(generator.vocabulary.data('harmony')['anticipation_max']):
if n.start < arriving.start < n.end and arriving.start - n.start <= fractions.Fraction(self.composition.data('harmony')['anticipation_max']):
c = generator.solo.types.ExpressionContract(n.event_id + ':anticipation', 'anticipation', 'early_chord_tone', (n,), n, arriving.chord, arriving.start)
if self.harmony.evaluate((n,), complete=True, expressions=(c,), infer=False).valid:
return tuple([c])
@@ -175,9 +177,9 @@ class Decoder:
for rule, boundary in self.color_bases(n):
if rule.get('resolve'):
for gap in (fractions.Fraction(), fractions.Fraction(1, 2), fractions.Fraction(1)):
if gap <= fractions.Fraction(generator.vocabulary.data('harmony')['resolution_gap']):
if gap <= fractions.Fraction(self.composition.data('harmony')['resolution_gap']):
requests.append(('color', rule, boundary, n.end + gap, (-2, -1, 1, 2)))
delay = generator.vocabulary.data('lead_ins')['delayed_approach']
delay = self.composition.data('lead_ins')['delayed_approach']
if n.end - n.start <= fractions.Fraction(delay['max_note']) and n.pitch.number() % 12 not in self.harmony.permitted(n.start, n.end):
for arrival in map(fractions.Fraction, delay['arrivals']):
if n.end <= n.start + arrival <= n.start + fractions.Fraction(delay['deadline']):
@@ -219,7 +221,7 @@ class Decoder:
if self.expressions_enabled and not self.harmony.ordinary(n):
for events, ns, contracts in self.resolutions(n):
add(events, ns, contracts)
rule = generator.vocabulary.data('lead_ins')['passing']
rule = self.composition.data('lead_ins')['passing']
if duration <= fractions.Fraction(rule['max_note']):
for step in rule['steps']:
p = number + step
@@ -235,8 +237,8 @@ class Decoder:
c = generator.solo.types.ExpressionContract(n.event_id + ':passing', 'passing', rule['id'], (n,), target, self.harmony.at(end).chord)
add(((time, end, number), (end, end + hold, p)), (n, target), (c, *extra))
if self.expressions_enabled:
limits = generator.vocabulary.data('roles')['difficulty']
for rule in generator.vocabulary.data('lead_ins')['patterns']:
limits = self.composition.data('roles')['difficulty']
for rule in self.composition.data('lead_ins')['patterns']:
difficulty = rule.get('difficulty')
if difficulty and self.analysis.tempo > limits[difficulty + '_max_bpm']:
continue
@@ -313,9 +315,9 @@ class Decoder:
return None
contracts = tuple(dataclasses.replace(c, preparation=state.notes[-1]) if c.kind in ('connection', 'passing') and c.preparation is None else c for c in contracts)
if self.harmony.ordinary(state.notes[-1]):
_, error = generator.solo.expressions.validate((state.notes[-1], *notes), contracts, self.harmony)
_, error = generator.solo.expressions.validate((state.notes[-1], *notes), contracts, self.harmony, composition=self.composition)
else:
_, error = generator.solo.expressions.validate((*state.notes, *notes), (*state.contracts, *contracts), self.harmony)
_, error = generator.solo.expressions.validate((*state.notes, *notes), (*state.contracts, *contracts), self.harmony, composition=self.composition)
if error:
return None
history = state.history