@stringsync/vexml
Version:
MusicXML to Vexflow
320 lines (319 loc) • 12 kB
JavaScript
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() }));
}
}