satie
Version:
A sheet music renderer for the web
177 lines (157 loc) • 6.52 kB
text/typescript
/**
* This file is part of Satie music engraver <https://github.com/jnetterf/satie>.
* Copyright (C) Joshua Netterfield <joshua.ca> 2015 - present.
*
* Satie is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Satie is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Satie. If not, see <http://www.gnu.org/licenses/>.
*/
import {Attributes, StartStop, PartSymbol, Clef, Time, StaffDetails,
Transpose, Key, Directive, MeasureStyle} from "musicxml-interfaces";
import {clone, forEach} from "lodash";
import {cloneObject} from "./private_util";
/**
* A snapshot of the current attribute state
*/
export interface IAttributesSnapshot extends Attributes {
measure: number;
divisions: number;
partSymbol: PartSymbol;
clefs: Clef[];
times: Time[];
transposes: Transpose[];
keySignatures: Key[];
clef: Clef;
measureStyle: MeasureStyle & {multipleRestInitiatedHere?: boolean};
time: Time;
staffDetails: StaffDetails[];
transpose: Transpose;
staves: number;
instruments: string;
keySignature: Key;
directives: Directive[];
}
export interface IAttributesSnapshotSpec {
before: IAttributesSnapshot;
current: Attributes;
staff: number;
measure: number;
}
export function createAttributesSnapshot({before, current, staff, measure}: IAttributesSnapshotSpec) {
let currentClefs = current.clefs || [];
let currentTimes = current.times || [];
let currentTransposes = current.transposes || [];
let currentKS = current.keySignatures || [];
let staffDetails: StaffDetails[] = [];
let beforeDetails = before.staffDetails || [];
let currentDetails = current.staffDetails || [];
for (let i = 0; i < beforeDetails.length || i < currentDetails.length; ++i) {
staffDetails[i] = createStaffDetailsSnapshot(currentDetails[i] || {},
clone(beforeDetails[i] || {}));
}
let clefs: Clef[] = [];
let beforeClefs = before.clefs || [];
for (let i = 0; i < beforeClefs.length || i < currentClefs.length; ++i) {
clefs[i] = currentClefs[i] || beforeClefs[i];
}
let times: Time[] = [];
let beforeTimes = before.times || [];
for (let i = 0; i < beforeTimes.length || i < currentTimes.length; ++i) {
times[i] = currentTimes[i] || beforeTimes[i];
}
let transposes: Transpose[] = [];
let beforeTransposes = before.transposes || [];
for (let i = 0; i < beforeTransposes.length || i < currentTransposes.length; ++i) {
transposes[i] = currentTransposes[i] || beforeTransposes[i];
}
let keySignatures: Key[] = [];
let beforeKS = before.keySignatures || [];
for (let i = 0; i < beforeKS.length || i < currentKS.length; ++i) {
keySignatures[i] = currentKS[i] || beforeKS[i];
}
let snapshot: IAttributesSnapshot = {
measure,
divisions: current.divisions || before.divisions,
partSymbol: current.partSymbol || before.partSymbol,
clef: currentClefs[staff] || before.clef,
time: currentTimes[0] || before.time, // TODO: time signatures per staff
staffDetails,
transpose: currentTransposes[staff] || before.transpose,
instruments: current.instruments || before.instruments,
keySignature: currentKS[0] || before.keySignature,
directives: current.directives,
staves: current.staves || before.staves,
measureStyle: createMeasureStyleSnapshot(current,
JSON.parse(JSON.stringify(before.measureStyle || <MeasureStyle> {}))),
clefs,
times,
transposes,
keySignatures,
};
return snapshot;
}
function createStaffDetailsSnapshot(newStaffDetails: StaffDetails, staffDetails: StaffDetails) {
newStaffDetails = cloneObject(newStaffDetails);
newStaffDetails.capo = newStaffDetails.capo || staffDetails.capo;
newStaffDetails.showFrets = newStaffDetails.showFrets || staffDetails.showFrets;
newStaffDetails.staffLines = newStaffDetails.staffLines || staffDetails.staffLines;
newStaffDetails.staffSize = newStaffDetails.staffSize || staffDetails.staffSize;
newStaffDetails.staffTunings = newStaffDetails.staffTunings || staffDetails.staffTunings;
newStaffDetails.staffType = newStaffDetails.staffType || staffDetails.staffType;
return newStaffDetails;
}
function createMeasureStyleSnapshot(current: Attributes, style: MeasureStyle & {multipleRestInitiatedHere: boolean}) {
let multipleRestInitiatedHere: boolean;
forEach(current.measureStyles, currentMeasureStyle => {
if (currentMeasureStyle.slash) {
if (currentMeasureStyle.slash.type === StartStop.Stop) {
delete style.slash;
} else {
style.slash = currentMeasureStyle.slash;
}
}
if (currentMeasureStyle.beatRepeat) {
if (currentMeasureStyle.beatRepeat.type === StartStop.Stop) {
delete style.beatRepeat;
} else {
style.beatRepeat = currentMeasureStyle.beatRepeat;
}
}
if (currentMeasureStyle.measureRepeat) {
if (currentMeasureStyle.measureRepeat.type === StartStop.Stop) {
delete style.measureRepeat;
} else {
style.measureRepeat = currentMeasureStyle.measureRepeat;
}
}
if (currentMeasureStyle.multipleRest) {
multipleRestInitiatedHere = true;
style.multipleRestInitiatedHere = true;
style.multipleRest = currentMeasureStyle.multipleRest;
}
});
if (!multipleRestInitiatedHere) {
style.multipleRestInitiatedHere = false;
}
if (style.multipleRest && !multipleRestInitiatedHere) {
let {count, useSymbols} = style.multipleRest;
if (count - 1) {
style.multipleRest = {
count: count - 1,
useSymbols: useSymbols
};
} else {
style.multipleRest = null;
}
}
return style;
};