From 038e6c312b9aeef74893812f4dbae322a5e1f619 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 27 Aug 2023 16:05:58 +0200 Subject: [PATCH] dedupe .pianoroll --- packages/core/pianoroll.mjs | 145 +++--------------------------------- 1 file changed, 12 insertions(+), 133 deletions(-) diff --git a/packages/core/pianoroll.mjs b/packages/core/pianoroll.mjs index 1592ab96..f3d2c38a 100644 --- a/packages/core/pianoroll.mjs +++ b/packages/core/pianoroll.mjs @@ -29,138 +29,26 @@ const getValue = (e) => { return value; }; -Pattern.prototype.pianoroll = function ({ - cycles = 4, - 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; +Pattern.prototype.pianoroll = function (options = {}) { + let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false } = options; + let from = -cycles * 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( - (ctx, events, t) => { - ctx.fillStyle = background; - ctx.globalAlpha = 1; // reset! - if (!smear) { - ctx.clearRect(0, 0, w, h); - ctx.fillRect(0, 0, w, h); - } + (ctx, haps, t) => { const inFrame = (event) => (!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.endClipped >= t + from; - events.filter(inFrame).forEach((event) => { - const isActive = event.whole.begin <= t && event.endClipped > t; - ctx.fillStyle = event.context?.color || inactive; - ctx.strokeStyle = event.context?.color || active; - ctx.globalAlpha = event.context.velocity ?? event.value?.gain ?? 1; - 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); - } + pianoroll({ + ...options, + time: t, + ctx, + haps: haps.filter(inFrame), }); - 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, 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; @@ -191,7 +79,7 @@ export function pianoroll({ fold = 0, vertical = 0, labels = false, - fill, + fill = 1, fillActive = false, strokeActive = true, stroke, @@ -241,7 +129,6 @@ export function pianoroll({ // foldValues = values.sort((a, b) => a - b); foldValues = values.sort((a, b) => String(a).localeCompare(String(b))); barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent; - ctx.fillStyle = background; ctx.globalAlpha = 1; // reset! if (!smear) { @@ -251,7 +138,7 @@ export function pianoroll({ haps.forEach((event) => { const isActive = event.whole.begin <= time && event.endClipped > time; let strokeCurrent = stroke ?? (strokeActive && isActive); - let fillCurrent = fill ?? (fillActive && isActive); + let fillCurrent = (!isActive && fill) || (isActive && fillActive); if (hideInactive && !isActive) { return; } @@ -304,19 +191,11 @@ export function pianoroll({ const customLabel = isActive ? activeLabel || inactiveLabel : inactiveLabel; const label = customLabel ?? defaultLabel; let measure = vertical ? durationPx : barThickness * 0.75; - ctx.font = `${measure}px ${fontFamily}`; - //ctx.strokeStyle = 'white'; - //ctx.lineWidth = 2; + ctx.font = `${measure}px ${fontFamily || 'monospace'}`; // font color ctx.fillStyle = /* isActive && */ !fillCurrent ? color : 'black'; ctx.textBaseline = 'top'; - //ctx.strokeText(label, ...coords); - - /* ctx.translate(coords[0], coords[1]); - ctx.rotate(Math.PI * 4); */ - ctx.fillText(label, ...coords); - //ctx.setTransform(1, 0, 0, 1, 0, 0); // Sets the identity matrix } }); ctx.globalAlpha = 1; // reset!