diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs
index 56a6b77d..efbae1f3 100644
--- a/packages/core/pattern.mjs
+++ b/packages/core/pattern.mjs
@@ -3192,6 +3192,25 @@ export const slice = register(
false, // turns off auto-patternification
);
+/**
+ *
+ * make something happen on event time
+ * uses browser timeout which is innacurate for audio tasks
+ * @name onTriggerTime
+ * @memberof Pattern
+ * @returns Pattern
+ * @example
+ * s("bd!8").onTriggerTime((hap) => {console.info(hap)})
+ */
+Pattern.prototype.onTriggerTime = function (func) {
+ return this.onTrigger((t_deprecate, hap, currentTime, cps = 1, targetTime) => {
+ const diff = targetTime - currentTime;
+ window.setTimeout(() => {
+ func(hap);
+ }, diff * 1000);
+ }, false);
+};
+
/**
* Works the same as slice, but changes the playback speed of each slice to match the duration of its step.
* @name splice
diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs
index b6ba5d5c..5fd83bce 100644
--- a/packages/core/signal.mjs
+++ b/packages/core/signal.mjs
@@ -486,13 +486,34 @@ export const wchooseCycles = (...pairs) => _wchooseWith(rand.segment(1), ...pair
export const wrandcat = wchooseCycles;
-// this function expects pat to be a pattern of floats...
-export const perlinWith = (pat) => {
- const pata = pat.fmap(Math.floor);
- const patb = pat.fmap((t) => Math.floor(t) + 1);
+function _perlin(t) {
+ let ta = Math.floor(t);
+ let tb = ta + 1;
const smootherStep = (x) => 6.0 * x ** 5 - 15.0 * x ** 4 + 10.0 * x ** 3;
const interp = (x) => (a) => (b) => a + smootherStep(x) * (b - a);
- return pat.sub(pata).fmap(interp).appBoth(pata.fmap(timeToRand)).appBoth(patb.fmap(timeToRand));
+ const v = interp(t - ta)(timeToRand(ta))(timeToRand(tb));
+ return v;
+}
+export const perlinWith = (tpat) => {
+ return tpat.fmap(_perlin);
+};
+
+function _berlin(t) {
+ const prevRidgeStartIndex = Math.floor(t);
+ const nextRidgeStartIndex = prevRidgeStartIndex + 1;
+
+ const prevRidgeBottomPoint = timeToRand(prevRidgeStartIndex);
+ const nextRidgeTopPoint = timeToRand(nextRidgeStartIndex) + prevRidgeBottomPoint;
+
+ const currentPercent = (t - prevRidgeStartIndex) / (nextRidgeStartIndex - prevRidgeStartIndex);
+ const interp = (a, b, t) => {
+ return a + (b - a) * t;
+ };
+ return interp(prevRidgeBottomPoint, nextRidgeTopPoint, currentPercent) / 2;
+}
+
+export const berlinWith = (tpat) => {
+ return tpat.fmap(_berlin);
};
/**
@@ -506,6 +527,18 @@ export const perlinWith = (pat) => {
*/
export const perlin = perlinWith(time.fmap((v) => Number(v)));
+/**
+ * Generates a continuous pattern of [berlin noise](conceived by Jame Coyne and Jade Rowland as a joke but turned out to be surprisingly cool and useful,
+ * like perlin noise but with sawtooth waves), in the range 0..1.
+ *
+ * @name berlin
+ * @example
+ * // ascending arpeggios
+ * n("0!16".add(berlin.fast(4).mul(14))).scale("d:minor")
+ *
+ */
+export const berlin = berlinWith(time.fmap((v) => Number(v)));
+
export const degradeByWith = register(
'degradeByWith',
(withPat, x, pat) => pat.fmap((a) => (_) => a).appLeft(withPat.filterValues((v) => v > x)),
diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs
index 24f7434a..6f526ec8 100644
--- a/packages/superdough/worklets.mjs
+++ b/packages/superdough/worklets.mjs
@@ -882,7 +882,6 @@ class ByteBeatProcessor extends AudioWorkletProcessor {
const funcValue = this.func(local_t);
let signal = (funcValue & 255) / 127.5 - 1;
const out = signal * 0.2;
-
for (let c = 0; c < output.length; c++) {
//prevent speaker blowout via clipping if threshold exceeds
output[c][i] = clamp(out, -0.4, 0.4);
diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap
index 34914664..0eacfdfb 100644
--- a/test/__snapshots__/examples.test.mjs.snap
+++ b/test/__snapshots__/examples.test.mjs.snap
@@ -1051,6 +1051,75 @@ exports[`runs examples > example "begin" example index 0 1`] = `
]
`;
+exports[`runs examples > example "berlin" example index 0 1`] = `
+[
+ "[ 0/1 → 1/16 | note:D3 ]",
+ "[ 1/16 → 1/8 | note:E3 ]",
+ "[ 1/8 → 3/16 | note:F3 ]",
+ "[ 3/16 → 1/4 | note:G3 ]",
+ "[ 1/4 → 5/16 | note:A3 ]",
+ "[ 5/16 → 3/8 | note:C4 ]",
+ "[ 3/8 → 7/16 | note:D4 ]",
+ "[ 7/16 → 1/2 | note:F4 ]",
+ "[ 1/2 → 9/16 | note:D4 ]",
+ "[ 9/16 → 5/8 | note:E4 ]",
+ "[ 5/8 → 11/16 | note:E4 ]",
+ "[ 11/16 → 3/4 | note:E4 ]",
+ "[ 3/4 → 13/16 | note:F3 ]",
+ "[ 13/16 → 7/8 | note:F3 ]",
+ "[ 7/8 → 15/16 | note:F3 ]",
+ "[ 15/16 → 1/1 | note:F3 ]",
+ "[ 1/1 → 17/16 | note:E3 ]",
+ "[ 17/16 → 9/8 | note:E3 ]",
+ "[ 9/8 → 19/16 | note:E3 ]",
+ "[ 19/16 → 5/4 | note:F3 ]",
+ "[ 5/4 → 21/16 | note:E3 ]",
+ "[ 21/16 → 11/8 | note:F3 ]",
+ "[ 11/8 → 23/16 | note:G3 ]",
+ "[ 23/16 → 3/2 | note:A3 ]",
+ "[ 3/2 → 25/16 | note:A3 ]",
+ "[ 25/16 → 13/8 | note:Bb3 ]",
+ "[ 13/8 → 27/16 | note:Bb3 ]",
+ "[ 27/16 → 7/4 | note:Bb3 ]",
+ "[ 7/4 → 29/16 | note:F3 ]",
+ "[ 29/16 → 15/8 | note:G3 ]",
+ "[ 15/8 → 31/16 | note:Bb3 ]",
+ "[ 31/16 → 2/1 | note:C4 ]",
+ "[ 2/1 → 33/16 | note:C4 ]",
+ "[ 33/16 → 17/8 | note:D4 ]",
+ "[ 17/8 → 35/16 | note:F4 ]",
+ "[ 35/16 → 9/4 | note:G4 ]",
+ "[ 9/4 → 37/16 | note:Bb3 ]",
+ "[ 37/16 → 19/8 | note:Bb3 ]",
+ "[ 19/8 → 39/16 | note:Bb3 ]",
+ "[ 39/16 → 5/2 | note:C4 ]",
+ "[ 5/2 → 41/16 | note:F3 ]",
+ "[ 41/16 → 21/8 | note:F3 ]",
+ "[ 21/8 → 43/16 | note:G3 ]",
+ "[ 43/16 → 11/4 | note:A3 ]",
+ "[ 11/4 → 45/16 | note:A3 ]",
+ "[ 45/16 → 23/8 | note:A3 ]",
+ "[ 23/8 → 47/16 | note:A3 ]",
+ "[ 47/16 → 3/1 | note:A3 ]",
+ "[ 3/1 → 49/16 | note:E3 ]",
+ "[ 49/16 → 25/8 | note:G3 ]",
+ "[ 25/8 → 51/16 | note:Bb3 ]",
+ "[ 51/16 → 13/4 | note:C4 ]",
+ "[ 13/4 → 53/16 | note:D4 ]",
+ "[ 53/16 → 27/8 | note:E4 ]",
+ "[ 27/8 → 55/16 | note:G4 ]",
+ "[ 55/16 → 7/2 | note:A4 ]",
+ "[ 7/2 → 57/16 | note:Bb3 ]",
+ "[ 57/16 → 29/8 | note:Bb3 ]",
+ "[ 29/8 → 59/16 | note:C4 ]",
+ "[ 59/16 → 15/4 | note:C4 ]",
+ "[ 15/4 → 61/16 | note:F3 ]",
+ "[ 61/16 → 31/8 | note:G3 ]",
+ "[ 31/8 → 63/16 | note:G3 ]",
+ "[ 63/16 → 4/1 | note:A3 ]",
+]
+`;
+
exports[`runs examples > example "binary" example index 0 1`] = `
[
"[ 0/1 → 1/3 | s:hh ]",
@@ -5950,6 +6019,43 @@ exports[`runs examples > example "often" example index 0 1`] = `
]
`;
+exports[`runs examples > example "onTriggerTime" example index 0 1`] = `
+[
+ "[ 0/1 → 1/8 | s:bd ]",
+ "[ 1/8 → 1/4 | s:bd ]",
+ "[ 1/4 → 3/8 | s:bd ]",
+ "[ 3/8 → 1/2 | s:bd ]",
+ "[ 1/2 → 5/8 | s:bd ]",
+ "[ 5/8 → 3/4 | s:bd ]",
+ "[ 3/4 → 7/8 | s:bd ]",
+ "[ 7/8 → 1/1 | s:bd ]",
+ "[ 1/1 → 9/8 | s:bd ]",
+ "[ 9/8 → 5/4 | s:bd ]",
+ "[ 5/4 → 11/8 | s:bd ]",
+ "[ 11/8 → 3/2 | s:bd ]",
+ "[ 3/2 → 13/8 | s:bd ]",
+ "[ 13/8 → 7/4 | s:bd ]",
+ "[ 7/4 → 15/8 | s:bd ]",
+ "[ 15/8 → 2/1 | s:bd ]",
+ "[ 2/1 → 17/8 | s:bd ]",
+ "[ 17/8 → 9/4 | s:bd ]",
+ "[ 9/4 → 19/8 | s:bd ]",
+ "[ 19/8 → 5/2 | s:bd ]",
+ "[ 5/2 → 21/8 | s:bd ]",
+ "[ 21/8 → 11/4 | s:bd ]",
+ "[ 11/4 → 23/8 | s:bd ]",
+ "[ 23/8 → 3/1 | s:bd ]",
+ "[ 3/1 → 25/8 | s:bd ]",
+ "[ 25/8 → 13/4 | s:bd ]",
+ "[ 13/4 → 27/8 | s:bd ]",
+ "[ 27/8 → 7/2 | s:bd ]",
+ "[ 7/2 → 29/8 | s:bd ]",
+ "[ 29/8 → 15/4 | s:bd ]",
+ "[ 15/4 → 31/8 | s:bd ]",
+ "[ 31/8 → 4/1 | s:bd ]",
+]
+`;
+
exports[`runs examples > example "orbit" example index 0 1`] = `
[
"[ 0/1 → 1/6 | s:hh delay:0.5 delaytime:0.25 orbit:1 ]",
diff --git a/website/src/config.ts b/website/src/config.ts
index 2f499d55..e88b7f3e 100644
--- a/website/src/config.ts
+++ b/website/src/config.ts
@@ -58,7 +58,7 @@ export const SIDEBAR: Sidebar = {
{ text: 'What is Strudel?', link: 'workshop/getting-started' },
{ text: 'Showcase', link: 'intro/showcase' },
{ text: 'Blog', link: 'blog' },
- { text: 'Community Bakery', link: 'bakery' },
+ /* { text: 'Community Bakery', link: 'bakery' }, */
],
Workshop: [
// { text: 'Getting Started', link: 'workshop/getting-started' },
diff --git a/website/src/repl/components/Header.jsx b/website/src/repl/components/Header.jsx
index 2963a5f9..a97e2bed 100644
--- a/website/src/repl/components/Header.jsx
+++ b/website/src/repl/components/Header.jsx
@@ -93,7 +93,7 @@ export function Header({ context, embedded = false }) {
>
{!isEmbedded && update}
- {!isEmbedded && (
+ {/* !isEmbedded && (
- )}
- {!isEmbedded && (
+ ) */}
+ {/* !isEmbedded && (
- )}
+ ) */}
{!isEmbedded && (
1. hit play - 2. change something -{' '}
3. hit update
-
- If you don't like what you hear, try shuffle!
+ {/*
+ If you don't like what you hear, try shuffle! */}
- To learn more about what this all means, check out the{' '} + {/* To learn more about what this all means, check out the{' '} */} + To get started, check out the interactive tutorial . Also feel free to join the{' '} - tidalcycles discord channel + discord channel {' '} to ask any questions, give feedback or just say hello.
diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index f88e5a6e..36e8099c 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -59,6 +59,8 @@ async function getModule(name) { return modules.find((m) => m.packageName === name); } +const initialCode = `// LOADING`; + export function useReplContext() { const { isSyncEnabled, audioEngineTarget } = useSettings(); const shouldUseWebaudio = audioEngineTarget !== audioEngineTargets.osc; @@ -77,7 +79,7 @@ export function useReplContext() { transpiler, autodraw: false, root: containerRef.current, - initialCode: '// LOADING', + initialCode, pattern: silence, drawTime, drawContext, @@ -133,9 +135,10 @@ export function useReplContext() { code = latestCode; msg = `Your last session has been loaded!`; } else { - const { code: randomTune, name } = await getRandomTune(); - code = randomTune; - msg = `A random code snippet named "${name}" has been loaded!`; + /* const { code: randomTune, name } = await getRandomTune(); + code = randomTune; */ + code = '$: s("[bd