UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

320 lines (319 loc) 12 kB
import { Metronome } from './metronome'; import { StaveCount } from './stavecount'; import { StaveLineCount } from './stavelinecount'; import { Clef } from './clef'; import { Key } from './key'; import { Time } from './time'; import { FragmentSignature } from './fragmentsignature'; import { PartSignature } from './partsignature'; import { StaveSignature } from './stavesignature'; /** Signature tracks how subsignatures evolve as the piece progresses. */ export class Signature { config; log; metronome; staveCounts; staveLineCounts; clefs; keys; times; changes; constructor(config, log, metronome, staveCounts, staveLineCounts, clefs, keys, times, changes) { this.config = config; this.log = log; this.metronome = metronome; this.staveCounts = staveCounts; this.staveLineCounts = staveLineCounts; this.clefs = clefs; this.keys = keys; this.times = times; this.changes = changes; } static default(config, log) { return new Signature(config, log, null, [], [], [], [], [], []); } static builder(config, log) { return new SignatureBuilder(config, log); } asFragmentSignature() { return new FragmentSignature(this.config, this.log, this.getMetronome()); } asPartSignature(partId) { return new PartSignature(this.config, this.log, this.getStaveCount(partId)); } asStaveSignature(partId, staveNumber) { return new StaveSignature(this.config, this.log, this.getStaveLineCount(partId, staveNumber), this.getClef(partId, staveNumber), this.getKey(partId, staveNumber), this.getTime(partId, staveNumber)); } getMetronome() { return this.metronome ?? Metronome.default(this.config, this.log); } getStaveCount(partId) { return this.staveCounts.find((s) => s.getPartId() === partId) ?? StaveCount.default(this.config, this.log, partId); } getStaveCounts() { return this.staveCounts; } getStaveLineCount(partId, staveNumber) { return (this.staveLineCounts.find((s) => s.getPartId() === partId && s.getStaveNumber() === staveNumber) ?? StaveLineCount.default(this.config, this.log, partId, staveNumber)); } getStaveLineCounts() { return this.staveLineCounts; } getClef(partId, staveNumber) { return (this.clefs.find((c) => c.getPartId() === partId && c.getStaveNumber() === staveNumber) ?? Clef.default(this.config, this.log, partId, staveNumber)); } getClefs() { return this.clefs; } getKey(partId, staveNumber) { return (this.keys.find((k) => k.getPartId() === partId && k.getStaveNumber() === staveNumber) ?? Key.default(this.config, this.log, partId, staveNumber)); } getPreviousKey(partId, staveNumber) { return this.keys.find((k) => k.getPartId() === partId && k.getStaveNumber() === staveNumber) ?? null; } getKeys() { return this.keys; } getTime(partId, staveNumber) { return (this.times.find((t) => t.getPartId() === partId && t.getStaveNumber() === staveNumber) ?? Time.default(this.config, this.log, partId, staveNumber)); } getTimes() { return this.times; } getChanges() { return this.changes; } hasChanges() { return this.changes.length > 0; } } class SignatureBuilder { config; log; previousSignature; metronome = null; // partId -> StaveCount staveCounts = new Map(); // partId -> staveNumber -> StaveLineCount staveLineCounts = new Map(); // partId -> staveNumber -> Clef clefs = new Map(); // partId -> staveNumber -> Key keys = new Map(); // partId -> staveNumber -> Time times = new Map(); constructor(config, log) { this.config = config; this.log = log; this.previousSignature = Signature.default(this.config, this.log); } setPreviousSignature(signature) { this.previousSignature = signature; return this; } setMetronome(metronome) { this.metronome = metronome; return this; } addStaveCount(staveCount) { const partId = staveCount.getPartId(); this.staveCounts.set(partId, staveCount); return this; } addStaveLineCount(staveLineCount) { const partId = staveLineCount.getPartId(); const staveNumber = staveLineCount.getStaveNumber(); if (!this.staveLineCounts.has(partId)) { this.staveLineCounts.set(partId, new Map()); } this.staveLineCounts.get(partId).set(staveNumber, staveLineCount); return this; } addKey(key) { const partId = key.getPartId(); const staveNumber = key.getStaveNumber(); if (!this.keys.has(partId)) { this.keys.set(partId, new Map()); } this.keys.get(partId).set(staveNumber, key); return this; } addClef(clef) { const partId = clef.getPartId(); const staveNumber = clef.getStaveNumber(); if (!this.clefs.has(partId)) { this.clefs.set(partId, new Map()); } this.clefs.get(partId).set(staveNumber, clef); return this; } addTime(time) { const partId = time.getPartId(); const staveNumber = time.getStaveNumber(); if (!this.times.has(partId)) { this.times.set(partId, new Map()); } this.times.get(partId).set(staveNumber, time); return this; } build() { const metronome = this.buildMetronome(); const staveCounts = this.buildStaveCounts(); const staveLineCounts = this.buildStaveLineCounts(); const clefs = this.buildClefs(); const keys = this.buildKeys(); const times = this.buildTimes(); const changes = [ ...this.diffMetronome(metronome), ...this.diffStaveCounts(staveCounts), ...this.diffStaveLineCounts(staveLineCounts), ...this.diffClefs(clefs), ...this.diffKeys(keys), ...this.diffTimes(times), ]; return new Signature(this.config, this.log, metronome, staveCounts, staveLineCounts, clefs, keys, times, changes); } buildMetronome() { return this.metronome ?? this.previousSignature?.getMetronome() ?? Metronome.default(this.config, this.log); } buildStaveCounts() { const next = this.staveCounts.values(); const existing = new Array(); for (const staveCount of this.previousSignature?.getStaveCounts() ?? []) { if (!this.staveCounts.has(staveCount.getPartId())) { existing.push(staveCount); } } return [...next, ...existing]; } buildStaveLineCounts() { const seen = new Array(); function isSeen(partId, staveNumber) { return seen.some((s) => s.partId === partId && s.staveNumber === staveNumber); } const next = new Array(); for (const [partId, partStaveLineCounts] of this.staveLineCounts) { for (const [staveNumber, staveLineCount] of partStaveLineCounts) { if (isSeen(partId, staveNumber)) { continue; } seen.push({ partId, staveNumber }); next.push(staveLineCount); } } const existing = new Array(); for (const staveLineCount of this.previousSignature?.getStaveLineCounts() ?? []) { if (!isSeen(staveLineCount.getPartId(), staveLineCount.getStaveNumber())) { existing.push(staveLineCount); } } return [...next, ...existing]; } buildClefs() { const seen = new Array(); function isSeen(partId, staveNumber) { return seen.some((s) => s.partId === partId && s.staveNumber === staveNumber); } const next = new Array(); for (const [partId, partClefs] of this.clefs) { for (const [staveNumber, clef] of partClefs) { if (isSeen(partId, staveNumber)) { continue; } seen.push({ partId, staveNumber }); next.push(clef); } } const existing = new Array(); for (const clef of this.previousSignature?.getClefs() ?? []) { if (!isSeen(clef.getPartId(), clef.getStaveNumber())) { existing.push(clef); } } return [...next, ...existing]; } buildKeys() { const seen = new Array(); function isSeen(partId, staveNumber) { return seen.some((s) => s.partId === partId && s.staveNumber === staveNumber); } const next = new Array(); for (const [partId, partKeys] of this.keys) { for (const [staveNumber, key] of partKeys) { if (isSeen(partId, staveNumber)) { continue; } seen.push({ partId, staveNumber }); next.push(key); } } const existing = new Array(); for (const key of this.previousSignature?.getKeys() ?? []) { if (!this.keys.has(key.getPartId())) { existing.push(key); } } return [...next, ...existing]; } buildTimes() { const seen = new Array(); function isSeen(partId, staveNumber) { return seen.some((s) => s.partId === partId && s.staveNumber === staveNumber); } const next = new Array(); for (const [partId, partTimes] of this.times) { for (const [staveNumber, time] of partTimes) { if (isSeen(partId, staveNumber)) { continue; } seen.push({ partId, staveNumber }); next.push(time); } } const existing = new Array(); for (const time of this.previousSignature?.getTimes() ?? []) { if (!isSeen(time.getPartId(), time.getStaveNumber())) { existing.push(time); } } return [...next, ...existing]; } diffMetronome(metronome) { if (!this.previousSignature || metronome.isEqual(this.previousSignature.getMetronome())) { return []; } else { return [{ type: 'metronome' }]; } } diffStaveCounts(staveCounts) { return staveCounts .filter((s) => !this.previousSignature || !s.isEqual(this.previousSignature.getStaveCount(s.getPartId()))) .map((s) => ({ type: 'stavecount', partId: s.getPartId() })); } diffStaveLineCounts(staveLineCounts) { return staveLineCounts .filter((s) => !this.previousSignature || !s.isEqual(this.previousSignature.getStaveLineCount(s.getPartId(), s.getStaveNumber()))) .map((s) => ({ type: 'stavelinecount', partId: s.getPartId(), staveNumber: s.getStaveNumber() })); } diffClefs(clefs) { return clefs .filter((c) => !this.previousSignature || !c.isEqual(this.previousSignature.getClef(c.getPartId(), c.getStaveNumber()))) .map((c) => ({ type: 'clef', partId: c.getPartId(), staveNumber: c.getStaveNumber() })); } diffKeys(keys) { return keys .filter((k) => !this.previousSignature || !k.isEqual(this.previousSignature.getKey(k.getPartId(), k.getStaveNumber()))) .map((k) => ({ type: 'key', partId: k.getPartId(), staveNumber: k.getStaveNumber() })); } diffTimes(times) { return times .filter((t) => !this.previousSignature || !t.isEqual(this.previousSignature.getTime(t.getPartId(), t.getStaveNumber()))) .map((t) => ({ type: 'time', partId: t.getPartId(), staveNumber: t.getStaveNumber() })); } }