Merge remote-tracking branch 'origin/main' into supradough

This commit is contained in:
Felix Roos 2025-06-12 10:22:08 +02:00
commit 30f7127a92
No known key found for this signature in database
10 changed files with 204 additions and 38 deletions

View file

@ -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

View file

@ -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)),

View file

@ -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);

View file

@ -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 ]",

View file

@ -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' },

View file

@ -93,7 +93,7 @@ export function Header({ context, embedded = false }) {
>
{!isEmbedded && <span>update</span>}
</button>
{!isEmbedded && (
{/* !isEmbedded && (
<button
title="shuffle"
className="hover:opacity-50 p-2 flex items-center space-x-1"
@ -101,8 +101,8 @@ export function Header({ context, embedded = false }) {
>
<span> shuffle</span>
</button>
)}
{!isEmbedded && (
) */}
{/* !isEmbedded && (
<button
title="share"
className={cx(
@ -113,7 +113,7 @@ export function Header({ context, embedded = false }) {
>
<span>share</span>
</button>
)}
) */}
{!isEmbedded && (
<a
title="learn"

View file

@ -142,21 +142,21 @@ function UserPatterns({ context }) {
</div>
<div className="overflow-auto h-full bg-background p-2 rounded-md">
{patternFilter === patternFilterName.user && (
<PatternButtons
onClick={(id) =>
updateCodeWindow(
context,
{ ...userPatterns[id], collection: userPattern.collection },
autoResetPatternOnChange,
)
}
patterns={userPatterns}
started={context.started}
activePattern={activePattern}
viewingPatternID={viewingPatternID}
/>
)}
{/* {patternFilter === patternFilterName.user && ( */}
<PatternButtons
onClick={(id) =>
updateCodeWindow(
context,
{ ...userPatterns[id], collection: userPattern.collection },
autoResetPatternOnChange,
)
}
patterns={userPatterns}
started={context.started}
activePattern={activePattern}
viewingPatternID={viewingPatternID}
/>
{/* )} */}
</div>
</div>
);
@ -250,6 +250,11 @@ export function PatternsTab({ context }) {
const { patternFilter } = useSettings();
return (
<div className="px-4 w-full text-foreground space-y-2 flex flex-col overflow-hidden max-h-full h-full">
<UserPatterns context={context} />
</div>
);
/* return (
<div className="px-4 w-full text-foreground space-y-2 flex flex-col overflow-hidden max-h-full h-full">
<ButtonGroup
value={patternFilter}
@ -263,5 +268,5 @@ export function PatternsTab({ context }) {
<PublicPatterns context={context} />
)}
</div>
);
); */
}

View file

@ -15,17 +15,18 @@ export function WelcomeTab({ context }) {
<br />
<span className="underline">1. hit play</span> - <span className="underline">2. change something</span> -{' '}
<span className="underline">3. hit update</span>
<br />
If you don't like what you hear, try <span className="underline">shuffle</span>!
{/* <br />
If you don't like what you hear, try <span className="underline">shuffle</span>! */}
</p>
<p>
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
<a href={`${baseNoTrailing}/workshop/getting-started/`} target="_blank">
interactive tutorial
</a>
. Also feel free to join the{' '}
<a href="https://discord.com/invite/HGEdXmRkzT" target="_blank">
tidalcycles discord channel
discord channel
</a>{' '}
to ask any questions, give feedback or just say hello.
</p>

View file

@ -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 <hh oh>]*2").bank("tr909").dec(.4)';
msg = `Default code has been loaded`;
}
editor.setCode(code);
setDocumentTitle(code);

View file

@ -7,7 +7,7 @@ import './Repl.css';
import { createClient } from '@supabase/supabase-js';
import { nanoid } from 'nanoid';
import { writeText } from '@tauri-apps/plugin-clipboard-manager';
import { $featuredPatterns, loadDBPatterns } from '@src/user_pattern_utils.mjs';
import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs';
// Create a single supabase client for interacting with your database
export const supabase = createClient(
@ -16,9 +16,9 @@ export const supabase = createClient(
);
let dbLoaded;
if (typeof window !== 'undefined') {
/* if (typeof window !== 'undefined') {
dbLoaded = loadDBPatterns();
}
} */
export async function initCode() {
// load code from url hash (either short hash from database or decode long hash)