Merge branch 'main' into voicings-doc

This commit is contained in:
Felix Roos 2024-12-23 23:07:46 +01:00
commit 991d8e4717
180 changed files with 19272 additions and 18871 deletions

View file

@ -147,7 +147,7 @@ Probier `ply` mit einem pattern zu automatisieren, z.b. `"<1 2 1 3>"`
.off(1/8, x=>x.add(4))
//.off(1/4, x=>x.add(7))
).scale("<C5:minor Db5:mixolydian>/4")
.s("triangle").room(.5).ds(".1:0").delay(.5)`}
.s("triangle").room(.5).dec(.1).delay(.5)`}
punchcard
/>

View file

@ -3,12 +3,12 @@ import HeadCommon from '../components/HeadCommon.astro';
import { Repl } from '../repl/Repl';
---
<html lang="en" class="dark">
<html lang="en" class="m-0 dark">
<head>
<HeadCommon />
<title>Strudel REPL</title>
</head>
<body class="h-app-height bg-background">
<body class="h-app-height bg-background m-0">
<Repl client:only="react" />
<a rel="me" href="https://social.toplap.org/@strudel" target="_blank" class="hidden">mastodon</a>
</body>

View file

@ -94,7 +94,7 @@ Each filter can receive an additional filter envelope controlling the cutoff val
client:idle
tune={`note("[c eb g <f bb>](3,8,<0 1>)".sub(12))
.s("<sawtooth>/64")
.lpf(sine.range(500,3000).slow(16))
.lpf(sine.range(300,2000).slow(16))
.lpa(0.005)
.lpd(perlin.range(.02,.2))
.lps(perlin.range(0,.5).slow(3))
@ -106,7 +106,7 @@ Each filter can receive an additional filter envelope controlling the cutoff val
.juxBy(.5,rev)
.sometimes(add(note(12)))
.stack(s("bd*2").bank('RolandTR909'))
.gain(.5)`}
.gain(.5).fast(2)`}
/>
There is one filter envelope for each filter type and thus one set of envelope filter parameters preceded either by `lp`, `hp` or `bp`:

View file

@ -11,55 +11,43 @@ import { JsDoc } from '../../docs/JsDoc';
The following functions will return a pattern.
These are the equivalents used by the Mini Notation:
| function | mini |
| ------------------------------ | ---------------- |
| `cat(x, y)` | `"<x y>"` |
| `seq(x, y)` | `"x y"` |
| `stack(x, y)` | `"x,y"` |
| `timeCat([3,x],[2,y])` | `"x@3 y@2"` |
| `polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
| `polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
| `silence` | `"~"` |
| function | mini |
| -------------------------------- | ---------------- |
| `cat(x, y)` | `"<x y>"` |
| `seq(x, y)` | `"x y"` |
| `stack(x, y)` | `"x,y"` |
| `s_cat([3,x],[2,y])` | `"x@3 y@2"` |
| `s_polymeter([a, b, c], [x, y])` | `"{a b c, x y}"` |
| `s_polymeterSteps(2, x, y, z)` | `"{x y z}%2"` |
| `silence` | `"~"` |
## cat
<JsDoc client:idle name="cat" h={0} />
You can also use cat as a chained function like this:
<JsDoc client:idle name="Pattern.cat" h={0} hideDescription />
## seq
<JsDoc client:idle name="seq" h={0} />
Or as a chained function:
<JsDoc client:idle name="Pattern.seq" h={0} hideDescription />
## stack
<JsDoc client:idle name="stack" h={0} />
As a chained function:
## s_cat
<JsDoc client:idle name="Pattern.stack" h={0} hideDescription />
## timeCat
<JsDoc client:idle name="timeCat" h={0} />
<JsDoc client:idle name="s_cat" h={0} />
## arrange
<JsDoc client:idle name="arrange" h={0} />
## polymeter
## s_polymeter
<JsDoc client:idle name="polymeter" h={0} />
<JsDoc client:idle name="s_polymeter" h={0} />
## polymeterSteps
## s_polymeterSteps
<JsDoc client:idle name="polymeterSteps" h={0} />
<JsDoc client:idle name="s_polymeterSteps" h={0} />
## silence

View file

@ -90,11 +90,13 @@ src(s0).kaleid(H("<4 5 6>"))
.modulateScale(osc(2,-0.25,1))
.out()
//
stack(
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')
).fft(4)
.scope({pos:0,smear:.95})`}
$: 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')
all(x=>x.fft(4).scope({pos:0,smear:.95}))`}
/>

View file

@ -1,30 +1,27 @@
---
title: MIDI & OSC
title: MIDI, OSC & MQTT
layout: ../../layouts/MainLayout.astro
---
import { MiniRepl } from '../../docs/MiniRepl';
import { JsDoc } from '../../docs/JsDoc';
# MIDI and OSC
# MIDI, OSC and MQTT
The default audio output of Strudel uses the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API).
It is also possible to use Strudel with MIDI and OSC / [SuperDirt](https://github.com/musikinformatik/SuperDirt/) instead.
Normally, Strudel is used to pattern sound, using its own '[web audio](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)'-based synthesiser called [SuperDough](https://github.com/tidalcycles/strudel/tree/main/packages/superdough).
# MIDI API
It is also possible to pattern other things with Strudel, such as software and hardware synthesisers with MIDI, other software using Open Sound Control/OSC (including the [SuperDirt](https://github.com/musikinformatik/SuperDirt/) synthesiser commonly used with Strudel's sibling [TidalCycles](https://tidalcycles.org/)), or the MQTT 'internet of things' protocol.
Strudel also supports midi via [webmidi](https://npmjs.com/package/webmidi).
# MIDI
Strudel supports MIDI without any additional software (thanks to [webmidi](https://npmjs.com/package/webmidi)), just by adding methods to your pattern:
## midi(outputName?)
Either connect a midi device or use the IAC Driver (Mac) or Midi Through Port (Linux) for internal midi messages.
If no outputName is given, it uses the first midi output it finds.
<MiniRepl
client:idle
tune={`stack("<C^7 A7 Dm7 G7>".voicings('lefthand'), "<C3 A2 D3 G2>").note()
.midi()`}
/>
<MiniRepl client:idle tune={`chord("<C^7 A7 Dm7 G7>").voicing().midi()`} />
In the console, you will see a log of the available MIDI devices as soon as you run the code, e.g. `Midi connected! Using "Midi Through Port-0".`
@ -45,26 +42,26 @@ But you can also control cc messages separately like this:
<MiniRepl
client:idle
tune={`stack(
note("c a f e"),
ccv(sine.segment(16).slow(4)).ccn(74)
).midi()`}
tune={`$: note("c a f e").midi()
$: ccv(sine.segment(16).slow(4)).ccn(74).midi()`}
/>
# SuperDirt API
# OSC/SuperDirt/StrudelDirt
In mainline tidal, the actual sound is generated via [SuperDirt](https://github.com/musikinformatik/SuperDirt/), which runs inside SuperCollider.
Strudel also supports using [SuperDirt](https://github.com/musikinformatik/SuperDirt/) as a backend, although it requires some developer tooling to run.
In TidalCycles, sound is usually generated using [SuperDirt](https://github.com/musikinformatik/SuperDirt/), which runs inside SuperCollider. Strudel also supports using SuperDirt, although it requires installing some additional software.
There is also [StrudelDirt](https://github.com/daslyfe/StrudelDirt) which is SuperDirt with some optimisations for working with Strudel. (A longer term aim is to merge these optimisations back into mainline SuperDirt)
## Prequisites
Getting [SuperDirt](https://github.com/musikinformatik/SuperDirt/) to work with Strudel, you need to
To get SuperDirt to work with Strudel, you need to
1. install SuperCollider + sc3 plugins, see [Tidal Docs](https://tidalcycles.org/docs/) (Install Tidal) for more info.
2. install [node.js](https://nodejs.org/en/)
3. download [Strudel Repo](https://github.com/tidalcycles/strudel/) (or git clone, if you have git installed)
4. run `pnpm i` in the strudel directory
5. run `pnpm run osc` to start the osc server, which forwards OSC messages from Strudel REPL to SuperCollider
2. install SuperDirt, or the [StrudelDirt](https://github.com/daslyfe/StrudelDirt) fork which is optimised for use with Strudel
3. install [node.js](https://nodejs.org/en/)
4. download [Strudel Repo](https://github.com/tidalcycles/strudel/) (or git clone, if you have git installed)
5. run `pnpm i` in the strudel directory
6. run `pnpm run osc` to start the osc server, which forwards OSC messages from Strudel REPL to SuperCollider
Now you're all set!
@ -79,18 +76,80 @@ Now you're all set!
If you now hear sound, congratulations! If not, you can get help on the [#strudel channel in the TidalCycles discord](https://discord.com/invite/HGEdXmRkzT).
Note: if you have the 'Audio Engine Target' in settings set to 'OSC', you do not need to add .osc() to the end of your pattern.
### Pattern.osc
<JsDoc client:idle name="Pattern.osc" h={0} />
## SuperDirt Params
The following functions can be used with [SuperDirt](https://github.com/musikinformatik/SuperDirt/):
`s n note freq channel orbit cutoff resonance hcutoff hresonance bandf bandq djf vowel cut begin end loop fadeTime speed unitA gain amp accelerate crush coarse delay lock leslie lrate lsize pan panspan pansplay room size dry shape squiz waveloss attack decay octave detune tremolodepth`
Please refer to [Tidal Docs](https://tidalcycles.org/) for more info.
<br />
But can we use Strudel [offline](/learn/pwa)?
# MQTT
MQTT is a lightweight network protocol, designed for 'internet of things' devices. For use with strudel, you will
need access to an MQTT server known as a 'broker' configured to accept secure 'websocket' connections. You could
run one yourself (e.g. by running [mosquitto](https://mosquitto.org/)), although getting an SSL certificate that
your web browser will trust might be a bit tricky for those without systems administration experience.
Alternatively, you can use [a public broker](https://www.hivemq.com/mqtt/public-mqtt-broker/).
Strudel does not yet support receiving messages over MQTT, only sending them.
## Usage
The following example shows how to send a pattern to an MQTT broker:
<MiniRepl
client:only="react"
tune={`"hello world"
.mqtt(undefined, // username (undefined for open/public servers)
undefined, // password
'/strudel-pattern', // mqtt 'topic'
'wss://mqtt.eclipseprojects.io:443/mqtt', // MQTT server address
'mystrudel', // MQTT client id - randomly generated if not supplied
0 // latency / delay before sending messages (0 = no delay)
)`}
/>
Other software can then receive the messages. For example using the [mosquitto](https://mosquitto.org/) commandline client tools:
```
> mosquitto_sub -h mqtt.eclipseprojects.io -p 1883 -t "/strudel-pattern"
hello
world
hello
world
...
```
Control patterns will be encoded as JSON, for example:
<MiniRepl
client:only="react"
tune={`sound("sax(3,8)").speed("2 3")
.mqtt(undefined, // username (undefined for open/public servers)
undefined, // password
'/strudel-pattern', // mqtt 'topic'
'wss://mqtt.eclipseprojects.io:443/mqtt', // MQTT server address
'mystrudel', // MQTT client id - randomly generated if not supplied
0 // latency / delay before sending messages (0 = no delay)
)`}
/>
Will send messages like the following:
```
{"s":"sax","speed":2}
{"s":"sax","speed":2}
{"s":"sax","speed":3}
{"s":"sax","speed":2}
...
```
Libraries for receiving MQTT are available for many programming languages.

View file

@ -268,11 +268,10 @@ With it, you can enter any sample name(s) to query from [freesound.org](https://
<MiniRepl
client:idle
tune={`samples('shabda:bass:4,hihat:4,rimshot:2')
stack(
n("0 1 2 3 0 1 2 3").s('bass'),
n("0 1*2 2 3*2").s('hihat'),
n("~ 0 ~ 1 ~ 0 0 1").s('rimshot')
).clip(1)`}
$: n("0 1 2 3 0 1 2 3").s('bass')
$: n("0 1*2 2 3*2").s('hihat').clip(1)
$: n("~ 0 ~ 1 ~ 0 0 1").s('rimshot')`}
/>
You can also generate artificial voice samples with any text, in multiple languages.
@ -282,10 +281,9 @@ Note that the language code and the gender parameters are optional and default t
client:idle
tune={`samples('shabda/speech:the_drum,forever')
samples('shabda/speech/fr-FR/m:magnifique')
stack(
s("the_drum*2").chop(16).speed(rand.range(0.85,1.1)),
s("forever magnifique").slow(4).late(0.125)
)`}
$: s("the_drum*2").chop(16).speed(rand.range(0.85,1.1))
$: s("forever magnifique").slow(4).late(0.125)`}
/>
# Sampler Effects

View file

@ -55,4 +55,12 @@ There is also `saw2`, `sine2`, `cosine2`, `tri2`, `square2` and `rand2` which ha
<JsDoc client:idle name="brandBy" h={0} />
## mouseX
<JsDoc client:idle name="mousex" h={0} />
## mouseY
<JsDoc client:idle name="mousey" h={0} />
Next up: [Random Modifiers](/learn/random-modifiers)

View file

@ -112,29 +112,14 @@ Also, samples are always loaded from a URL rather than from the disk, although [
## Evaluation
The Strudel REPL does not support [block based evaluation](https://github.com/tidalcycles/strudel/issues/34) yet.
You can use the following "workaround" to create multiple patterns that can be turned on and off:
You can use labeled statements and `_` to mute:
```
let a = note("c a f e")
<MiniRepl
client:visible
tune={`$: n("[0 .. 8]*8/9").scale("C:minor:pentatonic")
let b = s("bd sd")
stack(
a,
// b
)
```
Alternatively, you could write everything as one `stack` and use `.hush()` to silence a pattern:
```
stack(
note("c a f e"),
s("bd sd").hush()
)
```
Note that strudel will always use the last statement in your code as the pattern for querying
\_$: s("bd\*4").bank('RolandTR909')`}
/>
## Tempo

View file

@ -18,7 +18,7 @@ The basic waveforms are `sine`, `sawtooth`, `square` and `triangle`, which can b
client:idle
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("<sawtooth square triangle sine>")
.scope()`}
._scope()`}
/>
If you don't set a `sound` but a `note` the default value for `sound` is `triangle`!
@ -28,23 +28,23 @@ 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>").scope()`} />
<MiniRepl client:idle tune={`sound("<white pink brown>")._scope()`} />
Here's a more musical example of how to use noise for hihats:
<MiniRepl
client:idle
tune={`sound("bd*2,<white pink brown>*8")
.decay(.04).sustain(0).scope()`}
.decay(.04).sustain(0)._scope()`}
/>
Some amount of pink noise can also be added to any oscillator by using the `noise` paremeter:
<MiniRepl client:idle tune={`note("c3").noise("<0.1 0.25 0.5>").scope()`} />
<MiniRepl client:idle tune={`note("c3").noise("<0.1 0.25 0.5>")._scope()`} />
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(2)).scope()`} />
<MiniRepl client:idle tune={`s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(2))._scope()`} />
### Additive Synthesis
@ -55,7 +55,7 @@ To tame the harsh sound of the basic waveforms, we can set the `n` control to li
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("sawtooth")
.n("<32 16 8 4>")
.scope()`}
._scope()`}
/>
When the `n` control is used on a basic waveform, it defines the number of harmonic partials the sound is getting.
@ -65,7 +65,7 @@ You can also set `n` directly in mini notation with `sound`:
client:idle
tune={`note("c2 <eb2 <g2 g1>>".fast(2))
.sound("sawtooth:<32 16 8 4>")
.scope()`}
._scope()`}
/>
Note for tidal users: `n` in tidal is synonymous to `note` for synths only.
@ -119,13 +119,14 @@ Any sample preceded by the `wt_` prefix will be loaded as a wavetable. This mean
<MiniRepl
client:idle
tune={`samples('github:Bubobubobubobubo/Dough-Waveforms/main/');
tune={`samples('bubo:waveforms');
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(4)`}
.cutoff(2000).cutoff("<1000 2000 4000>").fast(4)
._scope()
`}
/>
## ZZFX
@ -159,7 +160,7 @@ It has 20 parameters in total, here is a snippet that uses all:
.tremolo(0) // 0-1 lfo volume modulation amount
//.duration(.2) // overwrite strudel event duration
//.gain(1) // change volume
.scope() // vizualise waveform (not zzfx related)
._scope() // vizualise waveform (not zzfx related)
`}
/>

