Comping
This commit is contained in:
@@ -6,6 +6,7 @@ import itertools
|
||||
|
||||
import generator.accompaniment
|
||||
import generator.model
|
||||
import generator.score
|
||||
|
||||
@dataclasses.dataclass
|
||||
class HarmonySpan:
|
||||
@@ -107,3 +108,41 @@ def combine(document: dict) -> None:
|
||||
a = fragments[start][-1] if start in fragments else originals[start]
|
||||
b = fragments[end][0] if end in fragments else originals[end]
|
||||
a['tie_to'], b['tie_from'] = b['id'], a['id']
|
||||
|
||||
def validate_comping(document: dict) -> None:
|
||||
"""Check complete sounding events, not barline or notation fragments."""
|
||||
tones, starts, cursor = {}, {}, fractions.Fraction()
|
||||
for measure in document['staves'][0]['measures']:
|
||||
for note in generator.score.measure_notes(measure):
|
||||
for pitch in note['pitches']:
|
||||
tones[pitch['id']] = pitch
|
||||
starts[pitch['id']] = cursor + generator.model.fraction(note['onset'])
|
||||
cursor += generator.model.fraction(measure['duration'])
|
||||
groups = {}
|
||||
for identifier, pitch in tones.items():
|
||||
if pitch.get('tie_from') in tones:
|
||||
continue
|
||||
start, tail = starts[identifier], pitch
|
||||
seen = {identifier}
|
||||
while tail.get('tie_to') in tones:
|
||||
target = tail['tie_to']
|
||||
if target in seen:
|
||||
raise ValueError('cyclic acoustic sustain')
|
||||
seen.add(target)
|
||||
tail = tones[target]
|
||||
end = starts[tail['id']] + generator.model.fraction(tail['duration'])
|
||||
string = pitch.get('tablature', {}).get('string')
|
||||
if string is None:
|
||||
raise ValueError('acoustic comping requires placed notes')
|
||||
groups.setdefault((start, end), set()).add('bass' if string >= 5 else 'upper')
|
||||
events = []
|
||||
for (start, end), lanes in groups.items():
|
||||
if len(lanes) == 2:
|
||||
if end - start <= 1:
|
||||
raise ValueError('combined acoustic sustain must exceed one beat')
|
||||
elif end - start not in (fractions.Fraction(1, 2), 1):
|
||||
raise ValueError('separate acoustic comping requires quarter/eighth notes')
|
||||
events.append((start, end))
|
||||
events.sort()
|
||||
if any(a[1] > b[0] for a, b in zip(events, events[1:])):
|
||||
raise ValueError('acoustic bass and upper must release together or remain separate')
|
||||
|
||||
Reference in New Issue
Block a user