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:
@@ -67,8 +71,8 @@ Use `?` and `!?` with `coin` for quick coin-flip decisions:
`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
kick snare hat 3 choose s . ;; random drum hit
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:
@@ -187,4 +191,4 @@ c4 0.4 e4 0.3 g4 0.2 b4 0.1 4 wchoose note
modal s .
```
The root note plays most often. Higher chord tones are rarer. Decay and harmonics vary continuously.
The root note plays most often. Higher chord tones are rarer. Decay and harmonics vary continuously.