mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-22 05:05:26 -04:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71e60ed8de | |||
| e2a29914c7 | |||
| a9a3d491a2 | |||
| a750764f88 | |||
| 5b397009ab | |||
| e68df9e789 | |||
| 4d3bbd5057 | |||
| c0184dd827 | |||
| 09fdc45ff2 | |||
| c01b4c651c | |||
| 634eed1d82 | |||
| 01810f379f | |||
| f3c4afaa54 | |||
| a8757fecc8 | |||
| a1d181d609 | |||
| 440f1cb081 | |||
| 95193b0bfd | |||
| 66673d211e | |||
| 64ab3c9ba7 | |||
| 6c4aa12c60 | |||
| a8f2537e31 | |||
| 81ca199a96 | |||
| 8d8b8ab99e | |||
| a4f10d5339 | |||
| 33a73c37bc | |||
| 4e6d39666a | |||
| a5b6022506 |
@@ -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
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ git remote set-url origin git@codeberg.org:uzu/strudel.git
|
||||
|
||||
To get in touch with the contributors, either
|
||||
|
||||
- [join the Tidal Discord Channel](https://discord.gg/remJ6gQA) and go to the #strudel channel
|
||||
- [join the Tidal Discord Channel](https://discord.com/invite/HGEdXmRkzT) and go to the #strudel channel
|
||||
- Find related discussions on the [tidal club forum](https://club.tidalcycles.org/)
|
||||
|
||||
## Ask a Question
|
||||
|
||||
@@ -32,7 +32,7 @@ This project is organized into many [packages](./packages), which are also avail
|
||||
|
||||
Read more about how to use these in your own project [here](https://strudel.cc/technical-manual/project-start).
|
||||
|
||||
You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE.md). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details.
|
||||
You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details.
|
||||
|
||||
Licensing info for the default sound banks can be found over on the [dough-samples](https://github.com/felixroos/dough-samples/blob/main/README.md) repository.
|
||||
|
||||
|
||||
@@ -1981,6 +1981,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
|
||||
*/
|
||||
@@ -2719,7 +2720,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);
|
||||
|
||||
@@ -85,6 +85,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/
|
||||
|
||||
@@ -37,6 +37,13 @@ describe('tonal', () => {
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale without tonic', () => {
|
||||
expect(
|
||||
n(0, 1, 2)
|
||||
.scale('major')
|
||||
.firstCycleValues.map((h) => h.note),
|
||||
).toEqual(['C3', 'D3', 'E3']);
|
||||
});
|
||||
it('scale with mininotation colon', () => {
|
||||
expect(
|
||||
n(0, 1, 2)
|
||||
|
||||
@@ -14,7 +14,7 @@ function scaleStep(step, scale) {
|
||||
scale = scale.replaceAll(':', ' ');
|
||||
step = Math.ceil(step);
|
||||
let { intervals, tonic, empty } = Scale.get(scale);
|
||||
if ((empty && isNote(scale)) || (!empty && !tonic)) {
|
||||
if ((empty && isNote(scale)) || (empty && !tonic)) {
|
||||
throw new Error(`incomplete scale. Make sure to use ":" instead of spaces, example: .scale("C:major")`);
|
||||
} else if (empty) {
|
||||
throw new Error(`invalid scale "${scale}"`);
|
||||
|
||||
@@ -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))`} /> |
|
||||
|
||||
@@ -20,7 +20,7 @@ export function WelcomeTab({ context }) {
|
||||
</p>
|
||||
<p>
|
||||
{/* To learn more about what this all means, check out the{' '} */}
|
||||
To get started, check out the
|
||||
To get started, check out the{' '}
|
||||
<a href={`${baseNoTrailing}/workshop/getting-started/`} target="_blank">
|
||||
interactive tutorial
|
||||
</a>
|
||||
@@ -43,7 +43,7 @@ export function WelcomeTab({ context }) {
|
||||
</a>
|
||||
. You can find the source code at{' '}
|
||||
<a href="https://codeberg.org/uzu/strudel" target="_blank">
|
||||
github
|
||||
codeberg
|
||||
</a>
|
||||
. You can also find <a href="https://github.com/felixroos/dough-samples/blob/main/README.md">licensing info</a>{' '}
|
||||
for the default sound banks there. Please consider to{' '}
|
||||
|
||||
@@ -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