dedupe .pianoroll
This commit is contained in:
parent
6f6def34f7
commit
038e6c312b
1 changed files with 12 additions and 133 deletions
|
|
@ -29,138 +29,26 @@ const getValue = (e) => {
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Pattern.prototype.pianoroll = function ({
|
Pattern.prototype.pianoroll = function (options = {}) {
|
||||||
cycles = 4,
|
let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false } = options;
|
||||||
playhead = 0.5,
|
|
||||||
overscan = 1,
|
|
||||||
flipTime = 0,
|
|
||||||
flipValues = 0,
|
|
||||||
hideNegative = false,
|
|
||||||
// inactive = '#C9E597',
|
|
||||||
// inactive = '#FFCA28',
|
|
||||||
inactive = '#7491D2',
|
|
||||||
active = '#FFCA28',
|
|
||||||
// background = '#2A3236',
|
|
||||||
background = 'transparent',
|
|
||||||
smear = 0,
|
|
||||||
playheadColor = 'white',
|
|
||||||
minMidi = 10,
|
|
||||||
maxMidi = 90,
|
|
||||||
autorange = 0,
|
|
||||||
timeframe: timeframeProp,
|
|
||||||
fold = 0,
|
|
||||||
vertical = 0,
|
|
||||||
labels = 0,
|
|
||||||
} = {}) {
|
|
||||||
const ctx = getDrawContext();
|
|
||||||
const w = ctx.canvas.width;
|
|
||||||
const h = ctx.canvas.height;
|
|
||||||
let from = -cycles * playhead;
|
let from = -cycles * playhead;
|
||||||
let to = cycles * (1 - playhead);
|
let to = cycles * (1 - playhead);
|
||||||
|
|
||||||
if (timeframeProp) {
|
|
||||||
console.warn('timeframe is deprecated! use from/to instead');
|
|
||||||
from = 0;
|
|
||||||
to = timeframeProp;
|
|
||||||
}
|
|
||||||
const timeAxis = vertical ? h : w;
|
|
||||||
const valueAxis = vertical ? w : h;
|
|
||||||
let timeRange = vertical ? [timeAxis, 0] : [0, timeAxis]; // pixel range for time
|
|
||||||
const timeExtent = to - from; // number of seconds that fit inside the canvas frame
|
|
||||||
const valueRange = vertical ? [0, valueAxis] : [valueAxis, 0]; // pixel range for values
|
|
||||||
let valueExtent = maxMidi - minMidi + 1; // number of "slots" for values, overwritten if autorange true
|
|
||||||
let barThickness = valueAxis / valueExtent; // pixels per value, overwritten if autorange true
|
|
||||||
let foldValues = [];
|
|
||||||
flipTime && timeRange.reverse();
|
|
||||||
flipValues && valueRange.reverse();
|
|
||||||
|
|
||||||
this.draw(
|
this.draw(
|
||||||
(ctx, events, t) => {
|
(ctx, haps, t) => {
|
||||||
ctx.fillStyle = background;
|
|
||||||
ctx.globalAlpha = 1; // reset!
|
|
||||||
if (!smear) {
|
|
||||||
ctx.clearRect(0, 0, w, h);
|
|
||||||
ctx.fillRect(0, 0, w, h);
|
|
||||||
}
|
|
||||||
const inFrame = (event) =>
|
const inFrame = (event) =>
|
||||||
(!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.endClipped >= t + from;
|
(!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.endClipped >= t + from;
|
||||||
events.filter(inFrame).forEach((event) => {
|
pianoroll({
|
||||||
const isActive = event.whole.begin <= t && event.endClipped > t;
|
...options,
|
||||||
ctx.fillStyle = event.context?.color || inactive;
|
time: t,
|
||||||
ctx.strokeStyle = event.context?.color || active;
|
ctx,
|
||||||
ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1;
|
haps: haps.filter(inFrame),
|
||||||
const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange);
|
|
||||||
let durationPx = scale(event.duration / timeExtent, 0, timeAxis);
|
|
||||||
const value = getValue(event);
|
|
||||||
const valuePx = scale(
|
|
||||||
fold ? foldValues.indexOf(value) / foldValues.length : (Number(value) - minMidi) / valueExtent,
|
|
||||||
...valueRange,
|
|
||||||
);
|
|
||||||
let margin = 0;
|
|
||||||
const offset = scale(t / timeExtent, ...timeRange);
|
|
||||||
let coords;
|
|
||||||
if (vertical) {
|
|
||||||
coords = [
|
|
||||||
valuePx + 1 - (flipValues ? barThickness : 0), // x
|
|
||||||
timeAxis - offset + timePx + margin + 1 - (flipTime ? 0 : durationPx), // y
|
|
||||||
barThickness - 2, // width
|
|
||||||
durationPx - 2, // height
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
coords = [
|
|
||||||
timePx - offset + margin + 1 - (flipTime ? durationPx : 0), // x
|
|
||||||
valuePx + 1 - (flipValues ? 0 : barThickness), // y
|
|
||||||
durationPx - 2, // widith
|
|
||||||
barThickness - 2, // height
|
|
||||||
];
|
|
||||||
}
|
|
||||||
isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords);
|
|
||||||
if (labels) {
|
|
||||||
const label = event.value.note ?? event.value.s + (event.value.n ? `:${event.value.n}` : '');
|
|
||||||
ctx.font = `${barThickness * 0.75}px monospace`;
|
|
||||||
ctx.strokeStyle = 'black';
|
|
||||||
ctx.fillStyle = isActive ? 'white' : 'black';
|
|
||||||
ctx.textBaseline = 'top';
|
|
||||||
ctx.fillText(label, ...coords);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
ctx.globalAlpha = 1; // reset!
|
|
||||||
const playheadPosition = scale(-from / timeExtent, ...timeRange);
|
|
||||||
// draw playhead
|
|
||||||
ctx.strokeStyle = playheadColor;
|
|
||||||
ctx.beginPath();
|
|
||||||
if (vertical) {
|
|
||||||
ctx.moveTo(0, playheadPosition);
|
|
||||||
ctx.lineTo(valueAxis, playheadPosition);
|
|
||||||
} else {
|
|
||||||
ctx.moveTo(playheadPosition, 0);
|
|
||||||
ctx.lineTo(playheadPosition, valueAxis);
|
|
||||||
}
|
|
||||||
ctx.stroke();
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: from - overscan,
|
from: from - overscan,
|
||||||
to: to + overscan,
|
to: to + overscan,
|
||||||
onQuery: (events) => {
|
|
||||||
const { min, max, values } = events.reduce(
|
|
||||||
({ min, max, values }, e) => {
|
|
||||||
const v = getValue(e);
|
|
||||||
return {
|
|
||||||
min: v < min ? v : min,
|
|
||||||
max: v > max ? v : max,
|
|
||||||
values: values.includes(v) ? values : [...values, v],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{ min: Infinity, max: -Infinity, values: [] },
|
|
||||||
);
|
|
||||||
if (autorange) {
|
|
||||||
minMidi = min;
|
|
||||||
maxMidi = max;
|
|
||||||
valueExtent = maxMidi - minMidi + 1;
|
|
||||||
}
|
|
||||||
foldValues = values.sort((a, b) => String(a).localeCompare(String(b)));
|
|
||||||
barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -191,7 +79,7 @@ export function pianoroll({
|
||||||
fold = 0,
|
fold = 0,
|
||||||
vertical = 0,
|
vertical = 0,
|
||||||
labels = false,
|
labels = false,
|
||||||
fill,
|
fill = 1,
|
||||||
fillActive = false,
|
fillActive = false,
|
||||||
strokeActive = true,
|
strokeActive = true,
|
||||||
stroke,
|
stroke,
|
||||||
|
|
@ -241,7 +129,6 @@ export function pianoroll({
|
||||||
// foldValues = values.sort((a, b) => a - b);
|
// foldValues = values.sort((a, b) => a - b);
|
||||||
foldValues = values.sort((a, b) => String(a).localeCompare(String(b)));
|
foldValues = values.sort((a, b) => String(a).localeCompare(String(b)));
|
||||||
barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent;
|
barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent;
|
||||||
|
|
||||||
ctx.fillStyle = background;
|
ctx.fillStyle = background;
|
||||||
ctx.globalAlpha = 1; // reset!
|
ctx.globalAlpha = 1; // reset!
|
||||||
if (!smear) {
|
if (!smear) {
|
||||||
|
|
@ -251,7 +138,7 @@ export function pianoroll({
|
||||||
haps.forEach((event) => {
|
haps.forEach((event) => {
|
||||||
const isActive = event.whole.begin <= time && event.endClipped > time;
|
const isActive = event.whole.begin <= time && event.endClipped > time;
|
||||||
let strokeCurrent = stroke ?? (strokeActive && isActive);
|
let strokeCurrent = stroke ?? (strokeActive && isActive);
|
||||||
let fillCurrent = fill ?? (fillActive && isActive);
|
let fillCurrent = (!isActive && fill) || (isActive && fillActive);
|
||||||
if (hideInactive && !isActive) {
|
if (hideInactive && !isActive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -304,19 +191,11 @@ export function pianoroll({
|
||||||
const customLabel = isActive ? activeLabel || inactiveLabel : inactiveLabel;
|
const customLabel = isActive ? activeLabel || inactiveLabel : inactiveLabel;
|
||||||
const label = customLabel ?? defaultLabel;
|
const label = customLabel ?? defaultLabel;
|
||||||
let measure = vertical ? durationPx : barThickness * 0.75;
|
let measure = vertical ? durationPx : barThickness * 0.75;
|
||||||
ctx.font = `${measure}px ${fontFamily}`;
|
ctx.font = `${measure}px ${fontFamily || 'monospace'}`;
|
||||||
//ctx.strokeStyle = 'white';
|
|
||||||
//ctx.lineWidth = 2;
|
|
||||||
// font color
|
// font color
|
||||||
ctx.fillStyle = /* isActive && */ !fillCurrent ? color : 'black';
|
ctx.fillStyle = /* isActive && */ !fillCurrent ? color : 'black';
|
||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
//ctx.strokeText(label, ...coords);
|
|
||||||
|
|
||||||
/* ctx.translate(coords[0], coords[1]);
|
|
||||||
ctx.rotate(Math.PI * 4); */
|
|
||||||
|
|
||||||
ctx.fillText(label, ...coords);
|
ctx.fillText(label, ...coords);
|
||||||
//ctx.setTransform(1, 0, 0, 1, 0, 0); // Sets the identity matrix
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ctx.globalAlpha = 1; // reset!
|
ctx.globalAlpha = 1; // reset!
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue