Feat: documentation, UI/UX
This commit is contained in:
@@ -1,140 +1,143 @@
|
||||
# Control Flow
|
||||
|
||||
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.
|
||||
Control flow in Cagire's Forth comes in two families. The first is compiled syntax — `if/then` and `case` — which the compiler handles directly as branch instructions. The second is quotation words — `?`, `!?`, `ifelse`, `select`, `apply` — which pop `( ... )` quotations from the stack and decide whether to run them. Probability and periodic execution (`chance`, `every`, `bjork`) are covered in the Randomness tutorial.
|
||||
|
||||
## if / else / then
|
||||
## Branching with if / else / then
|
||||
|
||||
The simplest branch. Push a condition, then `if`:
|
||||
Push a condition, then `if`. Everything between `if` and `then` runs only when the condition is truthy:
|
||||
|
||||
```forth
|
||||
coin if 0.8 gain then
|
||||
saw s c4 note .
|
||||
;; degrade sound if true
|
||||
coin if
|
||||
7 crush
|
||||
then
|
||||
sine sound
|
||||
c4 note
|
||||
1 decay
|
||||
.
|
||||
```
|
||||
|
||||
The gain is applied if the coin flip is true. The sound will always plays. Add `else` for a two-way split:
|
||||
The crush is applied on half the hits. The sound always plays. Add `else` for a two-way split:
|
||||
|
||||
```forth
|
||||
coin if
|
||||
c4 note
|
||||
c5 note
|
||||
else
|
||||
c3 note
|
||||
then
|
||||
saw s 0.6 gain .
|
||||
saw sound
|
||||
0.3 verb
|
||||
0.5 decay
|
||||
0.6 gain
|
||||
.
|
||||
```
|
||||
|
||||
These are compiled directly into branch instructions. For that reason, these words will not appear in the dictionary.
|
||||
These are compiled directly into branch instructions — they will not appear in the dictionary. This is a "low level" way to use conditionals in Cagire.
|
||||
|
||||
## ? and !?
|
||||
|
||||
When you already have a quotation, `?` executes it if the condition is truthy:
|
||||
|
||||
```forth
|
||||
( 0.4 verb ) coin ?
|
||||
saw s c4 note 0.5 gain . ;; reverb on half the hits
|
||||
```
|
||||
|
||||
`!?` is the opposite — executes when falsy:
|
||||
|
||||
```forth
|
||||
( 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
|
||||
( 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
|
||||
( 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
|
||||
```
|
||||
|
||||
## select
|
||||
|
||||
Choose the nth option from a list of quotations:
|
||||
|
||||
```forth
|
||||
( c4 ) ( e4 ) ( g4 ) ( b4 ) iter 4 mod select
|
||||
note sine s 0.5 decay .
|
||||
```
|
||||
|
||||
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
|
||||
## Matching with case
|
||||
|
||||
For matching a value against several options. Cleaner than a chain of `if`s when you have more than two branches:
|
||||
|
||||
```forth
|
||||
iter 4 mod case
|
||||
1 8 rand 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 .
|
||||
tri s
|
||||
2 fm 0.99 fmh
|
||||
0.6 gain 0.2 chorus
|
||||
1 decay
|
||||
800 lpf
|
||||
.
|
||||
```
|
||||
|
||||
A different root note each time the pattern loops.
|
||||
|
||||
The last line before `endcase` is the default — it runs when no `of` matched:
|
||||
A different root note each time the pattern loops. The last line before `endcase` is the default — it runs when no `of` matched:
|
||||
|
||||
```forth
|
||||
iter 3 mod case
|
||||
0 of 0.9 gain endof
|
||||
0.4 gain ;; default: quieter
|
||||
0.4 gain
|
||||
endcase
|
||||
saw s c4 note .
|
||||
saw s
|
||||
.5 decay
|
||||
c4 note
|
||||
.
|
||||
```
|
||||
|
||||
## times
|
||||
Like `if/then`, `case` is compiled syntax and does not appear in the dictionary.
|
||||
|
||||
Repeat a quotation n times. The variable `@i` is automatically set to the current iteration index (starting from 0):
|
||||
## Quotation Words
|
||||
|
||||
The remaining control flow words operate on quotations — `( ... )` blocks sitting on the stack. Each word pops one or more quotations and decides whether or how to execute them.
|
||||
|
||||
### ? and !?
|
||||
|
||||
`?` executes a quotation if the condition is truthy:
|
||||
|
||||
```forth
|
||||
3 ( c4 @i 4 * + note ) times
|
||||
sine s 0.4 gain 0.5 verb . ;; c4, e4, g#4 — a chord
|
||||
( 0.4 verb 6 crush ) coin ?
|
||||
tri sound 2 fm 0.5 fmh
|
||||
c3 note 0.5 gain 2 decay
|
||||
.
|
||||
```
|
||||
|
||||
Subdivide with `at`:
|
||||
Reverb on half the hits. `!?` is the opposite — executes when falsy:
|
||||
|
||||
```forth
|
||||
4 ( @i 4 / at sine s c4 note 0.3 gain . ) times
|
||||
( 0.5 delay 0.9 delayfeedback ) coin !?
|
||||
saw sound
|
||||
c4 note
|
||||
500 lpf
|
||||
0.5 decay
|
||||
0.5 gain
|
||||
.
|
||||
```
|
||||
|
||||
Four evenly spaced notes within the step.
|
||||
Quiet on half the hits. These pair well with `chance` and `fill` from the Randomness tutorial.
|
||||
|
||||
Vary intensity per iteration:
|
||||
### ifelse
|
||||
|
||||
Two quotations, one condition. The true branch comes first:
|
||||
|
||||
```forth
|
||||
8 (
|
||||
@i 8 / at
|
||||
@i 4 mod 0 = if 0.7 else 0.2 then gain
|
||||
tri s c5 note 0.1 decay .
|
||||
) times
|
||||
( c3 note ) ( c5 note ) coin ifelse
|
||||
saw sound 0.3 verb
|
||||
0.5 decay 0.6 gain
|
||||
.
|
||||
```
|
||||
|
||||
Eight notes per step. Every fourth one louder.
|
||||
Reads naturally: "c3 or c5, 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.
|
||||
|
||||
### select
|
||||
|
||||
Choose the nth quotation from a list. The index is 0-based:
|
||||
|
||||
```forth
|
||||
( c4 ) ( e4 ) ( g4 ) ( b4 ) 0 3 rand select
|
||||
note sine s 0.5 decay .
|
||||
```
|
||||
|
||||
Four notes of a major seventh chord picked randomly. Note that this is unnecessarily complex :)
|
||||
|
||||
### apply
|
||||
|
||||
When you have a quotation and want to execute it unconditionally:
|
||||
|
||||
```forth
|
||||
( dup + ) apply
|
||||
```
|
||||
|
||||
Pops the quotation and runs it. Simpler than `?` when there is no condition to check.
|
||||
|
||||
## More!
|
||||
|
||||
For probability gates, periodic execution, and euclidean rhythms, see the Randomness tutorial. For generators and ranges, see the Generators tutorial.
|
||||
|
||||
Reference in New Issue
Block a user