make 0.5hz cps the default (#931)

* 0.5 default cps

* 1 -> 0.5 cps defaults

* start moving examples to 2Hz

* more 2Hz doc edits

* small tweaks

* format

* adapt cycles page

* adapt pitch page

* tonal page

* accumulation

* synth page

* adapt conditional-modifiers

* audio effects page

* adapt signals doc

* fix: errors for signals

* adapt signals page

* start time modifiers

* adapt time modifiers

* adapt factories

* hydra + pattern intro

* adapt mini notation page

* start recipes

* adapt recipes page

* use code_v1 table

* delete old dbdump + add new csv based tool

* fix: tests

* fix: cpm

* shuffle featured patterns

* fix: snapshot

---------

Co-authored-by: Felix Roos <flix91@gmail.com>
This commit is contained in:
Alex McLean 2024-01-22 19:02:34 +00:00 committed by GitHub
parent 46c2c72637
commit 6422047cff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 3852 additions and 2386 deletions

View file

@ -33,7 +33,7 @@ While the Mini Notation is a powerful way to write rhythms concisely, it also ha
client:idle
tune={`stack(
note("c2 eb2(3,8)").s('sawtooth').cutoff(800),
s("bd,~ sd,hh*4")
s("bd(5,8), hh*8")
)`}
/>

View file

@ -28,6 +28,10 @@ import { JsDoc } from '../../docs/JsDoc';
<JsDoc client:idle name="Pattern.chunkBack" h={0} />
### fastChunk
<JsDoc client:idle name="Pattern.fastChunk" h={0} />
## arp
<JsDoc client:idle name="Pattern#arp" h={0} />

View file

@ -150,7 +150,7 @@ Pitch envelopes can breathe life into static sounds:
.s("gm_electric_guitar_jazz")
.penv("<.5 0 7 -2>*2").vib("4:.1")
.phaser(2).delay(.25).room(.3)
.size(4).fast(.75)`}
.size(4).fast(1.5)`}
/>
You also create some lovely chiptune-style sounds:
@ -163,7 +163,7 @@ You also create some lovely chiptune-style sounds:
.voicing().add(note("<0 1>/8"))
.dec(.1).room(.2)
.segment("<4 [2 8]>")
.penv("<0 <2 -2>>").patt(.02)`}
.penv("<0 <2 -2>>").patt(.02).fast(2)`}
/>
Let's break down all pitch envelope controls:

View file

