@@ -0,0 +1,84 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import fractions
|
||||
import itertools
|
||||
|
||||
import generator.accompaniment
|
||||
import generator.model
|
||||
import generator.score
|
||||
import generator.solo.types
|
||||
|
||||
def durations(length):
|
||||
if length.denominator & (length.denominator - 1) == 0:
|
||||
yield from generator.accompaniment.written_durations(length)
|
||||
else:
|
||||
kind, base = next((kind, value) for kind, value in reversed(generator.model.TYPE_LENGTHS.items()) if value >= length)
|
||||
yield length, {'type': kind, 'dots': 0, 'ratio': generator.model.rational(length / base)}
|
||||
|
||||
def measures(sources: list[dict], notes: tuple[generator.solo.types.Note, ...], prefix: str) -> list[dict]:
|
||||
"""Write attack spans as conventional durations; only continuations get ties."""
|
||||
result, previous = [], {}
|
||||
absolute = fractions.Fraction()
|
||||
for index, source in enumerate(sources):
|
||||
length = generator.model.fraction(source['duration'])
|
||||
events, cursor = [], absolute
|
||||
|
||||
def emit(start, end, tone=None, identity=None):
|
||||
for duration, written in durations(end - start):
|
||||
identifier = f'{prefix}_{index + 1}_{len(events) + 1}'
|
||||
pitches = [tone.pitch.written() | {'tablature': {'string': tone.position.string_number, 'fret': tone.position.fret}}] if tone else []
|
||||
event = generator.model.make_event(identifier, start - absolute, duration, 'note' if tone else 'rest', pitches, written)
|
||||
if tone:
|
||||
pitch = event['pitches'][0]
|
||||
if identity in previous:
|
||||
prior = previous[identity]
|
||||
prior['tie_to'] = pitch['id']
|
||||
pitch['tie_from'] = prior['id']
|
||||
previous[identity] = pitch
|
||||
events.append(event)
|
||||
start += duration
|
||||
|
||||
for identity, note in enumerate(notes):
|
||||
start, end = max(absolute, note.start), min(absolute + length, note.end)
|
||||
if start >= end:
|
||||
continue
|
||||
if start > cursor:
|
||||
emit(cursor, start)
|
||||
emit(start, end, note, identity)
|
||||
cursor = end
|
||||
if cursor < absolute + length:
|
||||
emit(cursor, absolute + length)
|
||||
measure = generator.score.make_measure(source['id'], source, events, section_label=source.get('section_label', ''))
|
||||
measure['transpose'] = {'diatonic': 0, 'chromatic': 0, 'octave_change': -1, 'explicit': True}
|
||||
measure['clef'] = {'sign': 'G', 'line': 2, 'octave_change': 0}
|
||||
for change in measure.get('attributes', []):
|
||||
change.pop('transpose', None)
|
||||
change.pop('clef', None)
|
||||
result.append(measure)
|
||||
absolute += length
|
||||
return result
|
||||
|
||||
def tuplets(measures):
|
||||
spans = []
|
||||
for measure in measures:
|
||||
for ratio, values in itertools.groupby(generator.score.measure_notes(measure), key=lambda n: generator.model.fraction(n['written'].get('ratio', generator.model.rational(1)))):
|
||||
notes = list(values)
|
||||
if ratio == 1:
|
||||
continue
|
||||
for first in range(0, len(notes), ratio.denominator):
|
||||
members = notes[first:first + ratio.denominator]
|
||||
identifier = members[0]['id'] + '_tuplet'
|
||||
spans.append({'id': identifier, 'voice': 'voice_arranged', 'start': members[0]['id'], 'end': members[-1]['id'], 'note_count': ratio.denominator, 'space_count': ratio.numerator, 'depth': 0, 'bracket': 'auto', 'show_number': 'actual'})
|
||||
for note in members:
|
||||
note['tuplet_ids'] = [identifier]
|
||||
return spans
|
||||
|
||||
def replace_region(document: dict, first: int, replacement: list[dict]) -> None:
|
||||
staff = document['staves'][0]
|
||||
replacement = copy.deepcopy(replacement)
|
||||
spans = tuplets(replacement)
|
||||
staff['measures'][first:first + len(replacement)] = replacement
|
||||
staff['tablature'] = True
|
||||
existing = {n['id'] for m in staff['measures'] for n in generator.score.measure_notes(m)}
|
||||
staff['tuplets'] = [t for t in staff.get('tuplets', []) if t['start'] in existing and t['end'] in existing] + spans
|
||||
Reference in New Issue
Block a user