Feat: docs should be good enough

This commit is contained in:
2026-03-01 19:38:52 +01:00
parent db44f9b98e
commit 2d3094464f
3 changed files with 368 additions and 193 deletions

View File

@@ -13,10 +13,17 @@ Every step has a duration. By default, sounds emit at the very start of that dur
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
0 0.5 at kick s .
```
Two kicks: one at start, one at midpoint.
```forth
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
@@ -33,10 +40,10 @@ Without `arp`, deltas multiply with polyphonic voices. If you have 3 notes and 2
```forth
0 0.5 at
c4 e4 g4 note sine s . ;; 6 emits: 3 notes x 2 deltas
c4 e4 g4 note 1.5 decay sine s .
```
This is a chord played twice per step.
6 emits: 3 notes x 2 deltas. A chord played twice per step.
## 1:1 Pairing: at With arp
@@ -44,16 +51,20 @@ This is a chord played twice per step.
```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
c4 e4 g4 arp note 0.5 decay 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
c4 e4 arp note 0.3 decay sine s .
```
C4, E4, C4, E4 — the shorter list wraps to fill 4 time points.
This is THE key distinction. Without `arp`: every note at every time. With `arp`: one note per time slot.
## Generating Deltas
@@ -89,12 +100,18 @@ Geometric spacing via `geom..`:
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
( 0 0.25 0.5 0.75 at ) 2 every
hat s .
```
( 0 0.5 at ) 0.5 chance ;; 50% chance of double-hit
16th-note hats every other bar.
```forth
( 0 0.5 at ) 0.5 chance
kick s .
```
50% chance of double-hit.
When the quotation doesn't execute, no deltas are set -- you get the default single emit at beat start.

View File

@@ -1,46 +1,46 @@
# 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.
This tutorial covers everything pitch-related: notes, intervals, chords, voicings, transposition, scales, and diatonic harmony. Each section builds on the previous one.
## MIDI Notes
## Notes
Write a note name followed by an octave number. It compiles to a MIDI integer:
A note name followed by an octave number compiles to a MIDI integer:
```forth
c4 ;; 60 (middle C)
a4 ;; 69 (concert A)
e3 ;; 52
c4 note sine s .
```
Sharps use `s` or `#`. Flats use `b`:
That plays middle C (MIDI 60). `a4` is concert A (69), `e3` is 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
fs4 note 0.5 decay saw s .
```
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
eb4 note 0.8 decay tri s .
```
`fs4` and `f#4` both mean F sharp 4 (MIDI 66). `bb3` is B flat 3 (58). Octave range is -1 to 9.
Notes are just integers. They work anywhere an integer works — you can do arithmetic on them, store them in variables, pass them to any word that expects a number.
## Intervals
An interval duplicates the top of the stack and adds semitones. This lets you build chords by stacking:
An interval duplicates the top of the stack and adds semitones. Stack two intervals to build a chord by hand:
```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)
c4 M3 P5 note 1.5 decay sine s .
```
Simple intervals (within one octave):
That builds a C major triad from scratch: C4 (60), then a major third above (64), then a perfect fifth above the root (67). Three notes on the stack, all played together.
```forth
a3 m3 P5 note 1.2 decay va s .
```
A minor triad: A3, C4, E4.
**Simple intervals** (within one octave):
| Interval | Semitones | Name |
|----------|-----------|------|
@@ -58,7 +58,7 @@ Simple intervals (within one octave):
| `M7` | 11 | Major 7th |
| `P8` | 12 | Octave |
Compound intervals (beyond one octave):
**Compound intervals** (beyond one octave):
| Interval | Semitones |
|----------|-----------|
@@ -75,108 +75,333 @@ Compound intervals (beyond one octave):
| `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:
Custom voicings with wide intervals:
```forth
c4 maj ;; stack: 60 64 67
c4 min7 ;; stack: 60 63 67 70
c4 dom9 ;; stack: 60 64 67 70 74
c3 P5 P8 M10 note 1.5 decay sine s .
```
**Triads:**
C3, G3, C4, E4 — an open-voiced C major spread across two octaves.
| 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 |
## Chords
**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:
Chord words replace a root note with all the chord tones. They're shortcuts for what intervals do manually:
```forth
c4 maj note sine s . ;; plays all 3 notes as one chord
c4 maj note 1.5 decay sine s .
```
That's the same C major triad, but in one word instead of `M3 P5`. A few more:
```forth
d3 min7 note 1.5 decay va s .
```
```forth
e3 dom9 note 1.2 decay saw s .
```
```forth
a3 sus2 note 1.5 decay tri s .
```
Common triads:
| Word | Intervals |
|------|-----------|
| `maj` | 0 4 7 |
| `m` | 0 3 7 |
| `dim` | 0 3 6 |
| `aug` | 0 4 8 |
| `sus2` | 0 2 7 |
| `sus4` | 0 5 7 |
| `pwr` | 0 7 |
Common seventh chords:
| Word | Intervals |
|------|-----------|
| `maj7` | 0 4 7 11 |
| `min7` | 0 3 7 10 |
| `dom7` | 0 4 7 10 |
| `dim7` | 0 3 6 9 |
| `m7b5` | 0 3 6 10 |
| `minmaj7` | 0 3 7 11 |
| `aug7` | 0 4 8 10 |
| `augmaj7` | 0 4 8 11 |
| `7sus4` | 0 5 7 10 |
Extended, add, altered, and other chord types are listed in the Reference section at the end.
## Voicings
Four words reshape chord voicings without changing the harmony.
`inv` moves the bottom note up an octave (inversion):
```forth
c4 maj inv note 1.5 decay sine s .
```
The root C goes up, giving E4 G4 C5 — first inversion. Apply it twice for second inversion:
```forth
c4 maj inv inv note 1.5 decay sine s .
```
G4 C5 E5. `dinv` does the opposite — moves the top note down an octave:
```forth
c4 maj dinv note 1.5 decay sine s .
```
G3 C4 E4. The fifth drops below the root.
`drop2` and `drop3` are jazz voicing techniques for four-note chords. `drop2` takes the second-from-top note and drops it an octave:
```forth
c4 maj7 drop2 note 1.2 decay va s .
```
From C4 E4 G4 B4, the G drops to G3: G3 C4 E4 B4. `drop3` drops the third-from-top:
```forth
c4 maj7 drop3 note 1.2 decay va s .
```
E drops to E3: E3 C4 G4 B4. These create wider, more open voicings common in jazz guitar and piano.
## Transposition
`tp` shifts every note on the stack by N semitones:
```forth
c4 maj 3 tp note 1.5 decay sine s .
```
C major transposed up 3 semitones becomes Eb major. Works with any number of notes:
```forth
c4 min7 -2 tp note 1.5 decay va s .
```
Shifts the whole chord down 2 semitones (Bb minor 7).
`oct` shifts a single note by octaves:
```forth
c4 1 oct note 0.3 decay sine s .
```
C5 (one octave up). Useful for bass lines:
```forth
0 2 4 5 7 5 4 2 8 cycle minor note
-2 oct 0.8 gain sine s .
```
## Scales
Scale words convert a degree index into a MIDI note. The base note is C4 (MIDI 60). Degrees wrap around with octave transposition:
Scale words convert a degree index into a MIDI note. By default the root is C4 (MIDI 60):
```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)
0 major note 0.5 decay sine s .
```
Use scales with `cycle` or `rand` to walk through pitches:
Degree 0 of the major scale: C4. Degrees wrap with octave transposition — degree 7 gives C5 (72), degree -1 gives B3 (59).
Walk through a scale with `cycle`:
```forth
0 1 2 3 4 5 6 7 8 cycle minor note sine s .
0 1 2 3 4 5 6 7 8 cycle minor note 0.5 decay sine s .
```
**Standard modes:**
Random notes from a scale:
| Word | Pattern (semitones) |
|------|-------------------|
```forth
0 7 rand pentatonic note 0.8 decay va s .
```
### Setting the key
By default scales are rooted at C4. Use `key!` to change the tonal center:
```forth
g3 key! 0 major note 0.5 decay sine s .
```
Now degree 0 is G3 (55) instead of C4. The key persists across steps until changed again:
```forth
a3 key! 0 3 5 7 3 cycle minor note 0.8 decay tri s .
```
A minor melody starting from A3.
**Common modes:**
| Word | Pattern |
|------|---------|
| `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 |
| `pentatonic` | 0 2 4 7 9 |
| `minpent` | 0 3 5 7 10 |
| `blues` | 0 3 5 6 7 10 |
| `harmonicminor` | 0 2 3 5 7 8 11 |
| `melodicminor` | 0 2 3 5 7 9 11 |
Jazz, symmetric, and modal variant scales are listed in the Reference section.
## Diatonic Harmony
`triad` and `seventh` build chords from scale degrees. Instead of specifying a chord type, you get whatever chord the scale produces at that degree:
```forth
0 major triad note 1.5 decay sine s .
```
Degree 0 of the major scale, stacked in thirds: C E G — a major triad. The scale determines the chord quality automatically. Degree 1 gives D F A (minor), degree 4 gives G B D (major):
```forth
4 major triad note 1.5 decay sine s .
```
`seventh` adds a fourth note:
```forth
0 major seventh note 1.2 decay va s .
```
C E G B — Cmaj7. Degree 1 gives Dm7, degree 4 gives G7 (dominant). The diatonic context determines everything.
Combine with `key!` to play diatonic chords in any key:
```forth
g3 key! 0 major triad note 1.5 decay sine s .
```
G major triad rooted at G3.
A I-vi-IV-V chord progression using `pcycle`:
```forth
( 0 major seventh ) ( 5 major seventh )
( 3 major seventh ) ( 4 major seventh ) 4 pcycle
note 1.2 decay va s .
```
Combine with voicings for smoother voice leading:
```forth
( 0 major seventh ) ( 5 major seventh inv )
( 3 major seventh ) ( 4 major seventh drop2 ) 4 pcycle
note 1.5 decay va s .
```
Arpeggiate diatonic chords using `arp` (see the *Timing with at* tutorial for details on `arp`):
```forth
0 major seventh arp note 0.5 decay sine s .
```
## Frequency Conversion
`mtof` converts a MIDI note to frequency in Hz. `ftom` does the reverse:
```forth
c4 mtof freq sine s .
```
Useful when a synth parameter expects Hz rather than MIDI.
## Reference
### All Chords
**Triads:**
| Word | Intervals |
|------|-----------|
| `maj` | 0 4 7 |
| `m` | 0 3 7 |
| `dim` | 0 3 6 |
| `aug` | 0 4 8 |
| `sus2` | 0 2 7 |
| `sus4` | 0 5 7 |
| `pwr` | 0 7 |
**Seventh chords:**
| Word | Intervals |
|------|-----------|
| `maj7` | 0 4 7 11 |
| `min7` | 0 3 7 10 |
| `dom7` | 0 4 7 10 |
| `dim7` | 0 3 6 9 |
| `m7b5` | 0 3 6 10 |
| `minmaj7` | 0 3 7 11 |
| `aug7` | 0 4 8 10 |
| `augmaj7` | 0 4 8 11 |
| `7sus4` | 0 5 7 10 |
**Sixth chords:**
| Word | Intervals |
|------|-----------|
| `maj6` | 0 4 7 9 |
| `min6` | 0 3 7 9 |
| `maj69` | 0 4 7 9 14 |
| `min69` | 0 3 7 9 14 |
**Extended chords:**
| Word | Intervals |
|------|-----------|
| `dom9` | 0 4 7 10 14 |
| `maj9` | 0 4 7 11 14 |
| `min9` | 0 3 7 10 14 |
| `9sus4` | 0 5 7 10 14 |
| `dom11` | 0 4 7 10 14 17 |
| `maj11` | 0 4 7 11 14 17 |
| `min11` | 0 3 7 10 14 17 |
| `dom13` | 0 4 7 10 14 21 |
| `maj13` | 0 4 7 11 14 21 |
| `min13` | 0 3 7 10 14 21 |
**Add chords:**
| Word | Intervals |
|------|-----------|
| `add9` | 0 4 7 14 |
| `add11` | 0 4 7 17 |
| `madd9` | 0 3 7 14 |
**Altered dominants:**
| Word | Intervals |
|------|-----------|
| `dom7b9` | 0 4 7 10 13 |
| `dom7s9` | 0 4 7 10 15 |
| `dom7b5` | 0 4 6 10 |
| `dom7s5` | 0 4 8 10 |
| `dom7s11` | 0 4 7 10 18 |
### All Scales
**Modes:**
| Word | Pattern |
|------|---------|
| `major` | 0 2 4 5 7 9 11 |
| `minor` / `aeolian` | 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 |
| `locrian` | 0 1 3 5 6 8 10 |
**Pentatonic and blues:**
@@ -187,13 +412,6 @@ Use scales with `cycle` or `rand` to walk through pitches:
| `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 |
@@ -201,6 +419,13 @@ Use scales with `cycle` or `rand` to walk through pitches:
| `harmonicminor` | 0 2 3 5 7 8 11 |
| `melodicminor` | 0 2 3 5 7 9 11 |
**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 |
**Jazz / Bebop:**
| Word | Pattern |
@@ -229,74 +454,3 @@ Use scales with `cycle` or `rand` to walk through pitches:
| `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 .
```

View File

@@ -7,21 +7,25 @@ Music needs surprise. A pattern that plays identically every time gets boring fa
`coin` pushes 0 or 1 with equal probability:
```forth
coin note sine s . ;; either 0 or 1 as the note
;; sometimes, reverb
sine sound
( 0.5 verb ) coin ?
1 decay
.
```
`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
60 72 rand note sine s .5 decay . ;; random MIDI note from 60 to 72
0.3 0.9 rand gain sine s .5 decay . ;; 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
200.0 8000.0 exprand freq sine s .5 decay . ;; mostly low frequencies
200.0 8000.0 logrand freq sine s .5 decay . ;; mostly high frequencies
```
These are useful for parameters where perception is logarithmic, like frequency and duration.
@@ -32,7 +36,7 @@ The probability words take a quotation and execute it with some chance. `chance`
```forth
( hat s . ) 0.25 chance ;; 25% chance
( hat s . ) 75 prob ;; 75% chance
( kick s . ) 75 prob ;; 75% chance
```
Named probability words save you from remembering numbers:
@@ -68,7 +72,7 @@ Use `?` and `!?` with `coin` for quick coin-flip decisions:
```forth
kick snare hat 3 choose s . ;; random drum hit
60 64 67 72 4 choose note sine s . ;; random note from a set
60 64 67 72 4 choose note sine s .5 decay . ;; random note from a set
```
When a chosen item is a quotation, it gets executed: