mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-12 22:15:27 -04:00
Merge branch 'main' into uzu
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
name: Strudel tests
|
||||
|
||||
on: [push, pull_request]
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -11,13 +11,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: apt install ztd
|
||||
run: apt update && apt install -y zstd
|
||||
- uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 9.12.2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
# cache: 'pnpm'
|
||||
cache: 'pnpm'
|
||||
- run: pnpm install
|
||||
- run: pnpm run format-check
|
||||
- run: pnpm run lint
|
||||
|
||||
Vendored
+12
@@ -8,6 +8,11 @@ import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.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 archBtw, { settings as archBtwSettings } from './themes/archBtw.mjs';
|
||||
import fruitDaw, { settings as fruitDawSettings } from './themes/fruitDaw.mjs';
|
||||
|
||||
import bluescreenlight, { settings as bluescreenlightsettings } from './themes/bluescreenlight.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';
|
||||
@@ -39,17 +44,20 @@ import { setTheme } from '@strudel/draw';
|
||||
export const themes = {
|
||||
strudelTheme,
|
||||
algoboy,
|
||||
archBtw,
|
||||
androidstudio,
|
||||
atomone,
|
||||
aura,
|
||||
bbedit,
|
||||
blackscreen,
|
||||
bluescreen,
|
||||
bluescreenlight,
|
||||
CutiePi,
|
||||
darcula,
|
||||
dracula,
|
||||
duotoneDark,
|
||||
eclipse,
|
||||
fruitDaw,
|
||||
githubDark,
|
||||
githubLight,
|
||||
greenText,
|
||||
@@ -78,10 +86,12 @@ export const themes = {
|
||||
export const settings = {
|
||||
strudelTheme: strudelThemeSettings,
|
||||
bluescreen: bluescreenSettings,
|
||||
bluescreenlight: bluescreenlightsettings,
|
||||
blackscreen: blackscreenSettings,
|
||||
whitescreen: whitescreenSettings,
|
||||
teletext: teletextSettings,
|
||||
algoboy: algoboySettings,
|
||||
archBtw: archBtwSettings,
|
||||
androidstudio: androidstudioSettings,
|
||||
atomone: atomOneSettings,
|
||||
aura: auraSettings,
|
||||
@@ -92,9 +102,11 @@ export const settings = {
|
||||
eclipse: eclipseSettings,
|
||||
CutiePi: CutiePiSettings,
|
||||
sonicPink: sonicPinkSettings,
|
||||
fruitDaw: fruitDawSettings,
|
||||
githubLight: githubLightSettings,
|
||||
githubDark: githubDarkSettings,
|
||||
greenText: greenTextSettings,
|
||||
|
||||
gruvboxDark: gruvboxDarkSettings,
|
||||
gruvboxLight: gruvboxLightSettings,
|
||||
materialDark: materialDarkSettings,
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Arch Btw
|
||||
* Modern terminal inspired theme
|
||||
* made by Jade
|
||||
*/
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from './theme-helper.mjs';
|
||||
|
||||
const hex = ['rgb(0, 0, 0)', 'rgb(82, 208, 250)', 'rgba(113, 208, 250, .4)', 'rgba(113, 208, 250, .15)'];
|
||||
|
||||
export const settings = {
|
||||
background: hex[0],
|
||||
lineBackground: 'transparent',
|
||||
foreground: hex[1],
|
||||
selection: hex[2],
|
||||
selectionMatch: hex[0],
|
||||
gutterBackground: hex[0],
|
||||
gutterForeground: hex[2],
|
||||
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[1],
|
||||
},
|
||||
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] },
|
||||
{ tag: [t.comment, t.brace, t.bracket], color: hex[2] },
|
||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[1] },
|
||||
{ tag: [t.attributeName, t.number], color: hex[1] },
|
||||
{ tag: t.keyword, color: hex[1] },
|
||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] },
|
||||
],
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* A lighter blue screen theme
|
||||
* made by Jade
|
||||
*/
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from './theme-helper.mjs';
|
||||
|
||||
const hex = ['rgb(75, 130, 247)', 'rgb(47, 108, 246)', 'rgb(255, 255, 255)', 'rgba(255, 255, 255,.3)'];
|
||||
|
||||
export const settings = {
|
||||
background: hex[0],
|
||||
lineBackground: 'transparent',
|
||||
foreground: hex[2],
|
||||
selection: hex[3],
|
||||
selectionMatch: hex[0],
|
||||
gutterBackground: hex[0],
|
||||
gutterForeground: hex[2],
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: hex[1],
|
||||
};
|
||||
|
||||
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[2] },
|
||||
{ tag: [t.comment, t.bracket, t.brace, t.compareOperator], color: hex[3] },
|
||||
{ tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] },
|
||||
{ tag: [t.attributeName, t.number], color: hex[2] },
|
||||
{ tag: t.keyword, color: hex[2] },
|
||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] },
|
||||
],
|
||||
});
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Fruit Daw
|
||||
* made by Jade
|
||||
*/
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
import { createTheme } from './theme-helper.mjs';
|
||||
|
||||
const hex = [
|
||||
'rgb(84, 93, 98)',
|
||||
'rgb(255, 255, 255)',
|
||||
'rgba(255, 255, 255, .25)',
|
||||
'rgb(67, 76, 81)',
|
||||
'rgb(186, 230, 115)',
|
||||
'rgb(252, 184, 67)',
|
||||
'rgb(124, 206, 254)',
|
||||
'rgb(83, 101, 102)',
|
||||
'rgba(46, 62, 72,.5)',
|
||||
'rgb(94, 100, 108)',
|
||||
'rgb(167, 216, 177)',
|
||||
];
|
||||
|
||||
export const settings = {
|
||||
background: hex[0],
|
||||
lineBackground: 'transparent',
|
||||
foreground: hex[10],
|
||||
selection: hex[8],
|
||||
selectionMatch: hex[0],
|
||||
gutterBackground: hex[3],
|
||||
gutterForeground: hex[2],
|
||||
gutterBorder: 'transparent',
|
||||
lineHighlight: hex[3],
|
||||
};
|
||||
|
||||
export default createTheme({
|
||||
theme: 'dark',
|
||||
settings,
|
||||
styles: [
|
||||
{
|
||||
tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction],
|
||||
color: hex[1],
|
||||
},
|
||||
{ tag: [t.bool, t.special(t.variableName)], color: hex[1] },
|
||||
{ tag: [t.comment, t.brace, t.bracket], color: hex[2] },
|
||||
{ tag: [t.variableName], color: hex[1] },
|
||||
{ tag: [t.labelName, t.propertyName, t.self, t.atom], color: hex[5] },
|
||||
{ tag: [t.attributeName, t.number], color: hex[6] },
|
||||
{ tag: t.keyword, color: hex[5] },
|
||||
{ tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[4] },
|
||||
],
|
||||
});
|
||||
@@ -1984,6 +1984,7 @@ export const apply = register('apply', function (func, pat) {
|
||||
|
||||
/**
|
||||
* Plays the pattern at the given cycles per minute.
|
||||
* @deprecated
|
||||
* @example
|
||||
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
|
||||
*/
|
||||
@@ -2722,7 +2723,7 @@ export function stepcat(...timepats) {
|
||||
if (timepats.length === 0) {
|
||||
return nothing;
|
||||
}
|
||||
const findsteps = (x) => (Array.isArray(x) ? x : [x._steps, x]);
|
||||
const findsteps = (x) => (Array.isArray(x) ? x : [x._steps ?? 1, x]);
|
||||
timepats = timepats.map(findsteps);
|
||||
if (timepats.find((x) => x[0] === undefined)) {
|
||||
const times = timepats.map((a) => a[0]).filter((x) => x !== undefined);
|
||||
|
||||
@@ -86,6 +86,17 @@ export function repl({
|
||||
const pause = () => scheduler.pause();
|
||||
const toggle = () => scheduler.toggle();
|
||||
const setCps = (cps) => scheduler.setCps(cps);
|
||||
|
||||
/**
|
||||
* Changes the global tempo to the given cycles per minute
|
||||
*
|
||||
* @name setcpm
|
||||
* @alias setCpm
|
||||
* @param {number} cpm cycles per minute
|
||||
* @example
|
||||
* setcpm(140/4) // =140 bpm in 4/4
|
||||
* $: s("bd*4,[- sd]*2").bank('tr707')
|
||||
*/
|
||||
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
|
||||
|
||||
// TODO - not documented as jsdoc examples as the test framework doesn't simulate enough context for `each` and `all`..
|
||||
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
stackCentre,
|
||||
stepcat,
|
||||
sometimes,
|
||||
expand,
|
||||
} from '../index.mjs';
|
||||
|
||||
import { steady } from '../signal.mjs';
|
||||
@@ -1179,6 +1180,9 @@ describe('Pattern', () => {
|
||||
it('calculates undefined steps as the average', () => {
|
||||
expect(sameFirst(stepcat(pure(1), pure(2), pure(3).setSteps(undefined)), fastcat(1, 2, 3)));
|
||||
});
|
||||
it('works with auto-reified values', () => {
|
||||
expect(sameFirst(stepcat(expand(3, 'bd'), 'rim'), stepcat(expand(3, 'bd'), pure('rim'))));
|
||||
});
|
||||
});
|
||||
describe('shrink', () => {
|
||||
it('can shrink', () => {
|
||||
|
||||
@@ -56,14 +56,15 @@ You can use button inputs to control different aspects of your music, such as ga
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`const gp = gamepad(0)
|
||||
// Use button values to control amplitude
|
||||
setcpm(120)
|
||||
// Use button values to control amplitude
|
||||
$: stack(
|
||||
s("[[hh hh] oh hh oh]/2").mask(gp.tglX).bank("RolandTR909"), // X btn for HH
|
||||
s("cr*1").mask(gp.Y).bank("RolandTR909"), // LB btn for CR
|
||||
s("bd").mask(gp.tglA).bank("RolandTR909"), // A btn for BD
|
||||
s("[ht - - mt - - lt - ]/2").mask(gp.tglB).bank("RolandTR909"), // B btn for Toms
|
||||
s("sd*4").mask(gp.RB).bank("RolandTR909"), // RB btn for SD
|
||||
).cpm(120)
|
||||
)
|
||||
`}
|
||||
/>
|
||||
|
||||
@@ -74,14 +75,13 @@ Analog sticks can be used for continuous control, such as pitch shifting or pann
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`const gp = gamepad(0)
|
||||
// Use analog stick for continuous control
|
||||
setcpm(120)
|
||||
// Use analog stick for continuous control
|
||||
$: note("c4 d3 a3 e3").sound("sawtooth")
|
||||
.lpf(gp.x1.range(100,4000))
|
||||
.lpq(gp.y1.range(5,30))
|
||||
.decay(gp.y2.range(0.1,2))
|
||||
.lpenv(gp.x2.range(-5,5))
|
||||
.cpm(120)
|
||||
`}
|
||||
.lpenv(gp.x2.range(-5,5))`}
|
||||
/>
|
||||
|
||||
### Button Sequences
|
||||
@@ -89,6 +89,7 @@ $: note("c4 d3 a3 e3").sound("sawtooth")
|
||||
You can define button sequences to trigger specific actions, like playing a sound when a sequence is detected.
|
||||
|
||||
<MiniRepl client:idle tune={`const gp = gamepad(0)
|
||||
setcpm(120)
|
||||
// Define button sequences
|
||||
const HADOUKEN = [
|
||||
'd', // Down
|
||||
@@ -99,7 +100,7 @@ const KONAMI = 'uuddlrlrba' //Konami Code ↑↑↓↓←→←→BA
|
||||
|
||||
// Check butto-n sequence (returns 1 while detected, 0 when not within last 1 second)
|
||||
$: s("free_hadouken -").slow(2)
|
||||
.mask(gp.btnSequence(HADOUKEN)).room(1).cpm(120)
|
||||
.mask(gp.btnSequence(HADOUKEN)).room(1)
|
||||
|
||||
// hadouken.wav by Syna-Max
|
||||
//https://freesound.org/people/Syna-Max/sounds/67674/
|
||||
|
||||
@@ -8450,6 +8450,35 @@ exports[`runs examples > example "seqPLoop" example index 0 1`] = `
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "setcpm" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/4 | s:bd bank:tr707 ]",
|
||||
"[ 1/4 → 1/2 | s:bd bank:tr707 ]",
|
||||
"[ 1/4 → 1/2 | s:sd bank:tr707 ]",
|
||||
"[ 1/2 → 3/4 | s:bd bank:tr707 ]",
|
||||
"[ 3/4 → 1/1 | s:bd bank:tr707 ]",
|
||||
"[ 3/4 → 1/1 | s:sd bank:tr707 ]",
|
||||
"[ 1/1 → 5/4 | s:bd bank:tr707 ]",
|
||||
"[ 5/4 → 3/2 | s:bd bank:tr707 ]",
|
||||
"[ 5/4 → 3/2 | s:sd bank:tr707 ]",
|
||||
"[ 3/2 → 7/4 | s:bd bank:tr707 ]",
|
||||
"[ 7/4 → 2/1 | s:bd bank:tr707 ]",
|
||||
"[ 7/4 → 2/1 | s:sd bank:tr707 ]",
|
||||
"[ 2/1 → 9/4 | s:bd bank:tr707 ]",
|
||||
"[ 9/4 → 5/2 | s:bd bank:tr707 ]",
|
||||
"[ 9/4 → 5/2 | s:sd bank:tr707 ]",
|
||||
"[ 5/2 → 11/4 | s:bd bank:tr707 ]",
|
||||
"[ 11/4 → 3/1 | s:bd bank:tr707 ]",
|
||||
"[ 11/4 → 3/1 | s:sd bank:tr707 ]",
|
||||
"[ 3/1 → 13/4 | s:bd bank:tr707 ]",
|
||||
"[ 13/4 → 7/2 | s:bd bank:tr707 ]",
|
||||
"[ 13/4 → 7/2 | s:sd bank:tr707 ]",
|
||||
"[ 7/2 → 15/4 | s:bd bank:tr707 ]",
|
||||
"[ 15/4 → 4/1 | s:bd bank:tr707 ]",
|
||||
"[ 15/4 → 4/1 | s:sd bank:tr707 ]",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`runs examples > example "shape" example index 0 1`] = `
|
||||
[
|
||||
"[ 0/1 → 1/8 | s:hh shape:0 ]",
|
||||
|
||||
@@ -97,6 +97,7 @@ const toneHelpersMocked = {
|
||||
'_pianoroll',
|
||||
'_spectrum',
|
||||
'markcss',
|
||||
'p',
|
||||
].forEach((mock) => {
|
||||
strudel.Pattern.prototype[mock] = function () {
|
||||
return this;
|
||||
@@ -163,6 +164,7 @@ evalScope(
|
||||
loadCsound,
|
||||
loadcsound,
|
||||
setcps: id,
|
||||
setcpm: id,
|
||||
Clock: {}, // whatever
|
||||
},
|
||||
);
|
||||
|
||||
@@ -168,9 +168,14 @@ Korrekt, der echte Vorteil dieser Schreibweise zeigt sich wenn du Elemente entfe
|
||||
|
||||
</Box>
|
||||
|
||||
**Tempo ändern mit `cpm`**
|
||||
**Tempo ändern mit `setcpm`**
|
||||
|
||||
<MiniRepl client:visible tune={`sound("<bd hh rim hh>*8").cpm(90/4)`} punchcard />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(90/4)
|
||||
sound("<bd hh rim hh>*8")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
@@ -233,8 +238,9 @@ Bolero:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound("sd sd*3 sd sd*3 sd sd sd sd*3 sd sd*3 sd*3 sd*3")
|
||||
.cpm(10)`}
|
||||
tune={`
|
||||
setcpm(10)
|
||||
sound("sd sd*3 sd sd*3 sd sd sd sd*3 sd sd*3 sd*3 sd*3")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -313,18 +319,23 @@ Das haben wir bisher gelernt:
|
||||
Die mit Apostrophen umgebene Mini-Notation benutzt man normalerweise in einer sogenannten Funktion.
|
||||
Die folgenden Funktionen haben wir bereits gesehen:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | -------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| sound | Spielt den Sound mit dem Namen | <MiniRepl client:visible tune={`sound("bd sd")`} /> |
|
||||
| bank | Wählt die Soundbank / Drum Machine | <MiniRepl client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
|
||||
| cpm | Tempo in **C**ycles **p**ro **M**inute | <MiniRepl client:visible tune={`sound("bd sd").cpm(90)`} /> |
|
||||
| n | Sample Nummer | <MiniRepl client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
|
||||
| Name | Description | Example |
|
||||
| ------ | -------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| sound | Spielt den Sound mit dem Namen | <MiniRepl client:visible tune={`sound("bd sd")`} /> |
|
||||
| bank | Wählt die Soundbank / Drum Machine | <MiniRepl client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
|
||||
| setcpm | Tempo in **C**ycles **p**ro **M**inute | <MiniRepl client:visible tune={`setcpm(90); sound("bd sd")`} /> |
|
||||
| n | Sample Nummer | <MiniRepl client:visible tune={`n("0 1 4 2").sound("jazz")`} /> |
|
||||
|
||||
## Beispiele
|
||||
|
||||
**Einfacher Rock Beat**
|
||||
|
||||
<MiniRepl client:visible tune={`sound("bd sd, hh*4").bank("RolandTR505").cpm(100/2)`} punchcard />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(100/2)
|
||||
sound("bd sd, hh*4").bank("RolandTR505")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Klassischer House**
|
||||
|
||||
@@ -339,7 +350,12 @@ Bestimmte Drum Patterns werden oft genreübergreifend wiederverwendet.
|
||||
|
||||
We Will Rock you
|
||||
|
||||
<MiniRepl client:visible tune={`sound("bd*2 cp").bank("RolandTR707").cpm(81/2)`} punchcard />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(81/2)
|
||||
sound("bd*2 cp").bank("RolandTR707")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Yellow Magic Orchestra - Firecracker**
|
||||
|
||||
@@ -354,12 +370,13 @@ We Will Rock you
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
tune={`setcpm(90/4)
|
||||
sound(\`
|
||||
[- - oh - ] [- - - - ] [- - - - ] [- - - - ],
|
||||
[hh hh - - ] [hh - hh - ] [hh - hh - ] [hh - hh - ],
|
||||
[- - - - ] [cp - - - ] [- - - - ] [cp - - - ],
|
||||
[bd - - - ] [- - - bd] [- - bd - ] [- - - bd]
|
||||
\`).cpm(90/4)`}
|
||||
\`)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -367,12 +384,13 @@ We Will Rock you
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
tune={`setcpm(88/4)
|
||||
sound(\`
|
||||
[- - - - ] [- - - - ] [- - - - ] [- - oh:1 - ],
|
||||
[hh hh hh hh] [hh hh hh hh] [hh hh hh hh] [hh hh - - ],
|
||||
[- - - - ] [cp - - - ] [- - - - ] [- cp - - ],
|
||||
[bd bd - - ] [- - bd - ] [bd bd - bd ] [- - - - ]
|
||||
\`).bank("RolandTR808").cpm(88/4)`}
|
||||
\`).bank("RolandTR808")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -380,11 +398,11 @@ We Will Rock you
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`s(\`jazz*2,
|
||||
tune={`setcpm(100/2)
|
||||
s(\`jazz*2,
|
||||
insect [crow metal] - -,
|
||||
- space:4 - space:1,
|
||||
- wind\`)
|
||||
.cpm(100/2)`}
|
||||
- wind\`)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
|
||||
@@ -56,13 +56,13 @@ Diese Seite ist eine Auflistung aller im Workshop vorgestellten Funktionen.
|
||||
|
||||
## Pattern-Effekte
|
||||
|
||||
| Name | Beschreibung | Beispiel |
|
||||
| ---- | --------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| cpm | Tempo in Cycles pro Minute | <MiniRepl client:visible tune={`sound("bd sd").cpm(90)`} /> |
|
||||
| fast | schneller | <MiniRepl client:visible tune={`sound("bd sd").fast(2)`} /> |
|
||||
| slow | langsamer | <MiniRepl client:visible tune={`sound("bd sd").slow(2)`} /> |
|
||||
| rev | rückwärts | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | einen Stereo-Kanal modifizieren | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | addiert Zahlen oder Noten | <MiniRepl client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | jedes Element schneller machen | <MiniRepl client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | verzögert eine modifizierte Kopie | <MiniRepl client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
| Name | Beschreibung | Beispiel |
|
||||
| ------ | --------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| setcpm | Tempo in Cycles pro Minute | <MiniRepl client:visible tune={`setcpm(90); sound("bd sd")`} /> |
|
||||
| fast | schneller | <MiniRepl client:visible tune={`sound("bd sd").fast(2)`} /> |
|
||||
| slow | langsamer | <MiniRepl client:visible tune={`sound("bd sd").slow(2)`} /> |
|
||||
| rev | rückwärts | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | einen Stereo-Kanal modifizieren | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | addiert Zahlen oder Noten | <MiniRepl client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | jedes Element schneller machen | <MiniRepl client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | verzögert eine modifizierte Kopie | <MiniRepl client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
|
||||
@@ -247,15 +247,17 @@ We can also declare different samples for different regions of the keyboard:
|
||||
|
||||
<MiniRepl
|
||||
client:idle
|
||||
tune={`samples({
|
||||
tune={`setcpm(60)
|
||||
samples({
|
||||
'moog': {
|
||||
'g2': 'moog/004_Mighty%20Moog%20G2.wav',
|
||||
'g3': 'moog/005_Mighty%20Moog%20G3.wav',
|
||||
'g4': 'moog/006_Mighty%20Moog%20G4.wav',
|
||||
}}, 'github:tidalcycles/dirt-samples');
|
||||
}}, 'github:tidalcycles/dirt-samples')
|
||||
|
||||
note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
|
||||
.s('moog').clip(1)
|
||||
.gain(.5).cpm(60)`}
|
||||
.s('moog').clip(1)
|
||||
.gain(.5)`}
|
||||
/>
|
||||
|
||||
The sampler will always pick the closest matching sample for the current note!
|
||||
|
||||
@@ -43,21 +43,33 @@ This is why the same CPS can produce different perceived tempos.
|
||||
|
||||
## Setting CPM
|
||||
|
||||
If you're familiar with BPM, you can use the `cpm` method to set the tempo in cycles per minute:
|
||||
If you're familiar with BPM, you can use the `setcpm` method to set the global tempo in cycles per minute:
|
||||
|
||||
<MiniRepl client:visible tune={`s("bd hh").cpm(110)`} />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(110)
|
||||
s("bd hh")`}
|
||||
/>
|
||||
|
||||
If you want to add more beats per cycle, you might want to divide the cpm:
|
||||
|
||||
<MiniRepl client:visible tune={`s("bd sd bd rim, hh*8").cpm(110/4)`} />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(110/4)
|
||||
s("bd sd bd rim, hh*8")`}
|
||||
/>
|
||||
|
||||
Or using 2 beats per cycle:
|
||||
|
||||
<MiniRepl client:visible tune={`s("bd sd, hh*4").cpm(110/2)`} />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(110/2)
|
||||
s("bd sd, hh*4")`}
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
To set a specific bpm, use `.cpm(bpm/bpc)`
|
||||
To set a specific bpm, use `setcpm(bpm/bpc)`
|
||||
|
||||
- bpm: the target beats per minute
|
||||
- bpc: the number of perceived beats per cycle
|
||||
@@ -74,22 +86,31 @@ Many music programs use it as a default.
|
||||
Strudel does not a have concept of bars or measures, there are only cycles.
|
||||
How you use them is up to you. Above, we've had this example:
|
||||
|
||||
<MiniRepl client:visible tune={`s("bd sd bd rim, hh*8").cpm(110/4)`} />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(110/4)
|
||||
s("bd sd bd rim, hh*8")`}
|
||||
/>
|
||||
|
||||
This could be interpreted as 4/4 time with a tempo of 110bpm.
|
||||
We could write out multiple bars like this:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`s(\`<
|
||||
tune={`setcpm(110/4)
|
||||
s(\`<
|
||||
[bd sd bd rim, hh*8]
|
||||
[bd sd bd rim*2, hh*8]
|
||||
>\`).cpm(110/4)`}
|
||||
>\`)`}
|
||||
/>
|
||||
|
||||
Instead of writing out each bar separately, we could express this much shorter:
|
||||
|
||||
<MiniRepl client:visible tune={`s("bd <sd rim*<1 2>>,hh*4").cpm(110/2)`} />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(110/2)
|
||||
s("bd <sd rim*<1 2>>,hh*4")`}
|
||||
/>
|
||||
|
||||
Here we can see that thinking in cycles rather than bars simplifies things a lot!
|
||||
These types of simplifications work because of the repetitive nature of rhythm.
|
||||
@@ -109,10 +130,11 @@ We could also write multiple bars with different time signatures:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`s(\`<
|
||||
tune={`setcpm(110*2)
|
||||
s(\`<
|
||||
[bd hh rim]@3
|
||||
[bd hh rim sd]@4
|
||||
>\`).cpm(110*2)`}
|
||||
>\`)`}
|
||||
/>
|
||||
|
||||
Here we switch between 3/4 and 4/4, keeping the same tempo.
|
||||
@@ -121,10 +143,11 @@ If we don't specify the length, we get what's called a metric modulation:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`s(\`<
|
||||
tune={`setcpm(110/2)
|
||||
s(\`<
|
||||
[bd hh rim]
|
||||
[bd hh rim sd]
|
||||
>\`).cpm(110/2)`}
|
||||
>\`)`}
|
||||
/>
|
||||
|
||||
Now the 3 elements get the same time as the 4 elements, which is why the tempo changes.
|
||||
|
||||
@@ -216,8 +216,9 @@ Finding the right notes can be difficult.. Scales are here to help:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`n("0 2 4 <[6,8] [7,9]>")
|
||||
.scale("C:minor").sound("piano").cpm(60)`}
|
||||
tune={`setcpm(60)
|
||||
n("0 2 4 <[6,8] [7,9]>")
|
||||
.scale("C:minor").sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -242,9 +243,10 @@ Just like anything, we can automate the scale with a pattern:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`n("<0 -3>, 2 4 <[6,8] [7,9]>")
|
||||
tune={`setcpm(60)
|
||||
n("<0 -3>, 2 4 <[6,8] [7,9]>")
|
||||
.scale("<C:major D:mixolydian>/4")
|
||||
.sound("piano").cpm(60)`}
|
||||
.sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -275,9 +277,10 @@ Try changing that number!
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`n("<[4@2 4] [5@2 5] [6@2 6] [5@2 5]>*2")
|
||||
tune={`setcpm(60)
|
||||
n("<[4@2 4] [5@2 5] [6@2 6] [5@2 5]>*2")
|
||||
.scale("<C2:mixolydian F2:mixolydian>/4")
|
||||
.sound("gm_acoustic_bass").cpm(60)`}
|
||||
.sound("gm_acoustic_bass")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -291,7 +294,12 @@ This is also sometimes called triplet swing. You'll often find it in blues and j
|
||||
|
||||
**Replicate**
|
||||
|
||||
<MiniRepl client:visible tune={`note("c!2 [eb,<g a bb a>]").sound("piano").cpm(60)`} punchcard />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(60)
|
||||
note("c!2 [eb,<g a bb a>]").sound("piano")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
|
||||
@@ -166,9 +166,14 @@ Try also changing the number at the end to change the tempo!
|
||||
|
||||
</Box>
|
||||
|
||||
**changing the tempo with cpm**
|
||||
**changing the tempo with setcpm**
|
||||
|
||||
<MiniRepl client:visible tune={`sound("<bd hh rim hh>*8").cpm(90/4)`} punchcard />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(90/4)
|
||||
sound("<bd hh rim hh>*8")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
<Box>
|
||||
|
||||
@@ -283,12 +288,12 @@ This is what we've learned so far:
|
||||
|
||||
The Mini-Notation is usually used inside some function. These are the functions we've seen so far:
|
||||
|
||||
| Name | Description | Example |
|
||||
| ----- | ----------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| sound | plays the sound of the given name | <MiniRepl client:visible tune={`sound("bd sd [- bd] sd")`} /> |
|
||||
| bank | selects the sound bank | <MiniRepl client:visible tune={`sound("bd sd [- bd] sd").bank("RolandTR909")`} /> |
|
||||
| cpm | sets the tempo in cycles per minute | <MiniRepl client:visible tune={`sound("bd sd [- bd] sd").cpm(45)`} /> |
|
||||
| n | select sample number | <MiniRepl client:visible tune={`n("0 1 4 2 0 6 3 2").sound("jazz")`} /> |
|
||||
| Name | Description | Example |
|
||||
| ------ | ----------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| sound | plays the sound of the given name | <MiniRepl client:visible tune={`sound("bd sd [- bd] sd")`} /> |
|
||||
| bank | selects the sound bank | <MiniRepl client:visible tune={`sound("bd sd [- bd] sd").bank("RolandTR909")`} /> |
|
||||
| setcpm | sets the tempo in cycles per minute | <MiniRepl client:visible tune={`setcpm(45); sound("bd sd [- bd] sd")`} /> |
|
||||
| n | select sample number | <MiniRepl client:visible tune={`n("0 1 4 2 0 6 3 2").sound("jazz")`} /> |
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -296,8 +301,8 @@ The Mini-Notation is usually used inside some function. These are the functions
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound("[bd sd]*2, hh*8").bank("RolandTR505")
|
||||
.cpm(100/4)`}
|
||||
tune={`setcpm(100/4)
|
||||
sound("[bd sd]*2, hh*8").bank("RolandTR505")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -314,14 +319,20 @@ Certain drum patterns are reused across genres.
|
||||
|
||||
We Will Rock you
|
||||
|
||||
<MiniRepl client:visible tune={`sound("bd*2 cp").bank("RolandTR707").cpm(81/2)`} punchcard />
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`setcpm(81/2)
|
||||
sound("bd*2 cp").bank("RolandTR707")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
**Yellow Magic Orchestra - Firecracker**
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound("bd sd, - - - hh - hh - -, - perc - perc:1*2")
|
||||
.bank("RolandCompurhythm1000").cpm(120/2)`}
|
||||
tune={`setcpm(120/2)
|
||||
sound("bd sd, - - - hh - hh - -, - perc - perc:1*2")
|
||||
.bank("RolandCompurhythm1000")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -329,12 +340,14 @@ We Will Rock you
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
tune={`
|
||||
setcpm(90/4)
|
||||
sound(\`
|
||||
[- - oh - ] [- - - - ] [- - - - ] [- - - - ],
|
||||
[hh hh - - ] [hh - hh - ] [hh - hh - ] [hh - hh - ],
|
||||
[- - - - ] [cp - - - ] [- - - - ] [cp - - - ],
|
||||
[bd - - - ] [- - - bd] [- - bd - ] [- - - bd]
|
||||
\`).cpm(90/4)`}
|
||||
\`)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -342,12 +355,13 @@ We Will Rock you
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`sound(\`
|
||||
tune={`setcpm(88/4)
|
||||
sound(\`
|
||||
[- - - - ] [- - - - ] [- - - - ] [- - oh:1 - ],
|
||||
[hh hh hh hh] [hh hh hh hh] [hh hh hh hh] [hh hh - - ],
|
||||
[- - - - ] [cp - - - ] [- - - - ] [~ cp - - ],
|
||||
[bd bd - - ] [- - bd - ] [bd bd - bd ] [- - - - ]
|
||||
\`).bank("RolandTR808").cpm(88/4)`}
|
||||
\`).bank("RolandTR808")`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -355,11 +369,11 @@ We Will Rock you
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`s(\`jazz*2,
|
||||
tune={`setcpm(100/2)
|
||||
s(\`jazz*2,
|
||||
insect [crow metal] - -,
|
||||
- space:4 - space:1,
|
||||
- wind\`)
|
||||
.cpm(100/2)`}
|
||||
- wind\`)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
|
||||
@@ -68,9 +68,10 @@ Try commenting out one or more by adding `//` before a line
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`note("c2 [eb3,g3] ".add("<0 <1 -1>>"))
|
||||
tune={`setcpm(60)
|
||||
note("c2 [eb3,g3] ".add("<0 <1 -1>>"))
|
||||
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass").room(.5).cpm(60)`}
|
||||
.sound("gm_acoustic_bass").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
@@ -84,9 +85,10 @@ We can add as often as we like:
|
||||
|
||||
<MiniRepl
|
||||
client:visible
|
||||
tune={`note("c2 [eb3,g3]".add("<0 <1 -1>>").add("0,7"))
|
||||
tune={`setcpm(60)
|
||||
note("c2 [eb3,g3]".add("<0 <1 -1>>").add("0,7"))
|
||||
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
|
||||
.sound("gm_acoustic_bass").room(.5).cpm(60)`}
|
||||
.sound("gm_acoustic_bass").room(.5)`}
|
||||
punchcard
|
||||
/>
|
||||
|
||||
|
||||
@@ -56,13 +56,13 @@ This page is just a listing of all functions covered in the workshop!
|
||||
|
||||
## Pattern Effects
|
||||
|
||||
| name | description | example |
|
||||
| ---- | ----------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| cpm | sets the tempo in cycles per minute | <MiniRepl client:visible tune={`sound("bd sd [~ bd] sd").cpm(45)`} /> |
|
||||
| fast | speed up | <MiniRepl client:visible tune={`sound("bd sd [~ bd] sd").fast(2)`} /> |
|
||||
| slow | slow down | <MiniRepl client:visible tune={`sound("bd sd [~ bd] sd").slow(2)`} /> |
|
||||
| rev | reverse | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | split left/right, modify right | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | add numbers / notes | <MiniRepl client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | speed up each event n times | <MiniRepl client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | copy, shift time & modify | <MiniRepl client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
| name | description | example |
|
||||
| ------ | ----------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| setcpm | sets the tempo in cycles per minute | <MiniRepl client:visible tune={`setcpm(45); sound("bd sd [~ bd] sd")`} /> |
|
||||
| fast | speed up | <MiniRepl client:visible tune={`sound("bd sd [~ bd] sd").fast(2)`} /> |
|
||||
| slow | slow down | <MiniRepl client:visible tune={`sound("bd sd [~ bd] sd").slow(2)`} /> |
|
||||
| rev | reverse | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").rev()`} /> |
|
||||
| jux | split left/right, modify right | <MiniRepl client:visible tune={`n("0 2 4 6").scale("C:minor").jux(rev)`} /> |
|
||||
| add | add numbers / notes | <MiniRepl client:visible tune={`n("0 2 4 6".add("<0 1 2 1>")).scale("C:minor")`} /> |
|
||||
| ply | speed up each event n times | <MiniRepl client:visible tune={`s("bd sd").ply("<1 2 3>")`} /> |
|
||||
| off | copy, shift time & modify | <MiniRepl client:visible tune={`s("bd sd, hh*4").off(1/8, x=>x.speed(2))`} /> |
|
||||
|
||||
@@ -313,26 +313,6 @@ stack(
|
||||
)
|
||||
.fast(2/3)
|
||||
.pianoroll()`;
|
||||
/*
|
||||
export const bridgeIsOver = `// "Bridge is over"
|
||||
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
// @by Felix Roos, bassline by BDP - The Bridge Is Over
|
||||
|
||||
samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'})
|
||||
stack(
|
||||
stack(
|
||||
note("c3*2 [[c3@1.4 bb2] ab2] gb2*2 <[[gb2@1.4 ab2] bb2] gb2>")
|
||||
.gain(.8).clip("[.5 1]*2"),
|
||||
n("<0 1 2 3 4 3 2 1>")
|
||||
.clip(.5)
|
||||
.echoWith(8, 1/32, (x,i)=>x.add(n(i)).velocity(Math.pow(.7,i)))
|
||||
.scale('c4 whole tone')
|
||||
.echo(3, 1/8, .5)
|
||||
).piano(),
|
||||
s("mad").slow(2)
|
||||
).cpm(78).slow(4)
|
||||
.pianoroll()
|
||||
`; */
|
||||
|
||||
export const goodTimes = `// "Good times"
|
||||
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
Reference in New Issue
Block a user