From e0a21a936bad89b1b8cf349fcde5b9a596122250 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 2 Jun 2025 12:57:37 +0100 Subject: [PATCH 1/2] berlin --- packages/core/signal.mjs | 43 ++++++++++++++++++++--- packages/superdough/worklets.mjs | 1 - test/__snapshots__/examples.test.mjs.snap | 2 ++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index b6ba5d5cc..8e2d12b74 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -486,13 +486,34 @@ export const wchooseCycles = (...pairs) => _wchooseWith(rand.segment(1), ...pair export const wrandcat = wchooseCycles; -// this function expects pat to be a pattern of floats... -export const perlinWith = (pat) => { - const pata = pat.fmap(Math.floor); - const patb = pat.fmap((t) => Math.floor(t) + 1); +function _perlin(t) { + let ta = Math.floor(t); + let tb = ta + 1; const smootherStep = (x) => 6.0 * x ** 5 - 15.0 * x ** 4 + 10.0 * x ** 3; const interp = (x) => (a) => (b) => a + smootherStep(x) * (b - a); - return pat.sub(pata).fmap(interp).appBoth(pata.fmap(timeToRand)).appBoth(patb.fmap(timeToRand)); + const v = interp(t - ta)(timeToRand(ta))(timeToRand(tb)); + return v; +} +export const perlinWith = (tpat) => { + return tpat.fmap(_perlin); +}; + +function _berlin(t) { + const prevRidgeStartIndex = Math.floor(t); + const nextRidgeStartIndex = prevRidgeStartIndex + 1; + + const prevRidgeBottomPoint = timeToRand(prevRidgeStartIndex); + const nextRidgeTopPoint = timeToRand(nextRidgeStartIndex) + prevRidgeBottomPoint; + + const currentPercent = (t - prevRidgeStartIndex) / (nextRidgeStartIndex - prevRidgeStartIndex); + const interp = (a, b, t) => { + return a + (b - a) * t; + }; + return interp(prevRidgeBottomPoint, nextRidgeTopPoint, currentPercent) / 2; +} + +export const berlinWith = (tpat) => { + return tpat.fmap(_berlin); }; /** @@ -506,6 +527,18 @@ export const perlinWith = (pat) => { */ export const perlin = perlinWith(time.fmap((v) => Number(v))); +/** + * Generates a continuous pattern of [berlin noise](conceived by Jame Coyne and Jade Rowland as a joke but turned out to be surprisingly cool and useful, + * like perlin noise but with sawtooth waves), in the range 0..1. + * + * @name berlin + * @example + * // ascending arpeggios + * $: n("0!16".add(berlin.fast(4).mul(14))).scale("d:minor")._pianoroll() + * + */ +export const berlin = berlinWith(time.fmap((v) => Number(v))); + export const degradeByWith = register( 'degradeByWith', (withPat, x, pat) => pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)), diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 24f7434a9..6f526ec8f 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -882,7 +882,6 @@ class ByteBeatProcessor extends AudioWorkletProcessor { const funcValue = this.func(local_t); let signal = (funcValue & 255) / 127.5 - 1; const out = signal * 0.2; - for (let c = 0; c < output.length; c++) { //prevent speaker blowout via clipping if threshold exceeds output[c][i] = clamp(out, -0.4, 0.4); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 34914664d..074803057 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1051,6 +1051,8 @@ exports[`runs examples > example "begin" example index 0 1`] = ` ] `; +exports[`runs examples > example "berlin" example index 0 1`] = `[]`; + exports[`runs examples > example "binary" example index 0 1`] = ` [ "[ 0/1 → 1/3 | s:hh ]", From cde943d73712d8db15908093a8f1fac46be854f5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 2 Jun 2025 13:02:41 +0100 Subject: [PATCH 2/2] fix test --- packages/core/signal.mjs | 2 +- test/__snapshots__/examples.test.mjs.snap | 69 ++++++++++++++++++++++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 8e2d12b74..5fd83bce3 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -534,7 +534,7 @@ export const perlin = perlinWith(time.fmap((v) => Number(v))); * @name berlin * @example * // ascending arpeggios - * $: n("0!16".add(berlin.fast(4).mul(14))).scale("d:minor")._pianoroll() + * n("0!16".add(berlin.fast(4).mul(14))).scale("d:minor") * */ export const berlin = berlinWith(time.fmap((v) => Number(v))); diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 074803057..b57d319a7 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1051,7 +1051,74 @@ exports[`runs examples > example "begin" example index 0 1`] = ` ] `; -exports[`runs examples > example "berlin" example index 0 1`] = `[]`; +exports[`runs examples > example "berlin" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | note:D3 ]", + "[ 1/16 → 1/8 | note:E3 ]", + "[ 1/8 → 3/16 | note:F3 ]", + "[ 3/16 → 1/4 | note:G3 ]", + "[ 1/4 → 5/16 | note:A3 ]", + "[ 5/16 → 3/8 | note:C4 ]", + "[ 3/8 → 7/16 | note:D4 ]", + "[ 7/16 → 1/2 | note:F4 ]", + "[ 1/2 → 9/16 | note:D4 ]", + "[ 9/16 → 5/8 | note:E4 ]", + "[ 5/8 → 11/16 | note:E4 ]", + "[ 11/16 → 3/4 | note:E4 ]", + "[ 3/4 → 13/16 | note:F3 ]", + "[ 13/16 → 7/8 | note:F3 ]", + "[ 7/8 → 15/16 | note:F3 ]", + "[ 15/16 → 1/1 | note:F3 ]", + "[ 1/1 → 17/16 | note:E3 ]", + "[ 17/16 → 9/8 | note:E3 ]", + "[ 9/8 → 19/16 | note:E3 ]", + "[ 19/16 → 5/4 | note:F3 ]", + "[ 5/4 → 21/16 | note:E3 ]", + "[ 21/16 → 11/8 | note:F3 ]", + "[ 11/8 → 23/16 | note:G3 ]", + "[ 23/16 → 3/2 | note:A3 ]", + "[ 3/2 → 25/16 | note:A3 ]", + "[ 25/16 → 13/8 | note:Bb3 ]", + "[ 13/8 → 27/16 | note:Bb3 ]", + "[ 27/16 → 7/4 | note:Bb3 ]", + "[ 7/4 → 29/16 | note:F3 ]", + "[ 29/16 → 15/8 | note:G3 ]", + "[ 15/8 → 31/16 | note:Bb3 ]", + "[ 31/16 → 2/1 | note:C4 ]", + "[ 2/1 → 33/16 | note:C4 ]", + "[ 33/16 → 17/8 | note:D4 ]", + "[ 17/8 → 35/16 | note:F4 ]", + "[ 35/16 → 9/4 | note:G4 ]", + "[ 9/4 → 37/16 | note:Bb3 ]", + "[ 37/16 → 19/8 | note:Bb3 ]", + "[ 19/8 → 39/16 | note:Bb3 ]", + "[ 39/16 → 5/2 | note:C4 ]", + "[ 5/2 → 41/16 | note:F3 ]", + "[ 41/16 → 21/8 | note:F3 ]", + "[ 21/8 → 43/16 | note:G3 ]", + "[ 43/16 → 11/4 | note:A3 ]", + "[ 11/4 → 45/16 | note:A3 ]", + "[ 45/16 → 23/8 | note:A3 ]", + "[ 23/8 → 47/16 | note:A3 ]", + "[ 47/16 → 3/1 | note:A3 ]", + "[ 3/1 → 49/16 | note:E3 ]", + "[ 49/16 → 25/8 | note:G3 ]", + "[ 25/8 → 51/16 | note:Bb3 ]", + "[ 51/16 → 13/4 | note:C4 ]", + "[ 13/4 → 53/16 | note:D4 ]", + "[ 53/16 → 27/8 | note:E4 ]", + "[ 27/8 → 55/16 | note:G4 ]", + "[ 55/16 → 7/2 | note:A4 ]", + "[ 7/2 → 57/16 | note:Bb3 ]", + "[ 57/16 → 29/8 | note:Bb3 ]", + "[ 29/8 → 59/16 | note:C4 ]", + "[ 59/16 → 15/4 | note:C4 ]", + "[ 15/4 → 61/16 | note:F3 ]", + "[ 61/16 → 31/8 | note:G3 ]", + "[ 31/8 → 63/16 | note:G3 ]", + "[ 63/16 → 4/1 | note:A3 ]", +] +`; exports[`runs examples > example "binary" example index 0 1`] = ` [