UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

706 lines (705 loc) 23.1 kB
/** * @file This file contains **mutable** context objects that are used during parsing. * * When parsing, parent contexts may need to drill state to a distant descendant. Conversely, a child context may need * to communicate state to a distant ancestor. Separate context objects are used to avoid passing state through many * layers and to avoid tight coupling between the main parsing classes. * * NOTE: This code must **not** depend on any other parsing code because it will create an undesireble dependency graph. */ export class ScoreContext { idProvider; // part ID -> nullable stave number -> multi rest count // When the stave number is null, the multi rest count applies to all staves in the part. multiRestCounts = new Map(); curves = new Array(); // curve number -> curve ID // See https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/slur/#:~:text=dotted%2C%20or%20wavy.-,number,-number%2Dlevel curveIds = new Map(); wedges = new Array(); // part ID -> stave number -> wedge status wedgeStatuses = new Map(); pedals = new Array(); // part ID -> pedal status pedalStatuses = new Map(); octaveShifts = new Array(); // part ID -> stave number -> octave shift status octaveShiftStatuses = new Map(); vibratos = new Array(); // vibrato number -> vibrato ID vibratoStatuses = new Map(); constructor(idProvider) { this.idProvider = idProvider; } nextId() { return this.idProvider.next(); } getMultiRestCount(partId, staveNumber) { return this.multiRestCounts.get(partId)?.get(null) ?? this.multiRestCounts.get(partId)?.get(staveNumber) ?? 0; } incrementMultiRestCount(partId, staveNumber, count) { if (!this.multiRestCounts.has(partId)) { this.multiRestCounts.set(partId, new Map()); } this.multiRestCounts.get(partId).set(staveNumber, count + this.getMultiRestCount(partId, staveNumber)); } decrementMultiRestCounts() { for (const partId of this.multiRestCounts.keys()) { const staveNumbers = this.multiRestCounts.get(partId).keys(); for (const staveNumber of staveNumbers) { const count = this.getMultiRestCount(partId, staveNumber); if (count > 0) { this.multiRestCounts.get(partId).set(staveNumber, count - 1); } } } } getCurves() { return this.curves; } beginCurve(curveNumber, placement, opening, articulation) { const id = this.nextId(); this.curves.push({ type: 'curve', id, placement, opening, articulation }); this.curveIds.set(curveNumber, id); return id; } continueCurve(curveNumber) { return this.curveIds.get(curveNumber) ?? null; } getWedges() { return this.wedges; } beginWedge(partId, staveNumber, wedgeType, placement) { const id = this.nextId(); this.wedges.push({ type: 'wedge', wedgeType, id, placement }); if (!this.wedgeStatuses.has(partId)) { this.wedgeStatuses.set(partId, new Map()); } this.wedgeStatuses.get(partId).set(staveNumber, { id, count: 0, delete: false }); return id; } continueOpenWedge(partId, staveNumber) { const status = this.wedgeStatuses.get(partId)?.get(staveNumber); if (!status) { return null; } if (status.delete) { this.wedgeStatuses.get(partId)?.delete(staveNumber); } status.count++; return status.id; } closeWedge(partId, staveNumber) { const status = this.wedgeStatuses.get(partId)?.get(staveNumber); if (!status) { return; } if (status.count > 1) { this.wedgeStatuses.get(partId)?.delete(staveNumber); } else { // We don't meet the criteria to be fully specified, so we'll mark for delete later. status.delete = true; } } getPedals() { return this.pedals; } beginPedal(partId, pedalType) { const id = this.nextId(); this.pedals.push({ type: 'pedal', id, pedalType }); this.pedalStatuses.set(partId, { id, next: 'default', count: 0, delete: false }); return id; } continueOpenPedal(partId) { const status = this.pedalStatuses.get(partId); if (!status) { return null; } const pedalMarkType = status.next; status.next = 'default'; // consume the next pedal mark type if (status.delete && status.count > 1) { this.pedalStatuses.delete(partId); } status.count++; return { type: 'pedalmark', pedalMarkType, pedalId: status.id }; } primeNextPedalMark(partId, pedalMarkType) { const status = this.pedalStatuses.get(partId); if (!status) { return; } status.next = pedalMarkType; if (pedalMarkType === 'change') { // We want to avoid ending the pedal on a change, so we'll reset the count. status.count = 0; } } closePedal(partId) { const status = this.pedalStatuses.get(partId); if (!status) { return; } if (status.count > 1) { this.pedalStatuses.delete(partId); } else { // We don't meet the criteria to be fully specified, so we'll mark for delete later. status.delete = true; } } getOctaveShifts() { return this.octaveShifts; } beginOctaveShift(partId, staveNumber, size) { const id = this.nextId(); this.octaveShifts.push({ type: 'octaveshift', id, size }); if (!this.octaveShiftStatuses.has(partId)) { this.octaveShiftStatuses.set(partId, new Map()); } this.octaveShiftStatuses.get(partId).set(staveNumber, { id, count: 0, delete: false }); return id; } continueOpenOctaveShift(partId, staveNumber) { const status = this.octaveShiftStatuses.get(partId)?.get(staveNumber); if (!status) { return null; } if (status.delete && status.count > 1) { this.octaveShiftStatuses.get(partId)?.delete(staveNumber); } status.count++; return status.id; } closeOctaveShift(partId, staveNumber) { const status = this.octaveShiftStatuses.get(partId)?.get(staveNumber); if (!status) { return; } if (status.count > 1) { this.octaveShiftStatuses.get(partId)?.delete(staveNumber); } else { // We don't meet the criteria to be fully specified, so we'll mark for delete later. status.delete = true; } } getVibratos() { return this.vibratos; } beginVibrato(vibratoNumber) { const id = this.nextId(); this.vibratos.push({ type: 'vibrato', id }); this.vibratoStatuses.set(vibratoNumber, id); return id; } continueVibrato(vibratoNumber) { return this.vibratoStatuses.get(vibratoNumber) ?? null; } } export class SystemContext { score; constructor(score) { this.score = score; } nextId() { return this.score.nextId(); } getMultiRestCount(partId, staveNumber) { return this.score.getMultiRestCount(partId, staveNumber); } incrementMultiRestCount(partId, staveNumber, count) { return this.score.incrementMultiRestCount(partId, staveNumber, count); } decrementMultiRestCounts() { return this.score.decrementMultiRestCounts(); } beginCurve(curveNumber, placement, opening, articulation) { return this.score.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.score.continueCurve(curveNumber); } beginWedge(partId, staveNumber, wedgeType, placement) { return this.score.beginWedge(partId, staveNumber, wedgeType, placement); } continueOpenWedge(partId, staveNumber) { return this.score.continueOpenWedge(partId, staveNumber); } closeWedge(partId, staveNumber) { this.score.closeWedge(partId, staveNumber); } beginPedal(partId, pedalType) { return this.score.beginPedal(partId, pedalType); } continueOpenPedal(partId) { return this.score.continueOpenPedal(partId); } primeNextPedalMark(partId, pedalMarkType) { this.score.primeNextPedalMark(partId, pedalMarkType); } closePedal(partId) { this.score.closePedal(partId); } beginOctaveShift(partId, staveNumber, size) { return this.score.beginOctaveShift(partId, staveNumber, size); } continueOpenOctaveShift(partId, staveNumber) { return this.score.continueOpenOctaveShift(partId, staveNumber); } closeOctaveShift(partId, staveNumber) { this.score.closeOctaveShift(partId, staveNumber); } beginVibrato(vibratoNumber) { return this.score.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.score.continueVibrato(vibratoNumber); } } export class MeasureContext { system; index; // part ID -> pitch -> octave -> accidental code accidentals = {}; constructor(system, index) { this.system = system; this.index = index; } nextId() { return this.system.nextId(); } getIndex() { return this.index; } getActiveAccidental(partId, pitch, octave) { return this.accidentals[partId]?.[pitch]?.[octave] ?? null; } setActiveAccidental(partId, pitch, octave, accidental) { this.accidentals[partId] ??= {}; this.accidentals[partId][pitch] ??= {}; this.accidentals[partId][pitch][octave] = accidental; } getMultiRestCount(partId, staveNumber) { return this.system.getMultiRestCount(partId, staveNumber); } beginCurve(curveNumber, placement, opening, articulation) { return this.system.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.system.continueCurve(curveNumber); } beginWedge(partId, staveNumber, wedgeType, placement) { return this.system.beginWedge(partId, staveNumber, wedgeType, placement); } continueOpenWedge(partId, staveNumber) { return this.system.continueOpenWedge(partId, staveNumber); } closeWedge(partId, staveNumber) { this.system.closeWedge(partId, staveNumber); } beginPedal(partId, pedalType) { return this.system.beginPedal(partId, pedalType); } continueOpenPedal(partId) { return this.system.continueOpenPedal(partId); } primeNextPedalMark(partId, pedalMarkType) { this.system.primeNextPedalMark(partId, pedalMarkType); } closePedal(partId) { this.system.closePedal(partId); } beginOctaveShift(partId, staveNumber, size) { return this.system.beginOctaveShift(partId, staveNumber, size); } continueOpenOctaveShift(partId, staveNumber) { return this.system.continueOpenOctaveShift(partId, staveNumber); } closeOctaveShift(partId, staveNumber) { this.system.closeOctaveShift(partId, staveNumber); } beginVibrato(vibratoNumber) { return this.system.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.system.continueVibrato(vibratoNumber); } } export class FragmentContext { measure; signature; constructor(measure, signature) { this.measure = measure; this.signature = signature; } nextId() { return this.measure.nextId(); } getSignature() { return this.signature; } getActiveAccidental(partId, pitch, octave) { return this.measure.getActiveAccidental(partId, pitch, octave); } setActiveAccidental(partId, pitch, octave, accidental) { this.measure.setActiveAccidental(partId, pitch, octave, accidental); } getMultiRestCount(partId, staveNumber) { return this.measure.getMultiRestCount(partId, staveNumber); } beginCurve(curveNumber, placement, opening, articulation) { return this.measure.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.measure.continueCurve(curveNumber); } beginWedge(partId, staveNumber, wedgeType, placement) { return this.measure.beginWedge(partId, staveNumber, wedgeType, placement); } continueOpenWedge(partId, staveNumber) { return this.measure.continueOpenWedge(partId, staveNumber); } closeWedge(partId, staveNumber) { this.measure.closeWedge(partId, staveNumber); } beginPedal(partId, pedalType) { return this.measure.beginPedal(partId, pedalType); } continueOpenPedal(partId) { return this.measure.continueOpenPedal(partId); } primeNextPedalMark(partId, pedalMarkType) { this.measure.primeNextPedalMark(partId, pedalMarkType); } closePedal(partId) { this.measure.closePedal(partId); } beginOctaveShift(partId, staveNumber, size) { return this.measure.beginOctaveShift(partId, staveNumber, size); } continueOpenOctaveShift(partId, staveNumber) { return this.measure.continueOpenOctaveShift(partId, staveNumber); } closeOctaveShift(partId, staveNumber) { this.measure.closeOctaveShift(partId, staveNumber); } beginVibrato(vibratoNumber) { return this.measure.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.measure.continueVibrato(vibratoNumber); } } export class PartContext { fragment; id; constructor(fragment, id) { this.fragment = fragment; this.id = id; } nextId() { return this.fragment.nextId(); } getId() { return this.id; } getKey(staveNumber) { return this.fragment.getSignature().getKey(this.id, staveNumber); } getTime(staveNumber) { return this.fragment.getSignature().getTime(this.id, staveNumber); } getActiveAccidental(pitch, octave) { return this.fragment.getActiveAccidental(this.id, pitch, octave); } setActiveAccidental(pitch, octave, accidental) { this.fragment.setActiveAccidental(this.id, pitch, octave, accidental); } getMultiRestCount(staveNumber) { return this.fragment.getMultiRestCount(this.id, staveNumber); } beginCurve(curveNumber, placement, opening, articulation) { return this.fragment.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.fragment.continueCurve(curveNumber); } beginWedge(placement, wedgeType, staveNumber) { return this.fragment.beginWedge(this.id, staveNumber, wedgeType, placement); } continueOpenWedge(staveNumber) { return this.fragment.continueOpenWedge(this.id, staveNumber); } closeWedge(staveNumber) { this.fragment.closeWedge(this.id, staveNumber); } beginPedal(pedalType) { return this.fragment.beginPedal(this.id, pedalType); } continueOpenPedal() { return this.fragment.continueOpenPedal(this.id); } primeNextPedalMark(pedalMarkType) { this.fragment.primeNextPedalMark(this.id, pedalMarkType); } closePedal() { this.fragment.closePedal(this.id); } beginOctaveShift(staveNumber, size) { return this.fragment.beginOctaveShift(this.id, staveNumber, size); } continueOpenOctaveShift(staveNumber) { return this.fragment.continueOpenOctaveShift(this.id, staveNumber); } closeOctaveShift(staveNumber) { this.fragment.closeOctaveShift(this.id, staveNumber); } beginVibrato(vibratoNumber) { return this.fragment.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.fragment.continueVibrato(vibratoNumber); } } export class StaveContext { part; number; constructor(part, number) { this.part = part; this.number = number; } nextId() { return this.part.nextId(); } getNumber() { return this.number; } getKey() { return this.part.getKey(this.number); } getTime() { return this.part.getTime(this.number); } getActiveAccidental(pitch, octave) { return this.part.getActiveAccidental(pitch, octave); } setActiveAccidental(pitch, octave, accidental) { this.part.setActiveAccidental(pitch, octave, accidental); } getMultiRestCount() { return this.part.getMultiRestCount(this.number); } beginCurve(curveNumber, placement, opening, articulation) { return this.part.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.part.continueCurve(curveNumber); } beginWedge(placement, wedgeType) { return this.part.beginWedge(placement, wedgeType, this.number); } continueOpenWedge() { return this.part.continueOpenWedge(this.number); } closeWedge() { this.part.closeWedge(this.number); } beginPedal(pedalType) { return this.part.beginPedal(pedalType); } continueOpenPedal() { return this.part.continueOpenPedal(); } primeNextPedalMark(pedalMarkType) { this.part.primeNextPedalMark(pedalMarkType); } closePedal() { this.part.closePedal(); } beginOctaveShift(size) { return this.part.beginOctaveShift(this.number, size); } continueOpenOctaveShift() { return this.part.continueOpenOctaveShift(this.number); } closeOctaveShift() { this.part.closeOctaveShift(this.number); } beginVibrato(vibratoNumber) { return this.part.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.part.continueVibrato(vibratoNumber); } } export class VoiceContext { stave; id; beams = new Array(); // tuplet number -> tuplet openTuplets = new Map(); closedTuplets = new Array(); constructor(stave, id) { this.stave = stave; this.id = id; } nextId() { return this.stave.nextId(); } getId() { return this.id; } getKey() { return this.stave.getKey(); } getTime() { return this.stave.getTime(); } getActiveAccidental(pitch, octave) { return this.stave.getActiveAccidental(pitch, octave); } setActiveAccidental(pitch, octave, accidental) { this.stave.setActiveAccidental(pitch, octave, accidental); } getMultiRestCount() { return this.stave.getMultiRestCount(); } beginCurve(curveNumber, placement, opening, articulation) { return this.stave.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.stave.continueCurve(curveNumber); } getBeams() { return this.beams; } beginBeam() { const id = this.nextId(); this.beams.push({ type: 'beam', id }); return id; } continueBeam() { return this.beams.at(-1)?.id ?? null; } getTuplets() { return this.closedTuplets; } beginTuplet(number, showNumber, placement) { const id = this.nextId(); this.openTuplets.set(number, { type: 'tuplet', id, showNumber, placement }); return id; } continueOpenTuplets() { return [...this.openTuplets.values()].map((tuplet) => tuplet.id); } closeTuplet(number) { if (!this.openTuplets.has(number)) { return null; } const tuplet = this.openTuplets.get(number); this.closedTuplets.push(tuplet); this.openTuplets.delete(number); return tuplet.id; } beginWedge(placement, wedgeType) { return this.stave.beginWedge(placement, wedgeType); } continueOpenWedge() { return this.stave.continueOpenWedge(); } closeWedge() { this.stave.closeWedge(); } beginPedal(pedalType) { return this.stave.beginPedal(pedalType); } continueOpenPedal() { return this.stave.continueOpenPedal(); } primeNextPedalMark(pedalMarkType) { this.stave.primeNextPedalMark(pedalMarkType); } closePedal() { this.stave.closePedal(); } beginOctaveShift(size) { return this.stave.beginOctaveShift(size); } continueOpenOctaveShift() { return this.stave.continueOpenOctaveShift(); } closeOctaveShift() { this.stave.closeOctaveShift(); } beginVibrato(vibratoNumber) { return this.stave.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.stave.continueVibrato(vibratoNumber); } } export class VoiceEntryContext { voice; pitch; octave; constructor(voice, pitch, octave) { this.voice = voice; this.pitch = pitch; this.octave = octave; } static note(voice, pitch, octave) { return new VoiceEntryContext(voice, pitch, octave); } static rest(voice) { return new VoiceEntryContext(voice, '', 0); } nextId() { return this.voice.nextId(); } getKeyAccidental() { return this.voice.getKey().getAccidentalCode(this.pitch); } getActiveAccidental() { return this.voice.getActiveAccidental(this.pitch, this.octave); } setActiveAccidental(accidental) { this.voice.setActiveAccidental(this.pitch, this.octave, accidental); } beginCurve(curveNumber, placement, opening, articulation) { return this.voice.beginCurve(curveNumber, placement, opening, articulation); } continueCurve(curveNumber) { return this.voice.continueCurve(curveNumber); } beginBeam() { return this.voice.beginBeam(); } continueBeam() { return this.voice.continueBeam(); } beginTuplet(number, showNumber, placement) { return this.voice.beginTuplet(number, showNumber, placement); } continueOpenTuplets() { return this.voice.continueOpenTuplets(); } closeTuplet(number) { return this.voice.closeTuplet(number); } continueOpenWedge() { return this.voice.continueOpenWedge(); } continueOpenPedal() { return this.voice.continueOpenPedal(); } continueOpenOctaveShift() { return this.voice.continueOpenOctaveShift(); } beginVibrato(vibratoNumber) { return this.voice.beginVibrato(vibratoNumber); } continueVibrato(vibratoNumber) { return this.voice.continueVibrato(vibratoNumber); } }