Feat: continue to improve documentation
This commit is contained in:
@@ -1,185 +1,140 @@
|
||||
# Control Flow
|
||||
|
||||
A drum pattern that plays the same sound on every step is not very interesting. You want kicks on the downbeats, snares on the backbeats, hats filling the gaps. Control flow is how you make decisions inside a step.
|
||||
Sometimes a step should behave differently depending on context — a coin flip, a fill, which iteration of the pattern is playing. Control flow words let you branch, choose, and repeat inside a step's script. Control structures are essential for programming and allow you to create complex and dynamic patterns.
|
||||
|
||||
## if / else / then
|
||||
|
||||
The simplest branch. Push a condition, then `if`:
|
||||
|
||||
```forth
|
||||
step 4 mod 0 = if kick s . then
|
||||
coin if 0.8 gain then
|
||||
saw s c4 note .
|
||||
```
|
||||
|
||||
Every fourth step gets a kick. The rest do nothing. Add `else` for a two-way split:
|
||||
The gain is applied if the coin flip is true. The sound will always plays. Add `else` for a two-way split:
|
||||
|
||||
```forth
|
||||
step 2 mod 0 = if
|
||||
kick s 0.8 gain .
|
||||
coin if
|
||||
c4 note
|
||||
else
|
||||
hat s 0.3 gain .
|
||||
c3 note
|
||||
then
|
||||
saw s 0.6 gain .
|
||||
```
|
||||
|
||||
These are compiler syntax -- you won't find them in the dictionary. Think nothing of it.
|
||||
These are compiled directly into branch instructions. For that reason, these words will not appear in the dictionary.
|
||||
|
||||
## ? and !?
|
||||
|
||||
When you already have a quotation, `?` executes it if the condition is truthy:
|
||||
|
||||
```forth
|
||||
{ snare s . } coin ?
|
||||
{ 0.4 verb } coin ?
|
||||
saw s c4 note 0.5 gain . ;; reverb on half the hits
|
||||
```
|
||||
|
||||
`!?` is the opposite -- executes when falsy:
|
||||
`!?` is the opposite — executes when falsy:
|
||||
|
||||
```forth
|
||||
{ hat s 0.2 gain . } coin !?
|
||||
{ 0.2 gain } coin !?
|
||||
saw s c4 note . ;; quiet on half the hits
|
||||
```
|
||||
|
||||
These pair well with `chance`, `prob`, and the other probability words:
|
||||
|
||||
```forth
|
||||
{ rim s . } fill ? ;; rim only during fills
|
||||
{ 0.5 verb } 0.3 chance ? ;; occasional reverb wash
|
||||
{ 12 + } fill ? ;; octave up during fills
|
||||
```
|
||||
|
||||
## ifelse
|
||||
|
||||
Two quotations, one condition. The true branch comes first:
|
||||
|
||||
```forth
|
||||
{ kick s . } { hat s . } step 2 mod 0 = ifelse
|
||||
```
|
||||
|
||||
Reads naturally: "kick or hat, depending on whether it's an even step."
|
||||
|
||||
```forth
|
||||
{ c3 note } { c4 note } coin ifelse
|
||||
saw s 0.6 gain . ;; bass or lead, coin flip
|
||||
```
|
||||
|
||||
Reads naturally: "c3 or c4, depending on the coin."
|
||||
|
||||
```forth
|
||||
{ 0.8 gain } { 0.3 gain } fill ifelse
|
||||
tri s c4 note 0.2 decay . ;; loud during fills, quiet otherwise
|
||||
```
|
||||
|
||||
## pick
|
||||
|
||||
Choose the nth option from a list of quotations:
|
||||
|
||||
```forth
|
||||
{ kick s . } { snare s . } { hat s . } step 3 mod pick
|
||||
```
|
||||
|
||||
Step 0 plays kick, step 1 plays snare, step 2 plays hat. The index is 0-based.
|
||||
|
||||
```forth
|
||||
{ c4 } { e4 } { g4 } { b4 } step 4 mod pick
|
||||
{ c4 } { e4 } { g4 } { b4 } iter 4 mod pick
|
||||
note sine s 0.5 decay .
|
||||
```
|
||||
|
||||
Four notes cycling through a major seventh chord, one per step.
|
||||
Four notes cycling through a major seventh chord, one per pattern iteration. The index is 0-based.
|
||||
|
||||
## apply
|
||||
|
||||
When you have a quotation and want to execute it unconditionally, use `apply`:
|
||||
|
||||
```forth
|
||||
{ dup + } apply ;; doubles the top value
|
||||
```
|
||||
|
||||
This is simpler than `?` when there is no condition to check. It pops the quotation and runs it.
|
||||
|
||||
## case / of / endof / endcase
|
||||
|
||||
For matching a value against several options. Cleaner than a chain of `if`s when you have more than two branches:
|
||||
|
||||
```forth
|
||||
step 8 mod case
|
||||
0 of kick s . endof
|
||||
4 of snare s . endof
|
||||
iter 4 mod case
|
||||
0 of c3 note endof
|
||||
1 of e3 note endof
|
||||
2 of g3 note endof
|
||||
3 of a3 note endof
|
||||
endcase
|
||||
saw s 0.6 gain 800 lpf .
|
||||
```
|
||||
|
||||
Steps 0 and 4 get sounds. Everything else falls through to `endcase` and nothing happens.
|
||||
A different root note each time the pattern loops.
|
||||
|
||||
A fuller pattern:
|
||||
The last line before `endcase` is the default — it runs when no `of` matched:
|
||||
|
||||
```forth
|
||||
step 8 mod case
|
||||
0 of kick s 0.9 gain . endof
|
||||
2 of hat s 0.3 gain . endof
|
||||
4 of snare s 0.7 gain . endof
|
||||
6 of hat s 0.3 gain . endof
|
||||
hat s 0.15 gain .
|
||||
endcase
|
||||
```
|
||||
|
||||
The last line before `endcase` is the default -- it runs when no `of` matched. Here it gives unmatched steps a ghost hat.
|
||||
|
||||
The `of` value can be any expression:
|
||||
|
||||
```forth
|
||||
step 16 mod case
|
||||
0 of kick s . endof
|
||||
3 1 + of snare s . endof
|
||||
2 4 * of kick s . snare s . endof
|
||||
iter 3 mod case
|
||||
0 of 0.9 gain endof
|
||||
0.4 gain ;; default: quieter
|
||||
endcase
|
||||
saw s c4 note .
|
||||
```
|
||||
|
||||
## times
|
||||
|
||||
Repeat a quotation n times. `@i` holds the current iteration (starting from 0):
|
||||
|
||||
```forth
|
||||
4 { @i 4 / at hat s . } times ;; four hats, evenly spaced
|
||||
```
|
||||
|
||||
Build chords:
|
||||
Repeat a quotation n times. The variable `@i` is automatically set to the current iteration index (starting from 0):
|
||||
|
||||
```forth
|
||||
3 { c4 @i 4 * + note } times
|
||||
sine s 0.4 gain 0.5 verb . ;; c4, e4, g#4
|
||||
sine s 0.4 gain 0.5 verb . ;; c4, e4, g#4 — a chord
|
||||
```
|
||||
|
||||
Subdivide and accent:
|
||||
Subdivide with `at`:
|
||||
|
||||
```forth
|
||||
4 { @i 4 / at sine s c4 note 0.3 gain . } times
|
||||
```
|
||||
|
||||
Four evenly spaced notes within the step.
|
||||
|
||||
Vary intensity per iteration:
|
||||
|
||||
```forth
|
||||
8 {
|
||||
@i 8 / at
|
||||
@i 4 mod 0 = if 0.7 else 0.2 then gain
|
||||
hat s .
|
||||
tri s c5 note 0.1 decay .
|
||||
} times
|
||||
```
|
||||
|
||||
Eight hats per step. Every fourth one louder.
|
||||
|
||||
## Putting It Together
|
||||
|
||||
A basic drum pattern using `case`:
|
||||
|
||||
```forth
|
||||
step 8 mod case
|
||||
0 of kick s . endof
|
||||
2 of { hat s . } often endof
|
||||
4 of snare s . endof
|
||||
6 of { rim s . } sometimes endof
|
||||
{ hat s 0.15 gain . } coin ?
|
||||
endcase
|
||||
```
|
||||
|
||||
Kicks and snares on the strong beats. Hats and rims show up probabilistically. The default sprinkles ghost hats.
|
||||
|
||||
A melodic step that picks a scale degree and adds micro-timing:
|
||||
|
||||
```forth
|
||||
{ c4 } { d4 } { e4 } { g4 } { a4 } step 5 mod pick
|
||||
note
|
||||
|
||||
step 3 mod 0 = if
|
||||
0 0.33 0.66 at ;; triplet feel on every third step
|
||||
then
|
||||
|
||||
saw s 0.4 gain 0.3 decay 0.2 verb .
|
||||
```
|
||||
|
||||
A `times` loop paired with `case` for a drum machine in one step:
|
||||
|
||||
```forth
|
||||
4 {
|
||||
@i case
|
||||
0 of kick s . endof
|
||||
1 of hat s 0.3 gain . endof
|
||||
2 of snare s . endof
|
||||
3 of { rim s . } 0.5 chance endof
|
||||
endcase
|
||||
@i 4 / at
|
||||
} times
|
||||
```
|
||||
|
||||
Four voices, four sub-positions, one step.
|
||||
Eight notes per step. Every fourth one louder.
|
||||
|
||||
Reference in New Issue
Block a user