From cbd448e9347f2e30c3060b277882ec122a17086f Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 24 Mar 2025 00:09:46 -0400 Subject: [PATCH 01/35] update docs --- packages/core/pattern.mjs | 16 +++++++++++----- packages/core/signal.mjs | 3 +++ .../src/repl/components/panel/SettingsTab.jsx | 18 ------------------ 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index c1e95211..4adb9bd6 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2106,10 +2106,11 @@ export const linger = register( /** * Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one. * @param {number} segments number of segments per cycle + * @synonyms seg * @example * note(saw.range(40,52).segment(24)) */ -export const segment = register('segment', function (rate, pat) { +export const {segment, seg} = register(['segment', 'seg'], function (rate, pat) { return pat.struct(pure(true)._fast(rate)).setSteps(rate); }); @@ -2485,16 +2486,21 @@ export const bypass = register( ); /** - * Loops the pattern inside at `offset` for `cycles`. + * Loops the pattern inside an `offset` for `cycles`. + * If you think of the entire span of time in cycles as a ribbon, you can grab a single piece and loop it. + * @synonym rib * @param {number} offset start point of loop in cycles * @param {number} cycles loop length in cycles * @example - * note("").ribbon(1, 2).fast(2) + * note("").ribbon(1, 2) * @example * // Looping a portion of randomness - * note(irand(8).segment(4).scale('C3 minor')).ribbon(1337, 2) + * n(irand(8).segment(4)).scale("c:pentatonic").ribbon(1337, 2) + * @example + * // rhythm generator + * s("bd!16?").ribbon(29,.5)._punchcard() */ -export const ribbon = register('ribbon', (offset, cycles, pat) => pat.early(offset).restart(pure(1).slow(cycles))); +export const {ribbon, rib} = register(['ribbon', 'rib'], (offset, cycles, pat) => pat.early(offset).restart(pure(1).slow(cycles))); export const hsla = register('hsla', (h, s, l, a, pat) => { return pat.color(`hsla(${h}turn,${s * 100}%,${l * 100}%,${a})`); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 5348173d..f09c4966 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -520,12 +520,15 @@ export const degradeByWith = register( * * @name degradeBy * @memberof Pattern + * @synonym * @param {number} amount - a number between 0 and 1 * @returns Pattern * @example * s("hh*8").degradeBy(0.2) * @example * s("[hh?0.2]*8") + * @example + * s("bd").segment(16).degradeBy(.5).ribbon(16,1)._punchcard() */ export const degradeBy = register( 'degradeBy', diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx index 4ad3cc9a..3a44cd0a 100644 --- a/website/src/repl/components/panel/SettingsTab.jsx +++ b/website/src/repl/components/panel/SettingsTab.jsx @@ -179,25 +179,7 @@ export function SettingsTab({ started }) { value={togglePanelTrigger} onChange={(value) => settingsMap.setKey('togglePanelTrigger', value)} items={{ click: 'Click', hover: 'Hover' }} - > - {/* { - if (cbEvent.target.checked) { - settingsMap.setKey('togglePanelTrigger', 'click'); - } - }} - value={togglePanelTrigger != 'hover'} /> - { - if (cbEvent.target.checked) { - settingsMap.setKey('togglePanelTrigger', 'hover'); - } - }} - value={togglePanelTrigger == 'hover'} - /> */} Date: Mon, 24 Mar 2025 00:15:36 -0400 Subject: [PATCH 02/35] update test --- packages/core/pattern.mjs | 8 +- packages/core/signal.mjs | 2 +- test/__snapshots__/examples.test.mjs.snap | 344 +++++++++++++--------- 3 files changed, 215 insertions(+), 139 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 4adb9bd6..49caeaf0 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2110,7 +2110,7 @@ export const linger = register( * @example * note(saw.range(40,52).segment(24)) */ -export const {segment, seg} = register(['segment', 'seg'], function (rate, pat) { +export const { segment, seg } = register(['segment', 'seg'], function (rate, pat) { return pat.struct(pure(true)._fast(rate)).setSteps(rate); }); @@ -2498,9 +2498,11 @@ export const bypass = register( * n(irand(8).segment(4)).scale("c:pentatonic").ribbon(1337, 2) * @example * // rhythm generator - * s("bd!16?").ribbon(29,.5)._punchcard() + * s("bd!16?").ribbon(29,.5) */ -export const {ribbon, rib} = register(['ribbon', 'rib'], (offset, cycles, pat) => pat.early(offset).restart(pure(1).slow(cycles))); +export const { ribbon, rib } = register(['ribbon', 'rib'], (offset, cycles, pat) => + pat.early(offset).restart(pure(1).slow(cycles)), +); export const hsla = register('hsla', (h, s, l, a, pat) => { return pat.color(`hsla(${h}turn,${s * 100}%,${l * 100}%,${a})`); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index f09c4966..22738ea9 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -528,7 +528,7 @@ export const degradeByWith = register( * @example * s("[hh?0.2]*8") * @example - * s("bd").segment(16).degradeBy(.5).ribbon(16,1)._punchcard() + * s("bd").segment(16).degradeBy(.5).ribbon(16,1) */ export const degradeBy = register( 'degradeBy', diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 71a26c81..80ee7344 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1,5 +1,181 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`runs examples > example "" example index 0 1`] = ` +[ + "[ 0/1 → 1/24 | note:40 ]", + "[ 1/24 → 1/12 | note:40.5 ]", + "[ 1/12 → 1/8 | note:41 ]", + "[ 1/8 → 1/6 | note:41.5 ]", + "[ 1/6 → 5/24 | note:42 ]", + "[ 5/24 → 1/4 | note:42.5 ]", + "[ 1/4 → 7/24 | note:43 ]", + "[ 7/24 → 1/3 | note:43.5 ]", + "[ 1/3 → 3/8 | note:44 ]", + "[ 3/8 → 5/12 | note:44.5 ]", + "[ 5/12 → 11/24 | note:45 ]", + "[ 11/24 → 1/2 | note:45.5 ]", + "[ 1/2 → 13/24 | note:46 ]", + "[ 13/24 → 7/12 | note:46.5 ]", + "[ 7/12 → 5/8 | note:47 ]", + "[ 5/8 → 2/3 | note:47.5 ]", + "[ 2/3 → 17/24 | note:48 ]", + "[ 17/24 → 3/4 | note:48.5 ]", + "[ 3/4 → 19/24 | note:49 ]", + "[ 19/24 → 5/6 | note:49.5 ]", + "[ 5/6 → 7/8 | note:50 ]", + "[ 7/8 → 11/12 | note:50.5 ]", + "[ 11/12 → 23/24 | note:51 ]", + "[ 23/24 → 1/1 | note:51.5 ]", + "[ 1/1 → 25/24 | note:40 ]", + "[ 25/24 → 13/12 | note:40.5 ]", + "[ 13/12 → 9/8 | note:41 ]", + "[ 9/8 → 7/6 | note:41.5 ]", + "[ 7/6 → 29/24 | note:42 ]", + "[ 29/24 → 5/4 | note:42.5 ]", + "[ 5/4 → 31/24 | note:43 ]", + "[ 31/24 → 4/3 | note:43.5 ]", + "[ 4/3 → 11/8 | note:44 ]", + "[ 11/8 → 17/12 | note:44.5 ]", + "[ 17/12 → 35/24 | note:45 ]", + "[ 35/24 → 3/2 | note:45.5 ]", + "[ 3/2 → 37/24 | note:46 ]", + "[ 37/24 → 19/12 | note:46.5 ]", + "[ 19/12 → 13/8 | note:47 ]", + "[ 13/8 → 5/3 | note:47.5 ]", + "[ 5/3 → 41/24 | note:48 ]", + "[ 41/24 → 7/4 | note:48.5 ]", + "[ 7/4 → 43/24 | note:49 ]", + "[ 43/24 → 11/6 | note:49.5 ]", + "[ 11/6 → 15/8 | note:50 ]", + "[ 15/8 → 23/12 | note:50.5 ]", + "[ 23/12 → 47/24 | note:51 ]", + "[ 47/24 → 2/1 | note:51.5 ]", + "[ 2/1 → 49/24 | note:40 ]", + "[ 49/24 → 25/12 | note:40.5 ]", + "[ 25/12 → 17/8 | note:41 ]", + "[ 17/8 → 13/6 | note:41.5 ]", + "[ 13/6 → 53/24 | note:42 ]", + "[ 53/24 → 9/4 | note:42.5 ]", + "[ 9/4 → 55/24 | note:43 ]", + "[ 55/24 → 7/3 | note:43.5 ]", + "[ 7/3 → 19/8 | note:44 ]", + "[ 19/8 → 29/12 | note:44.5 ]", + "[ 29/12 → 59/24 | note:45 ]", + "[ 59/24 → 5/2 | note:45.5 ]", + "[ 5/2 → 61/24 | note:46 ]", + "[ 61/24 → 31/12 | note:46.5 ]", + "[ 31/12 → 21/8 | note:47 ]", + "[ 21/8 → 8/3 | note:47.5 ]", + "[ 8/3 → 65/24 | note:48 ]", + "[ 65/24 → 11/4 | note:48.5 ]", + "[ 11/4 → 67/24 | note:49 ]", + "[ 67/24 → 17/6 | note:49.5 ]", + "[ 17/6 → 23/8 | note:50 ]", + "[ 23/8 → 35/12 | note:50.5 ]", + "[ 35/12 → 71/24 | note:51 ]", + "[ 71/24 → 3/1 | note:51.5 ]", + "[ 3/1 → 73/24 | note:40 ]", + "[ 73/24 → 37/12 | note:40.5 ]", + "[ 37/12 → 25/8 | note:41 ]", + "[ 25/8 → 19/6 | note:41.5 ]", + "[ 19/6 → 77/24 | note:42 ]", + "[ 77/24 → 13/4 | note:42.5 ]", + "[ 13/4 → 79/24 | note:43 ]", + "[ 79/24 → 10/3 | note:43.5 ]", + "[ 10/3 → 27/8 | note:44 ]", + "[ 27/8 → 41/12 | note:44.5 ]", + "[ 41/12 → 83/24 | note:45 ]", + "[ 83/24 → 7/2 | note:45.5 ]", + "[ 7/2 → 85/24 | note:46 ]", + "[ 85/24 → 43/12 | note:46.5 ]", + "[ 43/12 → 29/8 | note:47 ]", + "[ 29/8 → 11/3 | note:47.5 ]", + "[ 11/3 → 89/24 | note:48 ]", + "[ 89/24 → 15/4 | note:48.5 ]", + "[ 15/4 → 91/24 | note:49 ]", + "[ 91/24 → 23/6 | note:49.5 ]", + "[ 23/6 → 31/8 | note:50 ]", + "[ 31/8 → 47/12 | note:50.5 ]", + "[ 47/12 → 95/24 | note:51 ]", + "[ 95/24 → 4/1 | note:51.5 ]", +] +`; + +exports[`runs examples > example "" example index 0 2`] = ` +[ + "[ 0/1 → 1/1 | note:d ]", + "[ 1/1 → 2/1 | note:e ]", + "[ 2/1 → 3/1 | note:d ]", + "[ 3/1 → 4/1 | note:e ]", +] +`; + +exports[`runs examples > example "" example index 1 1`] = ` +[ + "[ 0/1 → 1/4 | note:A3 ]", + "[ 1/4 → 1/2 | note:C3 ]", + "[ 1/2 → 3/4 | note:D3 ]", + "[ 3/4 → 1/1 | note:G3 ]", + "[ 1/1 → 5/4 | note:E3 ]", + "[ 5/4 → 3/2 | note:C4 ]", + "[ 3/2 → 7/4 | note:A3 ]", + "[ 7/4 → 2/1 | note:E4 ]", + "[ 2/1 → 9/4 | note:A3 ]", + "[ 9/4 → 5/2 | note:C3 ]", + "[ 5/2 → 11/4 | note:D3 ]", + "[ 11/4 → 3/1 | note:G3 ]", + "[ 3/1 → 13/4 | note:E3 ]", + "[ 13/4 → 7/2 | note:C4 ]", + "[ 7/2 → 15/4 | note:A3 ]", + "[ 15/4 → 4/1 | note:E4 ]", +] +`; + +exports[`runs examples > example "" example index 2 1`] = ` +[ + "[ 1/16 → 1/8 | s:bd ]", + "[ 1/8 → 3/16 | s:bd ]", + "[ 3/16 → 1/4 | s:bd ]", + "[ 1/4 → 5/16 | s:bd ]", + "[ 3/8 → 7/16 | s:bd ]", + "[ 9/16 → 5/8 | s:bd ]", + "[ 5/8 → 11/16 | s:bd ]", + "[ 11/16 → 3/4 | s:bd ]", + "[ 3/4 → 13/16 | s:bd ]", + "[ 7/8 → 15/16 | s:bd ]", + "[ 17/16 → 9/8 | s:bd ]", + "[ 9/8 → 19/16 | s:bd ]", + "[ 19/16 → 5/4 | s:bd ]", + "[ 5/4 → 21/16 | s:bd ]", + "[ 11/8 → 23/16 | s:bd ]", + "[ 25/16 → 13/8 | s:bd ]", + "[ 13/8 → 27/16 | s:bd ]", + "[ 27/16 → 7/4 | s:bd ]", + "[ 7/4 → 29/16 | s:bd ]", + "[ 15/8 → 31/16 | s:bd ]", + "[ 33/16 → 17/8 | s:bd ]", + "[ 17/8 → 35/16 | s:bd ]", + "[ 35/16 → 9/4 | s:bd ]", + "[ 9/4 → 37/16 | s:bd ]", + "[ 19/8 → 39/16 | s:bd ]", + "[ 41/16 → 21/8 | s:bd ]", + "[ 21/8 → 43/16 | s:bd ]", + "[ 43/16 → 11/4 | s:bd ]", + "[ 11/4 → 45/16 | s:bd ]", + "[ 23/8 → 47/16 | s:bd ]", + "[ 49/16 → 25/8 | s:bd ]", + "[ 25/8 → 51/16 | s:bd ]", + "[ 51/16 → 13/4 | s:bd ]", + "[ 13/4 → 53/16 | s:bd ]", + "[ 27/8 → 55/16 | s:bd ]", + "[ 57/16 → 29/8 | s:bd ]", + "[ 29/8 → 59/16 | s:bd ]", + "[ 59/16 → 15/4 | s:bd ]", + "[ 15/4 → 61/16 | s:bd ]", + "[ 31/8 → 63/16 | s:bd ]", +] +`; + exports[`runs examples > example "_euclidRot" example index 0 1`] = ` [ "[ 0/1 → 1/5 | note:c3 ]", @@ -2322,6 +2498,39 @@ exports[`runs examples > example "degradeBy" example index 1 1`] = ` ] `; +exports[`runs examples > example "degradeBy" example index 2 1`] = ` +[ + "[ 1/8 → 3/16 | s:bd ]", + "[ 1/4 → 5/16 | s:bd ]", + "[ 5/16 → 3/8 | s:bd ]", + "[ 1/2 → 9/16 | s:bd ]", + "[ 9/16 → 5/8 | s:bd ]", + "[ 11/16 → 3/4 | s:bd ]", + "[ 15/16 → 1/1 | s:bd ]", + "[ 9/8 → 19/16 | s:bd ]", + "[ 5/4 → 21/16 | s:bd ]", + "[ 21/16 → 11/8 | s:bd ]", + "[ 3/2 → 25/16 | s:bd ]", + "[ 25/16 → 13/8 | s:bd ]", + "[ 27/16 → 7/4 | s:bd ]", + "[ 31/16 → 2/1 | s:bd ]", + "[ 17/8 → 35/16 | s:bd ]", + "[ 9/4 → 37/16 | s:bd ]", + "[ 37/16 → 19/8 | s:bd ]", + "[ 5/2 → 41/16 | s:bd ]", + "[ 41/16 → 21/8 | s:bd ]", + "[ 43/16 → 11/4 | s:bd ]", + "[ 47/16 → 3/1 | s:bd ]", + "[ 25/8 → 51/16 | s:bd ]", + "[ 13/4 → 53/16 | s:bd ]", + "[ 53/16 → 27/8 | s:bd ]", + "[ 7/2 → 57/16 | s:bd ]", + "[ 57/16 → 29/8 | s:bd ]", + "[ 59/16 → 15/4 | s:bd ]", + "[ 63/16 → 4/1 | s:bd ]", +] +`; + exports[`runs examples > example "delay" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd delay:0 ]", @@ -7121,40 +7330,6 @@ exports[`runs examples > example "rev" example index 0 1`] = ` ] `; -exports[`runs examples > example "ribbon" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | note:d ]", - "[ 1/2 → 1/1 | note:e ]", - "[ 1/1 → 3/2 | note:d ]", - "[ 3/2 → 2/1 | note:e ]", - "[ 2/1 → 5/2 | note:d ]", - "[ 5/2 → 3/1 | note:e ]", - "[ 3/1 → 7/2 | note:d ]", - "[ 7/2 → 4/1 | note:e ]", -] -`; - -exports[`runs examples > example "ribbon" example index 1 1`] = ` -[ - "[ 0/1 → 1/4 | note:G3 ]", - "[ 1/4 → 1/2 | note:C3 ]", - "[ 1/2 → 3/4 | note:D3 ]", - "[ 3/4 → 1/1 | note:F3 ]", - "[ 1/1 → 5/4 | note:Eb3 ]", - "[ 5/4 → 3/2 | note:Ab3 ]", - "[ 3/2 → 7/4 | note:G3 ]", - "[ 7/4 → 2/1 | note:C4 ]", - "[ 2/1 → 9/4 | note:G3 ]", - "[ 9/4 → 5/2 | note:C3 ]", - "[ 5/2 → 11/4 | note:D3 ]", - "[ 11/4 → 3/1 | note:F3 ]", - "[ 3/1 → 13/4 | note:Eb3 ]", - "[ 13/4 → 7/2 | note:Ab3 ]", - "[ 7/2 → 15/4 | note:G3 ]", - "[ 15/4 → 4/1 | note:C4 ]", -] -`; - exports[`runs examples > example "room" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd room:0 ]", @@ -7797,107 +7972,6 @@ but parts might be played more than once, or not at all, per cycle." example ind ] `; -exports[`runs examples > example "segment" example index 0 1`] = ` -[ - "[ 0/1 → 1/24 | note:40 ]", - "[ 1/24 → 1/12 | note:40.5 ]", - "[ 1/12 → 1/8 | note:41 ]", - "[ 1/8 → 1/6 | note:41.5 ]", - "[ 1/6 → 5/24 | note:42 ]", - "[ 5/24 → 1/4 | note:42.5 ]", - "[ 1/4 → 7/24 | note:43 ]", - "[ 7/24 → 1/3 | note:43.5 ]", - "[ 1/3 → 3/8 | note:44 ]", - "[ 3/8 → 5/12 | note:44.5 ]", - "[ 5/12 → 11/24 | note:45 ]", - "[ 11/24 → 1/2 | note:45.5 ]", - "[ 1/2 → 13/24 | note:46 ]", - "[ 13/24 → 7/12 | note:46.5 ]", - "[ 7/12 → 5/8 | note:47 ]", - "[ 5/8 → 2/3 | note:47.5 ]", - "[ 2/3 → 17/24 | note:48 ]", - "[ 17/24 → 3/4 | note:48.5 ]", - "[ 3/4 → 19/24 | note:49 ]", - "[ 19/24 → 5/6 | note:49.5 ]", - "[ 5/6 → 7/8 | note:50 ]", - "[ 7/8 → 11/12 | note:50.5 ]", - "[ 11/12 → 23/24 | note:51 ]", - "[ 23/24 → 1/1 | note:51.5 ]", - "[ 1/1 → 25/24 | note:40 ]", - "[ 25/24 → 13/12 | note:40.5 ]", - "[ 13/12 → 9/8 | note:41 ]", - "[ 9/8 → 7/6 | note:41.5 ]", - "[ 7/6 → 29/24 | note:42 ]", - "[ 29/24 → 5/4 | note:42.5 ]", - "[ 5/4 → 31/24 | note:43 ]", - "[ 31/24 → 4/3 | note:43.5 ]", - "[ 4/3 → 11/8 | note:44 ]", - "[ 11/8 → 17/12 | note:44.5 ]", - "[ 17/12 → 35/24 | note:45 ]", - "[ 35/24 → 3/2 | note:45.5 ]", - "[ 3/2 → 37/24 | note:46 ]", - "[ 37/24 → 19/12 | note:46.5 ]", - "[ 19/12 → 13/8 | note:47 ]", - "[ 13/8 → 5/3 | note:47.5 ]", - "[ 5/3 → 41/24 | note:48 ]", - "[ 41/24 → 7/4 | note:48.5 ]", - "[ 7/4 → 43/24 | note:49 ]", - "[ 43/24 → 11/6 | note:49.5 ]", - "[ 11/6 → 15/8 | note:50 ]", - "[ 15/8 → 23/12 | note:50.5 ]", - "[ 23/12 → 47/24 | note:51 ]", - "[ 47/24 → 2/1 | note:51.5 ]", - "[ 2/1 → 49/24 | note:40 ]", - "[ 49/24 → 25/12 | note:40.5 ]", - "[ 25/12 → 17/8 | note:41 ]", - "[ 17/8 → 13/6 | note:41.5 ]", - "[ 13/6 → 53/24 | note:42 ]", - "[ 53/24 → 9/4 | note:42.5 ]", - "[ 9/4 → 55/24 | note:43 ]", - "[ 55/24 → 7/3 | note:43.5 ]", - "[ 7/3 → 19/8 | note:44 ]", - "[ 19/8 → 29/12 | note:44.5 ]", - "[ 29/12 → 59/24 | note:45 ]", - "[ 59/24 → 5/2 | note:45.5 ]", - "[ 5/2 → 61/24 | note:46 ]", - "[ 61/24 → 31/12 | note:46.5 ]", - "[ 31/12 → 21/8 | note:47 ]", - "[ 21/8 → 8/3 | note:47.5 ]", - "[ 8/3 → 65/24 | note:48 ]", - "[ 65/24 → 11/4 | note:48.5 ]", - "[ 11/4 → 67/24 | note:49 ]", - "[ 67/24 → 17/6 | note:49.5 ]", - "[ 17/6 → 23/8 | note:50 ]", - "[ 23/8 → 35/12 | note:50.5 ]", - "[ 35/12 → 71/24 | note:51 ]", - "[ 71/24 → 3/1 | note:51.5 ]", - "[ 3/1 → 73/24 | note:40 ]", - "[ 73/24 → 37/12 | note:40.5 ]", - "[ 37/12 → 25/8 | note:41 ]", - "[ 25/8 → 19/6 | note:41.5 ]", - "[ 19/6 → 77/24 | note:42 ]", - "[ 77/24 → 13/4 | note:42.5 ]", - "[ 13/4 → 79/24 | note:43 ]", - "[ 79/24 → 10/3 | note:43.5 ]", - "[ 10/3 → 27/8 | note:44 ]", - "[ 27/8 → 41/12 | note:44.5 ]", - "[ 41/12 → 83/24 | note:45 ]", - "[ 83/24 → 7/2 | note:45.5 ]", - "[ 7/2 → 85/24 | note:46 ]", - "[ 85/24 → 43/12 | note:46.5 ]", - "[ 43/12 → 29/8 | note:47 ]", - "[ 29/8 → 11/3 | note:47.5 ]", - "[ 11/3 → 89/24 | note:48 ]", - "[ 89/24 → 15/4 | note:48.5 ]", - "[ 15/4 → 91/24 | note:49 ]", - "[ 91/24 → 23/6 | note:49.5 ]", - "[ 23/6 → 31/8 | note:50 ]", - "[ 31/8 → 47/12 | note:50.5 ]", - "[ 47/12 → 95/24 | note:51 ]", - "[ 95/24 → 4/1 | note:51.5 ]", -] -`; - exports[`runs examples > example "seq" example index 0 1`] = ` [ "[ 0/1 → 1/3 | note:e5 ]", From bfbd920b1d3d2e90c15fd3a0b8ebc8df5fe994e5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 24 Mar 2025 00:18:26 -0400 Subject: [PATCH 03/35] fix doc --- packages/core/signal.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 22738ea9..fdd7476f 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -520,7 +520,6 @@ export const degradeByWith = register( * * @name degradeBy * @memberof Pattern - * @synonym * @param {number} amount - a number between 0 and 1 * @returns Pattern * @example From 3eea908636bce6936b410fa3053dcf70bd5f5252 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 24 Mar 2025 00:33:00 -0400 Subject: [PATCH 04/35] fix doc --- packages/core/pattern.mjs | 6 ++++-- packages/core/signal.mjs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index 49caeaf0..6e294129 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -2105,8 +2105,9 @@ export const linger = register( /** * Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one. - * @param {number} segments number of segments per cycle + * @name segment * @synonyms seg + * @param {number} segments number of segments per cycle * @example * note(saw.range(40,52).segment(24)) */ @@ -2487,7 +2488,8 @@ export const bypass = register( /** * Loops the pattern inside an `offset` for `cycles`. - * If you think of the entire span of time in cycles as a ribbon, you can grab a single piece and loop it. + * If you think of the entire span of time in cycles as a ribbon, you can cut a single piece and loop it. + * @name ribbon * @synonym rib * @param {number} offset start point of loop in cycles * @param {number} cycles loop length in cycles diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index fdd7476f..66ac8a5a 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -527,6 +527,7 @@ export const degradeByWith = register( * @example * s("[hh?0.2]*8") * @example + * //beat generator * s("bd").segment(16).degradeBy(.5).ribbon(16,1) */ export const degradeBy = register( From 282180ef1cec109f270736ef2712ee06fa990fc6 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 24 Mar 2025 01:28:31 -0400 Subject: [PATCH 05/35] style update --- website/src/repl/components/UserFacingErrorMessage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/repl/components/UserFacingErrorMessage.jsx b/website/src/repl/components/UserFacingErrorMessage.jsx index ec8d8be3..9ccfacd8 100644 --- a/website/src/repl/components/UserFacingErrorMessage.jsx +++ b/website/src/repl/components/UserFacingErrorMessage.jsx @@ -4,5 +4,5 @@ export default function UserFacingErrorMessage(Props) { if (error == null) { return; } - return
{error.message || 'Unknown Error :-/'}
; + return
Error: {error.message || 'Unknown Error :-/'}
; } From dc8e5d53946612cb97b6fcb666e3525193a77738 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 24 Mar 2025 01:28:43 -0400 Subject: [PATCH 06/35] lint --- website/src/repl/components/UserFacingErrorMessage.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/website/src/repl/components/UserFacingErrorMessage.jsx b/website/src/repl/components/UserFacingErrorMessage.jsx index 9ccfacd8..1d9de037 100644 --- a/website/src/repl/components/UserFacingErrorMessage.jsx +++ b/website/src/repl/components/UserFacingErrorMessage.jsx @@ -4,5 +4,9 @@ export default function UserFacingErrorMessage(Props) { if (error == null) { return; } - return
Error: {error.message || 'Unknown Error :-/'}
; + return ( +
+ Error: {error.message || 'Unknown Error :-/'} +
+ ); } From dee75bc75954f0c5e00fddd563d8fee51c9b43ae Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 24 Mar 2025 01:32:48 -0400 Subject: [PATCH 07/35] update tests --- test/__snapshots__/examples.test.mjs.snap | 352 +++++++++++----------- 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 80ee7344..b9da0f53 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1,181 +1,5 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`runs examples > example "" example index 0 1`] = ` -[ - "[ 0/1 → 1/24 | note:40 ]", - "[ 1/24 → 1/12 | note:40.5 ]", - "[ 1/12 → 1/8 | note:41 ]", - "[ 1/8 → 1/6 | note:41.5 ]", - "[ 1/6 → 5/24 | note:42 ]", - "[ 5/24 → 1/4 | note:42.5 ]", - "[ 1/4 → 7/24 | note:43 ]", - "[ 7/24 → 1/3 | note:43.5 ]", - "[ 1/3 → 3/8 | note:44 ]", - "[ 3/8 → 5/12 | note:44.5 ]", - "[ 5/12 → 11/24 | note:45 ]", - "[ 11/24 → 1/2 | note:45.5 ]", - "[ 1/2 → 13/24 | note:46 ]", - "[ 13/24 → 7/12 | note:46.5 ]", - "[ 7/12 → 5/8 | note:47 ]", - "[ 5/8 → 2/3 | note:47.5 ]", - "[ 2/3 → 17/24 | note:48 ]", - "[ 17/24 → 3/4 | note:48.5 ]", - "[ 3/4 → 19/24 | note:49 ]", - "[ 19/24 → 5/6 | note:49.5 ]", - "[ 5/6 → 7/8 | note:50 ]", - "[ 7/8 → 11/12 | note:50.5 ]", - "[ 11/12 → 23/24 | note:51 ]", - "[ 23/24 → 1/1 | note:51.5 ]", - "[ 1/1 → 25/24 | note:40 ]", - "[ 25/24 → 13/12 | note:40.5 ]", - "[ 13/12 → 9/8 | note:41 ]", - "[ 9/8 → 7/6 | note:41.5 ]", - "[ 7/6 → 29/24 | note:42 ]", - "[ 29/24 → 5/4 | note:42.5 ]", - "[ 5/4 → 31/24 | note:43 ]", - "[ 31/24 → 4/3 | note:43.5 ]", - "[ 4/3 → 11/8 | note:44 ]", - "[ 11/8 → 17/12 | note:44.5 ]", - "[ 17/12 → 35/24 | note:45 ]", - "[ 35/24 → 3/2 | note:45.5 ]", - "[ 3/2 → 37/24 | note:46 ]", - "[ 37/24 → 19/12 | note:46.5 ]", - "[ 19/12 → 13/8 | note:47 ]", - "[ 13/8 → 5/3 | note:47.5 ]", - "[ 5/3 → 41/24 | note:48 ]", - "[ 41/24 → 7/4 | note:48.5 ]", - "[ 7/4 → 43/24 | note:49 ]", - "[ 43/24 → 11/6 | note:49.5 ]", - "[ 11/6 → 15/8 | note:50 ]", - "[ 15/8 → 23/12 | note:50.5 ]", - "[ 23/12 → 47/24 | note:51 ]", - "[ 47/24 → 2/1 | note:51.5 ]", - "[ 2/1 → 49/24 | note:40 ]", - "[ 49/24 → 25/12 | note:40.5 ]", - "[ 25/12 → 17/8 | note:41 ]", - "[ 17/8 → 13/6 | note:41.5 ]", - "[ 13/6 → 53/24 | note:42 ]", - "[ 53/24 → 9/4 | note:42.5 ]", - "[ 9/4 → 55/24 | note:43 ]", - "[ 55/24 → 7/3 | note:43.5 ]", - "[ 7/3 → 19/8 | note:44 ]", - "[ 19/8 → 29/12 | note:44.5 ]", - "[ 29/12 → 59/24 | note:45 ]", - "[ 59/24 → 5/2 | note:45.5 ]", - "[ 5/2 → 61/24 | note:46 ]", - "[ 61/24 → 31/12 | note:46.5 ]", - "[ 31/12 → 21/8 | note:47 ]", - "[ 21/8 → 8/3 | note:47.5 ]", - "[ 8/3 → 65/24 | note:48 ]", - "[ 65/24 → 11/4 | note:48.5 ]", - "[ 11/4 → 67/24 | note:49 ]", - "[ 67/24 → 17/6 | note:49.5 ]", - "[ 17/6 → 23/8 | note:50 ]", - "[ 23/8 → 35/12 | note:50.5 ]", - "[ 35/12 → 71/24 | note:51 ]", - "[ 71/24 → 3/1 | note:51.5 ]", - "[ 3/1 → 73/24 | note:40 ]", - "[ 73/24 → 37/12 | note:40.5 ]", - "[ 37/12 → 25/8 | note:41 ]", - "[ 25/8 → 19/6 | note:41.5 ]", - "[ 19/6 → 77/24 | note:42 ]", - "[ 77/24 → 13/4 | note:42.5 ]", - "[ 13/4 → 79/24 | note:43 ]", - "[ 79/24 → 10/3 | note:43.5 ]", - "[ 10/3 → 27/8 | note:44 ]", - "[ 27/8 → 41/12 | note:44.5 ]", - "[ 41/12 → 83/24 | note:45 ]", - "[ 83/24 → 7/2 | note:45.5 ]", - "[ 7/2 → 85/24 | note:46 ]", - "[ 85/24 → 43/12 | note:46.5 ]", - "[ 43/12 → 29/8 | note:47 ]", - "[ 29/8 → 11/3 | note:47.5 ]", - "[ 11/3 → 89/24 | note:48 ]", - "[ 89/24 → 15/4 | note:48.5 ]", - "[ 15/4 → 91/24 | note:49 ]", - "[ 91/24 → 23/6 | note:49.5 ]", - "[ 23/6 → 31/8 | note:50 ]", - "[ 31/8 → 47/12 | note:50.5 ]", - "[ 47/12 → 95/24 | note:51 ]", - "[ 95/24 → 4/1 | note:51.5 ]", -] -`; - -exports[`runs examples > example "" example index 0 2`] = ` -[ - "[ 0/1 → 1/1 | note:d ]", - "[ 1/1 → 2/1 | note:e ]", - "[ 2/1 → 3/1 | note:d ]", - "[ 3/1 → 4/1 | note:e ]", -] -`; - -exports[`runs examples > example "" example index 1 1`] = ` -[ - "[ 0/1 → 1/4 | note:A3 ]", - "[ 1/4 → 1/2 | note:C3 ]", - "[ 1/2 → 3/4 | note:D3 ]", - "[ 3/4 → 1/1 | note:G3 ]", - "[ 1/1 → 5/4 | note:E3 ]", - "[ 5/4 → 3/2 | note:C4 ]", - "[ 3/2 → 7/4 | note:A3 ]", - "[ 7/4 → 2/1 | note:E4 ]", - "[ 2/1 → 9/4 | note:A3 ]", - "[ 9/4 → 5/2 | note:C3 ]", - "[ 5/2 → 11/4 | note:D3 ]", - "[ 11/4 → 3/1 | note:G3 ]", - "[ 3/1 → 13/4 | note:E3 ]", - "[ 13/4 → 7/2 | note:C4 ]", - "[ 7/2 → 15/4 | note:A3 ]", - "[ 15/4 → 4/1 | note:E4 ]", -] -`; - -exports[`runs examples > example "" example index 2 1`] = ` -[ - "[ 1/16 → 1/8 | s:bd ]", - "[ 1/8 → 3/16 | s:bd ]", - "[ 3/16 → 1/4 | s:bd ]", - "[ 1/4 → 5/16 | s:bd ]", - "[ 3/8 → 7/16 | s:bd ]", - "[ 9/16 → 5/8 | s:bd ]", - "[ 5/8 → 11/16 | s:bd ]", - "[ 11/16 → 3/4 | s:bd ]", - "[ 3/4 → 13/16 | s:bd ]", - "[ 7/8 → 15/16 | s:bd ]", - "[ 17/16 → 9/8 | s:bd ]", - "[ 9/8 → 19/16 | s:bd ]", - "[ 19/16 → 5/4 | s:bd ]", - "[ 5/4 → 21/16 | s:bd ]", - "[ 11/8 → 23/16 | s:bd ]", - "[ 25/16 → 13/8 | s:bd ]", - "[ 13/8 → 27/16 | s:bd ]", - "[ 27/16 → 7/4 | s:bd ]", - "[ 7/4 → 29/16 | s:bd ]", - "[ 15/8 → 31/16 | s:bd ]", - "[ 33/16 → 17/8 | s:bd ]", - "[ 17/8 → 35/16 | s:bd ]", - "[ 35/16 → 9/4 | s:bd ]", - "[ 9/4 → 37/16 | s:bd ]", - "[ 19/8 → 39/16 | s:bd ]", - "[ 41/16 → 21/8 | s:bd ]", - "[ 21/8 → 43/16 | s:bd ]", - "[ 43/16 → 11/4 | s:bd ]", - "[ 11/4 → 45/16 | s:bd ]", - "[ 23/8 → 47/16 | s:bd ]", - "[ 49/16 → 25/8 | s:bd ]", - "[ 25/8 → 51/16 | s:bd ]", - "[ 51/16 → 13/4 | s:bd ]", - "[ 13/4 → 53/16 | s:bd ]", - "[ 27/8 → 55/16 | s:bd ]", - "[ 57/16 → 29/8 | s:bd ]", - "[ 29/8 → 59/16 | s:bd ]", - "[ 59/16 → 15/4 | s:bd ]", - "[ 15/4 → 61/16 | s:bd ]", - "[ 31/8 → 63/16 | s:bd ]", -] -`; - exports[`runs examples > example "_euclidRot" example index 0 1`] = ` [ "[ 0/1 → 1/5 | note:c3 ]", @@ -7330,6 +7154,81 @@ exports[`runs examples > example "rev" example index 0 1`] = ` ] `; +exports[`runs examples > example "ribbon" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | note:d ]", + "[ 1/1 → 2/1 | note:e ]", + "[ 2/1 → 3/1 | note:d ]", + "[ 3/1 → 4/1 | note:e ]", +] +`; + +exports[`runs examples > example "ribbon" example index 1 1`] = ` +[ + "[ 0/1 → 1/4 | note:A3 ]", + "[ 1/4 → 1/2 | note:C3 ]", + "[ 1/2 → 3/4 | note:D3 ]", + "[ 3/4 → 1/1 | note:G3 ]", + "[ 1/1 → 5/4 | note:E3 ]", + "[ 5/4 → 3/2 | note:C4 ]", + "[ 3/2 → 7/4 | note:A3 ]", + "[ 7/4 → 2/1 | note:E4 ]", + "[ 2/1 → 9/4 | note:A3 ]", + "[ 9/4 → 5/2 | note:C3 ]", + "[ 5/2 → 11/4 | note:D3 ]", + "[ 11/4 → 3/1 | note:G3 ]", + "[ 3/1 → 13/4 | note:E3 ]", + "[ 13/4 → 7/2 | note:C4 ]", + "[ 7/2 → 15/4 | note:A3 ]", + "[ 15/4 → 4/1 | note:E4 ]", +] +`; + +exports[`runs examples > example "ribbon" example index 2 1`] = ` +[ + "[ 1/16 → 1/8 | s:bd ]", + "[ 1/8 → 3/16 | s:bd ]", + "[ 3/16 → 1/4 | s:bd ]", + "[ 1/4 → 5/16 | s:bd ]", + "[ 3/8 → 7/16 | s:bd ]", + "[ 9/16 → 5/8 | s:bd ]", + "[ 5/8 → 11/16 | s:bd ]", + "[ 11/16 → 3/4 | s:bd ]", + "[ 3/4 → 13/16 | s:bd ]", + "[ 7/8 → 15/16 | s:bd ]", + "[ 17/16 → 9/8 | s:bd ]", + "[ 9/8 → 19/16 | s:bd ]", + "[ 19/16 → 5/4 | s:bd ]", + "[ 5/4 → 21/16 | s:bd ]", + "[ 11/8 → 23/16 | s:bd ]", + "[ 25/16 → 13/8 | s:bd ]", + "[ 13/8 → 27/16 | s:bd ]", + "[ 27/16 → 7/4 | s:bd ]", + "[ 7/4 → 29/16 | s:bd ]", + "[ 15/8 → 31/16 | s:bd ]", + "[ 33/16 → 17/8 | s:bd ]", + "[ 17/8 → 35/16 | s:bd ]", + "[ 35/16 → 9/4 | s:bd ]", + "[ 9/4 → 37/16 | s:bd ]", + "[ 19/8 → 39/16 | s:bd ]", + "[ 41/16 → 21/8 | s:bd ]", + "[ 21/8 → 43/16 | s:bd ]", + "[ 43/16 → 11/4 | s:bd ]", + "[ 11/4 → 45/16 | s:bd ]", + "[ 23/8 → 47/16 | s:bd ]", + "[ 49/16 → 25/8 | s:bd ]", + "[ 25/8 → 51/16 | s:bd ]", + "[ 51/16 → 13/4 | s:bd ]", + "[ 13/4 → 53/16 | s:bd ]", + "[ 27/8 → 55/16 | s:bd ]", + "[ 57/16 → 29/8 | s:bd ]", + "[ 29/8 → 59/16 | s:bd ]", + "[ 59/16 → 15/4 | s:bd ]", + "[ 15/4 → 61/16 | s:bd ]", + "[ 31/8 → 63/16 | s:bd ]", +] +`; + exports[`runs examples > example "room" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd room:0 ]", @@ -7972,6 +7871,107 @@ but parts might be played more than once, or not at all, per cycle." example ind ] `; +exports[`runs examples > example "segment" example index 0 1`] = ` +[ + "[ 0/1 → 1/24 | note:40 ]", + "[ 1/24 → 1/12 | note:40.5 ]", + "[ 1/12 → 1/8 | note:41 ]", + "[ 1/8 → 1/6 | note:41.5 ]", + "[ 1/6 → 5/24 | note:42 ]", + "[ 5/24 → 1/4 | note:42.5 ]", + "[ 1/4 → 7/24 | note:43 ]", + "[ 7/24 → 1/3 | note:43.5 ]", + "[ 1/3 → 3/8 | note:44 ]", + "[ 3/8 → 5/12 | note:44.5 ]", + "[ 5/12 → 11/24 | note:45 ]", + "[ 11/24 → 1/2 | note:45.5 ]", + "[ 1/2 → 13/24 | note:46 ]", + "[ 13/24 → 7/12 | note:46.5 ]", + "[ 7/12 → 5/8 | note:47 ]", + "[ 5/8 → 2/3 | note:47.5 ]", + "[ 2/3 → 17/24 | note:48 ]", + "[ 17/24 → 3/4 | note:48.5 ]", + "[ 3/4 → 19/24 | note:49 ]", + "[ 19/24 → 5/6 | note:49.5 ]", + "[ 5/6 → 7/8 | note:50 ]", + "[ 7/8 → 11/12 | note:50.5 ]", + "[ 11/12 → 23/24 | note:51 ]", + "[ 23/24 → 1/1 | note:51.5 ]", + "[ 1/1 → 25/24 | note:40 ]", + "[ 25/24 → 13/12 | note:40.5 ]", + "[ 13/12 → 9/8 | note:41 ]", + "[ 9/8 → 7/6 | note:41.5 ]", + "[ 7/6 → 29/24 | note:42 ]", + "[ 29/24 → 5/4 | note:42.5 ]", + "[ 5/4 → 31/24 | note:43 ]", + "[ 31/24 → 4/3 | note:43.5 ]", + "[ 4/3 → 11/8 | note:44 ]", + "[ 11/8 → 17/12 | note:44.5 ]", + "[ 17/12 → 35/24 | note:45 ]", + "[ 35/24 → 3/2 | note:45.5 ]", + "[ 3/2 → 37/24 | note:46 ]", + "[ 37/24 → 19/12 | note:46.5 ]", + "[ 19/12 → 13/8 | note:47 ]", + "[ 13/8 → 5/3 | note:47.5 ]", + "[ 5/3 → 41/24 | note:48 ]", + "[ 41/24 → 7/4 | note:48.5 ]", + "[ 7/4 → 43/24 | note:49 ]", + "[ 43/24 → 11/6 | note:49.5 ]", + "[ 11/6 → 15/8 | note:50 ]", + "[ 15/8 → 23/12 | note:50.5 ]", + "[ 23/12 → 47/24 | note:51 ]", + "[ 47/24 → 2/1 | note:51.5 ]", + "[ 2/1 → 49/24 | note:40 ]", + "[ 49/24 → 25/12 | note:40.5 ]", + "[ 25/12 → 17/8 | note:41 ]", + "[ 17/8 → 13/6 | note:41.5 ]", + "[ 13/6 → 53/24 | note:42 ]", + "[ 53/24 → 9/4 | note:42.5 ]", + "[ 9/4 → 55/24 | note:43 ]", + "[ 55/24 → 7/3 | note:43.5 ]", + "[ 7/3 → 19/8 | note:44 ]", + "[ 19/8 → 29/12 | note:44.5 ]", + "[ 29/12 → 59/24 | note:45 ]", + "[ 59/24 → 5/2 | note:45.5 ]", + "[ 5/2 → 61/24 | note:46 ]", + "[ 61/24 → 31/12 | note:46.5 ]", + "[ 31/12 → 21/8 | note:47 ]", + "[ 21/8 → 8/3 | note:47.5 ]", + "[ 8/3 → 65/24 | note:48 ]", + "[ 65/24 → 11/4 | note:48.5 ]", + "[ 11/4 → 67/24 | note:49 ]", + "[ 67/24 → 17/6 | note:49.5 ]", + "[ 17/6 → 23/8 | note:50 ]", + "[ 23/8 → 35/12 | note:50.5 ]", + "[ 35/12 → 71/24 | note:51 ]", + "[ 71/24 → 3/1 | note:51.5 ]", + "[ 3/1 → 73/24 | note:40 ]", + "[ 73/24 → 37/12 | note:40.5 ]", + "[ 37/12 → 25/8 | note:41 ]", + "[ 25/8 → 19/6 | note:41.5 ]", + "[ 19/6 → 77/24 | note:42 ]", + "[ 77/24 → 13/4 | note:42.5 ]", + "[ 13/4 → 79/24 | note:43 ]", + "[ 79/24 → 10/3 | note:43.5 ]", + "[ 10/3 → 27/8 | note:44 ]", + "[ 27/8 → 41/12 | note:44.5 ]", + "[ 41/12 → 83/24 | note:45 ]", + "[ 83/24 → 7/2 | note:45.5 ]", + "[ 7/2 → 85/24 | note:46 ]", + "[ 85/24 → 43/12 | note:46.5 ]", + "[ 43/12 → 29/8 | note:47 ]", + "[ 29/8 → 11/3 | note:47.5 ]", + "[ 11/3 → 89/24 | note:48 ]", + "[ 89/24 → 15/4 | note:48.5 ]", + "[ 15/4 → 91/24 | note:49 ]", + "[ 91/24 → 23/6 | note:49.5 ]", + "[ 23/6 → 31/8 | note:50 ]", + "[ 31/8 → 47/12 | note:50.5 ]", + "[ 47/12 → 95/24 | note:51 ]", + "[ 95/24 → 4/1 | note:51.5 ]", +] +`; + exports[`runs examples > example "seq" example index 0 1`] = ` [ "[ 0/1 → 1/3 | note:e5 ]", From 6d355a803ae490e0940ec24c00a91da75776f8d4 Mon Sep 17 00:00:00 2001 From: Rene Nyffenegger Date: Wed, 26 Mar 2025 14:54:15 +0100 Subject: [PATCH 08/35] Fix typo pattnr (#1316) --- website/src/pages/learn/stepwise.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/learn/stepwise.mdx b/website/src/pages/learn/stepwise.mdx index d3ca334a..e632ee7e 100644 --- a/website/src/pages/learn/stepwise.mdx +++ b/website/src/pages/learn/stepwise.mdx @@ -24,7 +24,7 @@ With the new stepwise `stepcat` function, the steps of the two patterns will be By default, steps are counted according to the 'top level' in mini-notation. For example `"a [b c] d e"` has five events in it per cycle, but is counted as four steps, where `[b c]` is counted as a single step. -However, you can mark a different metrical level to count steps relative to, using a `^` at the start of a sub-pattern. If we do this to the subpattern in our example: `"a [^b c] d e"`, then the pattern is now counted as having _eight_ steps. This is because 'b' and 'c' are each counted as single steps, and the events in the pattenr are twice as long, and so counted as two steps each. +However, you can mark a different metrical level to count steps relative to, using a `^` at the start of a sub-pattern. If we do this to the subpattern in our example: `"a [^b c] d e"`, then the pattern is now counted as having _eight_ steps. This is because 'b' and 'c' are each counted as single steps, and the events in the pattern are twice as long, and so counted as two steps each. ## Pacing the steps From e55b7c6ce338302691cbe16e1666e928ec8c7732 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 30 Mar 2025 00:55:12 -0400 Subject: [PATCH 09/35] mostly working --- packages/superdough/sampler.mjs | 1 + packages/superdough/superdough.mjs | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index f5e46d6b..45f6b33e 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -278,6 +278,7 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option const cutGroups = []; + export async function onTriggerSample(t, value, onended, bank, resolveUrl) { let { s, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index fc81f565..c6daabbf 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -374,6 +374,9 @@ export function resetGlobalEffects() { analysersData = {}; } +let allAudioNodeChains = []; +let maxPolyphony = 12; + export const superdough = async (value, t, hapDuration) => { const ac = getAudioContext(); t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t; @@ -472,14 +475,30 @@ export const superdough = async (value, t, hapDuration) => { } = value; gain = nanFallback(gain, 1); + console.info(allAudioNodeChains.length) + + for (let i = 0; i <= allAudioNodeChains.length - maxPolyphony; i++) { + const chain = allAudioNodeChains.shift() + chain.forEach(node => node?.disconnect()) + } + // if (allAudioNodeChains.length > maxPolyphony) { + + + // allAudioNodeChains.slice(0, allAudioNodeChains.length - maxPolyphony).flat().forEach((node) => { + // node.disconnect(); + // }); + // console.info(allAudioNodes.length) + // } //music programs/audio gear usually increments inputs/outputs from 1, so imitate that behavior channels = (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1); gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future - let toDisconnect = []; // audio nodes that will be disconnected when the source has ended + let audioNodes = []; + // audio nodes that will be disconnected when the source has ended + const onended = () => { - toDisconnect.forEach((n) => n?.disconnect()); + audioNodes.forEach((n) => n?.disconnect()); }; if (bank && s) { s = `${bank}_${s}`; @@ -654,9 +673,10 @@ export const superdough = async (value, t, hapDuration) => { // connect chain elements together chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); - // toDisconnect = all the node that should be disconnected in onended callback + // audioNodes = all the node that should be disconnected in onended callback // this is crucial for performance - toDisconnect = chain.concat([delaySend, reverbSend, analyserSend]); + audioNodes = chain.concat([delaySend, reverbSend, analyserSend]); + allAudioNodeChains.push(audioNodes) }; export const superdoughTrigger = (t, hap, ct, cps) => { From 8962e5a4f01b289dabe4b5f3e8dac8c555db3cb5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 30 Mar 2025 01:35:25 -0400 Subject: [PATCH 10/35] max poly --- packages/superdough/superdough.mjs | 46 ++++++++++++++--------------- website/src/repl/useReplContext.jsx | 4 +-- website/src/settings.mjs | 1 + 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index c6daabbf..c306f54e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -14,6 +14,10 @@ import { map } from 'nanostores'; import { logger } from './logger.mjs'; import { loadBuffer } from './sampler.mjs'; +let maxPolyphony = 128; +export function setMaxPolyphony(polyphony) { + maxPolyphony = polyphony; +} export const soundMap = map(); export function registerSound(key, onTrigger, data = {}) { @@ -162,7 +166,8 @@ function loadWorklets() { // this function should be called on first user interaction (to avoid console warning) export async function initAudio(options = {}) { - const { disableWorklets = false } = options; + const { disableWorklets = false, polyphony = maxPolyphony } = options; + setMaxPolyphony(polyphony); if (typeof window === 'undefined') { return; } @@ -374,8 +379,7 @@ export function resetGlobalEffects() { analysersData = {}; } -let allAudioNodeChains = []; -let maxPolyphony = 12; +let allAudioNodeChains = new Map(); export const superdough = async (value, t, hapDuration) => { const ac = getAudioContext(); @@ -475,30 +479,30 @@ export const superdough = async (value, t, hapDuration) => { } = value; gain = nanFallback(gain, 1); - console.info(allAudioNodeChains.length) - for (let i = 0; i <= allAudioNodeChains.length - maxPolyphony; i++) { - const chain = allAudioNodeChains.shift() - chain.forEach(node => node?.disconnect()) - } - // if (allAudioNodeChains.length > maxPolyphony) { + const chainID = Math.round(Math.random() * 10000); - - // allAudioNodeChains.slice(0, allAudioNodeChains.length - maxPolyphony).flat().forEach((node) => { - // node.disconnect(); - // }); - // console.info(allAudioNodes.length) - // } + // oldest audio nodes will be removed if maximum polyphony is exceeded + for (let i = 0; i <= allAudioNodeChains.size - maxPolyphony; i++) { + const ch = allAudioNodeChains.entries().next(); + const key = ch.value[0]; + const chain = ch.value[1]; + if (key == null) { + continue; + } + chain?.forEach((node) => node?.disconnect()); + allAudioNodeChains.delete(key); + } //music programs/audio gear usually increments inputs/outputs from 1, so imitate that behavior channels = (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1); gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future - let audioNodes = []; - // audio nodes that will be disconnected when the source has ended const onended = () => { - audioNodes.forEach((n) => n?.disconnect()); + const chain = allAudioNodeChains.get(chainID); + chain?.forEach((n) => n?.disconnect()); + allAudioNodeChains.delete(chainID); }; if (bank && s) { s = `${bank}_${s}`; @@ -672,11 +676,7 @@ export const superdough = async (value, t, hapDuration) => { // connect chain elements together chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); - - // audioNodes = all the node that should be disconnected in onended callback - // this is crucial for performance - audioNodes = chain.concat([delaySend, reverbSend, analyserSend]); - allAudioNodeChains.push(audioNodes) + allAudioNodeChains.set(chainID, [...chain, delaySend, reverbSend, analyserSend]); }; export const superdoughTrigger = (t, hap, ct, cps) => { diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index ba861627..801a0886 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -36,11 +36,11 @@ import './Repl.css'; import { setInterval, clearInterval } from 'worker-timers'; import { getMetadata } from '../metadata_parser'; -const { latestCode } = settingsMap.get(); +const { latestCode, maxPolyphony } = settingsMap.get(); let modulesLoading, presets, drawContext, clearCanvas, audioReady; if (typeof window !== 'undefined') { - audioReady = initAudioOnFirstClick(); + audioReady = initAudioOnFirstClick({ maxPolyphony }); modulesLoading = loadModules(); presets = prebake(); drawContext = getDrawContext(); diff --git a/website/src/settings.mjs b/website/src/settings.mjs index cd3d8936..132d84bd 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -40,6 +40,7 @@ export const defaultSettings = { audioEngineTarget: audioEngineTargets.webaudio, isButtonRowHidden: false, isCSSAnimationDisabled: false, + maxPolyphony: 128, }; let search = null; From 6f9bcc53dc43c3166564e0eca0c3ccae20a871ee Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 30 Mar 2025 16:33:47 -0400 Subject: [PATCH 11/35] ramp cut audio node --- packages/superdough/helpers.mjs | 1 + packages/superdough/sampler.mjs | 4 ++- packages/superdough/superdough.mjs | 45 ++++++++++++++++-------------- packages/superdough/synth.mjs | 30 +++++++++++++------- packages/superdough/worklets.mjs | 3 ++ 5 files changed, 51 insertions(+), 32 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index e51156fe..dfa8660f 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -212,6 +212,7 @@ export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) { constantNode.onended = () => { onComplete(); }; + return constantNode } const mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index 45f6b33e..f8eddffc 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -345,7 +345,9 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { }; let envEnd = holdEnd + release + 0.01; bufferSource.stop(envEnd); - const stop = (endTime, playWholeBuffer) => {}; + const stop = (endTime) => { + bufferSource.stop(endTime) + }; const handle = { node: out, bufferSource, stop }; // cut groups diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index c306f54e..3cb5349a 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -379,7 +379,7 @@ export function resetGlobalEffects() { analysersData = {}; } -let allAudioNodeChains = new Map(); +let activeSoundSources = new Map(); export const superdough = async (value, t, hapDuration) => { const ac = getAudioContext(); @@ -480,30 +480,24 @@ export const superdough = async (value, t, hapDuration) => { gain = nanFallback(gain, 1); - const chainID = Math.round(Math.random() * 10000); + const chainID = Math.round(Math.random() * 1000000); - // oldest audio nodes will be removed if maximum polyphony is exceeded - for (let i = 0; i <= allAudioNodeChains.size - maxPolyphony; i++) { - const ch = allAudioNodeChains.entries().next(); - const key = ch.value[0]; - const chain = ch.value[1]; - if (key == null) { - continue; - } - chain?.forEach((node) => node?.disconnect()); - allAudioNodeChains.delete(key); + // oldest audio nodes will be destroyed if maximum polyphony is exceeded + for (let i = 0; i <= activeSoundSources.size - maxPolyphony; i++) { + const ch = activeSoundSources.entries().next(); + const source = ch.value[1]; + const chainID = ch.value[0]; + const endTime = t + .25; + source?.node?.gain?.linearRampToValueAtTime(0, endTime); + source?.stop?.(endTime); + activeSoundSources.delete(chainID); } //music programs/audio gear usually increments inputs/outputs from 1, so imitate that behavior channels = (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1); - gain *= velocity; // velocity currently only multiplies with gain. it might do other things in the future + let audioNodes = []; - const onended = () => { - const chain = allAudioNodeChains.get(chainID); - chain?.forEach((n) => n?.disconnect()); - allAudioNodeChains.delete(chainID); - }; if (bank && s) { s = `${bank}_${s}`; value.s = s; @@ -515,10 +509,15 @@ export const superdough = async (value, t, hapDuration) => { sourceNode = source(t, value, hapDuration); } else if (getSound(s)) { const { onTrigger } = getSound(s); - const soundHandle = await onTrigger(t, value, onended); + const onEnded = () => { + audioNodes.forEach((n) => n?.disconnect()); + activeSoundSources.delete(chainID); + }; + const soundHandle = await onTrigger(t, value, onEnded); + if (soundHandle) { sourceNode = soundHandle.node; - soundHandle.stop(t + hapDuration); + activeSoundSources.set(chainID, soundHandle); } } else { throw new Error(`sound ${s} not found! Is it loaded?`); @@ -648,6 +647,7 @@ export const superdough = async (value, t, hapDuration) => { if (delay > 0 && delaytime > 0 && delayfeedback > 0) { const delyNode = getDelay(orbit, delaytime, delayfeedback, t); delaySend = effectSend(post, delyNode, delay); + audioNodes.push(delaySend); } // reverb let reverbSend; @@ -665,6 +665,7 @@ export const superdough = async (value, t, hapDuration) => { } const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim, roomIR); reverbSend = effectSend(post, reverbNode, room); + audioNodes.push(reverbSend); } // analyser @@ -672,11 +673,13 @@ export const superdough = async (value, t, hapDuration) => { if (analyze) { const analyserNode = getAnalyserById(analyze, 2 ** (fft + 5)); analyserSend = effectSend(post, analyserNode, 1); + audioNodes.push(analyserSend); } // connect chain elements together chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); - allAudioNodeChains.set(chainID, [...chain, delaySend, reverbSend, analyserSend]); + audioNodes = audioNodes.concat(chain); + // activeSoundSources.set(chainID, [...chain, delaySend, reverbSend, analyserSend].filter(Boolean)); }; export const superdoughTrigger = (t, hap, ct, cps) => { diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 86c073d0..f40b40c6 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -63,7 +63,9 @@ export function registerSynthSounds() { stop(envEnd); return { node, - stop: (releaseTime) => {}, + stop: (endTime) => { + stop(endTime); + }, }; }, { type: 'synth', prebake: true }, @@ -110,7 +112,9 @@ export function registerSynthSounds() { let envGain = gainNode(1); envGain = o.connect(envGain); - webAudioTimeout( + getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear'); + + let timeoutNode = webAudioTimeout( ac, () => { o.disconnect(); @@ -123,11 +127,11 @@ export function registerSynthSounds() { end, ); - getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear'); - return { node: envGain, - stop: (time) => {}, + stop: (time) => { + timeoutNode.stop(time); + }, }; }, { prebake: true, type: 'synth' }, @@ -169,7 +173,10 @@ export function registerSynthSounds() { let envGain = gainNode(1); envGain = o.connect(envGain); - webAudioTimeout( + + getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear'); + + let timeoutNode = webAudioTimeout( ac, () => { o.disconnect(); @@ -182,11 +189,11 @@ export function registerSynthSounds() { end, ); - getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear'); - return { node: envGain, - stop: (time) => {}, + stop: (time) => { + timeoutNode.stop(time); + }, }; }, { prebake: true, type: 'synth' }, @@ -229,7 +236,9 @@ export function registerSynthSounds() { stop(envEnd); return { node, - stop: (releaseTime) => {}, + stop: (endTime) => { + stop(endTime) + }, }; }, { type: 'synth', prebake: true }, @@ -299,6 +308,7 @@ export function getOscillator(s, t, value) { return { node: noiseMix?.node || o, stop: (time) => { + // console.info(time) fmModulator.stop(time); vibratoOscillator?.stop(time); noiseMix?.stop(time); diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 61d5d96e..286d8c03 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -663,6 +663,9 @@ registerProcessor('phase-vocoder-processor', PhaseVocoderProcessor); class PulseOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); + // this.port.onmessage = (event) => { + // console.info(event) + // }; this.pi = _PI; this.phi = -this.pi; // phase this.Y0 = 0; // feedback memories From d8e4055c6e5dd037386763252e83836e9edc842e Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 30 Mar 2025 20:22:24 -0400 Subject: [PATCH 12/35] settings panel --- packages/superdough/superdough.mjs | 11 ++++---- packages/superdough/synth.mjs | 12 +++++---- packages/superdough/worklets.mjs | 6 ++--- .../src/repl/components/panel/SettingsTab.jsx | 25 ++++++++++++++++++- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 3cb5349a..e5993024 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -14,9 +14,10 @@ import { map } from 'nanostores'; import { logger } from './logger.mjs'; import { loadBuffer } from './sampler.mjs'; -let maxPolyphony = 128; +export const DEFAULT_MAX_POLYPHONY = 128; +let maxPolyphony = DEFAULT_MAX_POLYPHONY; export function setMaxPolyphony(polyphony) { - maxPolyphony = polyphony; + maxPolyphony = parseInt(polyphony) ?? DEFAULT_MAX_POLYPHONY; } export const soundMap = map(); @@ -166,8 +167,8 @@ function loadWorklets() { // this function should be called on first user interaction (to avoid console warning) export async function initAudio(options = {}) { - const { disableWorklets = false, polyphony = maxPolyphony } = options; - setMaxPolyphony(polyphony); + const { disableWorklets = false, maxPolyphony } = options; + setMaxPolyphony(maxPolyphony); if (typeof window === 'undefined') { return; } @@ -487,7 +488,7 @@ export const superdough = async (value, t, hapDuration) => { const ch = activeSoundSources.entries().next(); const source = ch.value[1]; const chainID = ch.value[0]; - const endTime = t + .25; + const endTime = t + 0.25; source?.node?.gain?.linearRampToValueAtTime(0, endTime); source?.stop?.(endTime); activeSoundSources.delete(chainID); diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index f40b40c6..8bb47bb7 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -25,6 +25,10 @@ const getFrequencyFromValue = (value) => { return Number(freq); }; +function destroyAudioWorkletNode(node) { + node.disconnect(); + node.parameters.get('end')?.setValueAtTime(0, 0); +} const waveforms = ['triangle', 'square', 'sawtooth', 'sine']; const noises = ['pink', 'white', 'brown', 'crackle']; @@ -117,7 +121,7 @@ export function registerSynthSounds() { let timeoutNode = webAudioTimeout( ac, () => { - o.disconnect(); + destroyAudioWorkletNode(o); envGain.disconnect(); onended(); fm?.stop(); @@ -173,13 +177,12 @@ export function registerSynthSounds() { let envGain = gainNode(1); envGain = o.connect(envGain); - getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear'); let timeoutNode = webAudioTimeout( ac, () => { - o.disconnect(); + destroyAudioWorkletNode(o); envGain.disconnect(); onended(); fm?.stop(); @@ -237,7 +240,7 @@ export function registerSynthSounds() { return { node, stop: (endTime) => { - stop(endTime) + stop(endTime); }, }; }, @@ -308,7 +311,6 @@ export function getOscillator(s, t, value) { return { node: noiseMix?.node || o, stop: (time) => { - // console.info(time) fmModulator.stop(time); vibratoOscillator?.stop(time); noiseMix?.stop(time); diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 286d8c03..03d3a966 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -663,9 +663,6 @@ registerProcessor('phase-vocoder-processor', PhaseVocoderProcessor); class PulseOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); - // this.port.onmessage = (event) => { - // console.info(event) - // }; this.pi = _PI; this.phi = -this.pi; // phase this.Y0 = 0; // feedback memories @@ -713,6 +710,9 @@ class PulseOscillatorProcessor extends AudioWorkletProcessor { } process(inputs, outputs, params) { + if (this.disconnected) { + return false; + } if (currentTime <= params.begin[0]) { return true; } diff --git a/website/src/repl/components/panel/SettingsTab.jsx b/website/src/repl/components/panel/SettingsTab.jsx index 4ad3cc9a..4c174fa6 100644 --- a/website/src/repl/components/panel/SettingsTab.jsx +++ b/website/src/repl/components/panel/SettingsTab.jsx @@ -1,10 +1,12 @@ import { defaultSettings, settingsMap, useSettings } from '../../../settings.mjs'; import { themes } from '@strudel/codemirror'; +import { Textbox } from '../textbox/Textbox.jsx'; import { isUdels } from '../../util.mjs'; import { ButtonGroup } from './Forms.jsx'; import { AudioDeviceSelector } from './AudioDeviceSelector.jsx'; import { AudioEngineTargetSelector } from './AudioEngineTargetSelector.jsx'; import { confirmDialog } from '../../util.mjs'; +import { DEFAULT_MAX_POLYPHONY, setMaxPolyphony } from '@strudel/webaudio'; function Checkbox({ label, value, onChange, disabled = false }) { return ( @@ -53,7 +55,7 @@ function NumberSlider({ value, onChange, step = 1, ...rest }) { ); } -function FormItem({ label, children }) { +function FormItem({ label, children, sublabel }) { return (
@@ -105,6 +107,7 @@ export function SettingsTab({ started }) { audioDeviceName, audioEngineTarget, togglePanelTrigger, + maxPolyphony, } = useSettings(); const shouldAlwaysSync = isUdels(); const canChangeAudioDevice = AudioContext.prototype.setSinkId != null; @@ -139,6 +142,26 @@ export function SettingsTab({ started }) { }} /> + + + { + let v = parseInt(e.target.value); + v = isNaN(v) ? DEFAULT_MAX_POLYPHONY : v; + setMaxPolyphony(v); + settingsMap.setKey('maxPolyphony', v); + }} + onChange={(v) => { + v = Math.max(1, parseInt(v)); + settingsMap.setKey('maxPolyphony', isNaN(v) ? undefined : v); + }} + type="number" + placeholder="" + value={maxPolyphony ?? ''} + /> + settingsMap.setKey('theme', theme)} /> From 2f4cba4e9436c0962e4917d16d7564a12eadedb1 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 30 Mar 2025 20:26:44 -0400 Subject: [PATCH 13/35] format --- packages/superdough/helpers.mjs | 2 +- packages/superdough/sampler.mjs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index dfa8660f..6bde6937 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -212,7 +212,7 @@ export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) { constantNode.onended = () => { onComplete(); }; - return constantNode + return constantNode; } const mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index f8eddffc..18d1b779 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -278,7 +278,6 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option const cutGroups = []; - export async function onTriggerSample(t, value, onended, bank, resolveUrl) { let { s, @@ -346,7 +345,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { let envEnd = holdEnd + release + 0.01; bufferSource.stop(envEnd); const stop = (endTime) => { - bufferSource.stop(endTime) + bufferSource.stop(endTime); }; const handle = { node: out, bufferSource, stop }; From bceb37e8eab5de964f5386ac70f2cbbb51b42ca7 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 30 Mar 2025 20:32:19 -0400 Subject: [PATCH 14/35] rm comment --- packages/superdough/superdough.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index e5993024..9a3a0aa8 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -680,7 +680,6 @@ export const superdough = async (value, t, hapDuration) => { // connect chain elements together chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); audioNodes = audioNodes.concat(chain); - // activeSoundSources.set(chainID, [...chain, delaySend, reverbSend, analyserSend].filter(Boolean)); }; export const superdoughTrigger = (t, hap, ct, cps) => { From c2d4b529f6a49c9714b65812d4991b1ed56bb98d Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 3 Apr 2025 00:59:46 -0400 Subject: [PATCH 15/35] working --- packages/superdough/superdough.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index fc81f565..cd914822 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -17,7 +17,9 @@ import { loadBuffer } from './sampler.mjs'; export const soundMap = map(); export function registerSound(key, onTrigger, data = {}) { - soundMap.setKey(key.toLowerCase(), { onTrigger, data }); + key = key.toLowerCase().replace(/\s+/g, '_') + console.info(key, data) + soundMap.setKey(key, { onTrigger, data }); } function aliasBankMap(aliasMap) { From 6b8ee8ad11c92cd883d0fcf9554149a62feb59c8 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 3 Apr 2025 01:05:26 -0400 Subject: [PATCH 16/35] lint --- packages/superdough/superdough.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index cd914822..1126af0f 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -17,8 +17,7 @@ import { loadBuffer } from './sampler.mjs'; export const soundMap = map(); export function registerSound(key, onTrigger, data = {}) { - key = key.toLowerCase().replace(/\s+/g, '_') - console.info(key, data) + key = key.toLowerCase().replace(/\s+/g, '_'); soundMap.setKey(key, { onTrigger, data }); } From 05ea9c86fbe37ddc4beb4ea43db5c894a13fd33b Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 6 Apr 2025 16:08:42 -0400 Subject: [PATCH 17/35] working --- packages/core/controls.mjs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index e58bc8ca..85b4c239 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1780,3 +1780,32 @@ export const as = register('as', (mapping, pat) => { return v; }); }); + +/** + * Allows you to scrub an audio file like a tape loop by passing values that represents the position in the audio file + * in the optional array syntax ex: "0.5:2", the second value controls the speed of playback + * @name scrub + * @memberof Pattern + * @returns Pattern + * @example + * samples('github:switchangel/pad') + * s("swpad:0").scrub("{0.1!2 .25@3 0.7!2 <0.8:1.5>}%8") + * @example + * samples('github:yaxu/clean-breaks/main'); + * s("amen/4").fit().scrub("{0@3 0@2 4@3}%8".div(16)) + */ + +register( + 'scrub', + (beginPat, pat) => { + return beginPat.outerBind((v) => { + if (!Array.isArray(v)) { + v = [v]; + } + const [beginVal, speedMultiplier = 1] = v; + + return pat.begin(beginVal).mul(speed(speedMultiplier)).clip(1); + }); + }, + false, +); From a4682018224d3ed72b540ea3dfa35d0c7bffbaad Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 6 Apr 2025 16:32:37 -0400 Subject: [PATCH 18/35] add tests --- test/__snapshots__/examples.test.mjs.snap | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index b9da0f53..2d88d835 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -7871,6 +7871,52 @@ but parts might be played more than once, or not at all, per cycle." example ind ] `; +exports[`runs examples > example "scrub" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 1/8 → 1/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 1/4 → 5/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]", + "[ 5/8 → 3/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 3/4 → 7/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 7/8 → 1/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]", + "[ 1/1 → 9/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 9/8 → 5/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 5/4 → 13/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]", + "[ 13/8 → 7/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 7/4 → 15/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 15/8 → 2/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]", + "[ 2/1 → 17/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 17/8 → 9/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 9/4 → 21/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]", + "[ 21/8 → 11/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 11/4 → 23/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 23/8 → 3/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]", + "[ 3/1 → 25/8 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 25/8 → 13/4 | s:swpad n:0 begin:0.1 speed:1 clip:1 ]", + "[ 13/4 → 29/8 | s:swpad n:0 begin:0.25 speed:1 clip:1 ]", + "[ 29/8 → 15/4 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 15/4 → 31/8 | s:swpad n:0 begin:0.7 speed:1 clip:1 ]", + "[ 31/8 → 4/1 | s:swpad n:0 begin:0.8 speed:1.5 clip:1 ]", +] +`; + +exports[`runs examples > example "scrub" example index 1 1`] = ` +[ + "[ 0/1 → 3/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 3/8 → 5/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 5/8 → 1/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]", + "[ 1/1 → 11/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 11/8 → 13/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 13/8 → 2/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]", + "[ 2/1 → 19/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 19/8 → 21/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 21/8 → 3/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]", + "[ 3/1 → 27/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 27/8 → 29/8 | s:amen speed:0.25 unit:c begin:0 clip:1 ]", + "[ 29/8 → 4/1 | s:amen speed:0.25 unit:c begin:0.25 clip:1 ]", +] +`; + exports[`runs examples > example "segment" example index 0 1`] = ` [ "[ 0/1 → 1/24 | note:40 ]", From 695ac6a5d3891d5c45fe570e33628c1b055ba32a Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sun, 6 Apr 2025 16:34:14 -0400 Subject: [PATCH 19/35] var --- packages/core/controls.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 85b4c239..a6c650e0 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1795,7 +1795,7 @@ export const as = register('as', (mapping, pat) => { * s("amen/4").fit().scrub("{0@3 0@2 4@3}%8".div(16)) */ -register( +export const scrub = register( 'scrub', (beginPat, pat) => { return beginPat.outerBind((v) => { From 344d9ca1a8692f4bef1fe63ba201edab9434c3e5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 7 Apr 2025 23:43:29 -0400 Subject: [PATCH 20/35] working --- packages/superdough/superdough.mjs | 61 ++++++++++++++++++- .../components/panel/AudioDeviceSelector.jsx | 5 +- website/src/repl/useReplContext.jsx | 22 ++----- website/src/repl/util.mjs | 35 +---------- website/src/settings.mjs | 5 +- 5 files changed, 68 insertions(+), 60 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 8c471eb3..bb152dd9 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -15,6 +15,8 @@ import { logger } from './logger.mjs'; import { loadBuffer } from './sampler.mjs'; export const DEFAULT_MAX_POLYPHONY = 128; +export const DEFAULT_AUDIO_DEVICE_NAME = 'System Standard'; + let maxPolyphony = DEFAULT_MAX_POLYPHONY; export function setMaxPolyphony(polyphony) { maxPolyphony = parseInt(polyphony) ?? DEFAULT_MAX_POLYPHONY; @@ -91,6 +93,38 @@ export function getSound(s) { return soundMap.get()[s.toLowerCase()]; } +export const getAudioDevices = async () => { + await navigator.mediaDevices.getUserMedia({ audio: true }); + let mediaDevices = await navigator.mediaDevices.enumerateDevices(); + mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default'); + const devicesMap = new Map(); + devicesMap.set(DEFAULT_AUDIO_DEVICE_NAME, ''); + mediaDevices.forEach((device) => { + devicesMap.set(device.label, device.deviceId); + }); + return devicesMap; +}; + +export const setAudioDevice = async (id) => { + let audioCtx = getAudioContext(); + if (audioCtx.sinkId === id) { + return; + } + await audioCtx.suspend(); + await audioCtx.close(); + audioCtx = setDefaultAudioContext(); + await audioCtx.resume(); + const isValidID = (id ?? '').length > 0; + if (isValidID) { + try { + await audioCtx.setSinkId(id); + } catch { + logger('failed to set audio interface', 'warning'); + } + } + initializeAudioOutput(); +}; + const defaultDefaultValues = { s: 'triangle', gain: 0.8, @@ -161,19 +195,40 @@ export function getAudioContextCurrentTime() { let workletsLoading; function loadWorklets() { if (!workletsLoading) { - workletsLoading = getAudioContext().audioWorklet.addModule(workletsUrl); + const contextPromise = getAudioContext(); + workletsLoading = contextPromise.audioWorklet.addModule(workletsUrl); } + return workletsLoading; } // this function should be called on first user interaction (to avoid console warning) export async function initAudio(options = {}) { - const { disableWorklets = false, maxPolyphony } = options; + const { disableWorklets = false, maxPolyphony, audioDeviceName } = options; setMaxPolyphony(maxPolyphony); if (typeof window === 'undefined') { return; } - await getAudioContext().resume(); + + const audioCtx = getAudioContext(); + + if (audioDeviceName != null && audioDeviceName != DEFAULT_AUDIO_DEVICE_NAME) { + try { + const devices = await getAudioDevices(); + const id = devices.get(audioDeviceName); + const isValidID = (id ?? '').length > 0; + if (audioCtx.sinkId !== id && isValidID) { + await audioCtx.setSinkId(id); + } + logger( + `[superdough] Audio Device set to ${audioDeviceName}, it might take a few seconds before audio plays on all output channels`, + ); + } catch { + logger('[superdough] failed to set audio interface', 'warning'); + } + } + + await audioCtx.resume(); if (disableWorklets) { logger('[superdough]: AudioWorklets disabled with disableWorklets'); return; diff --git a/website/src/repl/components/panel/AudioDeviceSelector.jsx b/website/src/repl/components/panel/AudioDeviceSelector.jsx index d1f13c22..288ffc0a 100644 --- a/website/src/repl/components/panel/AudioDeviceSelector.jsx +++ b/website/src/repl/components/panel/AudioDeviceSelector.jsx @@ -1,6 +1,7 @@ import React, { useState } from 'react'; -import { getAudioDevices, setAudioDevice } from '../../util.mjs'; + import { SelectInput } from './SelectInput'; +import { getAudioDevices } from '@strudel/webaudio'; const initdevices = new Map(); @@ -21,9 +22,7 @@ export function AudioDeviceSelector({ audioDeviceName, onChange, isDisabled }) { if (!devicesInitialized) { return; } - const deviceID = devices.get(deviceName); onChange(deviceName); - setAudioDevice(deviceID); }; const options = new Map(); Array.from(devices.keys()).forEach((deviceName) => { diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index 801a0886..f0895aaa 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -14,7 +14,7 @@ import { resetLoadedSounds, initAudioOnFirstClick, } from '@strudel/webaudio'; -import { getAudioDevices, setAudioDevice, setVersionDefaultsFrom } from './util.mjs'; +import { setVersionDefaultsFrom } from './util.mjs'; import { StrudelMirror, defaultSettings } from '@strudel/codemirror'; import { clearHydra } from '@strudel/hydra'; import { useCallback, useEffect, useRef, useState } from 'react'; @@ -28,7 +28,7 @@ import { setViewingPatternData, } from '../user_pattern_utils.mjs'; import { superdirtOutput } from '@strudel/osc/superdirtoutput'; -import { audioEngineTargets, defaultAudioDeviceName } from '../settings.mjs'; +import { audioEngineTargets } from '../settings.mjs'; import { useStore } from '@nanostores/react'; import { prebake } from './prebake.mjs'; import { getRandomTune, initCode, loadModules, shareCode } from './util.mjs'; @@ -36,11 +36,11 @@ import './Repl.css'; import { setInterval, clearInterval } from 'worker-timers'; import { getMetadata } from '../metadata_parser'; -const { latestCode, maxPolyphony } = settingsMap.get(); +const { latestCode, maxPolyphony, audioDeviceName } = settingsMap.get(); let modulesLoading, presets, drawContext, clearCanvas, audioReady; if (typeof window !== 'undefined') { - audioReady = initAudioOnFirstClick({ maxPolyphony }); + audioReady = initAudioOnFirstClick({ maxPolyphony, audioDeviceName }); modulesLoading = loadModules(); presets = prebake(); drawContext = getDrawContext(); @@ -159,20 +159,6 @@ export function useReplContext() { editorRef.current?.updateSettings(editorSettings); }, [_settings]); - // on first load, set stored audio device if possible - useEffect(() => { - const { audioDeviceName } = _settings; - if (audioDeviceName !== defaultAudioDeviceName) { - getAudioDevices().then((devices) => { - const deviceID = devices.get(audioDeviceName); - if (deviceID == null) { - return; - } - setAudioDevice(deviceID); - }); - } - }, []); - // // UI Actions // diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index a8d18428..82f66cd4 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -1,6 +1,6 @@ import { evalScope, hash2code, logger } from '@strudel/core'; -import { settingPatterns, defaultAudioDeviceName } from '../settings.mjs'; -import { getAudioContext, initializeAudioOutput, setDefaultAudioContext, setVersionDefaults } from '@strudel/webaudio'; +import { settingPatterns } from '../settings.mjs'; +import { setVersionDefaults } from '@strudel/webaudio'; import { getMetadata } from '../metadata_parser'; import { isTauri } from '../tauri.mjs'; import './Repl.css'; @@ -159,37 +159,6 @@ export const isUdels = () => { return window.top?.location?.pathname.includes('udels'); }; -export const getAudioDevices = async () => { - await navigator.mediaDevices.getUserMedia({ audio: true }); - let mediaDevices = await navigator.mediaDevices.enumerateDevices(); - mediaDevices = mediaDevices.filter((device) => device.kind === 'audiooutput' && device.deviceId !== 'default'); - const devicesMap = new Map(); - devicesMap.set(defaultAudioDeviceName, ''); - mediaDevices.forEach((device) => { - devicesMap.set(device.label, device.deviceId); - }); - return devicesMap; -}; - -export const setAudioDevice = async (id) => { - let audioCtx = getAudioContext(); - if (audioCtx.sinkId === id) { - return; - } - await audioCtx.suspend(); - await audioCtx.close(); - audioCtx = setDefaultAudioContext(); - await audioCtx.resume(); - const isValidID = (id ?? '').length > 0; - if (isValidID) { - try { - await audioCtx.setSinkId(id); - } catch { - logger('failed to set audio interface', 'warning'); - } - } - initializeAudioOutput(); -}; export function setVersionDefaultsFrom(code) { try { diff --git a/website/src/settings.mjs b/website/src/settings.mjs index 132d84bd..c50c31f6 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -2,8 +2,7 @@ import { persistentMap } from '@nanostores/persistent'; import { useStore } from '@nanostores/react'; import { register } from '@strudel/core'; import { isUdels } from './repl/util.mjs'; - -export const defaultAudioDeviceName = 'System Standard'; +import { DEFAULT_AUDIO_DEVICE_NAME } from '@strudel/webaudio'; export const audioEngineTargets = { webaudio: 'webaudio', @@ -36,7 +35,7 @@ export const defaultSettings = { isPanelOpen: true, togglePanelTrigger: 'click', //click | hover userPatterns: '{}', - audioDeviceName: defaultAudioDeviceName, + audioDeviceName: DEFAULT_AUDIO_DEVICE_NAME, audioEngineTarget: audioEngineTargets.webaudio, isButtonRowHidden: false, isCSSAnimationDisabled: false, From b1090a1dd8a0ccb0a6c4c2e153a644afd5601eed Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 7 Apr 2025 23:52:15 -0400 Subject: [PATCH 21/35] rm deadcode --- packages/superdough/superdough.mjs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index bb152dd9..b94d5d31 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -105,26 +105,6 @@ export const getAudioDevices = async () => { return devicesMap; }; -export const setAudioDevice = async (id) => { - let audioCtx = getAudioContext(); - if (audioCtx.sinkId === id) { - return; - } - await audioCtx.suspend(); - await audioCtx.close(); - audioCtx = setDefaultAudioContext(); - await audioCtx.resume(); - const isValidID = (id ?? '').length > 0; - if (isValidID) { - try { - await audioCtx.setSinkId(id); - } catch { - logger('failed to set audio interface', 'warning'); - } - } - initializeAudioOutput(); -}; - const defaultDefaultValues = { s: 'triangle', gain: 0.8, From 9e233fe587ed720d56de84a3d4ee4f0d3c7ca9f0 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Mon, 7 Apr 2025 23:55:34 -0400 Subject: [PATCH 22/35] var name --- packages/superdough/superdough.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index b94d5d31..55ff2eab 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -175,8 +175,8 @@ export function getAudioContextCurrentTime() { let workletsLoading; function loadWorklets() { if (!workletsLoading) { - const contextPromise = getAudioContext(); - workletsLoading = contextPromise.audioWorklet.addModule(workletsUrl); + const audioCtx = getAudioContext(); + workletsLoading = audioCtx.audioWorklet.addModule(workletsUrl); } return workletsLoading; From a32bb58fb324e1520deeca9eb1fdfe5539fd0ac9 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Apr 2025 00:01:34 -0400 Subject: [PATCH 23/35] rm dependency --- packages/superdough/superdough.mjs | 4 ++-- website/src/settings.mjs | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 55ff2eab..e936d38e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -15,7 +15,7 @@ import { logger } from './logger.mjs'; import { loadBuffer } from './sampler.mjs'; export const DEFAULT_MAX_POLYPHONY = 128; -export const DEFAULT_AUDIO_DEVICE_NAME = 'System Standard'; +const DEFAULT_AUDIO_DEVICE_NAME = 'System Standard'; let maxPolyphony = DEFAULT_MAX_POLYPHONY; export function setMaxPolyphony(polyphony) { @@ -184,7 +184,7 @@ function loadWorklets() { // this function should be called on first user interaction (to avoid console warning) export async function initAudio(options = {}) { - const { disableWorklets = false, maxPolyphony, audioDeviceName } = options; + const { disableWorklets = false, maxPolyphony, audioDeviceName = DEFAULT_AUDIO_DEVICE_NAME } = options; setMaxPolyphony(maxPolyphony); if (typeof window === 'undefined') { return; diff --git a/website/src/settings.mjs b/website/src/settings.mjs index c50c31f6..1b4ba33f 100644 --- a/website/src/settings.mjs +++ b/website/src/settings.mjs @@ -2,7 +2,6 @@ import { persistentMap } from '@nanostores/persistent'; import { useStore } from '@nanostores/react'; import { register } from '@strudel/core'; import { isUdels } from './repl/util.mjs'; -import { DEFAULT_AUDIO_DEVICE_NAME } from '@strudel/webaudio'; export const audioEngineTargets = { webaudio: 'webaudio', @@ -35,7 +34,6 @@ export const defaultSettings = { isPanelOpen: true, togglePanelTrigger: 'click', //click | hover userPatterns: '{}', - audioDeviceName: DEFAULT_AUDIO_DEVICE_NAME, audioEngineTarget: audioEngineTargets.webaudio, isButtonRowHidden: false, isCSSAnimationDisabled: false, From 2b69004eb442913072eacbcd22b0217c6571b4a0 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 8 Apr 2025 00:04:22 -0400 Subject: [PATCH 24/35] lint --- website/src/repl/util.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index 82f66cd4..f49dd568 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -1,6 +1,6 @@ import { evalScope, hash2code, logger } from '@strudel/core'; import { settingPatterns } from '../settings.mjs'; -import { setVersionDefaults } from '@strudel/webaudio'; +import { setVersionDefaults } from '@strudel/webaudio'; import { getMetadata } from '../metadata_parser'; import { isTauri } from '../tauri.mjs'; import './Repl.css'; @@ -159,7 +159,6 @@ export const isUdels = () => { return window.top?.location?.pathname.includes('udels'); }; - export function setVersionDefaultsFrom(code) { try { const metadata = getMetadata(code); From ab76b4424a83967296987939df1299352a0356b5 Mon Sep 17 00:00:00 2001 From: Alex McLean Date: Fri, 11 Apr 2025 08:18:57 +0100 Subject: [PATCH 25/35] send delta in seconds, to match tidal/superdirt (#1323) --- packages/osc/osc.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index 4b9ab14e..3c7b92d4 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -37,7 +37,7 @@ function connect() { export function parseControlsFromHap(hap, cps) { hap.ensureObjectValue(); const cycle = hap.wholeOrPart().begin.valueOf(); - const delta = hap.duration.valueOf(); + const delta = hap.duration.valueOf() / cps; const controls = Object.assign({}, { cps, cycle, delta }, hap.value); // make sure n and note are numbers controls.n && (controls.n = parseNumeral(controls.n)); From 2968017300c9cd6bd2b112191f2d799540ab2c70 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 17 Apr 2025 23:32:20 -0400 Subject: [PATCH 26/35] disabled --- website/astro.config.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 63158422..8b4fa4f8 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -58,6 +58,9 @@ const options = { // https://astro.build/config export default defineConfig({ + devToolbar: { + enabled: false + }, integrations: [ react(), mdx(options), From 6e3aa693d5898f9982f23bde05aeec14af98c0d5 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Thu, 17 Apr 2025 23:35:10 -0400 Subject: [PATCH 27/35] format --- website/astro.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 8b4fa4f8..94c4e613 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -59,7 +59,7 @@ const options = { // https://astro.build/config export default defineConfig({ devToolbar: { - enabled: false + enabled: false, }, integrations: [ react(), From ad25ce7b2199c200870615a4a82ced83570081cf Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 18 Apr 2025 01:01:10 -0400 Subject: [PATCH 28/35] working --- website/src/pages/udels/index.astro | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/website/src/pages/udels/index.astro b/website/src/pages/udels/index.astro index 4ab699e7..157e5315 100644 --- a/website/src/pages/udels/index.astro +++ b/website/src/pages/udels/index.astro @@ -1,12 +1,14 @@ --- +import HeadCommon from '@components/HeadCommon.astro'; import { Udels } from '../../components/Udels/Udels.jsx'; - - -const { BASE_URL } = import.meta.env; -const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL; --- - - - - \ No newline at end of file + + + + Strudel UDELS + + + + + From 17e1ebeb93df8fa64d8b23f5abf29ddf9f122857 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Fri, 18 Apr 2025 01:02:13 -0400 Subject: [PATCH 29/35] udelsconfig --- website/astro.config.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 94c4e613..63158422 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -58,9 +58,6 @@ const options = { // https://astro.build/config export default defineConfig({ - devToolbar: { - enabled: false, - }, integrations: [ react(), mdx(options), From bd08b74ebc5ffd7c23fe708d98dd4ac09765a5d9 Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 19 Apr 2025 16:10:38 -0400 Subject: [PATCH 30/35] fixed strudel bool --- packages/codemirror/flash.mjs | 4 +- packages/codemirror/themes.mjs | 58 +++++++++---------- packages/codemirror/themes/algoboy.mjs | 1 + packages/codemirror/themes/atomone.mjs | 2 + packages/codemirror/themes/green-text.mjs | 39 +++++++++++++ packages/codemirror/themes/red-text.mjs | 39 +++++++++++++ packages/codemirror/themes/sonic-pink.mjs | 39 +++++++++++++ packages/codemirror/themes/strudel-theme.mjs | 4 +- packages/codemirror/themes/theme-helper.mjs | 3 + .../src/repl/components/panel/SelectInput.jsx | 2 +- .../src/repl/components/panel/SettingsTab.jsx | 2 +- .../src/repl/components/textbox/Textbox.jsx | 2 +- 12 files changed, 157 insertions(+), 38 deletions(-) create mode 100644 packages/codemirror/themes/green-text.mjs create mode 100644 packages/codemirror/themes/red-text.mjs create mode 100644 packages/codemirror/themes/sonic-pink.mjs diff --git a/packages/codemirror/flash.mjs b/packages/codemirror/flash.mjs index 6b37038f..243500bf 100644 --- a/packages/codemirror/flash.mjs +++ b/packages/codemirror/flash.mjs @@ -11,7 +11,9 @@ export const flashField = StateField.define({ for (let e of tr.effects) { if (e.is(setFlash)) { if (e.value && tr.newDoc.length > 0) { - const mark = Decoration.mark({ attributes: { style: `background-color: #FFCA2880` } }); + const mark = Decoration.mark({ + attributes: { style: `background-color: rgba(255,255,255, .4); filter: invert(10%)` }, + }); flash = Decoration.set([mark.range(0, tr.newDoc.length)]); } else { flash = Decoration.set([]); diff --git a/packages/codemirror/themes.mjs b/packages/codemirror/themes.mjs index c2f49065..771b6bdc 100644 --- a/packages/codemirror/themes.mjs +++ b/packages/codemirror/themes.mjs @@ -5,16 +5,15 @@ import whitescreen, { settings as whitescreenSettings } from './themes/whitescre import teletext, { settings as teletextSettings } from './themes/teletext.mjs'; import algoboy, { settings as algoboySettings } from './themes/algoboy.mjs'; import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs'; -import terminal, { settings as terminalSettings } from './themes/terminal.mjs'; -import abcdef, { settings as abcdefSettings } from './themes/abcdef.mjs'; +import sonicPink, { settings as sonicPinkSettings } from './themes/sonic-pink.mjs'; +import redText, { settings as redTextSettings } from './themes/red-text.mjs'; +import greenText, { settings as greenTextSettings } from './themes/green-text.mjs'; import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs'; import atomone, { settings as atomOneSettings } from './themes/atomone.mjs'; import aura, { settings as auraSettings } from './themes/aura.mjs'; -import bespin, { settings as bespinSettings } from './themes/bespin.mjs'; import darcula, { settings as darculaSettings } from './themes/darcula.mjs'; import dracula, { settings as draculaSettings } from './themes/dracula.mjs'; import duotoneDark, { settings as duotoneDarkSettings } from './themes/duotoneDark.mjs'; -import duotoneLight, { settings as duotoneLightSettings } from './themes/duotoneLight.mjs'; import eclipse, { settings as eclipseSettings } from './themes/eclipse.mjs'; import githubDark, { settings as githubDarkSettings } from './themes/githubDark.mjs'; import githubLight, { settings as githubLightSettings } from './themes/githubLight.mjs'; @@ -38,46 +37,45 @@ import bbedit, { settings as bbeditSettings } from './themes/bbedit.mjs'; import noctisLilac, { settings as noctisLilacSettings } from './themes/noctisLilac.mjs'; import { setTheme } from '@strudel/draw'; - export const themes = { strudelTheme, - bluescreen, - blackscreen, - whitescreen, - teletext, algoboy, + androidstudio, atomone, aura, - darcula, - dracula, // todo: optimize // bespin, //abcdef, - androidstudio, - duotoneDark, - githubDark, + bbedit, + blackscreen, + bluescreen, CutiePi, + darcula, + dracula, + duotoneDark, + eclipse, + githubDark, + githubLight, + greenText, gruvboxDark, + gruvboxLight, + sonicPink, materialDark, - nord, + materialLight, monokai, + noctisLilac, + nord, + redText, solarizedDark, + solarizedLight, sublime, + teletext, tokyoNight, + tokyoNightDay, tokyoNightStorm, vscodeDark, - //xcodeDark, - // LIGHT - bbedit, - //duotoneLight, - eclipse, - githubLight, - gruvboxLight, - materialLight, vscodeLight, - noctisLilac, - solarizedLight, - tokyoNightDay, + whitescreen, xcodeLight, }; @@ -88,21 +86,19 @@ export const settings = { whitescreen: whitescreenSettings, teletext: teletextSettings, algoboy: algoboySettings, - terminal: terminalSettings, - abcdef: abcdefSettings, androidstudio: androidstudioSettings, atomone: atomOneSettings, aura: auraSettings, bbedit: bbeditSettings, - bespin: bespinSettings, darcula: darculaSettings, dracula: draculaSettings, - duotoneLight: duotoneLightSettings, duotoneDark: duotoneDarkSettings, eclipse: eclipseSettings, CutiePi: CutiePiSettings, + sonicPink: sonicPinkSettings, githubLight: githubLightSettings, githubDark: githubDarkSettings, + greenText: greenTextSettings, gruvboxDark: gruvboxDarkSettings, gruvboxLight: gruvboxLightSettings, materialDark: materialDarkSettings, @@ -110,6 +106,7 @@ export const settings = { noctisLilac: noctisLilacSettings, nord: nordSettings, monokai: monokaiSettings, + redText: redTextSettings, solarizedLight: solarizedLightSettings, solarizedDark: solarizedDarkSettings, sublime: sublimeSettings, @@ -118,7 +115,6 @@ export const settings = { vscodeDark: vscodeDarkSettings, vscodeLight: vscodeLightSettings, xcodeLight: xcodeLightSettings, - //xcodeDark: xcodeDarkSettings, tokyoNightDay: tokyoNightDaySettings, }; diff --git a/packages/codemirror/themes/algoboy.mjs b/packages/codemirror/themes/algoboy.mjs index 30812d12..d3c7e069 100644 --- a/packages/codemirror/themes/algoboy.mjs +++ b/packages/codemirror/themes/algoboy.mjs @@ -54,6 +54,7 @@ export default createTheme({ tag: [t.keyword, t.tagName, t.arithmeticOperator], color: palette[1], }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: palette[0]}, { tag: [t.function(t.variableName), t.propertyName], color: palette[0] }, { tag: t.atom, color: palette[1] }, ], diff --git a/packages/codemirror/themes/atomone.mjs b/packages/codemirror/themes/atomone.mjs index 85951d15..eda87d4e 100644 --- a/packages/codemirror/themes/atomone.mjs +++ b/packages/codemirror/themes/atomone.mjs @@ -38,12 +38,14 @@ export default createTheme({ tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], color: 'hsl(207, 82%, 66%)', }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: 'hsl( 29, 54%, 61%)'}, { tag: [t.tagName, t.heading], color: '#e06c75' }, { tag: t.comment, color: '#54636D' }, { tag: [t.variableName, t.propertyName, t.labelName], color: 'hsl(220, 14%, 71%)' }, { tag: [t.attributeName, t.number], color: 'hsl( 29, 54%, 61%)' }, { tag: t.className, color: 'hsl( 39, 67%, 69%)' }, { tag: t.keyword, color: 'hsl(286, 60%, 67%)' }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: '#98c379' }, ], }); diff --git a/packages/codemirror/themes/green-text.mjs b/packages/codemirror/themes/green-text.mjs new file mode 100644 index 00000000..bd21f6d3 --- /dev/null +++ b/packages/codemirror/themes/green-text.mjs @@ -0,0 +1,39 @@ +/** + * @name Atom One + * Atom One dark syntax theme + * + * https://github.com/atom/one-dark-syntax + */ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from './theme-helper.mjs'; + +const hex = ['#000000', '#8ed675', '#56bd2a', '#54636D', '#171717']; + +export const settings = { + background: hex[0], + lineBackground: 'transparent', + foreground: hex[2], + selection: hex[4], + selectionMatch: hex[0], + gutterBackground: hex[0], + gutterForeground: hex[3], + gutterBorder: 'transparent', + lineHighlight: hex[0], +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { + tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], + color: hex[2], + }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] }, + { tag: t.comment, color: hex[3] }, + { tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] }, + { tag: [t.attributeName, t.number], color: hex[1] }, + { tag: t.keyword, color: hex[2] }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] }, + ], +}); diff --git a/packages/codemirror/themes/red-text.mjs b/packages/codemirror/themes/red-text.mjs new file mode 100644 index 00000000..9fe7f516 --- /dev/null +++ b/packages/codemirror/themes/red-text.mjs @@ -0,0 +1,39 @@ +/** + * @name Atom One + * Atom One dark syntax theme + * + * https://github.com/atom/one-dark-syntax + */ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from './theme-helper.mjs'; + +const hex = ['#000000', '#ff5356', '#bd312a', '#54636D', '#171717']; + +export const settings = { + background: hex[0], + lineBackground: 'transparent', + foreground: hex[2], + selection: hex[4], + selectionMatch: hex[0], + gutterBackground: hex[0], + gutterForeground: hex[3], + gutterBorder: 'transparent', + lineHighlight: hex[0], +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { + tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], + color: hex[2], + }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] }, + { tag: t.comment, color: hex[3] }, + { tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] }, + { tag: [t.attributeName, t.number], color: hex[1] }, + { tag: t.keyword, color: hex[2] }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] }, + ], +}); diff --git a/packages/codemirror/themes/sonic-pink.mjs b/packages/codemirror/themes/sonic-pink.mjs new file mode 100644 index 00000000..976597ec --- /dev/null +++ b/packages/codemirror/themes/sonic-pink.mjs @@ -0,0 +1,39 @@ +/** + * @name Atom One + * Atom One dark syntax theme + * + * https://github.com/atom/one-dark-syntax + */ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from './theme-helper.mjs'; + +const hex = ['#1e1e1e', '#fbde2d', '#ff1493', '#4c83ff', '#ededed', '#cccccc', '#ffffff30', '#dc2f8c']; + +export const settings = { + background: '#000000', + lineBackground: 'transparent', + foreground: hex[4], + selection: hex[6], + gutterBackground: hex[0], + gutterForeground: hex[5], + gutterBorder: 'transparent', + lineHighlight: hex[0], +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { + tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], + color: hex[4], + }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[3] }, + + { tag: t.comment, color: '#54636D' }, + { tag: [t.variableName, t.propertyName, t.labelName], color: hex[4] }, + { tag: [t.attributeName, t.number], color: hex[3] }, + { tag: t.keyword, color: hex[1] }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] }, + ], +}); diff --git a/packages/codemirror/themes/strudel-theme.mjs b/packages/codemirror/themes/strudel-theme.mjs index 7fd6bb65..49e4fad1 100644 --- a/packages/codemirror/themes/strudel-theme.mjs +++ b/packages/codemirror/themes/strudel-theme.mjs @@ -5,14 +5,11 @@ export const settings = { background: '#222', lineBackground: '#22222299', foreground: '#fff', - // foreground: '#75baff', caret: '#ffcc00', selection: 'rgba(128, 203, 196, 0.5)', selectionMatch: '#036dd626', - // lineHighlight: '#8a91991a', // original lineHighlight: '#00000050', gutterBackground: 'transparent', - // gutterForeground: '#8a919966', gutterForeground: '#8a919966', }; @@ -20,6 +17,7 @@ export default createTheme({ theme: 'dark', settings, styles: [ + { tag: [t.atom, t.bool, t.special(t.variableName)], color: '#89ddff'}, { tag: t.labelName, color: '#89ddff' }, { tag: t.keyword, color: '#c792ea' }, { tag: t.operator, color: '#89ddff' }, diff --git a/packages/codemirror/themes/theme-helper.mjs b/packages/codemirror/themes/theme-helper.mjs index ee9bad8e..95221299 100644 --- a/packages/codemirror/themes/theme-helper.mjs +++ b/packages/codemirror/themes/theme-helper.mjs @@ -1,7 +1,10 @@ import { EditorView } from '@codemirror/view'; +import { tags as t } from '@lezer/highlight'; import { syntaxHighlighting } from '@codemirror/language'; import { HighlightStyle } from '@codemirror/language'; + + export const createTheme = ({ theme, settings, styles }) => { const _theme = EditorView.theme( { diff --git a/website/src/repl/components/panel/SelectInput.jsx b/website/src/repl/components/panel/SelectInput.jsx index a28c3936..b4780967 100644 --- a/website/src/repl/components/panel/SelectInput.jsx +++ b/website/src/repl/components/panel/SelectInput.jsx @@ -5,7 +5,7 @@ export function SelectInput({ value, options, onChange, onClick, isDisabled }) { onChange(e.target.value)} > diff --git a/website/src/repl/components/textbox/Textbox.jsx b/website/src/repl/components/textbox/Textbox.jsx index f391dd6d..1984007d 100644 --- a/website/src/repl/components/textbox/Textbox.jsx +++ b/website/src/repl/components/textbox/Textbox.jsx @@ -3,7 +3,7 @@ import cx from '@src/cx.mjs'; export function Textbox({ onChange, className, ...inputProps }) { return ( onChange(e.target.value)} {...inputProps} /> From c852b5fab01be439d8f004f87c4635da136aa41f Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Sat, 19 Apr 2025 16:21:11 -0400 Subject: [PATCH 31/35] fix lint --- packages/codemirror/themes.mjs | 4 -- packages/codemirror/themes/abcdef.mjs | 55 ------------------- packages/codemirror/themes/algoboy.mjs | 2 +- packages/codemirror/themes/atomone.mjs | 4 +- packages/codemirror/themes/bespin.mjs | 39 ------------- packages/codemirror/themes/strudel-theme.mjs | 2 +- packages/codemirror/themes/theme-helper.mjs | 3 - packages/codemirror/themes/xcodeDark.mjs | 34 ------------ .../src/repl/components/textbox/Textbox.jsx | 5 +- 9 files changed, 8 insertions(+), 140 deletions(-) delete mode 100644 packages/codemirror/themes/abcdef.mjs delete mode 100644 packages/codemirror/themes/bespin.mjs delete mode 100644 packages/codemirror/themes/xcodeDark.mjs diff --git a/packages/codemirror/themes.mjs b/packages/codemirror/themes.mjs index 771b6bdc..6aa70471 100644 --- a/packages/codemirror/themes.mjs +++ b/packages/codemirror/themes.mjs @@ -31,7 +31,6 @@ import tokyoNightStorm, { settings as tokyoNightStormSettings } from './themes/t import tokyoNightDay, { settings as tokyoNightDaySettings } from './themes/tokyoNightDay.mjs'; import vscodeDark, { settings as vscodeDarkSettings } from './themes/vscodeDark.mjs'; import vscodeLight, { settings as vscodeLightSettings } from './themes/vscodeLight.mjs'; -// import xcodeDark, { settings as xcodeDarkSettings } from './themes/xcodeDark.mjs'; import xcodeLight, { settings as xcodeLightSettings } from './themes/xcodeLight.mjs'; import bbedit, { settings as bbeditSettings } from './themes/bbedit.mjs'; import noctisLilac, { settings as noctisLilacSettings } from './themes/noctisLilac.mjs'; @@ -43,9 +42,6 @@ export const themes = { androidstudio, atomone, aura, - // todo: optimize - // bespin, - //abcdef, bbedit, blackscreen, bluescreen, diff --git a/packages/codemirror/themes/abcdef.mjs b/packages/codemirror/themes/abcdef.mjs deleted file mode 100644 index 917823b9..00000000 --- a/packages/codemirror/themes/abcdef.mjs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @name abcdef - * @author codemirror.net - * https://codemirror.net/5/theme/abcdef.css - */ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from './theme-helper.mjs'; - -export const settings = { - background: '#0f0f0f', - lineBackground: '#0f0f0f99', - foreground: '#defdef', - caret: '#00FF00', - selection: '#515151', - selectionMatch: '#515151', - gutterBackground: '#555', - gutterForeground: '#FFFFFF', - lineHighlight: '#314151', -}; - -export default createTheme({ - theme: 'dark', - settings: { - background: '#0f0f0f', - foreground: '#defdef', - caret: '#00FF00', - selection: '#515151', - selectionMatch: '#515151', - // gutterBackground: '#555', - gutterBackground: 'transparent', - /* gutterForeground: '#FFFFFF', */ - gutterForeground: '#7a7b7c', - lineHighlight: '#0a6bcb3d', - }, - styles: [ - { tag: t.labelName, color: 'inherit' }, - { tag: t.keyword, color: 'darkgoldenrod', fontWeight: 'bold' }, - { tag: t.atom, color: '#77F' }, - { tag: t.comment, color: '#7a7b7c', fontStyle: 'italic' }, - { tag: t.number, color: 'violet' }, - { tag: t.definition(t.variableName), color: '#fffabc' }, - { tag: t.variableName, color: '#abcdef' }, - { tag: t.function(t.variableName), color: '#fffabc' }, - { tag: t.typeName, color: '#FFDD44' }, - { tag: t.tagName, color: '#def' }, - { tag: t.string, color: '#2b4' }, - { tag: t.meta, color: '#C9F' }, - // { tag: t.qualifier, color: '#FFF700' }, - // { tag: t.builtin, color: '#30aabc' }, - { tag: t.bracket, color: '#8a8a8a' }, - { tag: t.attributeName, color: '#DDFF00' }, - { tag: t.heading, color: 'aquamarine', fontWeight: 'bold' }, - { tag: t.link, color: 'blueviolet', fontWeight: 'bold' }, - ], -}); diff --git a/packages/codemirror/themes/algoboy.mjs b/packages/codemirror/themes/algoboy.mjs index d3c7e069..049ed596 100644 --- a/packages/codemirror/themes/algoboy.mjs +++ b/packages/codemirror/themes/algoboy.mjs @@ -54,7 +54,7 @@ export default createTheme({ tag: [t.keyword, t.tagName, t.arithmeticOperator], color: palette[1], }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: palette[0]}, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: palette[0] }, { tag: [t.function(t.variableName), t.propertyName], color: palette[0] }, { tag: t.atom, color: palette[1] }, ], diff --git a/packages/codemirror/themes/atomone.mjs b/packages/codemirror/themes/atomone.mjs index eda87d4e..4c9fdec6 100644 --- a/packages/codemirror/themes/atomone.mjs +++ b/packages/codemirror/themes/atomone.mjs @@ -38,14 +38,14 @@ export default createTheme({ tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], color: 'hsl(207, 82%, 66%)', }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: 'hsl( 29, 54%, 61%)'}, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: 'hsl( 29, 54%, 61%)' }, { tag: [t.tagName, t.heading], color: '#e06c75' }, { tag: t.comment, color: '#54636D' }, { tag: [t.variableName, t.propertyName, t.labelName], color: 'hsl(220, 14%, 71%)' }, { tag: [t.attributeName, t.number], color: 'hsl( 29, 54%, 61%)' }, { tag: t.className, color: 'hsl( 39, 67%, 69%)' }, { tag: t.keyword, color: 'hsl(286, 60%, 67%)' }, - + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: '#98c379' }, ], }); diff --git a/packages/codemirror/themes/bespin.mjs b/packages/codemirror/themes/bespin.mjs deleted file mode 100644 index 41af821b..00000000 --- a/packages/codemirror/themes/bespin.mjs +++ /dev/null @@ -1,39 +0,0 @@ -// this is different from https://thememirror.net/bespin -import { tags as t } from '@lezer/highlight'; -import { createTheme } from './theme-helper.mjs'; - -export const settings = { - background: '#28211c', - lineBackground: '#28211c99', - foreground: '#9d9b97', - caret: '#797977', - selection: '#36312e', - selectionMatch: '#4f382b', - gutterBackground: '#28211c', - gutterForeground: '#666666', - lineHighlight: 'rgba(255, 255, 255, 0.1)', -}; -export default createTheme({ - theme: 'dark', - settings: { - background: '#28211c', - foreground: '#9d9b97', - caret: '#797977', - selection: '#4f382b', - selectionMatch: '#4f382b', - gutterBackground: '#28211c', - gutterForeground: '#666666', - lineHighlight: '#ffffff1a', - }, - styles: [ - { tag: [t.atom, t.number, t.link, t.bool], color: '#9b859d' }, - { tag: t.comment, color: '#937121' }, - { tag: [t.keyword, t.tagName], color: '#cf6a4c' }, - { tag: t.string, color: '#f9ee98' }, - { tag: t.bracket, color: '#9d9b97' }, - { tag: [t.variableName], color: '#5ea6ea' }, - { tag: t.definition(t.variableName), color: '#cf7d34' }, - { tag: [t.function(t.variableName), t.className], color: '#cf7d34' }, - { tag: [t.propertyName, t.attributeName], color: '#54be0d' }, - ], -}); diff --git a/packages/codemirror/themes/strudel-theme.mjs b/packages/codemirror/themes/strudel-theme.mjs index 49e4fad1..7c4a3a91 100644 --- a/packages/codemirror/themes/strudel-theme.mjs +++ b/packages/codemirror/themes/strudel-theme.mjs @@ -17,7 +17,7 @@ export default createTheme({ theme: 'dark', settings, styles: [ - { tag: [t.atom, t.bool, t.special(t.variableName)], color: '#89ddff'}, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: '#89ddff' }, { tag: t.labelName, color: '#89ddff' }, { tag: t.keyword, color: '#c792ea' }, { tag: t.operator, color: '#89ddff' }, diff --git a/packages/codemirror/themes/theme-helper.mjs b/packages/codemirror/themes/theme-helper.mjs index 95221299..ee9bad8e 100644 --- a/packages/codemirror/themes/theme-helper.mjs +++ b/packages/codemirror/themes/theme-helper.mjs @@ -1,10 +1,7 @@ import { EditorView } from '@codemirror/view'; -import { tags as t } from '@lezer/highlight'; import { syntaxHighlighting } from '@codemirror/language'; import { HighlightStyle } from '@codemirror/language'; - - export const createTheme = ({ theme, settings, styles }) => { const _theme = EditorView.theme( { diff --git a/packages/codemirror/themes/xcodeDark.mjs b/packages/codemirror/themes/xcodeDark.mjs deleted file mode 100644 index 41440cfd..00000000 --- a/packages/codemirror/themes/xcodeDark.mjs +++ /dev/null @@ -1,34 +0,0 @@ -import { tags as t } from '@lezer/highlight'; -import { createTheme } from './theme-helper.mjs'; - -export const settings = { - background: '#292A30', - lineBackground: '#292A3099', - foreground: '#CECFD0', - caret: '#fff', - selection: '#727377', - selectionMatch: '#727377', - lineHighlight: '#2F3239', -}; - -export default createTheme({ - theme: 'dark', - settings: { - background: '#292A30', - foreground: '#CECFD0', - caret: '#fff', - selection: '#727377', - selectionMatch: '#727377', - lineHighlight: '#ffffff0f', - }, - styles: [ - { tag: [t.comment, t.quote], color: '#7F8C98' }, - { tag: [t.keyword], color: '#FF7AB2', fontWeight: 'bold' }, - { tag: [t.string, t.meta], color: '#FF8170' }, - { tag: [t.typeName], color: '#DABAFF' }, - { tag: [t.definition(t.variableName)], color: '#6BDFFF' }, - { tag: [t.name], color: '#6BAA9F' }, - { tag: [t.variableName], color: '#ACF2E4' }, - { tag: [t.regexp, t.link], color: '#FF8170' }, - ], -}); diff --git a/website/src/repl/components/textbox/Textbox.jsx b/website/src/repl/components/textbox/Textbox.jsx index 1984007d..6c3bc328 100644 --- a/website/src/repl/components/textbox/Textbox.jsx +++ b/website/src/repl/components/textbox/Textbox.jsx @@ -3,7 +3,10 @@ import cx from '@src/cx.mjs'; export function Textbox({ onChange, className, ...inputProps }) { return ( onChange(e.target.value)} {...inputProps} /> From 3dae2a80ab113b484664abdced52beeef2e55937 Mon Sep 17 00:00:00 2001 From: hpunq <128979576+hpunq@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:51:44 +0100 Subject: [PATCH 32/35] change import sounds label to import sounds folder --- website/src/repl/components/panel/ImportSoundsButton.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/website/src/repl/components/panel/ImportSoundsButton.jsx b/website/src/repl/components/panel/ImportSoundsButton.jsx index 7862b766..b668cba6 100644 --- a/website/src/repl/components/panel/ImportSoundsButton.jsx +++ b/website/src/repl/components/panel/ImportSoundsButton.jsx @@ -21,8 +21,9 @@ export default function ImportSoundsButton({ onComplete }) { return ( ); } From 759cdb627673f6883150b108fc3733e677bf624b Mon Sep 17 00:00:00 2001 From: hpunq <128979576+hpunq@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:53:42 +0100 Subject: [PATCH 33/35] add new import sounds tab with explanation and hyperlinks --- .../src/repl/components/panel/SoundsTab.jsx | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/website/src/repl/components/panel/SoundsTab.jsx b/website/src/repl/components/panel/SoundsTab.jsx index c91721e0..4c88e8ae 100644 --- a/website/src/repl/components/panel/SoundsTab.jsx +++ b/website/src/repl/components/panel/SoundsTab.jsx @@ -14,6 +14,8 @@ export function SoundsTab() { const sounds = useStore(soundMap); const { soundsFilter } = useSettings(); const [search, setSearch] = useState(''); + const { BASE_URL } = import.meta.env; + const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL; const soundEntries = useMemo(() => { if (!sounds) { @@ -37,6 +39,9 @@ export function SoundsTab() { if (soundsFilter === 'synths') { return filtered.filter(([_, { data }]) => ['synth', 'soundfont'].includes(data.type)); } + if (soundsFilter === 'importSounds') { + return []; + } return filtered; }, [sounds, soundsFilter, search]); @@ -51,7 +56,6 @@ export function SoundsTab() { ref?.stop(getAudioContext().currentTime + 0.01); }); }); - return (
setSearch(v)} /> @@ -65,9 +69,9 @@ export function SoundsTab() { drums: 'drum-machines', synths: 'Synths', user: 'User', + importSounds: 'import-sounds', }} > - settingsMap.setKey('soundsFilter', 'user')} />
@@ -101,7 +105,55 @@ export function SoundsTab() { ); })} - {!soundEntries.length ? 'No custom sounds loaded in this pattern (yet).' : ''} + {!soundEntries.length && soundsFilter === 'importSounds' ? ( +
+ settingsMap.setKey('soundsFilter', 'user')} /> +

+ To import sounds into strudel, they must be contained{' '} + + within a folder or subfolder + + . The best way to do this is to upload a “samples” folder containing subfolders of individual sounds or + soundbanks (see diagram below).{' '} +

+
+              {`└─ samples <-- import this folder
+   ├─ swoop
+   │  ├─ swoopshort.wav
+   │  ├─ swooplong.wav
+   │  └─ swooptight.wav
+   └─ smash
+      ├─ smashhigh.wav
+      ├─ smashlow.wav
+      └─ smashmiddle.wav`}
+            
+

+ The name of a subfolder corresponds to the sound name under the “user” tab. Multiple samples within a + subfolder are all labelled with the same name, but can be accessed using “.n( )” - remember sounds are + zero-indexed and in alphabetical order! +

+

+ For more information, and other ways to use your own sounds in strudel,{' '} + + check out the docs + + ! +

+

Preview Sounds

+
+              n("0 1 2 3 4 5").s("sample-name")
+            
+

+ Paste the line above into the main editor to hear the uploaded folder. Remember to use the name of your + sample as it appears under the "user" tab. +

+
+ ) : ( + '' + )} + {!soundEntries.length && soundsFilter !== 'importSounds' + ? 'No custom sounds loaded in this pattern (yet).' + : ''}
); From f539930509ec7c108f346c66534b54c0f2bc611b Mon Sep 17 00:00:00 2001 From: hpunq <128979576+hpunq@users.noreply.github.com> Date: Sat, 26 Apr 2025 01:04:11 +0100 Subject: [PATCH 34/35] formatted code with prettier --- website/src/repl/components/panel/ImportSoundsButton.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/website/src/repl/components/panel/ImportSoundsButton.jsx b/website/src/repl/components/panel/ImportSoundsButton.jsx index b668cba6..999bbf69 100644 --- a/website/src/repl/components/panel/ImportSoundsButton.jsx +++ b/website/src/repl/components/panel/ImportSoundsButton.jsx @@ -21,8 +21,7 @@ export default function ImportSoundsButton({ onComplete }) { return (