webdaw-modules
Version:
a set of modules for building a web-based DAW
295 lines (272 loc) • 9.97 kB
text/typescript
import { OpenSheetMusicDisplay, SourceMeasure } from "opensheetmusicdisplay";
import { getBoundingBoxData } from "./mapper3";
import { PartData, RepeatData } from "../musicxml/parser";
import { LoopData } from "./types";
import { BBox } from "../types";
import { getBoundingBoxMeasureAll } from "./getBoundingBoxMeasure";
export const getTicksAtBar = (parts: PartData[]) => {
parts.forEach(data => {
let bar = 0;
data.events.reduce((acc, val) => {
if (val.bar && val.bar !== bar) {
bar = val.bar;
// console.log(bar, val.ticks);
}
return bar;
}, 0);
});
};
export type AnchorData = {
measureNumber: number;
startTicks: number;
endTicks: number;
bbox: BBox;
bboxMeasure: BBox;
yPos: number;
numPixels: number;
numTicks: number;
pixelsPerTick: number;
ghost: boolean;
nextAnchor: AnchorData | null;
};
export const getPlayheadAnchorData = (
osmd: OpenSheetMusicDisplay,
repeats: RepeatData[],
loops: LoopData[],
ppq: number = 960
): { anchorData: AnchorData[]; measureStartTicks: number[]; upbeat: boolean } => {
const measureBoundingBoxes = getBoundingBoxMeasureAll(osmd);
// console.log("measureBoundingBoxes", measureBoundingBoxes);
const measureStartTicks = osmd.Sheet.SourceMeasures.map((measure: SourceMeasure) => {
return ppq * measure.AbsoluteTimestamp.RealValue * 4;
});
const { Numerator, Denominator } = osmd.Sheet.SourceMeasures[osmd.Sheet.SourceMeasures.length - 1].ActiveTimeSignature;
measureStartTicks.push(measureStartTicks[measureStartTicks.length - 1] + Numerator * (4 / Denominator) * 960);
// console.log(measureStartTicks, measureStartTicks[73]);
// console.log(ppq);
let upbeat = false;
const anchorData: AnchorData[] = [];
osmd.GraphicSheet.VerticalGraphicalStaffEntryContainers.forEach(container => {
const realValue = container.AbsoluteTimestamp.RealValue;
const runningTicks = ppq * 4 * realValue;
const data: AnchorData[] = [];
container.StaffEntries.forEach((entry, i) => {
let measureNumber = entry.parentMeasure.MeasureNumber;
if (measureNumber === 0 && upbeat === false) {
upbeat = true;
console.log("UPBEAT");
}
if (upbeat) {
measureNumber += 1;
}
const measureIndex = measureNumber - 1;
const bboxMeasure = measureBoundingBoxes[measureIndex];
// console.log(measureIndex, bboxMeasure);
const yPos = (entry.parentMeasure as any).parentMusicSystem.boundingBox.absolutePosition.y * 10;
if (typeof (entry.parentMeasure as any).multiRestElement !== "undefined") {
const { Numerator, Denominator } = osmd.Sheet.SourceMeasures[measureIndex].ActiveTimeSignature;
const numberOfMeasures = (entry.parentMeasure as any).multiRestElement.number_of_measures;
const diffTicks = numberOfMeasures * Numerator * (ppq / (Denominator / 4));
const numGhostAnchors = numberOfMeasures * Numerator;
const anchorTicks = diffTicks / numGhostAnchors;
const anchorPixels = bboxMeasure.width / numGhostAnchors;
let x = bboxMeasure.x;
for (let i = 0; i < numGhostAnchors; i++) {
data.push({
numPixels: anchorTicks,
startTicks: runningTicks + i * anchorTicks,
endTicks: 0,
numTicks: 0,
pixelsPerTick: 0,
measureNumber,
bbox: {
x: x + i * anchorPixels,
y: bboxMeasure.y,
width: anchorPixels,
height: bboxMeasure.height,
},
bboxMeasure,
yPos,
ghost: true,
nextAnchor: null,
});
}
} else {
const bbox = getBoundingBoxData((entry as any).boundingBox);
data.push({
numPixels: 0,
startTicks: runningTicks,
endTicks: 0,
numTicks: 0,
pixelsPerTick: 0,
measureNumber,
bbox,
// bboxMeasure: getBoundingBoxData((entry.parentMeasure as any).boundingBox),
bboxMeasure,
yPos,
ghost: false,
nextAnchor: null,
});
}
});
data.sort((a, b) => {
if (a.bbox.x < b.bbox.x) {
return -1;
}
if (a.bbox.x > b.bbox.x) {
return 1;
}
return 0;
});
// console.log(boxes);
// always get the first vertical graphical staff entry
const anchor = data[0];
if (anchor.ghost === false) {
anchorData.push(anchor);
} else {
let x;
for (let i = 0; i < data.length; i++) {
const tmpX = data[i].bbox.x;
if (tmpX !== x) {
x = tmpX;
anchorData.push(data[i]);
}
}
}
});
// console.log(anchorData);
// copy anchor data for all repeats
let diffTicks = 0;
let diffBars = 0;
const copies: AnchorData[] = [];
for (let i = 0; i < repeats.length; i++) {
const { start, end } = repeats[i];
const minTicks = measureStartTicks[start - 1];
const maxTicks = measureStartTicks[end];
// console.log(start, end, minTicks, maxTicks);
diffTicks += maxTicks - minTicks;
diffBars += end - (start - 1);
// console.log(min, max, minTicks, maxTicks, diffTicks);
for (let j = 0; j < anchorData.length; j++) {
const anchor = anchorData[j];
if (anchor.measureNumber >= start && anchor.measureNumber <= end) {
const clone = { ...anchor };
clone.startTicks += diffTicks;
clone.measureNumber += diffBars;
copies.push(clone);
}
}
}
// console.log(copies);
// update the ticks and measure number of the bars that come after the repeats
const result: AnchorData[] = anchorData.map(d => {
const { measureNumber, startTicks } = d;
const clone = { ...d };
let diffTicks = 0;
let diffBars = 0;
for (let i = 0; i < repeats.length; i++) {
const { start, end } = repeats[i];
// console.log(start, end);
const minTicks = measureStartTicks[start - 1];
const maxTicks = measureStartTicks[end];
diffTicks += maxTicks - minTicks;
// console.log(diffTicks);
diffBars += end - (start - 1);
if (measureNumber > end) {
clone.startTicks = startTicks + diffTicks;
clone.measureNumber = measureNumber + diffBars;
}
}
return clone;
});
// result.forEach(d => {
// console.log(d.measureNumber, d.ticks);
// });
// copies.forEach(d => {
// console.log(d.measureNumber, d.ticks);
// });
result.push(...copies);
result.sort((a, b) => {
if (a.startTicks < b.startTicks) {
return -1;
}
if (a.startTicks > b.startTicks) {
return 1;
}
return 0;
});
result.sort((a, b) => {
if (a.measureNumber < b.measureNumber) {
return -1;
}
if (a.measureNumber > b.measureNumber) {
return 1;
}
return 0;
});
const result1: number[] = [];
let currentMeasureNumber = 0;
result.forEach(r => {
const { startTicks, measureNumber } = r;
if (currentMeasureNumber !== measureNumber) {
currentMeasureNumber = measureNumber;
result1.push(startTicks);
}
});
// add ticks position of the end of the last bar
// const { Numerator, Denominator } = osmd.Sheet.SourceMeasures[osmd.Sheet.SourceMeasures.length - 1].ActiveTimeSignature;
const lastTicks = result1[result1.length - 1] + Numerator * (4 / Denominator) * 960;
result1.push(lastTicks);
for (let i = 0; i < result.length; i++) {
let a1 = result[i];
let a2 = result[i + 1];
if (a2) {
a1.endTicks = a2.startTicks;
a1.numPixels = a2.bbox.x - a1.bbox.x;
if (a2.yPos !== a1.yPos || a2.bbox.x < a1.bbox.x) {
// a1.numPixels = a1.bbox.width
a1.numPixels = a1.bboxMeasure.x + a1.bboxMeasure.width - a1.bbox.x;
}
a1.nextAnchor = a2;
} else {
a1.endTicks = lastTicks;
a1.numPixels = a1.bboxMeasure.x + a1.bboxMeasure.width - a1.bbox.x;
// create a dummy anchor
a1.nextAnchor = { ...a1, endTicks: lastTicks, measureNumber: a1.measureNumber + 1 };
}
const diffTicks = a1.endTicks - a1.startTicks;
a1.pixelsPerTick = a1.numPixels / diffTicks;
a1.numTicks = a1.endTicks - a1.startTicks;
}
if (loops.length) {
// console.log("optimize for loops", loops);
for (let i = 0; i < loops.length; i++) {
const { startBar, endBar } = loops[i];
for (let j = 0; j < result.length; j++) {
const anchor = result[j];
const prevAnchor = result[j - 1];
const nextAnchor = result[j + 1];
// if (anchor.measureNumber === start) {
// anchor.numPixels = anchor.bboxMeasure.x + anchor.bboxMeasure.width - anchor.bbox.x;
// anchor.pixelsPerTick = anchor.numPixels / (anchor.endTicks - anchor.startTicks);
// } else if (anchor.measureNumber === end && nextAnchor && nextAnchor.measureNumber === end + 1) {
// if (anchor.measureNumber === start && prevAnchor && prevAnchor.measureNumber === start - 1) {
// anchor.startTicks = measureStartTicks[anchor.measureNumber];
// anchor.bbox.x = anchor.bboxMeasure.x;
// anchor.numPixels += anchor.bbox.x - anchor.bboxMeasure.x;
// anchor.pixelsPerTick = anchor.numPixels / (anchor.endTicks - anchor.startTicks);
// }
if (anchor.measureNumber === endBar && nextAnchor && nextAnchor.measureNumber === endBar + 1) {
// console.log("update last anchor", anchor);
// anchor.endTicks = measureStartTicks[nextAnchor.measureNumber - 1];
anchor.numPixels = anchor.bboxMeasure.x + anchor.bboxMeasure.width - anchor.bbox.x;
anchor.pixelsPerTick = anchor.numPixels / (anchor.endTicks - anchor.startTicks);
}
}
}
}
// result.forEach(d => {
// console.log(d.measureNumber, d.startTicks, d.numPixels);
// });
return { anchorData: result, measureStartTicks: result1, upbeat };
};