View file

@ -55,20 +55,6 @@ Transposes notes inside the scale by the number of steps:
.note()`}
/>
### voicings(range?)
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()`}
/>
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.

View file

@ -0,0 +1,108 @@
---
title: Visual Feedback
layout: ../../layouts/MainLayout.astro
---
import { MiniRepl } from '../../docs/MiniRepl';
import { JsDoc } from '../../docs/JsDoc';
# Visual Feedback
There are several function that add visual feedback to your patterns.
## Mini Notation Highlighting
When you write mini notation with "double quotes" or \`backticks\`, the active parts of the mini notation will be highlighted:
<MiniRepl
client:idle
tune={`n("<0 2 1 3 2>*8")
.scale("<A1 D2>/4:minor:pentatonic")
.s("supersaw").lpf(300).lpenv("<4 3 2>\*4")`}
/>
You can change the color as well, even pattern it:
<MiniRepl
client:idle
tune={`n("<0 2 1 3 2>*8")
.scale("<A1 D2>/4:minor:pentatonic")
.s("supersaw").lpf(300).lpenv("<4 3 2>*4")
.color("cyan magenta")`}
/>
## Global vs Inline Visuals
The following functions all come with in 2 variants.
**Without prefix**: renders the visual to the background of the page:
<MiniRepl client:idle tune={`note("c a f e").color("white").punchcard()`} />
**With `_` prefix**: renders the visual inside the code. Allows for multiple visuals
<MiniRepl client:idle tune={`note("c a f e").color("white")._punchcard()`} />
Here we see the 2 variants for `punchcard`. The same goes for all others below.
To improve readability the following demos will all use the inline variant.
## Punchcard / Pianoroll
These 2 functions render a pianoroll style visual.
The only difference between the 2 is that `pianoroll` will render the pattern directly,
while `punchcard` will also take the transformations into account that occur afterwards:
<MiniRepl
client:idle
tune={`note("c a f e").color("white")
._punchcard()
.color("cyan")`}
autodraw
/>
Here, the `color` is still visible in the visual, even if it is applied after `_punchcard`.
On the contrary, the color is not visible when using `_pianoroll`:
<MiniRepl
client:idle
tune={`note("c a f e").color("white")
._pianoroll()
.color("cyan")`}
autodraw
/>
import Box from '@components/Box.astro';
<br />
<Box>
`punchcard` is less resource intensive because it uses the same data as used for the mini notation highlighting.
</Box>
The visual can be customized by passing options. Those options are the same for both functions.
What follows is the API doc of all the options you can pass:
<JsDoc client:idle name="pianoroll" h={0} />
## Spiral
<JsDoc client:idle name="spiral" h={0} />
## Scope
<JsDoc client:idle name="scope" h={0} />
## Pitchwheel
<JsDoc client:idle name="pitchwheel" h={0} />
## Spectrum
<JsDoc client:idle name="spectrum" h={0} />
## markcss
<JsDoc client:idle name="markcss" h={0} />

View file

@ -308,7 +308,7 @@ Running through different wavetables can also give interesting variations:
<MiniRepl
client:visible
tune={`samples('github:bubobubobubobubo/dough-waveforms')
note("c2*8").s("wt_dbass").n(run(8))`}
note("c2*8").s("wt_dbass").n(run(8)).fast(2)`}
/>
...adding a filter envelope + reverb:
@ -317,6 +317,6 @@ note("c2*8").s("wt_dbass").n(run(8))`}
client:visible
tune={`samples('github:bubobubobubobubo/dough-waveforms')
note("c2*8").s("wt_dbass").n(run(8))
.lpf(perlin.range(200,2000).slow(8))
.lpenv(-3).lpa(.1).room(.5)`}
.lpf(perlin.range(100,1000).slow(8))
.lpenv(-3).lpa(.1).room(.5).fast(2)`}
/>

