UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

383 lines (382 loc) 15.1 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.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;