UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

78 lines (77 loc) 2.74 kB
import * as vexflow from 'vexflow'; import { Rect } from '../spatial'; import { Fraction } from '../util'; const ADDITIONAL_COMPLEX_TIME_SIGNATURE_COMPONENT_WIDTH = 18; export class Time { config; log; document; key; constructor(config, log, document, key) { this.config = config; this.log = log; this.document = document; this.key = key; } render() { const timeSpecs = this.getTimeSpecs(); const vexflowTimeSignatures = timeSpecs.map((t) => new vexflow.TimeSignature(t)); const padding = ADDITIONAL_COMPLEX_TIME_SIGNATURE_COMPONENT_WIDTH * (timeSpecs.length - 1); const width = vexflowTimeSignatures.reduce((sum, t) => sum + t.getWidth(), padding); return { type: 'time', rect: Rect.empty(), // placeholder key: this.key, vexflowTimeSignatures, width, }; } getTimeSpecs() { const time = this.document.getStave(this.key).signature.time; const components = this.toFractions(time.components); switch (time.symbol) { case 'common': return ['C']; case 'cut': return ['C|']; case 'single-number': const sum = Fraction.sum(...components).simplify(); return [this.toSimpleTimeSpecs(sum)]; case 'hidden': return []; } if (components.length > 1) { return this.toComplexTimeSpecs(components); } return [this.toSimpleTimeSpecs(components[0])]; } toSimpleTimeSpecs(component) { return `${component.numerator}/${component.denominator}`; } toComplexTimeSpecs(components) { const denominators = new Array(); const memo = {}; for (const component of components) { const numerator = component.numerator; const denominator = component.denominator; if (typeof memo[denominator] === 'undefined') { denominators.push(denominator); } memo[denominator] ??= []; memo[denominator].push(numerator); } const result = new Array(); for (let index = 0; index < denominators.length; index++) { const denominator = denominators[index]; const isLast = index === denominators.length - 1; result.push(`${memo[denominator].join('+')}/${denominator}`); if (!isLast) { result.push('+'); } } return result; } toFractions(components) { return components.map((component) => new Fraction(component.numerator, component.denominator)); } }