View file

@ -0,0 +1,12 @@
---
import { Udels } from '../../components/Udels/Udels.jsx';
const { BASE_URL } = import.meta.env;
const baseNoTrailing = BASE_URL.endsWith('/') ? BASE_URL.slice(0, -1) : BASE_URL;
---
<body class="m-0">
<Udels client:only="react" />
</body>

View file

@ -60,11 +60,10 @@ We will learn how to automate with waves later...
<MiniRepl
client:visible
tune={`stack(
sound("hh*16").gain("[.25 1]*4"),
sound("bd*4,[~ sd:1]*2")
) `}
punchcard
tune={`$: sound("hh*16").gain("[.25 1]*4")
$: sound("bd*4,[~ sd:1]*2")`}
punchcard
/>
<Box>
@ -76,31 +75,21 @@ Rhythm is all about dynamics!
</Box>
**stacks within stacks**
Let's combine all of the above into a little tune:
<MiniRepl
client:visible
tune={`stack(
stack(
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>")
.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>")
)`}
tune={`$: 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>")
.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>")`}
/>
<Box>
Try to identify the individual parts of the stacks, pay attention to where the commas are.
The 3 parts (drums, bassline, chords) are exactly as earlier, just stacked together, separated by comma.
</Box>
**shape the sound with an adsr envelope**
<MiniRepl
@ -151,11 +140,10 @@ Can you guess what they do?
<MiniRepl
client:visible
tune={`stack(
note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
.sound("gm_electric_guitar_muted"),
sound("<bd rim>").bank("RolandTR707")
).delay(".5")`}
tune={`$: note("[~ [<[d3,a3,f4]!2 [d3,bb3,g4]!2> ~]]*2")
.sound("gm_electric_guitar_muted").delay(.5)
$: sound("bd rim").bank("RolandTR707").delay(".5")`}
/>
<Box>
@ -199,31 +187,32 @@ Add a delay too!
<MiniRepl
client:visible
tune={`stack(
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] ~>")
tune={`$: 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] ~>")
.scale("D4:minor").sound("gm_accordion:2")
room(2).gain(.5)
)`}
.room(2).gain(.5)`}
/>
Let's add a bass to make this complete:
<MiniRepl
client:visible
tune={`stack(
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] ~>")
tune={`$: 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] ~>")
.scale("D4:minor").sound("gm_accordion:2")
.room(2).gain(.4),
n("[0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~]/2")
.room(2).gain(.4)
$: n("[0 [~ 0] 4 [3 2] [0 ~] [0 ~] <0 2> ~]/2")
.scale("D2:minor")
.sound("sawtooth,triangle").lpf(800)
)`}
.sound("sawtooth,triangle").lpf(800)`}
/>
<Box>
@ -262,11 +251,11 @@ By the way, inside Mini-Notation, `fast` is `*` and `slow` is `/`.
<MiniRepl client:visible tune={`sound("[bd*4,~ rim ~ cp]*<1 [2 4]>")`} />
## automation with signals
## modulation with signals
Instead of changing values stepwise, we can also control them with signals:
<MiniRepl client:visible tune={`sound("hh*32").gain(sine)`} punchcard punchcardLabels={false} />
<MiniRepl client:visible tune={`sound("hh*16").gain(sine)`} punchcard punchcardLabels={false} />
<Box>
@ -290,7 +279,7 @@ What happens if you flip the range values?
</Box>
We can change the automation speed with slow / fast:
We can change the modulation speed with slow / fast:
<MiniRepl
client:visible
@ -301,7 +290,7 @@ We can change the automation speed with slow / fast:
<Box>
The whole automation will now take 8 cycles to repeat.
The whole modulation will now take 8 cycles to repeat.
</Box>

