mirror of
https://codeberg.org/uzu/strudel
synced 2026-11-01 16:38:32 -05:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24ac74e0ca | |||
| fdaa559377 | |||
| 064cf07e26 | |||
| d5362c208c | |||
| 7dd20fee57 | |||
| 5ab79f2c6f | |||
| 67ce7c8ad6 | |||
| e00b6f6216 | |||
| 37097a92af | |||
| 669217ee7d |
@@ -131,3 +131,6 @@ fabric.properties
|
||||
samples/*
|
||||
!samples/README.md
|
||||
.idea/
|
||||
|
||||
.vscode
|
||||
!settings.json
|
||||
@@ -37,5 +37,5 @@ All community members should be treated with respect and kindness. We welcome co
|
||||
* LLM-generated text (e.g. commit messages, issue comments, forum posts etc) generated by LLMs will be flagged, and ultimately deleted.
|
||||
* We encourage asking each other questions and working through problems to promote collective knowledge and creative ideas (never telling each other to "just google it").
|
||||
* We strongly discourage others from forking/importing our projects into corporate AI platforms, including Microsoft Github.
|
||||
* All contributions must be submitted by a human who understands the changes, is happy to answer questions about them, and where possible, help maintain the code in the future.
|
||||
* All contributions must be submitted by a person who understands the changes, is happy to answer questions about them, and where possible, help maintain the code in the future.
|
||||
* In community discussion channels (at the time of writing, this refers to the uzu discord server), all discussion (including criticism) of LLM technology and use should be kept to designated channels.
|
||||
@@ -43,10 +43,10 @@ There are many ways to contribute to this project! See [contribution guide](./CO
|
||||
|
||||
## Community
|
||||
|
||||
There is a #strudel channel on the TidalCycles discord: <https://discord.com/invite/HGEdXmRkzT>
|
||||
Strudel is a member of the 'Uzu' family of pattern-based live coding languages, along with TidalCycles and friends.
|
||||
|
||||
You can also ask questions and find related discussions on the tidal club forum: <https://club.tidalcycles.org/>
|
||||
There are a range of strudel channels on the Uzu discord: <https://discord.com/invite/HGEdXmRkzT>
|
||||
|
||||
The discord and forum is shared with the haskell (tidal) and python (vortex) siblings of this project.
|
||||
You can also ask questions and find related discussions on the Uzu web forum: <https://uzu.lurk.org/>
|
||||
|
||||
We also have a mastodon account: <a rel="me" href="https://social.toplap.org/@strudel">social.toplap.org/@strudel</a>
|
||||
|
||||
@@ -2702,7 +2702,32 @@ export const { seconds } = registerControl('seconds');
|
||||
export const { songPtr } = registerControl('songPtr');
|
||||
export const { uid } = registerControl('uid');
|
||||
export const { val } = registerControl('val');
|
||||
|
||||
/**
|
||||
* Plays the pattern at the given cycles per second. The cps can be patterned. This changes the global tempo, so it generally
|
||||
* makes sense to have either `cps` (or `cpm` - cycles per minute) set by only one of your patterns.
|
||||
* Note that this currently does not work when the 'Sync across Browser Tabs / Windows' setting is switched on.
|
||||
* @tags temporal
|
||||
* @example
|
||||
* s("bd hh sd hh").cps(0.5) // = 1 cycle every 2 seconds
|
||||
* @example
|
||||
* // a smooth change over 4 cycles
|
||||
* s("bd hh sd hh").cps(sine.slow(4).range(0.5,1.5))
|
||||
*/
|
||||
export const { cps } = registerControl('cps');
|
||||
|
||||
/**
|
||||
* Plays the pattern at the given cycles per minute. The cpm can be patterned. This changes the global tempo, so it generally
|
||||
* makes sense to have either `cpm` (or `cps` - cycles per second) set by only one of your patterns.
|
||||
* Note that this currently does not work when the 'Sync across Browser Tabs / Windows' setting is switched on.
|
||||
* @tags temporal
|
||||
* @example
|
||||
* s("bd hh sd hh").cpm(90) // = feels like 180 bpm
|
||||
* // a smooth change over 4 cycles
|
||||
* s("bd hh sd hh").cpm(sine.slow(4).range(30,70))
|
||||
*/
|
||||
export const { cpm } = registerControl('cpm');
|
||||
|
||||
/**
|
||||
* Multiplies the duration with the given number. Also cuts samples off at the end if they exceed the duration.
|
||||
*
|
||||
|
||||
@@ -84,10 +84,10 @@ export class Cyclist {
|
||||
// cps changes first, so the durations of the simultaneous haps
|
||||
// are adjusted accordingly.
|
||||
if (aBegin.eq(bBegin)) {
|
||||
if (a.value.cps !== undefined) {
|
||||
return b.value.cps !== undefined ? 0 : -1;
|
||||
if (a.value.cps !== undefined || a.value.cpm !== undefined) {
|
||||
return b.value.cps !== undefined || b.value.cpm !== undefined ? 0 : -1;
|
||||
}
|
||||
if (b.value.cps !== undefined) {
|
||||
if (b.value.cps !== undefined || b.value.cpm !== undefined) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -97,6 +97,8 @@ export class Cyclist {
|
||||
|
||||
haps.forEach((hap) => {
|
||||
const targetTime = (hap.whole.begin - this.checkpoint.cycles) / this.cps + this.checkpoint.seconds;
|
||||
let hapCps = hap.value.cps;
|
||||
hapCps ??= hap.value.cpm != null ? hap.value.cpm / 60 : undefined;
|
||||
if (tickEndTime < targetTime) {
|
||||
// If the targetTime exceeds the tickEndTime, this means
|
||||
// the cps has slowed down, and the latter haps from the query
|
||||
@@ -104,10 +106,10 @@ export class Cyclist {
|
||||
// They'll be scheduled during the next tick.
|
||||
return;
|
||||
}
|
||||
onSchedule?.(hap, targetTime + this.latency, hap.value.cps ?? this.cps);
|
||||
if (hap.value.cps !== undefined && this.cps != hap.value.cps) {
|
||||
onSchedule?.(hap, targetTime + this.latency, hapCps ?? this.cps);
|
||||
if (hapCps !== undefined && this.cps != hapCps) {
|
||||
cpsChanged = true;
|
||||
this.cps = hap.value.cps;
|
||||
this.cps = hapCps;
|
||||
this.num_ticks_since_checkpoint = 0;
|
||||
this.checkpoint = {
|
||||
cycles: hap.whole.begin,
|
||||
|
||||
@@ -2476,18 +2476,6 @@ export const apply = register('apply', function (func, pat) {
|
||||
return func(pat);
|
||||
});
|
||||
|
||||
/**
|
||||
* Plays the pattern at the given cycles per minute.
|
||||
* @tags temporal
|
||||
* @deprecated
|
||||
* @example
|
||||
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
|
||||
*/
|
||||
// this is redefined in repl.mjs, using the current cps as divisor
|
||||
export const cpm = register('cpm', function (cpm, pat) {
|
||||
return pat._fast(cpm / 60 / 1);
|
||||
});
|
||||
|
||||
/**
|
||||
* Nudge a pattern to start earlier in time. Equivalent of Tidal's <~ operator
|
||||
*
|
||||
|
||||
@@ -217,14 +217,10 @@ export function repl({
|
||||
} catch (err) {
|
||||
console.warn('injectPatternMethods: error:', err);
|
||||
}
|
||||
const cpm = register('cpm', function (cpm, pat) {
|
||||
return pat._fast(cpm / 60 / scheduler.cps);
|
||||
});
|
||||
return evalScope({
|
||||
all,
|
||||
each,
|
||||
hush,
|
||||
cpm,
|
||||
setCps,
|
||||
setcps: setCps,
|
||||
setCpm,
|
||||
|
||||
@@ -1,5 +1,68 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`runs examples > example "" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd cps:0.5 ]",
|
||||
"[ 1/4 → 1/2 | s:hh cps:0.5 ]",
|
||||
"[ 1/2 → 3/4 | s:sd cps:0.5 ]",
|
||||
"[ 3/4 → 1/1 | s:hh cps:0.5 ]",
|
||||
"[ 1/1 → 5/4 | s:bd cps:0.5 ]",
|
||||
"[ 5/4 → 3/2 | s:hh cps:0.5 ]",
|
||||
"[ 3/2 → 7/4 | s:sd cps:0.5 ]",
|
||||
"[ 7/4 → 2/1 | s:hh cps:0.5 ]",
|
||||
"[ 2/1 → 9/4 | s:bd cps:0.5 ]",
|
||||
"[ 9/4 → 5/2 | s:hh cps:0.5 ]",
|
||||
"[ 5/2 → 11/4 | s:sd cps:0.5 ]",
|
||||
"[ 11/4 → 3/1 | s:hh cps:0.5 ]",
|
||||
"[ 3/1 → 13/4 | s:bd cps:0.5 ]",
|
||||
"[ 13/4 → 7/2 | s:hh cps:0.5 ]",
|
||||
"[ 7/2 → 15/4 | s:sd cps:0.5 ]",
|
||||
"[ 15/4 → 4/1 | s:hh cps:0.5 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "" example index 0 2`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd cpm:50 ]",
|
||||
"[ 1/4 → 1/2 | s:hh cpm:57.6536686473018 ]",
|
||||
"[ 1/2 → 3/4 | s:sd cpm:64.14213562373095 ]",
|
||||
"[ 3/4 → 1/1 | s:hh cpm:68.47759065022574 ]",
|
||||
"[ 1/1 → 5/4 | s:bd cpm:70 ]",
|
||||
"[ 5/4 → 3/2 | s:hh cpm:68.47759065022574 ]",
|
||||
"[ 3/2 → 7/4 | s:sd cpm:64.14213562373095 ]",
|
||||
"[ 7/4 → 2/1 | s:hh cpm:57.6536686473018 ]",
|
||||
"[ 2/1 → 9/4 | s:bd cpm:50 ]",
|
||||
"[ 9/4 → 5/2 | s:hh cpm:42.34633135269821 ]",
|
||||
"[ 5/2 → 11/4 | s:sd cpm:35.85786437626905 ]",
|
||||
"[ 11/4 → 3/1 | s:hh cpm:31.52240934977427 ]",
|
||||
"[ 3/1 → 13/4 | s:bd cpm:30 ]",
|
||||
"[ 13/4 → 7/2 | s:hh cpm:31.522409349774268 ]",
|
||||
"[ 7/2 → 15/4 | s:sd cpm:35.85786437626905 ]",
|
||||
"[ 15/4 → 4/1 | s:hh cpm:42.34633135269819 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "" example index 1 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd cps:1 ]",
|
||||
"[ 1/4 → 1/2 | s:hh cps:1.1913417161825448 ]",
|
||||
"[ 1/2 → 3/4 | s:sd cps:1.3535533905932737 ]",
|
||||
"[ 3/4 → 1/1 | s:hh cps:1.4619397662556435 ]",
|
||||
"[ 1/1 → 5/4 | s:bd cps:1.5 ]",
|
||||
"[ 5/4 → 3/2 | s:hh cps:1.4619397662556435 ]",
|
||||
"[ 3/2 → 7/4 | s:sd cps:1.3535533905932737 ]",
|
||||
"[ 7/4 → 2/1 | s:hh cps:1.1913417161825448 ]",
|
||||
"[ 2/1 → 9/4 | s:bd cps:1 ]",
|
||||
"[ 9/4 → 5/2 | s:hh cps:0.8086582838174552 ]",
|
||||
"[ 5/2 → 11/4 | s:sd cps:0.6464466094067263 ]",
|
||||
"[ 11/4 → 3/1 | s:hh cps:0.5380602337443567 ]",
|
||||
"[ 3/1 → 13/4 | s:bd cps:0.5 ]",
|
||||
"[ 13/4 → 7/2 | s:hh cps:0.5380602337443567 ]",
|
||||
"[ 7/2 → 15/4 | s:sd cps:0.6464466094067262 ]",
|
||||
"[ 15/4 → 4/1 | s:hh cps:0.8086582838174547 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "_euclidRot" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/5 | note:c3 ]",
|
||||
@@ -2789,29 +2852,6 @@ exports[`runs examples > example "cosine" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "cpm" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/3 | s:hh ]",
|
||||
"[ 0/1 → 2/3 | s:bd ]",
|
||||
"[ 1/3 → 2/3 | s:hh ]",
|
||||
"[ 2/3 → 1/1 | s:hh ]",
|
||||
"[ 2/3 → 4/3 | s:sd ]",
|
||||
"[ 1/1 → 4/3 | s:hh ]",
|
||||
"[ 4/3 → 5/3 | s:hh ]",
|
||||
"[ 4/3 → 2/1 | s:bd ]",
|
||||
"[ 5/3 → 2/1 | s:hh ]",
|
||||
"[ 2/1 → 7/3 | s:hh ]",
|
||||
"[ 2/1 → 8/3 | s:sd ]",
|
||||
"[ 7/3 → 8/3 | s:hh ]",
|
||||
"[ 8/3 → 3/1 | s:hh ]",
|
||||
"[ 8/3 → 10/3 | s:bd ]",
|
||||
"[ 3/1 → 10/3 | s:hh ]",
|
||||
"[ 10/3 → 11/3 | s:hh ]",
|
||||
"[ 10/3 → 4/1 | s:sd ]",
|
||||
"[ 11/3 → 4/1 | s:hh ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "crush" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/6 | s:hh crush:16 ]",
|
||||
|
||||
@@ -14,7 +14,7 @@ These pages will introduce you to [Strudel](https://strudel.cc/), a web-based [l
|
||||
|
||||
# What is Strudel?
|
||||
|
||||
[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://froos.cc/) in 2022.
|
||||
[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://froos.cc/) in 2022, quickly growing into a collective free/open source project with major [contributors and maintainers](https://codeberg.org/uzu/strudel/activity/contributors) including [Jade Rowland](https://www.instagram.com/_switch_angel/) and [Aria](https://www.instagram.com/gglossingg/).
|
||||
Tidal Cycles, also known as Tidal, is a language for [algorithmic pattern](https://algorithmicpattern.org), and though it is most commonly used for [making music](https://tidalcycles.org/docs/showcase), it can be used for any kind of pattern making activity, including [weaving](https://www.youtube.com/watch?v=TfEmEsusXjU).
|
||||
|
||||
Tidal was first implemented as a library written in the [Haskell](https://www.haskell.org/) functional programming language, and by itself it does not make any sound.
|
||||
|
||||
Reference in New Issue
Block a user