UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

201 lines (200 loc) 8.08 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Time = void 0; const util = __importStar(require("../../util")); const fraction_1 = require("./fraction"); /** Represents a musical time signature. */ class Time { config; log; partId; staveNumber; components; symbol; constructor(config, log, partId, staveNumber, components, symbol) { this.config = config; this.log = log; this.partId = partId; this.staveNumber = staveNumber; this.components = components; this.symbol = symbol; } static default(config, log, partId, staveNumber) { return Time.common(config, log, partId, staveNumber); } static create(config, log, partId, staveNumber, musicXML) { const time = musicXML.time; if (time.isHidden()) { return Time.hidden(config, log, partId, staveNumber); } // The symbol overrides any other time specifications. This is done to avoid incompatible symbol and time signature // specifications. const symbol = time.getSymbol(); switch (symbol) { case 'common': return Time.common(config, log, partId, staveNumber); case 'cut': return Time.cut(config, log, partId, staveNumber); case 'hidden': return Time.hidden(config, log, partId, staveNumber); } const beats = time.getBeats(); const beatTypes = time.getBeatTypes(); const times = new Array(); const len = Math.min(beats.length, beatTypes.length); for (let index = 0; index < len; index++) { const beatsPerMeasure = beats[index]; const beatValue = beatTypes[index]; const nextTime = Time.parse(config, log, partId, staveNumber, beatsPerMeasure, beatValue); times.push(nextTime); } if (times.length === 0) { return null; } if (symbol === 'single-number') { return Time.singleNumber(config, log, partId, staveNumber, Time.combine(config, log, partId, staveNumber, times)); } if (times.length === 1) { return times[0]; } return Time.combine(config, log, partId, staveNumber, times); } /** Creates a Time for each stave. */ static createMulti(config, log, partId, staveCount, musicXML) { const times = new Array(); for (let index = 0; index < staveCount; index++) { const time = Time.create(config, log, partId, index + 1, musicXML); times.push(time); } return times; } /** Returns a simple Time, composed of two numbers. */ static simple(config, log, partId, staveNumber, beatsPerMeasure, beatValue) { const components = [new util.Fraction(beatsPerMeasure, beatValue)]; return new Time(config, log, partId, staveNumber, components, null); } /** * Returns a Time composed of many components. * * The parameter type signature ensures that there are at least two Fractions present. */ static complex(config, log, partId, staveNumber, components) { return new Time(config, log, partId, staveNumber, components, null); } /** * Returns a Time that should be hidden. * * NOTE: It contains time signature components, but purely to simplify rendering downstream. It shouldn't be used for * calculations. */ static hidden(config, log, partId, staveNumber) { const components = [new util.Fraction(4, 4)]; return new Time(config, log, partId, staveNumber, components, 'hidden'); } /** Returns a Time in cut time. */ static cut(config, log, partId, staveNumber) { const components = [new util.Fraction(2, 2)]; return new Time(config, log, partId, staveNumber, components, 'cut'); } /** Returns a Time in common time. */ static common(config, log, partId, staveNumber) { const components = [new util.Fraction(4, 4)]; return new Time(config, log, partId, staveNumber, components, 'common'); } /** Combines multiple time signatures into a single one, ignoring any symbols. */ static combine(config, log, partId, staveNumber, times) { const components = times.flatMap((time) => time.components); return new Time(config, log, partId, staveNumber, components, null); } /** Creates a new time signature that should be displayed as a single number. */ static singleNumber(config, log, partId, staveNumber, time) { return new Time(config, log, partId, staveNumber, [time.toFraction()], 'single-number'); } static parse(config, log, partId, staveNumber, beatsPerMeasure, beatValue) { const denominator = parseInt(beatValue.trim(), 10); const numerators = beatsPerMeasure.split('+').map((b) => parseInt(b.trim(), 10)); if (numerators.length > 1) { const fractions = numerators.map((numerator) => new util.Fraction(numerator, denominator)); return Time.complex(config, log, partId, staveNumber, fractions); } return Time.simple(config, log, partId, staveNumber, numerators[0], denominator); } parse() { return { type: 'time', symbol: this.symbol, components: this.getComponents().map((component) => component.parse()), }; } getPartId() { return this.partId; } getStaveNumber() { return this.staveNumber; } /** Returns a fraction that represents the combination of all */ toFraction() { let sum = new util.Fraction(0, 1); for (const component of this.components) { sum = sum.add(component); } return sum.simplify(); } isEqual(timeSignature) { return (this.partId === timeSignature.partId && this.staveNumber === timeSignature.staveNumber && this.isEquivalent(timeSignature)); } isEquivalent(timeSignature) { if (this.symbol !== timeSignature.symbol) { return false; } if (this.components.length !== timeSignature.components.length) { return false; } for (let i = 0; i < this.components.length; i++) { // We use isEqual instead of isEquivalent because they would be _displayed_ differently. if (!this.components[i].isEqual(timeSignature.components[i])) { return false; } } return true; } getComponents() { return this.components.map((component) => new fraction_1.Fraction(component)); } } exports.Time = Time;