124 lines
6.7 KiB
Python
124 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
from fractions import Fraction
|
|
|
|
from generator.model import LETTERS, TYPE_LENGTHS, alteration, fraction, key_from_fifths
|
|
from generator.engraving.fonts import Fonts
|
|
from generator.engraving.notation import ScoreIndex, accidental
|
|
from generator.engraving.types import Primitive, bounds, line
|
|
|
|
def staff_attributes(index: ScoreIndex, state: dict, time: Fraction, fonts: Fonts, space: float, source: str, fields: tuple[str, ...]) -> list[Primitive]:
|
|
result = []
|
|
x = 0
|
|
references = (source,)
|
|
clef = state['clef']
|
|
lines = state['staff_details']['lines']
|
|
if 'clef' in fields and clef['sign'] != 'none':
|
|
sign = clef['sign']
|
|
name = {'G': 'gClef', 'F': 'fClef', 'C': 'cClef', 'percussion': 'unpitchedPercussionClef1'}[sign]
|
|
y = -space * (clef.get('line', 2) - 1) if sign in ('G', 'F', 'C') else -space * (lines - 1) / 2
|
|
glyph = fonts.glyph(name, x, y, space, 'clef', references)
|
|
result.append(glyph)
|
|
if octave := clef.get('octave_change', 0):
|
|
label = str(abs(octave) * 7 + 1)
|
|
result.append(fonts.text(label, (glyph.box.left + glyph.box.right) / 2, glyph.box.top - 2 if octave > 0 else glyph.box.bottom + 14, 14, 'clef-octave', references, True))
|
|
x = bounds(result).right + space
|
|
if 'key' in fields and clef['sign'] != 'percussion':
|
|
key = state['key']
|
|
steps = key.get('steps')
|
|
if steps is None:
|
|
order = 'fcgdaeb' if key.get('fifths', 0) >= 0 else 'beadgcf'
|
|
steps = [{'letter': c, 'alter': key['alterations'][LETTERS.index(c)]} for c in order if alteration(key['alterations'][LETTERS.index(c)])]
|
|
cancellation = key.get('cancel', 0)
|
|
if cancellation:
|
|
order = 'fcgdaeb' if cancellation > 0 else 'beadgcf'
|
|
old = key_from_fifths(cancellation)['alterations']
|
|
steps = [{'letter': c, 'alter': 0} for c in order if alteration(old[LETTERS.index(c)]) and alteration(old[LETTERS.index(c)]) != alteration(key['alterations'][LETTERS.index(c)])] + steps
|
|
for step in steps:
|
|
pitch = {'letter': step['letter'], 'octave': step.get('octave', 5), 'id': source}
|
|
position = index.pitch_position(pitch, time)
|
|
if 'octave' not in step:
|
|
while position > 9:
|
|
position -= 7
|
|
while position < 2:
|
|
position += 7
|
|
items = accidental(fonts, alteration(step['alter']), {'name': step.get('accidental', '')}, space, references)
|
|
result.extend(p.moved(x, -position * space / 2) for p in items)
|
|
x += bounds(items).right - bounds(items).left + 4
|
|
if steps:
|
|
x += space / 2
|
|
if 'meter' in fields:
|
|
meter = state['meter']
|
|
if meter.get('unmetered'):
|
|
result.append(fonts.text('senza misura', x, -space * 2, 16, 'meter', references))
|
|
elif meter.get('symbol') in ('common', 'cut'):
|
|
result.append(fonts.glyph('timeSigCommon' if meter['symbol'] == 'common' else 'timeSigCutCommon', x, -space * 2, space, 'meter', references))
|
|
else:
|
|
for gi, group in enumerate(meter['groups']):
|
|
if gi:
|
|
plus = fonts.glyph('timeSigPlus', x, -space * 2, space, 'meter', references)
|
|
result.append(plus)
|
|
x = plus.box.right + 7
|
|
numerator = '+'.join(map(str, group['beats']))
|
|
denominator = str(group['unit'])
|
|
rows = [(numerator, -space * 2)] if meter.get('symbol') == 'single-number' else [(numerator, -space * 3), (denominator, -space)]
|
|
groups = []
|
|
for value, y in rows:
|
|
items = []
|
|
cursor = 0
|
|
if y == -space and meter.get('symbol') in ('note', 'dotted-note'):
|
|
unit = Fraction(4, group['unit'])
|
|
kind = next((name for name, length in TYPE_LENGTHS.items() if length == unit), None)
|
|
glyph_name = {'whole': 'noteWhole', 'half': 'noteHalfUp', 'quarter': 'noteQuarterUp', 'eighth': 'note8thUp'}.get(kind, 'note' + str(kind) + 'Up')
|
|
if kind and glyph_name in fonts.names:
|
|
glyph = fonts.glyph(glyph_name, 0, y + space, space * 0.55, 'meter', references)
|
|
items.append(glyph)
|
|
if meter['symbol'] == 'dotted-note':
|
|
items.append(fonts.glyph('augmentationDot', glyph.box.right + 3, y + space / 2, space * 0.55, 'meter', references))
|
|
groups.append(items)
|
|
continue
|
|
for char in value:
|
|
glyph = fonts.glyph('timeSigPlus' if char == '+' else 'timeSig' + char, cursor, y, space, 'meter', references)
|
|
items.append(glyph)
|
|
cursor = glyph.box.right + 1
|
|
groups.append(items)
|
|
width = max(bounds(items).right for items in groups)
|
|
for items in groups:
|
|
result.extend(p.moved(x + (width - bounds(items).right) / 2, 0) for p in items)
|
|
x += width + 6
|
|
if 'staff_details' in fields and state['staff_details'].get('capo'):
|
|
result.append(fonts.text(f'Capo {state["staff_details"]["capo"]}', x, -space * (lines + 1), 16, 'staff-details', references))
|
|
return result
|
|
|
|
def barline(style: str, x: float, top: float, source: str) -> list[Primitive]:
|
|
if style == 'none':
|
|
return []
|
|
references = (source,)
|
|
if style in ('dashed', 'dotted'):
|
|
dash, gap = (5, 4) if style == 'dashed' else (1, 4)
|
|
return [line('barline', references, ((x, y), (x, min(y + dash, 0)))) for y in range(round(top), 0, dash + gap)]
|
|
if style in ('tick', 'short'):
|
|
return [line('barline', references, ((x, top - 4 if style == 'tick' else top * 0.75), (x, top + 5 if style == 'tick' else top * 0.25)))]
|
|
weights = {'regular': [1.4], 'heavy': [4], 'light-light': [1.4, 1.4], 'light-heavy': [1.4, 4], 'heavy-light': [4, 1.4], 'heavy-heavy': [4, 4]}[style]
|
|
return [line('barline', references, ((x - (len(weights) - 1 - i) * 5, top), (x - (len(weights) - 1 - i) * 5, 0)), width) for i, width in enumerate(weights)]
|
|
|
|
def tempo(value: dict, fonts: Fonts, x: float, y: float, space: float, source: str) -> list[Primitive]:
|
|
from generator.engraving.notation import flags, head_glyph
|
|
written = value['written']
|
|
references = (source,)
|
|
glyph = fonts.glyph(head_glyph(written, None), x, y, space * 0.7, 'tempo', references)
|
|
items = [glyph]
|
|
stem_x = glyph.box.right - 0.5
|
|
if written['type'] not in ('whole', 'breve', 'long', 'maxima'):
|
|
items.append(line('tempo', references, ((stem_x, y), (stem_x, y - 23)), 1))
|
|
if flags(written):
|
|
name = 'flag' + ('8th' if written['type'] == 'eighth' else written['type']) + 'Up'
|
|
items.append(fonts.glyph(name, stem_x, y - 23, space * 0.7, 'tempo', references))
|
|
cursor = max(p.box.right for p in items) + 3
|
|
for _ in range(written.get('dots', 0)):
|
|
dot = fonts.glyph('augmentationDot', cursor, y - 3, space * 0.7, 'tempo', references)
|
|
items.append(dot)
|
|
cursor = dot.box.right + 3
|
|
items.append(fonts.text(' = ' + str(fraction(value['bpm'])), cursor, y + 3, 16, 'tempo', references))
|
|
return items
|