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/8] 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 b6ba5d5c..8e2d12b7 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 24f7434a..6f526ec8 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 34914664..07480305 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/8] 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 8e2d12b7..5fd83bce 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 07480305..b57d319a 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`] = ` [ From df923174ede5ba5df5d54db5a22d90ff05778e1f Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 3 Jun 2025 01:22:37 +0100 Subject: [PATCH 3/8] working --- packages/core/pattern.mjs | 19 ++++++++++++ test/__snapshots__/examples.test.mjs.snap | 37 +++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 56a6b77d..efbae1f3 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -3192,6 +3192,25 @@ export const slice = register( false, // turns off auto-patternification ); +/** + * + * make something happen on event time + * uses browser timeout which is innacurate for audio tasks + * @name onTriggerTime + * @memberof Pattern + * @returns Pattern + * @example + * s("bd!8").onTriggerTime((hap) => {console.info(hap)}) + */ +Pattern.prototype.onTriggerTime = function (func) { + return this.onTrigger((t_deprecate, hap, currentTime, cps = 1, targetTime) => { + const diff = targetTime - currentTime; + window.setTimeout(() => { + func(hap); + }, diff * 1000); + }, false); +}; + /** * Works the same as slice, but changes the playback speed of each slice to match the duration of its step. * @name splice diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 34914664..2ee135b9 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -5950,6 +5950,43 @@ exports[`runs examples > example "often" example index 0 1`] = ` ] `; +exports[`runs examples > example "onTriggerTime" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:bd ]", + "[ 1/8 → 1/4 | s:bd ]", + "[ 1/4 → 3/8 | s:bd ]", + "[ 3/8 → 1/2 | s:bd ]", + "[ 1/2 → 5/8 | s:bd ]", + "[ 5/8 → 3/4 | s:bd ]", + "[ 3/4 → 7/8 | s:bd ]", + "[ 7/8 → 1/1 | s:bd ]", + "[ 1/1 → 9/8 | s:bd ]", + "[ 9/8 → 5/4 | s:bd ]", + "[ 5/4 → 11/8 | s:bd ]", + "[ 11/8 → 3/2 | s:bd ]", + "[ 3/2 → 13/8 | s:bd ]", + "[ 13/8 → 7/4 | s:bd ]", + "[ 7/4 → 15/8 | s:bd ]", + "[ 15/8 → 2/1 | s:bd ]", + "[ 2/1 → 17/8 | s:bd ]", + "[ 17/8 → 9/4 | s:bd ]", + "[ 9/4 → 19/8 | s:bd ]", + "[ 19/8 → 5/2 | s:bd ]", + "[ 5/2 → 21/8 | s:bd ]", + "[ 21/8 → 11/4 | s:bd ]", + "[ 11/4 → 23/8 | s:bd ]", + "[ 23/8 → 3/1 | s:bd ]", + "[ 3/1 → 25/8 | s:bd ]", + "[ 25/8 → 13/4 | s:bd ]", + "[ 13/4 → 27/8 | s:bd ]", + "[ 27/8 → 7/2 | s:bd ]", + "[ 7/2 → 29/8 | s:bd ]", + "[ 29/8 → 15/4 | s:bd ]", + "[ 15/4 → 31/8 | s:bd ]", + "[ 31/8 → 4/1 | s:bd ]", +] +`; + exports[`runs examples > example "orbit" example index 0 1`] = ` [ "[ 0/1 → 1/6 | s:hh delay:0.5 delaytime:0.25 orbit:1 ]", From 2c9f9d785f13e76d5c7304372d0e8c9f4df615d1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 12 Jun 2025 08:39:27 +0200 Subject: [PATCH 4/8] hotfix: close bakery --- website/src/config.ts | 2 +- website/src/repl/components/Header.jsx | 8 ++-- .../src/repl/components/panel/PatternsTab.jsx | 37 +++++++++++-------- 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/website/src/config.ts b/website/src/config.ts index 2f499d55..e88b7f3e 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -58,7 +58,7 @@ export const SIDEBAR: Sidebar = { { text: 'What is Strudel?', link: 'workshop/getting-started' }, { text: 'Showcase', link: 'intro/showcase' }, { text: 'Blog', link: 'blog' }, - { text: 'Community Bakery', link: 'bakery' }, + /* { text: 'Community Bakery', link: 'bakery' }, */ ], Workshop: [ // { text: 'Getting Started', link: 'workshop/getting-started' }, diff --git a/website/src/repl/components/Header.jsx b/website/src/repl/components/Header.jsx index 2963a5f9..a97e2bed 100644 --- a/website/src/repl/components/Header.jsx +++ b/website/src/repl/components/Header.jsx @@ -93,7 +93,7 @@ export function Header({ context, embedded = false }) { > {!isEmbedded && update} - {!isEmbedded && ( + {/* !isEmbedded && ( - )} - {!isEmbedded && ( + ) */} + {/* !isEmbedded && ( - )} + ) */} {!isEmbedded && (
- {patternFilter === patternFilterName.user && ( - - updateCodeWindow( - context, - { ...userPatterns[id], collection: userPattern.collection }, - autoResetPatternOnChange, - ) - } - patterns={userPatterns} - started={context.started} - activePattern={activePattern} - viewingPatternID={viewingPatternID} - /> - )} + {/* {patternFilter === patternFilterName.user && ( */} + + updateCodeWindow( + context, + { ...userPatterns[id], collection: userPattern.collection }, + autoResetPatternOnChange, + ) + } + patterns={userPatterns} + started={context.started} + activePattern={activePattern} + viewingPatternID={viewingPatternID} + /> + {/* )} */}
); @@ -250,6 +250,11 @@ export function PatternsTab({ context }) { const { patternFilter } = useSettings(); return ( +
+ +
+ ); + /* return (
)}
- ); + ); */ } From 79f7938b3381b426e85b67ec163abaee5423ed92 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 12 Jun 2025 08:51:03 +0200 Subject: [PATCH 5/8] hotfix: add default code --- website/src/repl/useReplContext.jsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index f88e5a6e..708506cc 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -59,6 +59,8 @@ async function getModule(name) { return modules.find((m) => m.packageName === name); } +const initialCode = `$: s("[bd ]*2").bank("tr909").dec(.4)`; + export function useReplContext() { const { isSyncEnabled, audioEngineTarget } = useSettings(); const shouldUseWebaudio = audioEngineTarget !== audioEngineTargets.osc; @@ -77,7 +79,7 @@ export function useReplContext() { transpiler, autodraw: false, root: containerRef.current, - initialCode: '// LOADING', + initialCode, pattern: silence, drawTime, drawContext, From 40ba5d44652d18dc84bcc4d9f9d542f18d573551 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 12 Jun 2025 09:11:19 +0200 Subject: [PATCH 6/8] hotfix: don't load patterns from db --- website/src/repl/util.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index f49dd568..9db280da 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -7,7 +7,7 @@ import './Repl.css'; import { createClient } from '@supabase/supabase-js'; import { nanoid } from 'nanoid'; import { writeText } from '@tauri-apps/plugin-clipboard-manager'; -import { $featuredPatterns, loadDBPatterns } from '@src/user_pattern_utils.mjs'; +import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs'; // Create a single supabase client for interacting with your database export const supabase = createClient( @@ -16,9 +16,9 @@ export const supabase = createClient( ); let dbLoaded; -if (typeof window !== 'undefined') { +/* if (typeof window !== 'undefined') { dbLoaded = loadDBPatterns(); -} +} */ export async function initCode() { // load code from url hash (either short hash from database or decode long hash) From ef3b9a68e362218bef0a79f0954fad5fa6a38aae Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 12 Jun 2025 09:21:57 +0200 Subject: [PATCH 7/8] hotfix: leave out part about shuffle in welcome tab --- website/src/repl/components/panel/WelcomeTab.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/src/repl/components/panel/WelcomeTab.jsx b/website/src/repl/components/panel/WelcomeTab.jsx index 1049a581..a43f02ad 100644 --- a/website/src/repl/components/panel/WelcomeTab.jsx +++ b/website/src/repl/components/panel/WelcomeTab.jsx @@ -15,17 +15,18 @@ export function WelcomeTab({ context }) {
1. hit play - 2. change something -{' '} 3. hit update -
- If you don't like what you hear, try shuffle! + {/*
+ If you don't like what you hear, try shuffle! */}

- To learn more about what this all means, check out the{' '} + {/* To learn more about what this all means, check out the{' '} */} + To get started, check out the interactive tutorial . Also feel free to join the{' '} - tidalcycles discord channel + discord channel {' '} to ask any questions, give feedback or just say hello.

From 694a22af85494444c3d07c3c48da48d20afbe390 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 12 Jun 2025 09:51:04 +0200 Subject: [PATCH 8/8] hotfix: bring back loading code + fill in default pattern later --- website/src/repl/useReplContext.jsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index 708506cc..36e8099c 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -59,7 +59,7 @@ async function getModule(name) { return modules.find((m) => m.packageName === name); } -const initialCode = `$: s("[bd ]*2").bank("tr909").dec(.4)`; +const initialCode = `// LOADING`; export function useReplContext() { const { isSyncEnabled, audioEngineTarget } = useSettings(); @@ -135,9 +135,10 @@ export function useReplContext() { code = latestCode; msg = `Your last session has been loaded!`; } else { - const { code: randomTune, name } = await getRandomTune(); - code = randomTune; - msg = `A random code snippet named "${name}" has been loaded!`; + /* const { code: randomTune, name } = await getRandomTune(); + code = randomTune; */ + code = '$: s("[bd ]*2").bank("tr909").dec(.4)'; + msg = `Default code has been loaded`; } editor.setCode(code); setDocumentTitle(code);