@stringsync/vexml
Version:
MusicXML to Vexflow
78 lines (77 loc) • 2.74 kB
JavaScript
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));
}
}