@stringsync/vexml
Version:
MusicXML to Vexflow
383 lines (382 loc) • 15.1 kB
JavaScript
"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.EventCalculator = void 0;
const musicxml = __importStar(require("../../musicxml"));
const util_1 = require("../../util");
const stavecount_1 = require("./stavecount");
const stavelinecount_1 = require("./stavelinecount");
const clef_1 = require("./clef");
const key_1 = require("./key");
const time_1 = require("./time");
const metronome_1 = require("./metronome");
const note_1 = require("./note");
const rest_1 = require("./rest");
const chord_1 = require("./chord");
const dynamics_1 = require("./dynamics");
const wedge_1 = require("./wedge");
const pedal_1 = require("./pedal");
const octaveshift_1 = require("./octaveshift");
class EventCalculator {
config;
log;
musicXML;
measureBeat = util_1.Fraction.zero();
events = new Array();
quarterNoteDivisions = 1;
// partId -> voiceId
previousExplicitVoiceId = {};
// partId -> staveNumber
previousExplicitStaveNumber = {};
// partId -> staveCount
previousExplicitStaveCount = {};
// staveNumber -> Key
previousKeys = new Map();
constructor(config, log, musicXML) {
this.config = config;
this.log = log;
this.musicXML = musicXML;
}
calculate() {
this.events = [];
for (const part of this.musicXML.scorePartwise.getParts()) {
this.quarterNoteDivisions = 1;
this.previousKeys = new Map();
const partId = part.getId();
const measures = part.getMeasures();
this.previousExplicitStaveNumber[partId] = 1;
this.previousExplicitVoiceId[partId] = '1';
this.previousExplicitStaveCount[partId] = 1;
for (let measureIndex = 0; measureIndex < measures.length; measureIndex++) {
this.measureBeat = util_1.Fraction.zero();
for (const entry of measures[measureIndex].getEntries()) {
this.process(entry, partId, measureIndex);
}
}
}
return this.events;
}
process(entry, partId, measureIndex) {
if (entry instanceof musicxml.Note) {
this.processNote(entry, partId, measureIndex);
}
if (entry instanceof musicxml.Backup) {
this.processBackup(entry);
}
if (entry instanceof musicxml.Forward) {
this.processForward(entry);
}
if (entry instanceof musicxml.Attributes) {
this.processAttributes(entry, partId, measureIndex);
}
if (entry instanceof musicxml.Direction) {
this.processDirection(entry, partId, measureIndex);
}
}
processNote(note, partId, measureIndex) {
const staveNumber = this.resolveStaveNumber(partId, note.getStaveNumber());
const voiceId = this.resolveVoiceId(partId, note.getVoice());
const quarterNotes = note.getDuration();
const duration = new util_1.Fraction(quarterNotes, this.quarterNoteDivisions);
if (note.isChordTail()) {
return;
}
if (note.isGrace()) {
return;
}
if (note.isChordHead()) {
this.events.push({
type: 'chord',
partId,
measureIndex,
staveNumber,
voiceId,
measureBeat: this.measureBeat,
duration,
chord: chord_1.Chord.create(this.config, this.log, this.measureBeat, duration, { note }),
});
}
else if (note.isRest()) {
this.events.push({
type: 'rest',
partId,
measureIndex,
staveNumber,
voiceId,
measureBeat: this.measureBeat,
duration,
rest: rest_1.Rest.create(this.config, this.log, this.measureBeat, duration, { note }),
});
}
else {
this.events.push({
type: 'note',
partId,
measureIndex,
staveNumber,
voiceId,
measureBeat: this.measureBeat,
duration,
note: note_1.Note.create(this.config, this.log, this.measureBeat, duration, { note }),
});
}
this.measureBeat = this.measureBeat.add(duration);
}
processBackup(backup) {
const quarterNotes = backup.getDuration();
const duration = new util_1.Fraction(quarterNotes, this.quarterNoteDivisions);
this.measureBeat = this.measureBeat.subtract(duration);
if (this.measureBeat.isLessThan(util_1.Fraction.zero())) {
this.measureBeat = util_1.Fraction.zero();
}
}
processForward(forward) {
const quarterNotes = forward.getDuration();
const duration = new util_1.Fraction(quarterNotes, this.quarterNoteDivisions);
this.measureBeat = this.measureBeat.add(duration);
}
processAttributes(attributes, partId, measureIndex) {
this.quarterNoteDivisions = attributes.getQuarterNoteDivisions() ?? this.quarterNoteDivisions;
const staveCount = attributes.getStaveCount() ?? this.previousExplicitStaveCount[partId];
if (attributes.getStaveCount()) {
this.events.push({
type: 'stavecount',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveCount: new stavecount_1.StaveCount(this.config, this.log, partId, staveCount),
});
}
const staveLineCounts = attributes
.getStaveDetails()
.map((staveDetails) => stavelinecount_1.StaveLineCount.create(this.config, this.log, partId, { staveDetails }));
for (const staveLineCount of staveLineCounts) {
this.events.push({
type: 'stavelinecount',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber: staveLineCount.getStaveNumber(),
staveLineCount,
});
}
const clefs = attributes.getClefs().map((clef) => clef_1.Clef.create(this.config, this.log, partId, { clef }));
for (const clef of clefs) {
this.events.push({
type: 'clef',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber: clef.getStaveNumber(),
clef,
});
}
// Processing keys is particularly messy because they can be applied to a specific stave or all staves. We need to
// keep track of the previous key to know if we need to show cancel accidentals.
for (const attributeKey of attributes.getKeys()) {
const staveNumber = attributeKey.getStaveNumber();
if (typeof staveNumber === 'number') {
// If the key is applied to a specific stave, proceed forward as normal.
this.resolveStaveNumber(partId, staveNumber);
const previousKey = this.previousKeys.get(staveNumber) ?? null;
const key = key_1.Key.create(this.config, this.log, partId, staveNumber, previousKey, { key: attributeKey });
this.previousKeys.set(staveNumber, key);
this.events.push({
type: 'key',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber,
key,
});
}
else {
// Otherwise, apply the key to all staves, checking the previous key as we go along.
for (let index = 0; index < staveCount; index++) {
const previousKey = this.previousKeys.get(index + 1) ?? null;
const key = key_1.Key.create(this.config, this.log, partId, index + 1, previousKey, { key: attributeKey });
this.previousKeys.set(index + 1, key);
this.events.push({
type: 'key',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber: index + 1,
key,
});
}
}
}
const times = attributes
.getTimes()
.flatMap((time) => {
const staveNumber = time.getStaveNumber();
if (typeof staveNumber === 'number') {
return [time_1.Time.create(this.config, this.log, partId, this.resolveStaveNumber(partId, staveNumber), { time })];
}
else {
return time_1.Time.createMulti(this.config, this.log, partId, staveCount, { time });
}
})
.filter((time) => time !== null);
for (const time of times) {
this.events.push({
type: 'time',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber: time.getStaveNumber(),
time,
});
}
const measureStyle = attributes.getMeasureStyles().find((measureStyle) => measureStyle.getMultipleRestCount() > 0);
if (measureStyle) {
this.events.push({
type: 'multirest',
partId,
measureIndex,
measureBeat: this.measureBeat,
measureCount: measureStyle.getMultipleRestCount(),
staveNumber: measureStyle.getStaveNumber(),
});
}
}
processDirection(direction, partId, measureIndex) {
const metronome = direction.getMetronome();
const mark = metronome?.getMark();
if (metronome && mark) {
this.events.push({
type: 'metronome',
partId,
measureIndex,
measureBeat: this.measureBeat,
metronome: metronome_1.Metronome.create(this.config, this.log, { metronome, mark }),
});
}
const segnos = direction.getSegnos();
if (segnos.length > 0) {
this.events.push({
type: 'segno',
partId,
measureIndex,
measureBeat: this.measureBeat,
});
}
const coda = direction.getCodas();
if (coda.length > 0) {
this.events.push({
type: 'coda',
partId,
measureIndex,
measureBeat: this.measureBeat,
});
}
const dynamicType = direction
.getDynamics()
.flatMap((d) => d.getTypes())
.at(0);
if (dynamicType) {
const staveNumber = this.resolveStaveNumber(partId, direction.getStaveNumber());
const voiceId = this.resolveVoiceId(partId, direction.getVoice());
this.events.push({
type: 'dynamics',
partId,
measureIndex,
staveNumber,
voiceId,
measureBeat: this.measureBeat,
dynamics: new dynamics_1.Dynamics(this.config, this.log, this.measureBeat, dynamicType),
});
}
const wedge = direction
.getWedges()
.map((wedge) => wedge_1.Wedge.create({ direction, wedge }))
.at(0);
if (wedge) {
const staveNumber = this.resolveStaveNumber(partId, direction.getStaveNumber());
const voiceId = this.resolveVoiceId(partId, direction.getVoice());
this.events.push({
type: 'wedge',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber,
voiceId,
wedge,
});
}
const pedal = direction
.getPedals()
.map((pedal) => pedal_1.Pedal.create(this.config, this.log, { pedal }))
.at(0);
if (pedal) {
const staveNumber = this.resolveStaveNumber(partId, direction.getStaveNumber());
const voiceId = this.resolveVoiceId(partId, direction.getVoice());
this.events.push({
type: 'pedal',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber,
voiceId,
pedal,
});
}
const octaveShift = direction
.getOctaveShifts()
.map((octaveShift) => octaveshift_1.OctaveShift.create(this.config, this.log, { octaveShift }))
.at(0);
if (octaveShift) {
const staveNumber = this.resolveStaveNumber(partId, direction.getStaveNumber());
const voiceId = this.resolveVoiceId(partId, direction.getVoice());
this.events.push({
type: 'octaveshift',
partId,
measureIndex,
measureBeat: this.measureBeat,
staveNumber,
voiceId,
octaveShift,
});
}
}
resolveVoiceId(partId, voiceId) {
return (this.previousExplicitVoiceId[partId] = voiceId ?? this.previousExplicitVoiceId[partId]);
}
resolveStaveNumber(partId, staveNumber) {
return (this.previousExplicitStaveNumber[partId] = staveNumber ?? this.previousExplicitStaveNumber[partId]);
}
}
exports.EventCalculator = EventCalculator;