From 1ddf9687503e2d56bff1fc2224ba60a4c1f03530 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Jul 2022 22:26:14 +0200 Subject: [PATCH 1/5] fix: draw playhead only once --- packages/tone/pianoroll.mjs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/tone/pianoroll.mjs b/packages/tone/pianoroll.mjs index 810dbb1e..ba116721 100644 --- a/packages/tone/pianoroll.mjs +++ b/packages/tone/pianoroll.mjs @@ -58,7 +58,6 @@ Pattern.prototype.pianoroll = function ({ flipTime && timeRange.reverse(); flipValues && valueRange.reverse(); - const playheadPosition = scale(-from / timeExtent, ...timeRange); this.draw( (ctx, events, t) => { ctx.fillStyle = background; @@ -71,15 +70,6 @@ Pattern.prototype.pianoroll = function ({ ctx.fillStyle = event.context?.color || inactive; ctx.strokeStyle = event.context?.color || active; ctx.globalAlpha = event.context.velocity ?? 1; - ctx.beginPath(); - if (vertical) { - ctx.moveTo(0, playheadPosition); - ctx.lineTo(valueAxis, playheadPosition); - } else { - ctx.moveTo(playheadPosition, 0); - ctx.lineTo(playheadPosition, valueAxis); - } - ctx.stroke(); const timePx = scale((event.whole.begin - (flipTime ? to : from)) / timeExtent, ...timeRange); let durationPx = scale(event.duration / timeExtent, 0, timeAxis); const value = getValue(event); @@ -107,6 +97,18 @@ Pattern.prototype.pianoroll = function ({ } isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords); }); + const playheadPosition = scale(-from / timeExtent, ...timeRange); + // draw playhead + ctx.strokeStyle = 'white'; + 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 f38718004f4f8ad7e42f652b43b572766df31630 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 28 Jul 2022 22:27:54 +0200 Subject: [PATCH 2/5] pianoroll optimizations - fix flickering due to global alpha mutation - change default inactive color - add smear option - add playheadColor option --- packages/tone/pianoroll.mjs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/tone/pianoroll.mjs b/packages/tone/pianoroll.mjs index ba116721..7f46785d 100644 --- a/packages/tone/pianoroll.mjs +++ b/packages/tone/pianoroll.mjs @@ -22,10 +22,14 @@ Pattern.prototype.pianoroll = function ({ flipTime = 0, flipValues = 0, hideNegative = false, - inactive = '#C9E597', + // inactive = '#C9E597', + // inactive = '#FFCA28', + inactive = '#7491D2', active = '#FFCA28', // background = '#2A3236', background = 'transparent', + smear = 0, + playheadColor = 'white', minMidi = 10, maxMidi = 90, autorange = 0, @@ -61,8 +65,11 @@ Pattern.prototype.pianoroll = function ({ this.draw( (ctx, events, t) => { ctx.fillStyle = background; - ctx.clearRect(0, 0, w, h); - ctx.fillRect(0, 0, w, h); + ctx.globalAlpha = 1; // reset! + if (!smear) { + ctx.clearRect(0, 0, w, h); + ctx.fillRect(0, 0, w, h); + } const inFrame = (event) => (!hideNegative || event.whole.begin >= 0) && event.whole.begin <= t + to && event.whole.end >= t + from; events.filter(inFrame).forEach((event) => { @@ -97,9 +104,10 @@ Pattern.prototype.pianoroll = function ({ } isActive ? ctx.strokeRect(...coords) : ctx.fillRect(...coords); }); + ctx.globalAlpha = 1; // reset! const playheadPosition = scale(-from / timeExtent, ...timeRange); // draw playhead - ctx.strokeStyle = 'white'; + ctx.strokeStyle = playheadColor; ctx.beginPath(); if (vertical) { ctx.moveTo(0, playheadPosition); From d79716c3f8486a1f9550bc4b3e548d19b33b443d Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 30 Jul 2022 09:27:43 +0100 Subject: [PATCH 3/5] add chooseInWith/chooseCycles --- packages/core/signal.mjs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index b3eb2d3b..98d75591 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -134,6 +134,19 @@ export const chooseWith = (pat, xs) => { export const choose = (...xs) => chooseWith(rand, xs); +export const chooseInWith = (pat, xs) => { + xs = xs.map(reify); + if (xs.length == 0) { + return silence; + } + return pat + .range(0, xs.length) + .fmap((i) => xs[Math.floor(i)]) + .innerJoin(); +}; + +export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs); + const _wchooseWith = function (pat, ...pairs) { const values = pairs.map((pair) => reify(pair[0])); const weights = []; From 7fbd4527e5adb7fe02864b493ab33867df01895b Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 30 Jul 2022 22:24:26 +0100 Subject: [PATCH 4/5] chooseInWith, choose2, randcat, and some reformatting --- packages/core/signal.mjs | 72 +++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 98d75591..cc59849f 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -47,7 +47,6 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t)); */ export const sine = sine2._fromBipolar(); - /** * A cosine signal between 0 and 1. * @@ -59,7 +58,6 @@ export const sine = sine2._fromBipolar(); export const cosine = sine._early(Fraction(1).div(4)); export const cosine2 = sine2._early(Fraction(1).div(4)); - /** * A square signal between 0 and 1. * @@ -112,7 +110,14 @@ const timeToRandsPrime = (seed, n) => { const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n); +/** + * A continuous pattern of random numbers, between 0 and 1 + */ export const rand = signal(timeToRand); +/** + * A continuous pattern of random numbers, between -1 and 1 + */ + export const rand2 = rand._toBipolar(); export const _brandBy = (p) => rand.fmap((x) => x < p); export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); @@ -121,32 +126,67 @@ export const brand = _brandBy(0.5); export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i)); export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin(); -export const chooseWith = (pat, xs) => { +export const __chooseWith = (pat, xs) => { xs = xs.map(reify); if (xs.length == 0) { return silence; } - return pat - .range(0, xs.length) - .fmap((i) => xs[Math.floor(i)]) - .outerJoin(); + return pat.range(0, xs.length).fmap((i) => xs[Math.floor(i)]); +}; +/** + * Choose from the list of values (or patterns of values) using the given + * pattern of numbers, which should be in the range of 0..1 + * @param {Pattern} pat + * @param {*} xs + * @returns + */ +export const chooseWith = (pat, xs) => { + return __chooseWith(pat, xs).outerJoin(); }; +/** + * As with {chooseWith}, but the structure comes from the chosen values, rather + * than the pattern you're using to choose with. + * @param {Pattern} pat + * @param {*} xs + * @returns + */ +export const chooseInWith = (pat, xs) => { + return __chooseWith(pat, xs).innerJoin(); +}; + +/** + * Chooses randomly from the given list of values. + * @param {...any} xs + * @returns {Pattern} - a continuous pattern. + */ export const choose = (...xs) => chooseWith(rand, xs); -export const chooseInWith = (pat, xs) => { - xs = xs.map(reify); - if (xs.length == 0) { - return silence; - } - return pat - .range(0, xs.length) - .fmap((i) => xs[Math.floor(i)]) - .innerJoin(); +/** + * Chooses from the given list of values (or patterns of values), according + * to the pattern that the method is called on. The pattern should be in + * the range 0 .. 1. + * @param {...any} xs + * @returns {Pattern} + */ +Pattern.prototype.choose = function (...xs) { + return chooseWith(this, xs); +}; + +/** + * As with choose, but the pattern that this method is called on should be + * in the range -1 .. 1 + * @param {...any} xs + * @returns {Pattern} + */ + Pattern.prototype.choose2 = function (...xs) { + return chooseWith(this._fromBipolar(), xs); }; export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs); +export const randcat = chooseCycles; + const _wchooseWith = function (pat, ...pairs) { const values = pairs.map((pair) => reify(pair[0])); const weights = []; From f5efa18e96da62102f600349370f41d370ed4a96 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 2 Aug 2022 22:56:04 +0200 Subject: [PATCH 5/5] fix: jsdoc comments --- packages/core/signal.mjs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index cc59849f..a13674f1 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -117,7 +117,7 @@ export const rand = signal(timeToRand); /** * A continuous pattern of random numbers, between -1 and 1 */ - export const rand2 = rand._toBipolar(); +export const rand2 = rand._toBipolar(); export const _brandBy = (p) => rand.fmap((x) => x < p); export const brandBy = (pPat) => reify(pPat).fmap(_brandBy).innerJoin(); @@ -136,9 +136,9 @@ export const __chooseWith = (pat, xs) => { /** * Choose from the list of values (or patterns of values) using the given * pattern of numbers, which should be in the range of 0..1 - * @param {Pattern} pat - * @param {*} xs - * @returns + * @param {Pattern} pat + * @param {*} xs + * @returns {Pattern} */ export const chooseWith = (pat, xs) => { return __chooseWith(pat, xs).outerJoin(); @@ -147,9 +147,9 @@ export const chooseWith = (pat, xs) => { /** * As with {chooseWith}, but the structure comes from the chosen values, rather * than the pattern you're using to choose with. - * @param {Pattern} pat - * @param {*} xs - * @returns + * @param {Pattern} pat + * @param {*} xs + * @returns {Pattern} */ export const chooseInWith = (pat, xs) => { return __chooseWith(pat, xs).innerJoin(); @@ -179,7 +179,7 @@ Pattern.prototype.choose = function (...xs) { * @param {...any} xs * @returns {Pattern} */ - Pattern.prototype.choose2 = function (...xs) { +Pattern.prototype.choose2 = function (...xs) { return chooseWith(this._fromBipolar(), xs); };