View file

@ -314,11 +314,11 @@ Let's recap what we've learned in this chapter:
New functions:
| Name | Description | Example |
| ----- | ----------------------------------- | --------------------------------------------------------------------------------- |
| note | set pitch as number or letter | <MiniRepl client:visible tune={`note("b g e c").sound("piano")`} /> |
| scale | interpret `n` as scale degree | <MiniRepl client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
| stack | play patterns in parallel (read on) | <MiniRepl client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
| Name | Description | Example |
| ----- | ----------------------------- | --------------------------------------------------------------------------------- |
| note | set pitch as number or letter | <MiniRepl client:visible tune={`note("b g e c").sound("piano")`} /> |
| scale | interpret `n` as scale degree | <MiniRepl client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
| $: | play patterns in parallel | <MiniRepl client:visible tune={'$: s("bd sd")\n$: note("c eb g")'} /> |
## Examples
@ -356,25 +356,35 @@ New functions:
<Box>
It's called `stack` 😙
You can use `$:` 😙
</Box>
## Playing multiple patterns
If you want to play multiple patterns at the same time, make sure to write `$:` before each:
<MiniRepl
client:visible
tune={`stack(
note("<[c2 c3]*4 [bb1 bb2]*4 [f2 f3]*4 [eb2 eb3]*4>")
.sound("gm_synth_bass_1").lpf(800),
n(\`<
tune={`$: 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]
>*4\`).scale("C4:minor")
.sound("gm_synth_strings_1"),
sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
.bank("RolandTR909")
)`}
.sound("gm_synth_strings_1")
$: sound("bd*4, [~ <sd cp>]*2, [~ hh]*4")
.bank("RolandTR909")`}
/>
<Box>
Try changing `$` to `_$` to mute a part!
</Box>
This is starting to sound like actual music! We have sounds, we have notes, now the last piece of the puzzle is missing: [effects](/workshop/first-effects)

View file

@ -25,20 +25,16 @@ This is the same as:
<MiniRepl
client:visible
tune={`stack(
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()
)`}
tune={`$: 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()`}
/>
Let's visualize what happens here:
<MiniRepl
client:visible
tune={`stack(
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()
)`}
tune={`$: 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
/>
@ -56,11 +52,9 @@ This is like doing
<MiniRepl
client:visible
tune={`stack(
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')
)`}
tune={`$: 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
/>
@ -110,17 +104,15 @@ We can add as often as we like:
<MiniRepl
client:visible
tune={`stack(
n("0 [2 4] <3 5> [~ <4 1>]".add("<0 [0,2,4]>"))
tune={`$: n("0 [2 4] <3 5> [~ <4 1>]".add("<0 [0,2,4]>"))
.scale("C5:minor")
.sound("gm_xylophone")
.room(.4).delay(.125),
note("c2 [eb3,g3]".add("<0 <1 -1>>"))
.room(.4).delay(.125)
$: note("c2 [eb3,g3]".add("<0 <1 -1>>"))
.adsr("[.1 0]:.2:[1 0]")
.sound("gm_acoustic_bass")
.room(.5),
n("0 1 [2 3] 2").sound("jazz").jux(rev)
)`}
.room(.5)
$: n("0 1 [2 3] 2").sound("jazz").jux(rev)`}
/>
**ply**
@ -145,13 +137,17 @@ Try patterning the `ply` function, for example using `"<1 2 1 3>"`
.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)`}
.s("triangle").room(.5).dec(.1)`}
punchcard
/>
<Box>
In the notation `x=>x.`, the `x` is the shifted pattern, which where modifying.
In the notation `.off(1/16, x=>x.add(4))`, says:
- take the original pattern named as `x`
- modify `x` with `.add(4)`, and
- play it offset to the original pattern by `1/16` of a cycle.
</Box>
@ -161,7 +157,7 @@ off is also useful for modifying other sounds, and can even be nested:
client:visible
tune={`s("bd sd [rim bd] sd,[~ hh]*4").bank("CasioRZ1")
.off(2/16, x=>x.speed(1.5).gain(.25)
.off(3/16, y=>y.vowel("<a e i o>*8")))`}
.off(3/16, y=>y.vowel("<a e i o>*8")))`}
/>
| name | description | example |

View file

@ -39,7 +39,7 @@ This page is just a listing of all functions covered in the workshop!
| --------- | ----------------------------- | --------------------------------------------------------------------------------- |
| note | set pitch as number or letter | <MiniRepl client:visible tune={`note("b g e c").sound("piano")`} /> |
| n + scale | set note in scale | <MiniRepl client:visible tune={`n("6 4 2 0").scale("C:minor").sound("piano")`} /> |
| stack | play patterns in parallel | <MiniRepl client:visible tune={`stack(s("bd sd"),note("c eb g"))`} /> |
| $: | play patterns in parallel | <MiniRepl client:visible tune={'$: s("bd sd")\n$: note("c eb g")'} /> |
## Audio Effects