@ -36,7 +36,8 @@ osc(1, -0.9, 300)
.scale(2)
.out()
note("[a,c,e,<a4 ab4 g4 gb4>,b4]/4").s("sawtooth").vib(2)
note("[a,c,e,<a4 ab4 g4 gb4>,b4]/2")
.s("sawtooth").vib(2)
.lpf(600).lpa(2).lpenv(6)
`}
/>
@ -90,7 +91,7 @@ src(s0).kaleid(H("<4 5 6>"))
.out()
//
stack(
s("bd*2,[hh:0:<.5 1>]*4,~ rim").bank("RolandTR909").speed(.9),
s("bd*4,[hh:0:<.5 1>]*8,~ rim").bank("RolandTR909").speed(.9),
note("[<g1!3 <bb1 <f1 d1>>>]*3").s("sawtooth")
.room(.75).sometimes(add(note(12))).clip(.3)
.lpa(.05).lpenv(-4).lpf(2000).lpq(8).ftype('24db')

View file

@ -22,27 +22,25 @@ Before diving deeper into the details, here is a flavour of how the Mini-Notatio
<MiniRepl
client:idle
tune={`note(\`[
[
[e5 [b4 c5] d5 [c5 b4]]
[a4 [a4 c5] e5 [d5 c5]]
[b4 [~ c5] d5 e5]
[c5 a4 a4 ~]
[[~ d5] [~ f5] a5 [g5 f5]]
[e5 [~ c5] e5 [d5 c5]]
[b4 [b4 c5] d5 e5]
[c5 a4 a4 ~]
],[
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[a2 a3 a2 a3 a2 a3 b1 c2]
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]
]
]/16\`)`}
tune={`note(\`<
[e5 [b4 c5] d5 [c5 b4]]
[a4 [a4 c5] e5 [d5 c5]]
[b4 [~ c5] d5 e5]
[c5 a4 a4 ~]
[[~ d5] [~ f5] a5 [g5 f5]]
[e5 [~ c5] e5 [d5 c5]]
[b4 [b4 c5] d5 e5]
[c5 a4 a4 ~]
,
[[e2 e3]*4]
[[a2 a3]*4]
[[g#2 g#3]*2 [e2 e3]*2]
[a2 a3 a2 a3 a2 a3 b1 c2]
[[d2 d3]*4]
[[c2 c3]*4]
[[b1 b2]*2 [e2 e3]*2]
[[a1 a2]*4]
>\`)`}
/>
## Mini Notation Format
@ -74,9 +72,21 @@ Taking the two examples above, we have four and eight events respectively, and s
This is perhaps counter-intuitive if you are used to adding notes in a sequencer or piano roll and the overall length increasing.
But, it will begin to make sense as we go through more elements of mini-notation.
## Multiplication
A sequence can be sped up by multiplying it by a number using the asterisk symbol (`*`):
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2")`} punchcard />
The multiplication by two here means that the sequence will play twice per cycle.
Multiplications can also be decimal (`*2.75`):
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2.75")`} punchcard />
## Division
We can slow the sequence down by enclosing it in brackets and dividing it by a number (`/2`):
Contrary to multiplication, division can slow the sequence down by enclosing it in brackets and dividing it by a number (`/2`):
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]/2")`} punchcard />
@ -102,18 +112,11 @@ The advantage of the angle brackets, is that we can add more events without need
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5 e5 b4>")`} punchcard />
This is more similar to traditional music sequencers and piano rolls, where adding a note increases the perceived overall duration.
We can also play a certain number of notes per cycle by using angle brackets with multiplication:
## Multiplication
<MiniRepl client:idle tune={`note("<e5 b4 d5 c5 a4 c5>*8")`} punchcard />
Contrary to division, a sequence can be sped up by multiplying it by a number using the asterisk symbol (`*`):
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2")`} punchcard />
The multiplication by two here means that the sequence will play twice per cycle.
As with divisions, multiplications can be decimal (`*2.75`):
<MiniRepl client:idle tune={`note("[e5 b4 d5 c5]*2.75")`} punchcard />
Now we are playing 8 notes per cycle!
## Subdividing time with bracket nesting
@ -149,13 +152,13 @@ The following are the same:
But to play multiple chords in a sequence, we have to wrap them in brackets:
<MiniRepl client:idle tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>")`} punchcard />
<MiniRepl client:idle tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4] [b3,e4,g4]>*2")`} punchcard />
## Elongation
With the "@" symbol, we can specify temporal "weight" of a sequence child:
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} punchcard />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>*2")`} punchcard />
Here, the first chord has a weight of 2, making it twice the length of the other chords. The default weight is 1.
@ -163,19 +166,19 @@ Here, the first chord has a weight of 2, making it twice the length of the other
Using "!" we can repeat without speeding up:
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} punchcard />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>*2")`} punchcard />
## Mini-notation review
To recap what we've learned so far, compare the following patterns:
<MiniRepl client:idle tune={`note("<g3 b3 e4 [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]/2 [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]*2 [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4] _ [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")`} />
<MiniRepl client:idle tune={`note("<g3 b3 e4 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4] [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]/2 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]*2 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4] _ [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]@2 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
<MiniRepl client:idle tune={`note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>*2")`} />
## Euclidian rhythms
@ -196,7 +199,7 @@ But using the Euclidian rhythm notation, we only need to express "3 beats over 8
This makes it easy to write patterns with interesting rhythmic structures and variations that still sound familiar:
<MiniRepl client:idle tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(4)`} punchcard canvasHeight={50} />
<MiniRepl client:idle tune={`note("e5(2,8) b4(3,8) d5(2,8) c5(3,8)").slow(2)`} punchcard canvasHeight={50} />
Note that since the example above does not use the third `offset` parameter, it can be written simply as `"(3,8)"`.

View file

@ -18,7 +18,7 @@ Strudel allows loading samples in the form of audio files of various formats (wa
By default, strudel comes with a built-in "sample map", providing a solid base to play with.
<MiniRepl client:idle tune={`s("bd sd,hh*8, misc/2")`} />
<MiniRepl client:idle tune={`s("bd sd [~ bd] sd,hh*16, misc")`} />
Here, we are using the `s` function to play back different default samples (`bd`, `sd`, `hh` and `misc`) to get a drum beat.
@ -59,15 +59,15 @@ If we open the `sounds` tab and then `drum machines`, we can see that the drum s
We _could_ use them like this:
<MiniRepl client:idle tune={`s("RolandTR808_bd RolandTR808_sd,RolandTR808_hh*8")`} />
<MiniRepl client:idle tune={`s("RolandTR808_bd RolandTR808_sd,RolandTR808_hh*16")`} />
... but thats obviously a bit much to write. Using the `bank` function, we can shorten this to:
<MiniRepl client:idle tune={`s("bd sd,hh*8").bank("RolandTR808")`} />
<MiniRepl client:idle tune={`s("bd sd bd sd,hh*16").bank("RolandTR808")`} />
You could even pattern the bank to switch between different drum machines:
<MiniRepl client:idle tune={`s("bd sd,hh*8").bank("<RolandTR808 RolandTR909>")`} />
<MiniRepl client:idle tune={`s("bd sd bd sd,hh*16").bank("RolandTR808 RolandTR909")`} />
Behind the scenes, `bank` will just prepend the drum machine name to the sample name with `_` to get the full name.
This of course only works because the name after `_` (`bd`, `sd` etc..) is standardized.
@ -79,17 +79,21 @@ If we open the `sounds` tab again, followed by tab `drum machines`, there is als
For example `RolandTR909_hh(4)` means there are 4 samples of a TR909 hihat available.
By default, `s` will play the first sample, but we can select the other ones using `n`, starting from 0:
<MiniRepl client:idle tune={`s("hh*4").bank("RolandTR909").n("<0 1 2 3>")`} />
<MiniRepl client:idle tune={`s("hh*8").bank("RolandTR909").n("0 1 2 3")`} />
Numbers that are too high will just wrap around to the beginning
<MiniRepl client:idle tune={`s("hh*4").bank("RolandTR909").n("<0 1 2 3 4 5 6 7>")`} />
<MiniRepl client:idle tune={`s("hh*8").bank("RolandTR909").n("0 1 2 3 4 5 6 7")`} />
Here, 0-3 will play the same sounds as 4-7, because `RolandTR909_hh` only has 4 sounds.
Selecting sounds also works inside the mini notation, using "`:`" like this:
<MiniRepl client:idle tune={`s("bd:1 bd:2,hh:0 hh:1 hh:2 hh:3").bank("RolandTR909")`} />
<MiniRepl
client:idle
tune={`s("bd*4,hh:0 hh:1 hh:2 hh:3 hh:4 hh:5 hh:6 hh:7")
.bank("RolandTR909")`}
/>
# Loading Custom Samples
@ -103,7 +107,7 @@ In this example we create a map using sounds from the default sample map:
sd: 'sd/rytm-01-classic.wav',
hh: 'hh27/000_hh27closedhh.wav',
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
s("bd sd,hh*8")`}
s("bd sd bd sd,hh*16")`}
/>
When you load your own samples, you can choose the names that you will then refer to in your pattern string inside the `s` function.
@ -116,7 +120,7 @@ Compare with this example which uses the same samples, but with different names.
snaredrum: 'sd/rytm-01-classic.wav',
hihat: 'hh27/000_hh27closedhh.wav',
}, 'https://raw.githubusercontent.com/tidalcycles/Dirt-Samples/master/');
s("bassdrum snaredrum, hihat*8")`}
s("bassdrum snaredrum bassdrum snaredrum, hihat*16")`}
/>
Here we have changed the "map" to include longer sample names.
@ -140,7 +144,7 @@ Because GitHub is a popular place for uploading open source samples, it has its
sd: 'sd/rytm-01-classic.wav',
hh: 'hh27/000_hh27closedhh.wav',
}, 'github:tidalcycles/Dirt-Samples/master/');
s("bd sd,hh*8")`}
s("bd sd bd sd,hh*16")`}
/>
The format is `github:user/repo/branch/`.
@ -157,7 +161,7 @@ We can see there are some guitar samples inside the `/samples` folder, so let's
g3: 'samples/guitar/guitar_3.wav',
g4: 'samples/guitar/guitar_4.wav'
}, 'github:jarmitage/jarmitage.github.io/master/');
s("[g0 g1 g2 g3 g4]/5")`}
s("<g0 g1 g2 g3 g4>/2")`}
/>
## Multiple Samples per Sound
@ -171,7 +175,7 @@ It is also possible, to declare multiple files for one sound, using the array no
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
s("<bd:0 bd:1>,~ <sd:0 sd:1>,[hh:0 hh:1]*2")`}
s("bd:0 bd:1,~ <sd:0 sd:1> ~ sd:0,[hh:0 hh:1]*4")`}
/>
The `:0` `:1` etc. are the indices of the array.
@ -184,7 +188,7 @@ The sample number can also be set using `n`:
sd: ['sd/rytm-01-classic.wav','sd/rytm-00-hard.wav'],
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
s("bd,~ sd,hh*4").n("<0 1>")`}
s("bd bd,~ sd ~ sd,hh*8").n("<0 1>")`}
/>
In that case, we might load our guitar sample map a different way:
@ -200,7 +204,7 @@ In that case, we might load our guitar sample map a different way:
'samples/guitar/guitar_4.wav'
]
}, 'github:jarmitage/jarmitage.github.io/master/');
s("[guitar:0 guitar:1 guitar:2 guitar:3 guitar:4]/5")`}
s("<guitar:0 guitar:1 guitar:2 guitar:3 guitar:4>*2")`}
/>
And as above, we can choose the sample number using `n` for even more flexibility:
@ -216,7 +220,7 @@ And as above, we can choose the sample number using `n` for even more flexibilit
'samples/guitar/guitar_4.wav'
]
}, 'github:jarmitage/jarmitage.github.io/master/');
n("<0 1 2 3 4>").s("guitar")`}
n("<0 1 2 3 4>*2").s("guitar")`}
/>
## Pitched Sounds
@ -271,7 +275,7 @@ We can also declare different samples for different regions of the keyboard:
}}, 'github:tidalcycles/Dirt-Samples/master/');
note("g2!2 <bb2 c3>!2, <c4@3 [<eb4 bb3> g4 f4]>")
.s('moog').clip(1)
.gain(.5)`}
.gain(.5).cpm(60)`}
/>
The sampler will always pick the closest matching sample for the current note!
@ -285,9 +289,9 @@ With it, you can enter any sample name(s) to query from [freesound.org](https://
client:idle
tune={`await samples('shabda:bass:4,hihat:4,rimshot:2')
stack(
n("0 1 2 3").s('bass').slow(2),
n("0 1 2 3 0 1 2 3").s('bass'),
n("0 1*2 2 3*2").s('hihat'),
n("~ 0 ~ 1").s('rimshot')
n("~ 0 ~ 1 ~ 0 0 1").s('rimshot')
).clip(1)`}
/>
@ -299,8 +303,8 @@ Note that the language code and the gender parameters are optional and default t
tune={`await samples('shabda/speech:the_drum,forever')
await samples('shabda/speech/fr-FR/m:magnifique')
stack(
s("the_drum").chop(16).speed(rand.range(0.85,1.1)),
s("forever magnifique").slow(8).late(0.25)
s("the_drum*2").chop(16).speed(rand.range(0.85,1.1)),
s("forever magnifique").slow(4).late(0.125)
)`}
/>

View file

@ -16,7 +16,7 @@ The basic waveforms are `sine`, `sawtooth`, `square` and `triangle`, which can b
<MiniRepl
client:idle
tune={`note("c2 <eb2 <g2 g1>>")
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("<sawtooth square triangle sine>")
.scope()`}
/>
@ -28,7 +28,7 @@ If you don't set a `sound` but a `note` the default value for `sound` is `triang
You can also use noise as a source by setting the waveform to: `white`, `pink` or `brown`. These are different
flavours of noise, here written from hard to soft.
<MiniRepl client:idle tune={`sound("<white pink brown>/2").scope()`} />
<MiniRepl client:idle tune={`sound("<white pink brown>").scope()`} />
Here's a more musical example of how to use noise for hihats:
@ -44,7 +44,7 @@ Some amount of pink noise can also be added to any oscillator by using the `nois
You can also use the `crackle` type to play some subtle noise crackles. You can control noise amount by using the `density` parameter:
<MiniRepl client:idle tune={`s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4)).scope()`} />
<MiniRepl client:idle tune={`s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(2)).scope()`} />
### Additive Synthesis
@ -52,7 +52,7 @@ To tame the harsh sound of the basic waveforms, we can set the `n` control to li
<MiniRepl
client:idle
tune={`note("c2 <eb2 <g2 g1>>")
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("sawtooth")
.n("<32 16 8 4>")
.scope()`}
@ -63,7 +63,7 @@ You can also set `n` directly in mini notation with `sound`:
<MiniRepl
client:idle
tune={`note("c2 <eb2 <g2 g1>>")
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("sawtooth:<32 16 8 4>")
.scope()`}
/>
@ -125,7 +125,7 @@ note("<[g3,b3,e4]!2 [a3,c3,e4] [b3,d3,f#4]>")
.n("<1 2 3 4 5 6 7 8 9 10>/2").room(0.5).size(0.9)
.s('wt_flute').velocity(0.25).often(n => n.ply(2))
.release(0.125).decay("<0.1 0.25 0.3 0.4>").sustain(0)
.cutoff(2000).scope({}).cutoff("<1000 2000 4000>").fast(2)`}
.cutoff(2000).scope({}).cutoff("<1000 2000 4000>").fast(4)`}
/>
## ZZFX
@ -137,8 +137,8 @@ It has 20 parameters in total, here is a snippet that uses all:
<MiniRepl
client:idle
tune={`note("<c2 eb2 f2 g2>") // also supports freq
.s("<z_sawtooth z_tan z_noise z_sine z_square>")
tune={`note("c2 eb2 f2 g2") // also supports freq
.s("{z_sawtooth z_tan z_noise z_sine z_square}%4")
.zrand(0) // randomization
// zzfx envelope
.attack(0.001)

View file

@ -14,6 +14,19 @@ These functions use [tonaljs](https://github.com/tonaljs/tonal) to provide helpe
<JsDoc client:idle name="voicing" h={0} />
Here's an example of how you can play chords and a bassline:
<MiniRepl
client:idle
tune={`chord("<C^7 A7b13 Dm7 G7>*2")
.dict('ireal').layer(
x=>x.struct("[~ x]*2").voicing()
,
x=>n("0*4").set(x).mode("root:g2").voicing()
.s('sawtooth').cutoff("800:4:2")
)`}
/>
### scale(name)
<JsDoc client:idle name="scale" h={0} />
@ -22,13 +35,13 @@ These functions use [tonaljs](https://github.com/tonaljs/tonal) to provide helpe
Transposes all notes to the given number of semitones:
<MiniRepl client:only="react" tune={`"c2 c3".fast(2).transpose("<0 -2 5 3>".slow(2)).note()`} />
<MiniRepl client:only="react" tune={`"[c2 c3]*4".transpose("<0 -2 5 3>").note()`} />
This method gets really exciting when we use it with a pattern as above.
Instead of numbers, scientific interval notation can be used as well:
<MiniRepl client:only="react" tune={`"c2 c3".fast(2).transpose("<1P -2M 4P 3m>".slow(2)).note()`} />
<MiniRepl client:only="react" tune={`"[c2 c3]*4".transpose("<1P -2M 4P 3m>").note()`} />
### scaleTranspose(steps)
@ -36,9 +49,9 @@ Transposes notes inside the scale by the number of steps:
<MiniRepl
client:idle
tune={`"-8 [2,4,6]"
tune={`"[-8 [2,4,6]]*2"
.scale('C4 bebop major')
.scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>")
.scaleTranspose("<0 -1 -2 -3 -4 -5 -6 -4>*2")
.note()`}
/>
@ -46,22 +59,28 @@ Transposes notes inside the scale by the number of steps:
Turns chord symbols into voicings, using the smoothest voice leading possible:
<MiniRepl client:only="react" tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()`} />
<MiniRepl
client:only="react"
tune={`stack(
"<C^7 A7 Dm7 G7>".voicings('lefthand'),
"<C3 A2 D3 G2>"
).note()`}
/>
Note: This function might be removed, as `voicing` (without s) is a newer implementation.
### rootNotes(octave = 2)
Turns chord symbols into root notes of chords in given octave.
<MiniRepl client:only="react" tune={`"<C^7 A7b13 Dm7 G7>".rootNotes(3).note()`} />
<MiniRepl client:only="react" tune={`"<C^7 A7b13 Dm7 G7>*2".rootNotes(3).note()`} />
Together with layer, struct and voicings, this can be used to create a basic backing track:
<MiniRepl
client:idle
tune={`"<C^7 A7b13 Dm7 G7>".layer(
x => x.voicings('lefthand').struct("~ x").note(),
tune={`"<C^7 A7b13 Dm7 G7>*2".layer(
x => x.voicings('lefthand').struct("[~ x]*2").note(),
x => x.rootNotes(2).note().s('sawtooth').cutoff(800)
)`}
/>
So far, we've stayed within the browser. [MIDI and OSC](/learn/input-output/) are ways to break out of it.

View file

@ -61,7 +61,7 @@ A sample can be looped and chopped like this:
<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit().chop(16)`}
s("amen/4").fit().chop(32)`}
punchcard
/>
@ -72,9 +72,9 @@ Let's add randmized doubling + reversing:
<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit().chop(16).cut(1)
.sometimesBy(.5, ply(2))
.sometimesBy(.25, mul(speed(-1)))`}
s("amen/4").fit().chop(16).cut(1)
.sometimesBy(.5, ply("2"))
.sometimesBy(.25, mul(speed("-1")))`}
punchcard
/>
@ -83,9 +83,9 @@ If we want to specify the order of samples, we can replace `chop` with `slice`:
<MiniRepl
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen/8").fit()
.slice(8, "<0 1 2 3 4*2 5 6 [6 7]>")
.cut(1).rarely(ply(2))`}
s("amen/4").fit()
.slice(8, "<0 1 2 3 4*2 5 6 [6 7]>*2")
.cut(1).rarely(ply("2"))`}
punchcard
/>
@ -95,8 +95,8 @@ If we use `splice` instead of `slice`, the speed adjusts to the duration of the
client:visible
tune={`await samples('github:yaxu/clean-breaks/main')
s("amen")
.splice(8, "<0 1 2 3 4*2 5 6 [6 7]>")
.cut(1).rarely(ply(2))`}
.splice(8, "<0 1 2 3 4*2 5 6 [6 7]>*2")
.cut(1).rarely(ply("2"))`}
punchcard
/>
@ -104,26 +104,38 @@ Note that we don't need `fit`, because `splice` will do that by itself.
## Filter Envelopes
A minimal filter envelope looks like this:
Using `lpenv`, we can make the filter move:
<MiniRepl
client:visible
tune={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth")
.lpf(400).lpa(.2).lpenv(4)
.lpf(400).lpenv(4)
.scope()`}
/>
We can flip the envelope by setting `lpenv` negative + add some resonance `lpq`:
The type of envelope depends on the methods you're setting. Let's set `lpa`:
<MiniRepl
client:visible
tune={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth").lpq(8)
.lpf(400).lpa(.2).lpenv(-4)
.lpf(400).lpa(.2).lpenv(4)
.scope()`}
/>
Now the filter is attacking, rather than decaying as before (decay is the default). We can also do both
<MiniRepl
client:visible
tune={`note("g1 bb1 <c2 eb2> d2")
.s("sawtooth").lpq(8)
.lpf(400).lpa(.1).lpd(.1).lpenv(4)
.scope()`}
/>
You can play around with `lpa` | `lpd` | `lps` | `lpd` to see what the filter envelope will do.
## Layering Sounds
We can layer sounds by separating them with ",":
@ -183,7 +195,7 @@ A polyrhythm is when 2 different tempos happen at the same time.
This is a polymeter:
<MiniRepl client:visible tune={`s("<bd rim>,<hh hh oh>").fast(2)`} punchcard />
<MiniRepl client:visible tune={`s("<bd rim, hh hh oh>*4")`} punchcard />
A polymeter is when 2 different bar lengths play at the same tempo.
@ -201,8 +213,8 @@ Using `run` with `n`, we can rush through a sample bank:
<MiniRepl
client:visible
tune={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla")`}
tune={`await samples('bubo:fox')
n(run(8)).s("ftabla")`}
punchcard
/>
@ -212,17 +224,17 @@ In this case, I hear the beginning at the third sample, which can be accounted f
<MiniRepl
client:visible
tune={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
n(run(8)).s("ftabla").early(2/8)`}
tune={`await samples('bubo:fox')
n(run(8)).s("ftabla").early(2/8)`}
/>
Let's add some randomness:
<MiniRepl
client:visible
tune={`await samples('github:Bubobubobubobubo/Dough-Fox/main')
tune={`await samples('bubo:fox')
n(run(8)).s("ftabla").early(2/8)
.sometimes(mul(speed(1.5)))`}
.sometimes(mul(speed("1.5")))`}
/>
## Tape Warble
@ -231,7 +243,7 @@ We can emulate a pitch warbling effect like this:
<MiniRepl
client:visible
tune={`note("c4 bb f eb")
tune={`note("<c4 bb f eb>*8")
.add(note(perlin.range(0,.5))) // <------ warble
.clip(2).s("gm_electric_guitar_clean")`}
/>
@ -243,7 +255,7 @@ There are a number of ways to change the sound duration. Using clip:
<MiniRepl
client:visible
tune={`note("f ab bb c")
.clip("<2 1 .5 .25>/2")`}
.clip("<2 1 .5 .25>")`}
/>
The value of clip is relative to the duration of each event.
@ -252,20 +264,18 @@ We can also create overlaps using release:
<MiniRepl
client:visible
tune={`note("f ab bb c")
.release("<2 1 .5 .002>/2")`}
.release("<2 1 .5 .25>")`}
/>
This will smoothly fade out each sound for the given number of seconds.
We could also make the notes shorter with decay / sustain:
We could also make the notes shorter by using a decay envelope:
<MiniRepl
client:visible
tune={`note("f ab bb c")
.decay("<.2 .1 .02>/2").sustain(0)`}
.decay("<2 1 .5 .25>")`}
/>
For now, there is a limitation where decay values that exceed the event duration may cause little cracks, so use higher numbers with caution..
When using samples, we also have `.end` to cut relative to the sample length:
<MiniRepl client:visible tune={`s("oh*4").end("<1 .5 .25 .1>")`} />
@ -274,9 +284,9 @@ Compare that to clip:
<MiniRepl client:visible tune={`s("oh*4").clip("<1 .5 .25 .1>")`} />
or decay / sustain
or decay:
<MiniRepl client:visible tune={`s("oh*4").decay("<.2 .12 .06 .01>").sustain(0)`} />
<MiniRepl client:visible tune={`s("oh*4").decay("<1 .5 .25 .1>")`} />
## Wavetable Synthesis

View file

@ -15,20 +15,20 @@ Strudel's mother language, TidalCycles, even has it in its name.
## Cycles and BPM
In most music software, the unit BPM (beats per minute) is used to set the tempo.
Strudel expresses tempo as CPS (cycles per second), with a default of 1CPS:
Strudel expresses tempo as CPS (cycles per second), with a default of 0.5 CPS:
<MiniRepl client:visible tune={`s("bd")`} />
Here we can hear the 1CPS in action: The kick repeats once per second like a clock.
We could say 1CPS = 1BPS (beats per second) = 60BPM. Let's add another kick:
Here we can hear the 0.5CPS in action: The kick repeats once every two seconds.
Let's make it 4 kicks:
<MiniRepl client:visible tune={`s("bd bd")`} />
<MiniRepl client:visible tune={`s("bd bd bd bd")`} />
Now we have 2 kicks per second, but the whole pattern still plays at 1CPS.
Now we have 4 kicks per cycle, but the whole pattern still plays at 0.5CPS.
In terms of BPM, most musicians would tell you this is playing at 120bpm.
What about this one:
<MiniRepl client:visible tune={`s("bd hh")`} />
<MiniRepl client:visible tune={`s("bd hh bd hh")`} />
Because the second sound is now a hihat, the tempo feels slower again.
This brings us to an important realization:
@ -103,7 +103,7 @@ To get a time signature, just change the number of elements per bar. Here is a r
or with 5:
<MiniRepl client:visible tune={`s("<bd hh hh bd hh hh bd rim bd hh>*5")`} />
<MiniRepl client:visible tune={`s("bd hh hh bd hh hh bd rim bd hh")`} />
We could also write multiple bars with different time signatures:

View file

@ -35,7 +35,7 @@ The upper limit decreases with age. What's your upper limit?
In Strudel, we can play frequencies directly with the `freq` control:
<MiniRepl client:visible tune={`freq("200 [300,500] 400 [500,<600 670 712 670>]")`} />
<MiniRepl client:visible tune={`freq("<200 [300,500] 400 [500,<600 670 712 670>]>*8")`} />
## Frequency vs Pitch Perception

View file

@ -18,7 +18,7 @@ We have sounds, we have notes, now let's look at effects!
<MiniRepl
client:visible
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("sawtooth").lpf(800)`}
/>
@ -35,8 +35,8 @@ lpf = **l**ow **p**ass **f**ilter
<MiniRepl
client:visible
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
.sound("sawtooth").lpf("200 1000")`}
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("sawtooth").lpf("200 1000 200 1000")`}
/>
<Box>
@ -52,8 +52,8 @@ We will learn how to automate with waves later...
<MiniRepl
client:visible
tune={`note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
.sound("sawtooth").vowel("<a e i o>/2")`}
tune={`note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>")
.sound("sawtooth").vowel("<a e i o>")`}
/>
**gain**
@ -61,8 +61,8 @@ We will learn how to automate with waves later...
<MiniRepl
client:visible
tune={`stack(
sound("hh*8").gain("[.25 1]*2"),
sound("bd*2,~ sd:1")
sound("hh*16").gain("[.25 1]*4"),
sound("bd*4,[~ sd:1]*2")
) `}
punchcard
/>
@ -84,13 +84,13 @@ Let's combine all of the above into a little tune:
client:visible
tune={`stack(
stack(
sound("hh*8").gain("[.25 1]*2"),
sound("bd*2,~ sd:1")
sound("hh*8").gain("[.25 1]*4"),
sound("bd*4,[~ sd:1]*2")
),
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
.sound("sawtooth").lpf("200 1000"),
note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>/2")
.sound("sawtooth").vowel("<a e i o>/2")
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("sawtooth").lpf("200 1000 200 1000"),
note("<[c3,g3,e4] [bb2,f3,d4] [a2,f3,c4] [bb2,g3,eb4]>")
.sound("sawtooth").vowel("<a e i o>")
) `}
/>
@ -105,7 +105,7 @@ The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked toget
<MiniRepl
client:visible
tune={`note("<c3 bb2 f3 eb3>")
tune={`note("c3 bb2 f3 eb3")
.sound("sawtooth").lpf(600)
.attack(.1)
.decay(.1)
@ -141,7 +141,7 @@ Can you guess what they do?
<MiniRepl
client:visible
tune={`note("<c3 bb2 f3 eb3>")
tune={`note("c3 bb2 f3 eb3")
.sound("sawtooth").lpf(600)
.adsr(".1:.1:.5:.2")
`}
@ -152,7 +152,7 @@ Can you guess what they do?
<MiniRepl
client:visible
tune={`stack(
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
.sound("gm_electric_guitar_muted"),
sound("<bd rim>").bank("RolandTR707")
).delay(".5")`}
@ -182,7 +182,7 @@ What happens if you use `.delay(".8:.06:.8")` ? Can you guess what the third num
<MiniRepl
client:visible
tune={`n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
tune={`n("<4 [3@3 4] [<2 0> ~@16] ~>")
.scale("D4:minor").sound("gm_accordion:2")
.room(2)`}
/>
@ -200,10 +200,10 @@ Add a delay too!
<MiniRepl
client:visible
tune={`stack(
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
.sound("gm_electric_guitar_muted").delay(.5),
sound("<bd rim>").bank("RolandTR707").delay(.5),
n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
n("<4 [3@3 4] [<2 0> ~@16] ~>")
.scale("D4:minor").sound("gm_accordion:2")
.room(2).gain(.5)
)`}
@ -214,13 +214,13 @@ Let's add a bass to make this complete:
<MiniRepl
client:visible
tune={`stack(
note("~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]")
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
.sound("gm_electric_guitar_muted").delay(.5),
sound("<bd rim>").bank("RolandTR707").delay(.5),
n("<4 [3@3 4] [<2 0> ~@16] ~>/2")
n("<4 [3@3 4] [<2 0> ~@16] ~>")
.scale("D4:minor").sound("gm_accordion:2")
.room(2).gain(.4),
n("<0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~>*2")
n("[0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~]/2")
.scale("D2:minor")
.sound("sawtooth,triangle").lpf(800)
)`}
@ -237,19 +237,18 @@ Try adding `.hush()` at the end of one of the patterns in the stack...
<MiniRepl
client:visible
tune={`sound("numbers:1 numbers:2 numbers:3 numbers:4")
.pan("0 0.3 .6 1")
.slow(2)`}
.pan("0 0.3 .6 1")`}
/>
**speed**
<MiniRepl client:visible tune={`sound("bd rim").speed("<1 2 -1 -2>").room(.2)`} />
<MiniRepl client:visible tune={`sound("bd rim [~ bd] rim").speed("<1 2 -1 -2>").room(.2)`} />
**fast and slow**
We can use `fast` and `slow` to change the tempo of a pattern outside of Mini-Notation:
<MiniRepl client:visible tune={`sound("bd*2,~ rim").slow(2)`} />
<MiniRepl client:visible tune={`sound("bd*4,~ rim ~ cp").slow(2)`} />
<Box>
@ -261,13 +260,13 @@ What happens if you use a pattern like `.fast("<1 [2 4]>")`?
By the way, inside Mini-Notation, `fast` is `*` and `slow` is `/`.
<MiniRepl client:visible tune={`sound("[bd*2,~ rim]*<1 [2 4]>")`} />
<MiniRepl client:visible tune={`sound("[bd*4,~ rim ~ cp]*<1 [2 4]>")`} />
## automation with signals
Instead of changing values stepwise, we can also control them with signals:
<MiniRepl client:visible tune={`sound("hh*16").gain(sine)`} punchcard punchcardLabels={false} />
<MiniRepl client:visible tune={`sound("hh*32").gain(sine)`} punchcard punchcardLabels={false} />
<Box>
@ -283,7 +282,7 @@ The gain is visualized as transparency in the pianoroll.
By default, waves oscillate between 0 to 1. We can change that with `range`:
<MiniRepl client:visible tune={`sound("hh*8").lpf(saw.range(500, 2000))`} />
<MiniRepl client:visible tune={`sound("hh*16").lpf(saw.range(500, 2000))`} />
<Box>
@ -295,9 +294,9 @@ We can change the automation speed with slow / fast:
<MiniRepl
client:visible
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("sawtooth")
.lpf(sine.range(100, 2000).slow(8))`}
.lpf(sine.range(100, 2000).slow(4))`}
/>
<Box>
@ -308,15 +307,15 @@ The whole automation will now take 8 cycles to repeat.
## Recap
| name | example |
| ----- | --------------------------------------------------------------------------------------- |
| lpf | <MiniRepl client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
| vowel | <MiniRepl client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
| gain | <MiniRepl client:visible tune={`s("hh*8").gain("[.25 1]*2")`} /> |
| delay | <MiniRepl client:visible tune={`s("bd rim").delay(.5)`} /> |
| room | <MiniRepl client:visible tune={`s("bd rim").room(.5)`} /> |
| pan | <MiniRepl client:visible tune={`s("bd rim").pan("0 1")`} /> |
| speed | <MiniRepl client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
| range | <MiniRepl client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
| name | example |
| ----- | ---------------------------------------------------------------------------------------- |
| lpf | <MiniRepl client:visible tune={`note("c2 c3 c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
| vowel | <MiniRepl client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
| gain | <MiniRepl client:visible tune={`s("hh*16").gain("[.25 1]*2")`} /> |
| delay | <MiniRepl client:visible tune={`s("bd rim bd cp").delay(.5)`} /> |
| room | <MiniRepl client:visible tune={`s("bd rim bd cp").room(.5)`} /> |
| pan | <MiniRepl client:visible tune={`s("bd rim bd cp").pan("0 1")`} /> |
| speed | <MiniRepl client:visible tune={`s("bd rim bd cp").speed("<1 2 -1 -2>")`} /> |
| range | <MiniRepl client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
Let us now take a look at some of Tidal's typical [pattern effects](/workshop/pattern-effects).

View file

@ -175,11 +175,11 @@ Try adding more notes inside the brackets and notice how it does **not** get fas
**Play one sequence per cycle**
{/* <[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2 */}
{/* <[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4> */}
<MiniRepl
client:visible
tune={`note("<[36 48]*4 [34 46]*4 [41 53]*4 [39 51]*4>/2")
tune={`note("<[36 48]*4 [34 46]*4 [41 53]*4 [39 51]*4>")
.sound("gm_acoustic_bass")`}
punchcard
/>
@ -197,7 +197,7 @@ This is also useful for unpitched sounds:
<MiniRepl
client:visible
tune={`sound("bd*2, ~ <sd cp>, [~ hh]*2")
tune={`sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
.bank("RolandTR909")`}
punchcard
/>
@ -209,7 +209,7 @@ 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")`}
.scale("C:minor").sound("piano").cpm(60)`}
punchcard
/>
@ -236,7 +236,7 @@ Just like anything, we can automate the scale with a pattern:
client:visible
tune={`n("<0 -3>, 2 4 <[6,8] [7,9]>")
.scale("<C:major D:mixolydian>/4")
.sound("piano")`}
.sound("piano").cpm(60)`}
punchcard
/>
@ -269,7 +269,7 @@ Try changing that number!
client:visible
tune={`n("<[4@2 4] [5@2 5] [6@2 6] [5@2 5]>*2")
.scale("<C2:mixolydian F2:mixolydian>/4")
.sound("gm_acoustic_bass")`}
.sound("gm_acoustic_bass").cpm(60)`}
punchcard
/>
@ -283,7 +283,7 @@ 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")`} punchcard />
<MiniRepl client:visible tune={`note("c!2 [eb,<g a bb a>]").sound("piano").cpm(60)`} punchcard />
<Box>
@ -300,7 +300,7 @@ Let's recap what we've learned in this chapter:
| Concept | Syntax | Example |
| --------- | ------ | -------------------------------------------------------- |
| Slow down | \/ | <MiniRepl client:visible tune={`note("[c a f e]/2")`} /> |
| Alternate | \<\> | <MiniRepl client:visible tune={`note("c <e g>")`} /> |
| Alternate | \<\> | <MiniRepl client:visible tune={`note("c a f <e g>")`} /> |
| Elongate | @ | <MiniRepl client:visible tune={`note("c@3 e")`} /> |
| Replicate | ! | <MiniRepl client:visible tune={`note("c!3 e")`} /> |
@ -318,7 +318,7 @@ New functions:
<MiniRepl
client:visible
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
tune={`note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("gm_synth_bass_1")
.lpf(800) // <-- we'll learn about this soon`}
/>
@ -332,7 +332,7 @@ New functions:
[~ 0] 1 [0 1] [~ 1]
[~ 0] 3 [0 3] [~ 3]
[~ 0] 2 [0 2] [~ 2]
>*2\`).scale("C4:minor")
>*4\`).scale("C4:minor")
.sound("gm_synth_strings_1")`}
/>
@ -340,7 +340,7 @@ New functions:
<MiniRepl
client:visible
tune={`sound("bd*2, ~ <sd cp>, [~ hh]*2")
tune={`sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
.bank("RolandTR909")`}
/>
@ -355,16 +355,16 @@ It's called `stack` 😙
<MiniRepl
client:visible
tune={`stack(
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>/2")
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("gm_synth_bass_1").lpf(800),
n(\`<
[~ 0] 2 [0 2] [~ 2]
[~ 0] 1 [0 1] [~ 1]
[~ 0] 3 [0 3] [~ 3]
[~ 0] 2 [0 2] [~ 2]
>*2\`).scale("C4:minor")
>*4\`).scale("C4:minor")
.sound("gm_synth_strings_1"),
sound("bd*2, ~ <sd cp>, [~ hh]*2")
sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
.bank("RolandTR909")
)`}
/>

View file

@ -147,11 +147,11 @@ We will look at other ways to change the tempo later!
**Add a rests in a sequence with '~'**
<MiniRepl client:visible tune={`sound("bd hh ~ rim")`} punchcard />
<MiniRepl client:visible tune={`sound("bd hh ~ rim ~ bd hh rim")`} punchcard />
**Sub-Sequences with [brackets]**
<MiniRepl client:visible tune={`sound("bd [hh hh] sd [hh bd]")`} punchcard />
<MiniRepl client:visible tune={`sound("bd [hh hh] sd [hh bd] bd ~ [hh sd] cp")`} punchcard />
<Box>
@ -163,15 +163,15 @@ Similar to the whole sequence, the content of a sub-sequence will be squished to
**Multiplication: Speed things up**
<MiniRepl client:visible tune={`sound("bd hh*2 rim hh*3")`} punchcard />
<MiniRepl client:visible tune={`sound("bd hh*2 rim hh*3 bd [~ hh*2] rim hh*2")`} punchcard />
**Multiplication: Speed up sequences**
**Multiplication: Speed up subsequences**
<MiniRepl client:visible tune={`sound("bd [hh rim]*2")`} punchcard />
<MiniRepl client:visible tune={`sound("bd [hh rim]*2 bd [hh rim]*1.5")`} punchcard />
**Multiplication: Speeeeeeeeed things up**
<MiniRepl client:visible tune={`sound("bd hh*16 rim hh*8")`} punchcard />
<MiniRepl client:visible tune={`sound("bd hh*32 rim hh*16")`} punchcard />
<Box>
@ -181,7 +181,7 @@ Pitch = really fast rhythm
**Sub-Sub-Sequences with [[brackets]]**
<MiniRepl client:visible tune={`sound("bd [[rim rim] hh]")`} punchcard />
<MiniRepl client:visible tune={`sound("bd [[rim rim] hh] bd cp")`} punchcard />
<Box>
@ -234,34 +234,39 @@ This is shorter and more readable than:
Now we've learned the basics of the so called Mini-Notation, the rhythm language of Tidal.
This is what we've leared so far:
| Concept | Syntax | Example |
| ----------------- | -------- | --------------------------------------------------------------------- |
| Sequence | space | <MiniRepl client:visible tune={`sound("bd bd sd hh")`} /> |
| Sample Number | :x | <MiniRepl client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
| Rests | ~ | <MiniRepl client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
| Sub-Sequences | \[\] | <MiniRepl client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
| Sub-Sub-Sequences | \[\[\]\] | <MiniRepl client:visible tune={`sound("bd [metal [jazz sd]]")`} /> |
| Speed up | \* | <MiniRepl client:visible tune={`sound("bd sd*2 cp*3")`} /> |
| Parallel | , | <MiniRepl client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
| Concept | Syntax | Example |
| ----------------- | -------- | ----------------------------------------------------------------------- |
| Sequence | space | <MiniRepl client:visible tune={`sound("bd bd sd hh")`} /> |
| Sample Number | :x | <MiniRepl client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
| Rests | ~ | <MiniRepl client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
| Sub-Sequences | \[\] | <MiniRepl client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
| Sub-Sub-Sequences | \[\[\]\] | <MiniRepl client:visible tune={`sound("bd [metal [jazz [sd cp]]]")`} /> |
| Speed up | \* | <MiniRepl client:visible tune={`sound("bd sd*2 cp*3")`} /> |
| Parallel | , | <MiniRepl client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
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")`} /> |
| bank | selects the sound bank | <MiniRepl client:visible tune={`sound("bd sd").bank("RolandTR909")`} /> |
| cpm | sets the tempo in cycles per minute | <MiniRepl client:visible tune={`sound("bd sd").cpm(90)`} /> |
| n | select sample number | <MiniRepl client:visible tune={`n("0 1 4 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")`} /> |
| 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")`} /> |
## Examples
**Basic rock beat**
<MiniRepl client:visible tune={`sound("bd sd, hh*4").bank("RolandTR505").cpm(100/2)`} punchcard />
<MiniRepl
client:visible
tune={`sound("[bd sd]*2, hh*8").bank("RolandTR505")
.cpm(100/4)`}
punchcard
/>
**Classic house**
<MiniRepl client:visible tune={`sound("bd*2, ~ cp, [~ hh]*2").bank("RolandTR909")`} punchcard />
<MiniRepl client:visible tune={`sound("bd*4, [~ cp]*2, [~ hh]*4").bank("RolandTR909")`} punchcard />
<Box>
@ -279,7 +284,7 @@ We Will Rock you
<MiniRepl
client:visible
tune={`sound("bd sd, ~ ~ ~ hh ~ hh ~ ~, ~ perc ~ perc:1*2")
.bank("RolandCompurhythm1000")`}
.bank("RolandCompurhythm1000").cpm(120/2)`}
punchcard
/>

View file

@ -15,19 +15,19 @@ In this chapter, we are going to look at functions that are more unique to tidal
**reverse patterns with rev**
<MiniRepl client:visible tune={`n("0 1 [4 3] 2").sound("jazz").rev()`} />
<MiniRepl client:visible tune={`n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").rev()`} />
**play pattern left and modify it right with jux**
<MiniRepl client:visible tune={`n("0 1 [4 3] 2").sound("jazz").jux(rev)`} />
<MiniRepl client:visible tune={`n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").jux(rev)`} />
This is the same as:
<MiniRepl
client:visible
tune={`stack(
n("0 1 [4 3] 2").sound("jazz").pan(0),
n("0 1 [4 3] 2").sound("jazz").pan(1).rev()
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(0),
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(1).rev()
)`}
/>
@ -36,8 +36,8 @@ Let's visualize what happens here:
<MiniRepl
client:visible
tune={`stack(
n("0 1 [4 3] 2").sound("jazz").pan(0).color("cyan"),
n("0 1 [4 3] 2").sound("jazz").pan(1).color("magenta").rev()
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(0).color("cyan"),
n("0 1 [4 3] 2 0 2 [~ 3] 4").sound("jazz").pan(1).color("magenta").rev()
)`}
punchcard
/>
@ -50,16 +50,16 @@ Try commenting out one of the two by adding `//` before a line
**multiple tempos**
<MiniRepl client:visible tune={`note("c2, eb3 g3 [bb3 c4]").sound("piano").slow("1,2,3")`} />
<MiniRepl client:visible tune={`note("c2, eb3 g3 [bb3 c4]").sound("piano").slow("0.5,1,1.5")`} />
This is like doing
<MiniRepl
client:visible
tune={`stack(
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1).color('cyan'),
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(2).color('magenta'),
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(3).color('yellow')
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(0.5).color('cyan'),
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1).color('magenta'),
note("c2, eb3 g3 [bb3 c4]").s("piano").slow(1.5).color('yellow')
)`}
punchcard
/>
@ -74,9 +74,9 @@ Try commenting out one or more by adding `//` before a line
<MiniRepl
client:visible
tune={`note("c2 [eb3,g3]".add("<0 <1 -1>>"))
tune={`note("c2 [eb3,g3] ".add("<0 <1 -1>>"))
.color("<cyan <magenta yellow>>").adsr("[.1 0]:.2:[1 0]")
.sound("gm_acoustic_bass").room(.5)`}
.sound("gm_acoustic_bass").room(.5).cpm(60)`}
punchcard
/>
@ -92,7 +92,7 @@ We can add as often as we like:
client:visible
tune={`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)`}
.sound("gm_acoustic_bass").room(.5).cpm(60)`}
punchcard
/>
@ -100,7 +100,7 @@ We can add as often as we like:
<MiniRepl
client:visible
tune={`n("<0 [2 4] <3 5> [~ <4 1>]>*2".add("<0 [0,2,4]>/4"))
tune={`n("0 [2 4] <3 5> [~ <4 1>]".add("<0 [0,2,4]>"))
.scale("C5:minor").release(.5)
.sound("gm_xylophone").room(.5)`}
punchcard
@ -111,7 +111,7 @@ We can add as often as we like:
<MiniRepl
client:visible
tune={`stack(
n("<0 [2 4] <3 5> [~ <4 1>]>*2".add("<0 [0,2,4]>/4"))
n("0 [2 4] <3 5> [~ <4 1>]".add("<0 [0,2,4]>"))
.scale("C5:minor")
.sound("gm_xylophone")
.room(.4).delay(.125),
@ -119,17 +119,17 @@ We can add as often as we like:
.adsr("[.1 0]:.2:[1 0]")
.sound("gm_acoustic_bass")
.room(.5),
n("0 1 [2 3] 2").sound("jazz").jux(rev).slow(2)
n("0 1 [2 3] 2").sound("jazz").jux(rev)
)`}
/>
**ply**
<MiniRepl client:visible tune={`sound("hh, bd rim").bank("RolandTR707").ply(2)`} punchcard />
<MiniRepl client:visible tune={`sound("hh hh, bd rim [~ cp] rim").bank("RolandTR707").ply(2)`} punchcard />
this is like writing:
<MiniRepl client:visible tune={`sound("hh*2, bd*2 rim*2").bank("RolandTR707")`} punchcard />
<MiniRepl client:visible tune={`sound("hh*2 hh*2, bd*2 rim*2 [~ cp*2] rim*2").bank("RolandTR707")`} punchcard />
<Box>
@ -141,10 +141,10 @@ Try patterning the `ply` function, for example using `"<1 2 1 3>"`
<MiniRepl
client:visible
tune={`n("<0 [4 <3 2>] <2 3> [~ 1]>"
.off(1/8, x=>x.add(4))
//.off(1/4, x=>x.add(7))
).scale("<C5:minor Db5:mixolydian>/4")
tune={`n("0 [4 <3 2>] <2 3> [~ 1]"
.off(1/16, x=>x.add(4))
//.off(1/8, x=>x.add(7))
).scale("<C5:minor Db5:mixolydian>/2")
.s("triangle").room(.5).ds(".1:0").delay(.5)`}
punchcard
/>
@ -159,14 +159,14 @@ off is also useful for sounds:
<MiniRepl
client:visible
tune={`s("bd sd,[~ hh]*2").bank("CasioRZ1")
.off(1/8, x=>x.speed(1.5).gain(.25))`}
tune={`s("bd sd [rim bd] sd,[~ hh]*4").bank("CasioRZ1")
.off(1/16, x=>x.speed(1.5).gain(.25))`}
/>
| name | description | example |
| ---- | ------------------------------ | ----------------------------------------------------------------------------------- |
| 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 |
| ---- | ------------------------------ | ------------------------------------------------------------------------------------------- |
| rev | reverse | <MiniRepl client:visible tune={`n("0 2 4 6 ~ 7 9 5").scale("C:minor").rev()`} /> |
| jux | split left/right, modify right | <MiniRepl client:visible tune={`n("0 2 4 6 ~ 7 9 5").scale("C:minor").jux(rev)`} /> |
| add | add numbers / notes | <MiniRepl client:visible tune={`n("0 2 4 6 ~ 7 9 5".add("<0 1 2 1>")).scale("C:minor")`} /> |
| ply | speed up each event n times | <MiniRepl client:visible tune={`s("bd sd [~ bd] sd").ply("<1 2 3>")`} /> |
| off | copy, shift time & modify | <MiniRepl client:visible tune={`s("bd sd [~ bd] sd, hh*8").off(1/16, x=>x.speed(2))`} /> |

View file

@ -13,12 +13,12 @@ This page is just a listing of all functions covered in the workshop!
| Concept | Syntax | Example |
| ----------------- | -------- | --------------------------------------------------------------------- |
| Sequence | space | <MiniRepl client:visible tune={`sound("bd bd sn hh")`} /> |
| Sequence | space | <MiniRepl client:visible tune={`sound("bd bd sd hh bd cp sd hh")`} /> |
| Sample Number | :x | <MiniRepl client:visible tune={`sound("hh:0 hh:1 hh:2 hh:3")`} /> |
| Rests | ~ | <MiniRepl client:visible tune={`sound("metal ~ jazz jazz:1")`} /> |
| Sub-Sequences | \[\] | <MiniRepl client:visible tune={`sound("bd wind [metal jazz] hh")`} /> |
| Sub-Sub-Sequences | \[\[\]\] | <MiniRepl client:visible tune={`sound("bd [metal [jazz sn]]")`} /> |
| Speed up | \* | <MiniRepl client:visible tune={`sound("bd sn*2 cp*3")`} /> |
| Sub-Sub-Sequences | \[\[\]\] | <MiniRepl client:visible tune={`sound("bd [metal [jazz sd]]")`} /> |
| Speed up | \* | <MiniRepl client:visible tune={`sound("bd sd*2 cp*3")`} /> |
| Parallel | , | <MiniRepl client:visible tune={`sound("bd*2, hh*2 [hh oh]")`} /> |
| Slow down | \/ | <MiniRepl client:visible tune={`note("[c a f e]/2")`} /> |
| Alternate | \<\> | <MiniRepl client:visible tune={`note("c <e g>")`} /> |
@ -45,22 +45,22 @@ This page is just a listing of all functions covered in the workshop!
| name | example |
| ----- | --------------------------------------------------------------------------------------- |
| lpf | <MiniRepl client:visible tune={`note("c2 c3").s("sawtooth").lpf("<400 2000>")`} /> |
| lpf | <MiniRepl client:visible tune={`note("c2 c3 c2 c3").s("sawtooth").lpf("400 2000")`} /> |
| vowel | <MiniRepl client:visible tune={`note("c3 eb3 g3").s("sawtooth").vowel("<a e i o>")`} /> |
| gain | <MiniRepl client:visible tune={`s("hh*8").gain("[.25 1]*2")`} /> |
| delay | <MiniRepl client:visible tune={`s("bd rim").delay(.5)`} /> |
| room | <MiniRepl client:visible tune={`s("bd rim").room(.5)`} /> |
| pan | <MiniRepl client:visible tune={`s("bd rim").pan("0 1")`} /> |
| speed | <MiniRepl client:visible tune={`s("bd rim").speed("<1 2 -1 -2>")`} /> |
| range | <MiniRepl client:visible tune={`s("hh*16").lpf(saw.range(200,4000))`} /> |
| gain | <MiniRepl client:visible tune={`s("hh*16").gain("[.25 1]*4")`} /> |
| delay | <MiniRepl client:visible tune={`s("bd rim bd cp").delay(.5)`} /> |
| room | <MiniRepl client:visible tune={`s("bd rim bd cp").room(.5)`} /> |
| pan | <MiniRepl client:visible tune={`s("bd rim bd cp").pan("0 1")`} /> |
| speed | <MiniRepl client:visible tune={`s("bd rim bd cp").speed("<1 2 -1 -2>")`} /> |
| range | <MiniRepl client:visible tune={`s("hh*32").lpf(saw.range(200,4000))`} /> |
## Pattern Effects
| name | description | example |
| ---- | ----------------------------------- | ----------------------------------------------------------------------------------- |
| cpm | sets the tempo in cycles per minute | <MiniRepl client:visible tune={`sound("bd sd").cpm(90)`} /> |
| fast | speed up | <MiniRepl client:visible tune={`sound("bd sd").fast(2)`} /> |
| slow | slow down | <MiniRepl client:visible tune={`sound("bd sd").slow(2)`} /> |
| 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")`} /> |

View file

@ -30,7 +30,6 @@ import { getRandomTune, initCode, loadModules, shareCode, ReplContext } from './
import PlayCircleIcon from '@heroicons/react/20/solid/PlayCircleIcon';
import './Repl.css';
const { code: randomTune, name } = getRandomTune();
const { latestCode } = settingsMap.get();
let modulesLoading, presets, drawContext, clearCanvas, isIframe;
@ -98,7 +97,7 @@ export function Repl({ embedded = false }) {
// init settings
initCode().then((decoded) => {
initCode().then(async (decoded) => {
let msg;
if (decoded) {
editor.setCode(decoded);
@ -107,6 +106,7 @@ export function Repl({ embedded = false }) {
editor.setCode(latestCode);
msg = `Your last session has been loaded!`;
} else {
const { code: randomTune, name } = await getRandomTune();
editor.setCode(randomTune);
msg = `A random code snippet named "${name}" has been loaded!`;
}
@ -159,7 +159,7 @@ export function Repl({ embedded = false }) {
const resetEditor = async () => {
clearCanvas();
resetLoadedSounds();
editorRef.current.repl.setCps(1);
editorRef.current.repl.setCps(0.5);
await prebake(); // declare default samples
};
@ -176,7 +176,7 @@ export function Repl({ embedded = false }) {
editorRef.current.evaluate();
};
const handleShuffle = async () => {
const patternData = getRandomTune();
const patternData = await getRandomTune();
const code = patternData.code;
logger(`[repl] ✨ loading random tune "${patternData.id}"`);
setActivePattern(patternData.id);

View file

@ -314,7 +314,7 @@ 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
@ -333,7 +333,7 @@ stack(
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/
@ -709,6 +709,8 @@ export const dinofunk = `// "Dinofunk"
// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
// @by Felix Roos
setcps(1)
samples({bass:'https://cdn.freesound.org/previews/614/614637_2434927-hq.mp3',
dino:{b4:'https://cdn.freesound.org/previews/316/316403_5123851-hq.mp3'}})
setVoicingRange('lefthand', ['c3','a4'])

View file

@ -9,8 +9,7 @@ import { createClient } from '@supabase/supabase-js';
import { nanoid } from 'nanoid';
import { writeText } from '@tauri-apps/api/clipboard';
import { createContext } from 'react';
import { stockPatterns } from './useExamplePatterns';
import { 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(
@ -18,8 +17,9 @@ export const supabase = createClient(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBpZHhkc3hwaGxoempuem1pZnRoIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTYyMzA1NTYsImV4cCI6MTk3MTgwNjU1Nn0.bqlw7802fsWRnqU5BLYtmXk_k-D1VFmbkHMywWc15NM',
);
let dbLoaded;
if (typeof window !== 'undefined') {
loadDBPatterns();
dbLoaded = loadDBPatterns();
}
export async function initCode() {
@ -34,7 +34,7 @@ export async function initCode() {
return hash2code(codeParam);
} else if (hash) {
return supabase
.from('code')
.from('code_v1')
.select('code')
.eq('hash', hash)
.then(({ data, error }) => {
@ -61,10 +61,11 @@ export const parseJSON = (json) => {
}
};
export function getRandomTune() {
const allTunes = Object.entries(stockPatterns);
export async function getRandomTune() {
await dbLoaded;
const featuredTunes = Object.entries($featuredPatterns.get());
const randomItem = (arr) => arr[Math.floor(Math.random() * arr.length)];
const [id, data] = randomItem(allTunes);
const [_, data] = randomItem(featuredTunes);
return data;
}
@ -111,7 +112,7 @@ export async function shareCode(codeToShare) {
// generate uuid in the browser
const hash = nanoid(12);
const shareUrl = window.location.origin + window.location.pathname + '?' + hash;
const { error } = await supabase.from('code').insert([{ code: codeToShare, hash, ['public']: isPublic }]);
const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]);
if (!error) {
lastShared = codeToShare;
// copy shareUrl to clipboard

View file

@ -44,11 +44,11 @@ export const setViewingPatternData = (data) => {
};
export function loadPublicPatterns() {
return supabase.from('code').select().eq('public', true).limit(20).order('id', { ascending: false });
return supabase.from('code_v1').select().eq('public', true).limit(20).order('id', { ascending: false });
}
export function loadFeaturedPatterns() {
return supabase.from('code').select().eq('featured', true).limit(20).order('id', { ascending: false });
return supabase.from('code_v1').select().eq('featured', true).limit(20).order('id', { ascending: false });
}
export async function loadDBPatterns() {