Feat: refactoring codebase
This commit is contained in:
100
docs/tutorials/at.md
Normal file
100
docs/tutorials/at.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Timing with at
|
||||
|
||||
Every step has a duration. By default, sounds emit at the very start of that duration. `at` changes *when* within the step sounds fire -- giving you sub-step rhythmic control without adding more steps.
|
||||
|
||||
## The Basics
|
||||
|
||||
`at` drains the entire stack and stores the values as timing offsets. Each value is a fraction of the step duration: 0 = start, 0.5 = halfway, 1.0 = next step boundary.
|
||||
|
||||
```forth
|
||||
0.5 at kick s . ;; kick at the midpoint
|
||||
```
|
||||
|
||||
Push multiple values before calling `at` to get multiple emits from a single `.`:
|
||||
|
||||
```forth
|
||||
0 0.5 at kick s . ;; two kicks: one at start, one at midpoint
|
||||
0 0.25 0.5 0.75 at hat s . ;; four hats, evenly spaced
|
||||
```
|
||||
|
||||
The deltas persist across multiple `.` calls until `clear` or a new `at`:
|
||||
|
||||
```forth
|
||||
0 0.5 at
|
||||
kick s . ;; 2 kicks
|
||||
hat s . ;; 2 hats (same timing)
|
||||
clear
|
||||
snare s . ;; 1 snare (deltas cleared)
|
||||
```
|
||||
|
||||
## Cross-product: at Without arp
|
||||
|
||||
Without `arp`, deltas multiply with polyphonic voices. If you have 3 notes and 2 deltas, you get 6 emits -- every note at every delta:
|
||||
|
||||
```forth
|
||||
0 0.5 at
|
||||
c4 e4 g4 note sine s . ;; 6 emits: 3 notes x 2 deltas
|
||||
```
|
||||
|
||||
This is a chord played twice per step.
|
||||
|
||||
## 1:1 Pairing: at With arp
|
||||
|
||||
`arp` changes the behavior. Instead of cross-product, deltas and arp values pair up 1:1. Each delta gets one note from the arpeggio:
|
||||
|
||||
```forth
|
||||
0 0.33 0.66 at
|
||||
c4 e4 g4 arp note sine s . ;; c4 at 0, e4 at 0.33, g4 at 0.66
|
||||
```
|
||||
|
||||
If the lists differ in length, the shorter one wraps around:
|
||||
|
||||
```forth
|
||||
0 0.25 0.5 0.75 at
|
||||
c4 e4 arp note sine s . ;; c4, e4, c4, e4 at 4 time points
|
||||
```
|
||||
|
||||
This is THE key distinction. Without `arp`: every note at every time. With `arp`: one note per time slot.
|
||||
|
||||
## Generating Deltas
|
||||
|
||||
You rarely type deltas by hand. Use generators:
|
||||
|
||||
Evenly spaced via `.,`:
|
||||
|
||||
```forth
|
||||
0 1 0.25 ., at hat s . ;; 0 0.25 0.5 0.75 1.0
|
||||
```
|
||||
|
||||
Euclidean distribution via `euclid`:
|
||||
|
||||
```forth
|
||||
3 8 euclid at hat s . ;; 3 hats at positions 0, 3, 5
|
||||
```
|
||||
|
||||
Random timing via `gen`:
|
||||
|
||||
```forth
|
||||
{ 0.0 1.0 rand } 4 gen at hat s . ;; 4 hats at random positions
|
||||
```
|
||||
|
||||
Geometric spacing via `geom..`:
|
||||
|
||||
```forth
|
||||
0.0 2.0 4 geom.. at hat s . ;; exponentially spaced
|
||||
```
|
||||
|
||||
## Gating at
|
||||
|
||||
Wrap `at` expressions in quotations for conditional timing:
|
||||
|
||||
```forth
|
||||
{ 0 0.25 0.5 0.75 at } 2 every ;; 16th-note hats every other bar
|
||||
hat s .
|
||||
|
||||
{ 0 0.5 at } 0.5 chance ;; 50% chance of double-hit
|
||||
kick s .
|
||||
```
|
||||
|
||||
When the quotation doesn't execute, no deltas are set -- you get the default single emit at beat start.
|
||||
|
||||
138
docs/tutorials/generators.md
Normal file
138
docs/tutorials/generators.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Generators & Sequences
|
||||
|
||||
Sequences of values drive music: arpeggios, parameter sweeps, rhythmic patterns. Cagire has dedicated words for building sequences on the stack, transforming them, and collapsing them to single values.
|
||||
|
||||
## Ranges
|
||||
|
||||
`..` pushes an integer range onto the stack. Both endpoints are inclusive. If start exceeds end, it counts down:
|
||||
|
||||
```forth
|
||||
1 5 .. ;; 1 2 3 4 5
|
||||
5 1 .. ;; 5 4 3 2 1
|
||||
0 7 .. ;; 0 1 2 3 4 5 6 7
|
||||
```
|
||||
|
||||
`.,` adds a step parameter. Works with floats:
|
||||
|
||||
```forth
|
||||
0 1 0.25 ., ;; 0 0.25 0.5 0.75 1
|
||||
0 10 2 ., ;; 0 2 4 6 8 10
|
||||
1 0 0.5 ., ;; 1 0.5 0 (descending)
|
||||
```
|
||||
|
||||
`geom..` builds a geometric sequence. Takes start, ratio, and count:
|
||||
|
||||
```forth
|
||||
1 2 4 geom.. ;; 1 2 4 8
|
||||
100 0.5 4 geom.. ;; 100 50 25 12.5
|
||||
```
|
||||
|
||||
Build a harmonic series:
|
||||
|
||||
```forth
|
||||
110 2 5 geom.. 5 rev freq
|
||||
```
|
||||
|
||||
That gives you 110, 220, 440, 880, 1760 (reversed), ready to feed into `freq`.
|
||||
|
||||
## Computed Sequences
|
||||
|
||||
`gen` executes a quotation n times and collects all results. The quotation must push exactly one value per call:
|
||||
|
||||
```forth
|
||||
{ 1 6 rand } 4 gen ;; 4 random values between 1 and 6
|
||||
{ coin } 8 gen ;; 8 random 0s and 1s
|
||||
```
|
||||
|
||||
Contrast with `times`, which executes for side effects and does not collect. `times` sets `@i` to the current index:
|
||||
|
||||
```forth
|
||||
4 { @i } times ;; 0 1 2 3 (pushes @i each iteration)
|
||||
4 { @i 60 + note sine s . } times ;; plays 4 notes, collects nothing
|
||||
```
|
||||
|
||||
The distinction: `gen` is for building data. `times` is for doing things.
|
||||
|
||||
## Euclidean Patterns
|
||||
|
||||
`euclid` distributes k hits evenly across n positions and pushes the hit indices:
|
||||
|
||||
```forth
|
||||
3 8 euclid ;; 0 3 5
|
||||
4 8 euclid ;; 0 2 4 6
|
||||
5 8 euclid ;; 0 1 3 5 6
|
||||
```
|
||||
|
||||
`euclidrot` adds a rotation parameter that shifts the pattern:
|
||||
|
||||
```forth
|
||||
3 8 0 euclidrot ;; 0 3 5 (no rotation)
|
||||
3 8 1 euclidrot ;; 1 4 6
|
||||
3 8 2 euclidrot ;; 1 4 6
|
||||
```
|
||||
|
||||
These give you raw indices as data on the stack. This is different from `bjork` and `pbjork` (covered in the Randomness tutorial), which execute a quotation on matching steps. `euclid` gives you numbers to work with; `bjork` triggers actions.
|
||||
|
||||
## Transforming Sequences
|
||||
|
||||
Four words reshape values already on the stack. All take n (the count of items to operate on) from the top:
|
||||
|
||||
`rev` reverses order:
|
||||
|
||||
```forth
|
||||
1 2 3 4 4 rev ;; 4 3 2 1
|
||||
c4 e4 g4 3 rev ;; g4 e4 c4 (descending arpeggio)
|
||||
```
|
||||
|
||||
`shuffle` randomizes order:
|
||||
|
||||
```forth
|
||||
c4 e4 g4 b4 4 shuffle ;; random permutation each time
|
||||
```
|
||||
|
||||
`sort` and `rsort` for ascending and descending:
|
||||
|
||||
```forth
|
||||
3 1 4 1 5 5 sort ;; 1 1 3 4 5
|
||||
3 1 4 1 5 5 rsort ;; 5 4 3 1 1
|
||||
```
|
||||
|
||||
## Reducing Sequences
|
||||
|
||||
`sum` and `prod` collapse n values into one:
|
||||
|
||||
```forth
|
||||
1 2 3 4 4 sum ;; 10
|
||||
1 2 3 4 4 prod ;; 24
|
||||
```
|
||||
|
||||
Useful for computing averages or accumulating values:
|
||||
|
||||
```forth
|
||||
{ 1 6 rand } 4 gen 4 sum ;; sum of 4 dice rolls
|
||||
```
|
||||
|
||||
## Replication
|
||||
|
||||
`dupn` (alias `!`) duplicates a value n times:
|
||||
|
||||
```forth
|
||||
440 3 dupn ;; 440 440 440
|
||||
c4 4 dupn ;; c4 c4 c4 c4
|
||||
```
|
||||
|
||||
Build a drone chord -- same note, different octaves:
|
||||
|
||||
```forth
|
||||
c3 note 0.5 gain sine s .
|
||||
c3 note 12 + 0.5 gain sine s .
|
||||
c3 note 24 + 0.3 gain sine s .
|
||||
```
|
||||
|
||||
Or replicate a value for batch processing:
|
||||
|
||||
```forth
|
||||
0.5 4 dupn 4 sum ;; 2.0
|
||||
```
|
||||
|
||||
The generator words produce raw material. The transform words shape it. Together they let you express complex musical ideas in a few words.
|
||||
302
docs/tutorials/harmony.md
Normal file
302
docs/tutorials/harmony.md
Normal file
@@ -0,0 +1,302 @@
|
||||
# Notes & Harmony
|
||||
|
||||
Cagire speaks music theory. Notes, intervals, chords, and scales are all first-class words that compile to stack operations on MIDI values. This tutorial covers every pitch-related feature.
|
||||
|
||||
## MIDI Notes
|
||||
|
||||
Write a note name followed by an octave number. It compiles to a MIDI integer:
|
||||
|
||||
```forth
|
||||
c4 ;; 60 (middle C)
|
||||
a4 ;; 69 (concert A)
|
||||
e3 ;; 52
|
||||
```
|
||||
|
||||
Sharps use `s` or `#`. Flats use `b`:
|
||||
|
||||
```forth
|
||||
fs4 ;; 66 (F sharp 4)
|
||||
f#4 ;; 66 (same thing)
|
||||
bb3 ;; 58 (B flat 3)
|
||||
eb4 ;; 63
|
||||
```
|
||||
|
||||
Octave range is -1 to 9. The formula is `(octave + 1) * 12 + base + modifier`, where C=0, D=2, E=4, F=5, G=7, A=9, B=11.
|
||||
|
||||
Note literals push a single integer onto the stack, just like writing `60` directly. They work everywhere an integer works:
|
||||
|
||||
```forth
|
||||
c4 note sine s . ;; play middle C as a sine
|
||||
a4 note 0.5 gain modal s . ;; concert A, quieter
|
||||
```
|
||||
|
||||
## Intervals
|
||||
|
||||
An interval duplicates the top of the stack and adds semitones. This lets you build chords by stacking:
|
||||
|
||||
```forth
|
||||
c4 M3 P5 ;; stack: 60 64 67 (C major triad)
|
||||
c4 m3 P5 ;; stack: 60 63 67 (C minor triad)
|
||||
a3 P5 ;; stack: 57 64 (A plus a fifth)
|
||||
```
|
||||
|
||||
Simple intervals (within one octave):
|
||||
|
||||
| Interval | Semitones | Name |
|
||||
|----------|-----------|------|
|
||||
| `P1` / `unison` | 0 | Perfect unison |
|
||||
| `m2` | 1 | Minor 2nd |
|
||||
| `M2` | 2 | Major 2nd |
|
||||
| `m3` | 3 | Minor 3rd |
|
||||
| `M3` | 4 | Major 3rd |
|
||||
| `P4` | 5 | Perfect 4th |
|
||||
| `aug4` / `dim5` / `tritone` | 6 | Tritone |
|
||||
| `P5` | 7 | Perfect 5th |
|
||||
| `m6` | 8 | Minor 6th |
|
||||
| `M6` | 9 | Major 6th |
|
||||
| `m7` | 10 | Minor 7th |
|
||||
| `M7` | 11 | Major 7th |
|
||||
| `P8` | 12 | Octave |
|
||||
|
||||
Compound intervals (beyond one octave):
|
||||
|
||||
| Interval | Semitones |
|
||||
|----------|-----------|
|
||||
| `m9` | 13 |
|
||||
| `M9` | 14 |
|
||||
| `m10` | 15 |
|
||||
| `M10` | 16 |
|
||||
| `P11` | 17 |
|
||||
| `aug11` | 18 |
|
||||
| `P12` | 19 |
|
||||
| `m13` | 20 |
|
||||
| `M13` | 21 |
|
||||
| `m14` | 22 |
|
||||
| `M14` | 23 |
|
||||
| `P15` | 24 |
|
||||
|
||||
## Chords
|
||||
|
||||
Chord words take a root note and push all the chord tones. They eat the root and replace it with the full voicing:
|
||||
|
||||
```forth
|
||||
c4 maj ;; stack: 60 64 67
|
||||
c4 min7 ;; stack: 60 63 67 70
|
||||
c4 dom9 ;; stack: 60 64 67 70 74
|
||||
```
|
||||
|
||||
**Triads:**
|
||||
|
||||
| Word | Intervals | Example (C4) |
|
||||
|------|-----------|-------------|
|
||||
| `maj` | 0 4 7 | 60 64 67 |
|
||||
| `m` | 0 3 7 | 60 63 67 |
|
||||
| `dim` | 0 3 6 | 60 63 66 |
|
||||
| `aug` | 0 4 8 | 60 64 68 |
|
||||
| `sus2` | 0 2 7 | 60 62 67 |
|
||||
| `sus4` | 0 5 7 | 60 65 67 |
|
||||
|
||||
**Seventh chords:**
|
||||
|
||||
| Word | Intervals | Example (C4) |
|
||||
|------|-----------|-------------|
|
||||
| `maj7` | 0 4 7 11 | 60 64 67 71 |
|
||||
| `min7` | 0 3 7 10 | 60 63 67 70 |
|
||||
| `dom7` | 0 4 7 10 | 60 64 67 70 |
|
||||
| `dim7` | 0 3 6 9 | 60 63 66 69 |
|
||||
| `m7b5` | 0 3 6 10 | 60 63 66 70 |
|
||||
| `minmaj7` | 0 3 7 11 | 60 63 67 71 |
|
||||
| `aug7` | 0 4 8 10 | 60 64 68 70 |
|
||||
|
||||
**Sixth chords:**
|
||||
|
||||
| Word | Intervals | Example (C4) |
|
||||
|------|-----------|-------------|
|
||||
| `maj6` | 0 4 7 9 | 60 64 67 69 |
|
||||
| `min6` | 0 3 7 9 | 60 63 67 69 |
|
||||
|
||||
**Extended chords:**
|
||||
|
||||
| Word | Intervals | Example (C4) |
|
||||
|------|-----------|-------------|
|
||||
| `dom9` | 0 4 7 10 14 | 60 64 67 70 74 |
|
||||
| `maj9` | 0 4 7 11 14 | 60 64 67 71 74 |
|
||||
| `min9` | 0 3 7 10 14 | 60 63 67 70 74 |
|
||||
| `dom11` | 0 4 7 10 14 17 | 60 64 67 70 74 77 |
|
||||
| `min11` | 0 3 7 10 14 17 | 60 63 67 70 74 77 |
|
||||
| `dom13` | 0 4 7 10 14 21 | 60 64 67 70 74 81 |
|
||||
|
||||
**Add chords:**
|
||||
|
||||
| Word | Intervals | Example (C4) |
|
||||
|------|-----------|-------------|
|
||||
| `add9` | 0 4 7 14 | 60 64 67 74 |
|
||||
| `add11` | 0 4 7 17 | 60 64 67 77 |
|
||||
| `madd9` | 0 3 7 14 | 60 63 67 74 |
|
||||
|
||||
**Altered dominants:**
|
||||
|
||||
| Word | Intervals | Example (C4) |
|
||||
|------|-----------|-------------|
|
||||
| `dom7b9` | 0 4 7 10 13 | 60 64 67 70 73 |
|
||||
| `dom7s9` | 0 4 7 10 15 | 60 64 67 70 75 |
|
||||
| `dom7b5` | 0 4 6 10 | 60 64 66 70 |
|
||||
| `dom7s5` | 0 4 8 10 | 60 64 68 70 |
|
||||
|
||||
Chord tones are varargs -- they eat the entire stack. So a chord word should come right after the root note:
|
||||
|
||||
```forth
|
||||
c4 maj note sine s . ;; plays all 3 notes as one chord
|
||||
```
|
||||
|
||||
## Scales
|
||||
|
||||
Scale words convert a degree index into a MIDI note. The base note is C4 (MIDI 60). Degrees wrap around with octave transposition:
|
||||
|
||||
```forth
|
||||
0 major ;; 60 (C4 -- degree 0)
|
||||
4 major ;; 67 (G4 -- degree 4)
|
||||
7 major ;; 72 (C5 -- degree 7, wraps to next octave)
|
||||
-1 major ;; 59 (B3 -- negative degrees go down)
|
||||
```
|
||||
|
||||
Use scales with `cycle` or `rand` to walk through pitches:
|
||||
|
||||
```forth
|
||||
0 1 2 3 4 5 6 7 8 cycle minor note sine s .
|
||||
```
|
||||
|
||||
**Standard modes:**
|
||||
|
||||
| Word | Pattern (semitones) |
|
||||
|------|-------------------|
|
||||
| `major` | 0 2 4 5 7 9 11 |
|
||||
| `minor` | 0 2 3 5 7 8 10 |
|
||||
| `dorian` | 0 2 3 5 7 9 10 |
|
||||
| `phrygian` | 0 1 3 5 7 8 10 |
|
||||
| `lydian` | 0 2 4 6 7 9 11 |
|
||||
| `mixolydian` | 0 2 4 5 7 9 10 |
|
||||
| `aeolian` | 0 2 3 5 7 8 10 |
|
||||
| `locrian` | 0 1 3 5 6 8 10 |
|
||||
|
||||
**Pentatonic and blues:**
|
||||
|
||||
| Word | Pattern |
|
||||
|------|---------|
|
||||
| `pentatonic` | 0 2 4 7 9 |
|
||||
| `minpent` | 0 3 5 7 10 |
|
||||
| `blues` | 0 3 5 6 7 10 |
|
||||
|
||||
**Chromatic and whole tone:**
|
||||
|
||||
| Word | Pattern |
|
||||
|------|---------|
|
||||
| `chromatic` | 0 1 2 3 4 5 6 7 8 9 10 11 |
|
||||
| `wholetone` | 0 2 4 6 8 10 |
|
||||
|
||||
**Harmonic and melodic minor:**
|
||||
|
||||
| Word | Pattern |
|
||||
|------|---------|
|
||||
| `harmonicminor` | 0 2 3 5 7 8 11 |
|
||||
| `melodicminor` | 0 2 3 5 7 9 11 |
|
||||
|
||||
**Jazz / Bebop:**
|
||||
|
||||
| Word | Pattern |
|
||||
|------|---------|
|
||||
| `bebop` | 0 2 4 5 7 9 10 11 |
|
||||
| `bebopmaj` | 0 2 4 5 7 8 9 11 |
|
||||
| `bebopmin` | 0 2 3 5 7 8 9 10 |
|
||||
| `altered` | 0 1 3 4 6 8 10 |
|
||||
| `lyddom` | 0 2 4 6 7 9 10 |
|
||||
|
||||
**Symmetric:**
|
||||
|
||||
| Word | Pattern |
|
||||
|------|---------|
|
||||
| `halfwhole` | 0 1 3 4 6 7 9 10 |
|
||||
| `wholehalf` | 0 2 3 5 6 8 9 11 |
|
||||
| `augmented` | 0 3 4 7 8 11 |
|
||||
| `tritone` | 0 1 4 6 7 10 |
|
||||
| `prometheus` | 0 2 4 6 9 10 |
|
||||
|
||||
**Modal variants (from melodic minor):**
|
||||
|
||||
| Word | Pattern |
|
||||
|------|---------|
|
||||
| `dorianb2` | 0 1 3 5 7 9 10 |
|
||||
| `lydianaug` | 0 2 4 6 8 9 11 |
|
||||
| `mixb6` | 0 2 4 5 7 8 10 |
|
||||
| `locrian2` | 0 2 3 5 6 8 10 |
|
||||
|
||||
## Octave Shifting
|
||||
|
||||
`oct` transposes a note by octaves:
|
||||
|
||||
```forth
|
||||
c4 1 oct ;; 72 (C5)
|
||||
c4 -1 oct ;; 48 (C3)
|
||||
c4 2 oct ;; 84 (C6)
|
||||
```
|
||||
|
||||
Stack effect: `(note shift -- transposed)`. The shift is multiplied by 12 and added to the note.
|
||||
|
||||
## Frequency Conversion
|
||||
|
||||
`mtof` converts a MIDI note to frequency in Hz. `ftom` does the reverse:
|
||||
|
||||
```forth
|
||||
69 mtof ;; 440.0 (A4)
|
||||
60 mtof ;; 261.63 (C4)
|
||||
440 ftom ;; 69.0
|
||||
```
|
||||
|
||||
Useful when a synth parameter expects Hz rather than MIDI:
|
||||
|
||||
```forth
|
||||
c4 mtof freq sine s .
|
||||
```
|
||||
|
||||
## Putting It Together
|
||||
|
||||
A chord progression cycling every pattern iteration:
|
||||
|
||||
```forth
|
||||
{ c3 maj7 } { f3 maj7 } { g3 dom7 } { c3 maj7 } 4 pcycle
|
||||
note sine s .
|
||||
```
|
||||
|
||||
Arpeggiate a chord across the step's time divisions:
|
||||
|
||||
```forth
|
||||
c4 min7 arp note 0.5 decay sine s .
|
||||
```
|
||||
|
||||
Random notes from a scale:
|
||||
|
||||
```forth
|
||||
0 7 rand minor note sine s .
|
||||
```
|
||||
|
||||
A bass line walking scale degrees:
|
||||
|
||||
```forth
|
||||
0 2 4 5 7 5 4 2 8 cycle minor note
|
||||
-2 oct 0.8 gain sine s .
|
||||
```
|
||||
|
||||
Chord voicings with random inversion:
|
||||
|
||||
```forth
|
||||
e3 min9
|
||||
{ } { 1 oct } 2 choose
|
||||
note modal s .
|
||||
```
|
||||
|
||||
Stacked intervals for custom voicings:
|
||||
|
||||
```forth
|
||||
c3 P5 P8 M10 ;; C3, G3, C4, E4
|
||||
note sine s .
|
||||
```
|
||||
201
docs/tutorials/randomness.md
Normal file
201
docs/tutorials/randomness.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Randomness
|
||||
|
||||
Music needs surprise. A pattern that plays identically every time gets boring fast. Cagire has a rich set of words for injecting randomness and controlled variation into your sequences.
|
||||
|
||||
## Random Numbers
|
||||
|
||||
`coin` pushes 0 or 1 with equal probability:
|
||||
|
||||
```forth
|
||||
coin note sine s . ;; either 0 or 1 as the note
|
||||
```
|
||||
|
||||
`rand` takes a range and returns a random value. If both bounds are integers, the result is an integer. If either is a float, you get a float:
|
||||
|
||||
```forth
|
||||
60 72 rand note sine s . ;; random MIDI note from 60 to 72
|
||||
0.3 0.9 rand gain sine s . ;; random gain between 0.3 and 0.9
|
||||
```
|
||||
|
||||
`exprand` and `logrand` give you weighted distributions. `exprand` is biased toward the low end, `logrand` toward the high end:
|
||||
|
||||
```forth
|
||||
200.0 8000.0 exprand freq sine s . ;; mostly low frequencies
|
||||
200.0 8000.0 logrand freq sine s . ;; mostly high frequencies
|
||||
```
|
||||
|
||||
These are useful for parameters where perception is logarithmic, like frequency and duration.
|
||||
|
||||
## Conditional Execution
|
||||
|
||||
The probability words take a quotation and execute it with some chance. `chance` takes a float from 0.0 to 1.0, `prob` takes a percentage from 0 to 100:
|
||||
|
||||
```forth
|
||||
{ hat s . } 0.25 chance ;; 25% chance
|
||||
{ hat s . } 75 prob ;; 75% chance
|
||||
```
|
||||
|
||||
Named probability words save you from remembering numbers:
|
||||
|
||||
| Word | Probability |
|
||||
|------|------------|
|
||||
| `always` | 100% |
|
||||
| `almostAlways` | 90% |
|
||||
| `often` | 75% |
|
||||
| `sometimes` | 50% |
|
||||
| `rarely` | 25% |
|
||||
| `almostNever` | 10% |
|
||||
| `never` | 0% |
|
||||
|
||||
```forth
|
||||
{ hat s . } often ;; 75%
|
||||
{ snare s . } sometimes ;; 50%
|
||||
{ clap s . } rarely ;; 25%
|
||||
```
|
||||
|
||||
`always` and `never` are useful when you want to temporarily mute or unmute a voice without deleting code. Change `sometimes` to `never` to silence it, `always` to bring it back.
|
||||
|
||||
Use `?` and `!?` with `coin` for quick coin-flip decisions:
|
||||
|
||||
```forth
|
||||
{ hat s . } coin ? ;; execute if coin is 1
|
||||
{ rim s . } coin !? ;; execute if coin is 0
|
||||
```
|
||||
|
||||
## Selection
|
||||
|
||||
`choose` picks randomly from n items on the stack:
|
||||
|
||||
```forth
|
||||
kick snare hat 3 choose s . ;; random drum hit
|
||||
60 64 67 72 4 choose note sine s . ;; random note from a set
|
||||
```
|
||||
|
||||
When a chosen item is a quotation, it gets executed:
|
||||
|
||||
```forth
|
||||
{ 0.1 decay } { 0.5 decay } { 0.9 decay } 3 choose
|
||||
sine s .
|
||||
```
|
||||
|
||||
`wchoose` lets you assign weights to each option. Push value/weight pairs:
|
||||
|
||||
```forth
|
||||
kick 0.5 snare 0.3 hat 0.2 3 wchoose s .
|
||||
```
|
||||
|
||||
Kick plays 50% of the time, snare 30%, hat 20%. Weights don't need to sum to 1 -- they're normalized automatically.
|
||||
|
||||
`shuffle` randomizes the order of n items on the stack:
|
||||
|
||||
```forth
|
||||
60 64 67 72 4 shuffle ;; stack now has the same 4 values in random order
|
||||
```
|
||||
|
||||
Combined with `note`, this gives you a random permutation of a chord every time the step runs.
|
||||
|
||||
## Cycling
|
||||
|
||||
Cycling steps through values deterministically. No randomness -- pure rotation.
|
||||
|
||||
`cycle` selects based on how many times this step has played (its `runs` count):
|
||||
|
||||
```forth
|
||||
60 64 67 3 cycle note sine s . ;; 60, 64, 67, 60, 64, 67, ...
|
||||
```
|
||||
|
||||
`pcycle` selects based on the pattern iteration count (`iter`):
|
||||
|
||||
```forth
|
||||
kick snare 2 pcycle s . ;; kick on even iterations, snare on odd
|
||||
```
|
||||
|
||||
The difference matters when patterns have different lengths. `cycle` counts per-step, `pcycle` counts per-pattern.
|
||||
|
||||
Quotations work here too:
|
||||
|
||||
```forth
|
||||
{ c4 note } { e4 note } { g4 note } 3 cycle
|
||||
sine s .
|
||||
```
|
||||
|
||||
`bounce` ping-pongs instead of wrapping around:
|
||||
|
||||
```forth
|
||||
60 64 67 72 4 bounce note sine s . ;; 60, 64, 67, 72, 67, 64, 60, 64, ...
|
||||
```
|
||||
|
||||
## Periodic Execution
|
||||
|
||||
`every` runs a quotation once every n pattern iterations:
|
||||
|
||||
```forth
|
||||
{ crash s . } 4 every ;; crash cymbal every 4th iteration
|
||||
```
|
||||
|
||||
`bjork` and `pbjork` use Bjorklund's algorithm to distribute k hits across n positions as evenly as possible. Classic Euclidean rhythms:
|
||||
|
||||
```forth
|
||||
{ hat s . } 3 8 bjork ;; tresillo: x..x..x. (by step runs)
|
||||
{ hat s . } 5 8 pbjork ;; cinquillo: x.xx.xx. (by pattern iterations)
|
||||
```
|
||||
|
||||
`bjork` counts by step runs (how many times this particular step has played). `pbjork` counts by pattern iterations. Some classic patterns:
|
||||
|
||||
| k | n | Name |
|
||||
|---|---|------|
|
||||
| 3 | 8 | tresillo |
|
||||
| 5 | 8 | cinquillo |
|
||||
| 5 | 16 | bossa nova |
|
||||
| 7 | 16 | samba |
|
||||
|
||||
## Seeding
|
||||
|
||||
By default, every run produces different random values. Use `seed` to make randomness reproducible:
|
||||
|
||||
```forth
|
||||
42 seed
|
||||
60 72 rand note sine s . ;; always the same "random" note
|
||||
```
|
||||
|
||||
The seed is set at the start of the script. Same seed, same sequence. Useful when you want a specific random pattern to repeat.
|
||||
|
||||
## Combining Words
|
||||
|
||||
The real power comes from mixing techniques. A hi-hat pattern with ghost notes:
|
||||
|
||||
```forth
|
||||
hat s
|
||||
{ 0.3 0.6 rand gain } { 0.8 gain } 2 cycle
|
||||
.
|
||||
```
|
||||
|
||||
Full volume on even runs, random quiet on odd runs.
|
||||
|
||||
A bass line that changes every 4 bars:
|
||||
|
||||
```forth
|
||||
{ c2 note } { e2 note } { g2 note } { a2 note } 4 pcycle
|
||||
{ 0.5 decay } often
|
||||
sine s .
|
||||
```
|
||||
|
||||
Layered percussion with different densities:
|
||||
|
||||
```forth
|
||||
{ kick s . } always
|
||||
{ snare s . } 2 every
|
||||
{ hat s . } 5 8 bjork
|
||||
{ rim s . } rarely
|
||||
```
|
||||
|
||||
A melodic step with weighted note selection and random timbre:
|
||||
|
||||
```forth
|
||||
c4 0.4 e4 0.3 g4 0.2 b4 0.1 4 wchoose note
|
||||
0.3 0.7 rand decay
|
||||
1.0 4.0 exprand harmonics
|
||||
modal s .
|
||||
```
|
||||
|
||||
The root note plays most often. Higher chord tones are rarer. Decay and harmonics vary continuously.
|
||||
71
docs/tutorials/variables.md
Normal file
71
docs/tutorials/variables.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Using Variables
|
||||
|
||||
Variables let you name values and share data between steps. They are global -- any step can read what another step wrote.
|
||||
|
||||
## Store and Fetch
|
||||
|
||||
`!name` stores the top of the stack into a variable. `@name` fetches it back. Variables spring into existence when you first store to them. Fetching a variable that was never stored returns 0.
|
||||
|
||||
```forth
|
||||
10 !x ;; store 10 in x
|
||||
@x ;; pushes 10
|
||||
@y ;; pushes 0 (never stored)
|
||||
```
|
||||
|
||||
## Store and Keep
|
||||
|
||||
`,name` stores just like `!name` but keeps the value on the stack. Useful when you want to name something and keep using it:
|
||||
|
||||
```forth
|
||||
440 ,freq sine s . ;; stores 440 in freq AND passes it to the pipeline
|
||||
```
|
||||
|
||||
Without `,`, you'd need `dup`:
|
||||
|
||||
```forth
|
||||
440 dup !freq sine s . ;; equivalent, but noisier
|
||||
```
|
||||
|
||||
## Sharing Between Steps
|
||||
|
||||
Variables are shared across all steps. One step can store a value that another reads:
|
||||
|
||||
```forth
|
||||
;; step 0: pick a root note
|
||||
c4 iter 7 mod + !root
|
||||
|
||||
;; step 4: read it
|
||||
@root 7 + note sine s .
|
||||
```
|
||||
|
||||
Every time the pattern loops, step 0 picks a new root. Step 4 always harmonizes with it.
|
||||
|
||||
## Accumulators
|
||||
|
||||
Fetch, modify, store back. A classic pattern for evolving values:
|
||||
|
||||
```forth
|
||||
@n 1 + !n ;; increment n each time this step runs
|
||||
@n 12 mod note sine s . ;; cycle through 12 notes
|
||||
```
|
||||
|
||||
Reset on some condition:
|
||||
|
||||
```forth
|
||||
@n 1 + !n
|
||||
{ 0 !n } @n 16 > ? ;; reset after 16
|
||||
```
|
||||
|
||||
## Naming Sounds
|
||||
|
||||
Store a sound name in a variable, reuse it across steps:
|
||||
|
||||
```forth
|
||||
;; step 0: choose the sound
|
||||
"sine" !synth
|
||||
|
||||
;; step 1, 2, 3...
|
||||
c4 note @synth s .
|
||||
```
|
||||
|
||||
Change one step, all steps follow.
|
||||
Reference in New Issue
Block a user