UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

103 lines (102 loc) 3.88 kB
/** A generic way of representing a curved connector in music notation. */ export class Curve { config; log; curveNumber; phase; placement; opening; articulation; constructor(config, log, curveNumber, phase, placement, opening, articulation) { this.config = config; this.log = log; this.curveNumber = curveNumber; this.phase = phase; this.placement = placement; this.opening = opening; this.articulation = articulation; } static create(config, log, musicXML) { return [...Curve.createSlurs(config, log, musicXML), ...Curve.createTies(config, log, musicXML)]; } static createSlurs(config, log, musicXML) { const curves = new Array(); for (const slur of musicXML.notation.getSlurs()) { const placement = slur.getPlacement() ?? 'auto'; let phase; switch (slur.getType()) { case 'start': phase = 'start'; break; default: phase = 'continue'; break; } let opening = 'auto'; switch (slur.getOrientation()) { // Yes, these translations are correct. case 'over': opening = 'down'; break; case 'under': opening = 'up'; break; } let curveNumber = slur.getNumber(); let articulation = 'unspecified'; const slides = musicXML.notation.getSlides(); const hammerOns = musicXML.notation.getTechnicals().flatMap((t) => t.getHammerOns()); const pullOffs = musicXML.notation.getTechnicals().flatMap((t) => t.getPullOffs()); if (slides.length > 0) { curveNumber = slides.at(0)?.getNumber() ?? curveNumber; articulation = 'slide'; } else if (hammerOns.length > 0) { curveNumber = hammerOns.at(0)?.getNumber() ?? curveNumber; articulation = 'hammeron'; } else if (pullOffs.length > 0) { curveNumber = pullOffs.at(0)?.getNumber() ?? curveNumber; articulation = 'pulloff'; } curves.push(new Curve(config, log, curveNumber, phase, placement, opening, articulation)); } return curves; } static createTies(config, log, musicXML) { const curves = new Array(); for (const tied of musicXML.notation.getTieds()) { const placement = tied.getPlacement() ?? 'auto'; let phase; switch (tied.getType()) { case 'start': phase = 'start'; break; default: phase = 'continue'; break; } let opening = 'auto'; switch (tied.getOrientation()) { // Yes, these translations are correct. case 'over': opening = 'down'; break; case 'under': opening = 'up'; break; } const curveNumber = tied.getNumber(); const articulation = 'unspecified'; curves.push(new Curve(config, log, curveNumber, phase, placement, opening, articulation)); } return curves; } parse(voiceEntryCtx) { if (this.phase === 'start') { return voiceEntryCtx.beginCurve(this.curveNumber, this.placement, this.opening, this.articulation); } return (voiceEntryCtx.continueCurve(this.curveNumber) ?? voiceEntryCtx.beginCurve(this.curveNumber, this.placement, this.opening, this.articulation)); } }