from __future__ import annotations import fractions import generator.engraving.fonts import generator.engraving.notation import generator.engraving.types import generator.model def staff_attributes(index: generator.engraving.notation.ScoreIndex, state: dict, time: fractions.Fraction, fonts: generator.engraving.fonts.Fonts, space: float, source: str, fields: tuple[str, ...]) -> list[generator.engraving.types.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 = generator.engraving.types.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'][generator.model.LETTERS.index(c)]} for c in order if generator.model.alteration(key['alterations'][generator.model.LETTERS.index(c)])] cancellation = key.get('cancel', 0) if cancellation: order = 'fcgdaeb' if cancellation > 0 else 'beadgcf' old = generator.model.key_from_fifths(cancellation)['alterations'] steps = [{'letter': c, 'alter': 0} for c in order if generator.model.alteration(old[generator.model.LETTERS.index(c)]) and generator.model.alteration(old[generator.model.LETTERS.index(c)]) != generator.model.alteration(key['alterations'][generator.model.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 = generator.engraving.notation.accidental(fonts, generator.model.alteration(step['alter']), {'name': step.get('accidental', '')}, space, references) result.extend(p.moved(x, -position * space / 2) for p in items) x += generator.engraving.types.bounds(items).right - generator.engraving.types.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 = fractions.Fraction(4, group['unit']) kind = next((name for name, length in generator.model.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(generator.engraving.types.bounds(items).right for items in groups) for items in groups: result.extend(p.moved(x + (width - generator.engraving.types.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[generator.engraving.types.Primitive]: if style == 'none': return [] references = (source,) if style in ('dashed', 'dotted'): dash, gap = (5, 4) if style == 'dashed' else (1, 4) return [generator.engraving.types.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 [generator.engraving.types.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 [generator.engraving.types.line('barline', references, ((x - (len(weights) - 1 - i) * 5, top), (x - (len(weights) - 1 - i) * 5, 0)), width) for i, width in enumerate(weights)]