Feat: refactoring codebase
This commit is contained in:
115
docs/forth/about_forth.md
Normal file
115
docs/forth/about_forth.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# About Forth
|
||||
|
||||
Forth is a _stack-based_ programming language created by Charles H. Moore in the early 1970s. It was designed with simplicity, directness, and interactive exploration in mind. Forth has been used for many years to do scientific work and program embedded systems: it was used to control telescopes and was running on some devices used in space missions among other things. Forth quickly evolved into multiple implementations targetting various computer architectures. None of them really took off and became popular. Nonetheless, the ideas behind Forth continue to garner the interest of many different people in very different (often unrelated) fields. Nowadays, Forth languages are used by hackers and artists for their peculiarity. Forth is simple, direct and beautiful to implement. Forth is an elegant and minimal language to learn. It is easy to understand, to extend and to apply to a specific task. The Forth we use in Cagire is specialized in making live music. We think of it as a DSL: a _Domain Specific Language_.
|
||||
|
||||
## Why Forth?
|
||||
|
||||
Most programming languages nowadays use a complex syntax made of `variables`, `expressions` and `statements` like `x = 3 + 4`. Forth works differently. It is way more simple than that, has almost no syntax and performs computations in a quite unique way. You push values onto a `stack` and apply `words` that transform them:
|
||||
|
||||
```forth
|
||||
3 4 +
|
||||
```
|
||||
|
||||
This program leaves the number `7` on the stack. There are no variables, no parentheses, no syntax to remember. You just end up with words and numbers separated by spaces. For live coding music, this directness is quite exciting. All you do is think in terms of transformations and add things to the stack: take a note, shift it up, add reverb, play it.
|
||||
|
||||
## The Stack
|
||||
|
||||
The stack is where values live. When you type a number, it goes on the stack. When you type a word, it usually takes values off and puts new ones back.
|
||||
|
||||
```forth
|
||||
3 ( stack: 3 )
|
||||
4 ( stack: 3 4 )
|
||||
+ ( stack: 7 )
|
||||
```
|
||||
|
||||
The stack is `last-in, first-out`. The most recent value is always on top. This means that its often better to read Forth programs from the end to the beginning: from right to left, from the bottom to the top.
|
||||
|
||||
## Words
|
||||
|
||||
Everything in Forth is either a `number` or a `word`. Words are like functions but conceptually simpler. They have no arguments or return values in the traditional sense. They just manipulate the stack directly.
|
||||
|
||||
```forth
|
||||
dup ( duplicate the top value )
|
||||
drop ( discard the top value )
|
||||
swap ( swap the top two values )
|
||||
```
|
||||
|
||||
Words compose naturally on the stack. To double a number:
|
||||
|
||||
```forth
|
||||
3 dup + ( 3 3 +)
|
||||
```
|
||||
|
||||
There are a lot of words in a Forth and thus, Cagire has a `Dictionary` embedded directly into the application. You can also create your own words. They will work just like the already existing words. There are good reasons to create new words on-the-fly:
|
||||
|
||||
- To make synth definitions.
|
||||
- To abstract _some piece of code_ that you use frequently.
|
||||
- To share data and processes between different steps.
|
||||
|
||||
## Values
|
||||
|
||||
Four types of values can live on the stack:
|
||||
|
||||
- **Integers**: `42`, `-7`, `0`
|
||||
- **Floats**: `0.5`, `3.14`, `-1.0`
|
||||
- **Strings**: `"kick"`, `"hello"`
|
||||
- **Quotations**: `{ dup + }` (code as data)
|
||||
|
||||
Quotations are special. They let you pass code around as a value. This is how conditionals and loops work. Think nothing of it for now, you will learn more about how to use it later on.
|
||||
|
||||
## Stack Notation
|
||||
|
||||
Documentation uses a notation to show what words do:
|
||||
|
||||
```
|
||||
( before -- after )
|
||||
```
|
||||
|
||||
For example, `+` has the signature `( a b -- sum )`. It takes two values and leaves one.
|
||||
|
||||
## The Command Register
|
||||
|
||||
Traditional Forth programs print text to a terminal. Cagire's Forth builds sound commands instead. This happens through an invisible accumulator called the command register. The command register has two parts:
|
||||
- a **sound name** (what instrument to play)
|
||||
- a list of **parameters** (how to play it)
|
||||
|
||||
Three types of words interact with it:
|
||||
|
||||
```forth
|
||||
kick sound ;; sets the sound name
|
||||
0.5 gain ;; adds a parameter
|
||||
. ;; emits the command and clears the register
|
||||
```
|
||||
|
||||
The word `sound` (or its shorthand `s`) sets what sound to play. Parameter words like `gain`, `freq`, `decay`, or `verb` add key-value pairs to the register. Nothing happens until you emit with `.` (dot). At that moment, the register is packaged into a command and sent to the audio engine.
|
||||
|
||||
This design lets you build sounds incrementally:
|
||||
|
||||
```forth
|
||||
"sine" sound
|
||||
c4 note
|
||||
0.5 gain
|
||||
0.3 decay
|
||||
0.4 verb
|
||||
.
|
||||
```
|
||||
|
||||
Each line adds something to the register. The final `.` triggers the sound. You can also write it all on one line:
|
||||
|
||||
```forth
|
||||
"sine" s c4 note 0.5 gain 0.3 decay 0.4 verb .
|
||||
```
|
||||
|
||||
The order of parameters does not matter. You can even emit multiple times in a single step. If you need to discard the register without emitting, use `clear`:
|
||||
|
||||
```forth
|
||||
"kick" s 0.5 gain clear ;; nothing plays, register is emptied
|
||||
"hat" s . ;; only the hat plays
|
||||
```
|
||||
|
||||
This is useful when conditionals might cancel a sound before it emits.
|
||||
|
||||
## More details
|
||||
|
||||
- Each step has its own stack and independant runtime.
|
||||
- Word definitions and variable definitions are shared by all steps.
|
||||
185
docs/forth/control_flow.md
Normal file
185
docs/forth/control_flow.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# 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.
|
||||
|
||||
## if / else / then
|
||||
|
||||
The simplest branch. Push a condition, then `if`:
|
||||
|
||||
```forth
|
||||
step 4 mod 0 = if kick s . then
|
||||
```
|
||||
|
||||
Every fourth step gets a kick. The rest do nothing. Add `else` for a two-way split:
|
||||
|
||||
```forth
|
||||
step 2 mod 0 = if
|
||||
kick s 0.8 gain .
|
||||
else
|
||||
hat s 0.3 gain .
|
||||
then
|
||||
```
|
||||
|
||||
These are compiler syntax -- you won't find them in the dictionary. Think nothing of it.
|
||||
|
||||
## ? and !?
|
||||
|
||||
When you already have a quotation, `?` executes it if the condition is truthy:
|
||||
|
||||
```forth
|
||||
{ snare s . } coin ?
|
||||
```
|
||||
|
||||
`!?` is the opposite -- executes when falsy:
|
||||
|
||||
```forth
|
||||
{ hat s 0.2 gain . } coin !?
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## 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
|
||||
note sine s 0.5 decay .
|
||||
```
|
||||
|
||||
Four notes cycling through a major seventh chord, one per step.
|
||||
|
||||
## 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
|
||||
endcase
|
||||
```
|
||||
|
||||
Steps 0 and 4 get sounds. Everything else falls through to `endcase` and nothing happens.
|
||||
|
||||
A fuller pattern:
|
||||
|
||||
```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
|
||||
endcase
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
```forth
|
||||
3 { c4 @i 4 * + note } times
|
||||
sine s 0.4 gain 0.5 verb . ;; c4, e4, g#4
|
||||
```
|
||||
|
||||
Subdivide and accent:
|
||||
|
||||
```forth
|
||||
8 {
|
||||
@i 8 / at
|
||||
@i 4 mod 0 = if 0.7 else 0.2 then gain
|
||||
hat s .
|
||||
} 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.
|
||||
95
docs/forth/definitions.md
Normal file
95
docs/forth/definitions.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Creating Words
|
||||
|
||||
One of Forth's most powerful features is the ability to define new words. A word definition gives a name to a sequence of operations. Once defined, you can use the new word just like any built-in word.
|
||||
|
||||
## The Syntax
|
||||
|
||||
Use `:` to start a definition and `;` to end it:
|
||||
|
||||
```forth
|
||||
: double dup + ;
|
||||
```
|
||||
|
||||
This creates a word called `double` that duplicates the top value and adds it to itself. Now you can use it:
|
||||
|
||||
```forth
|
||||
3 double ;; leaves 6 on the stack
|
||||
5 double ;; leaves 10 on the stack
|
||||
```
|
||||
|
||||
The definition is simple: everything between `:` and `;` becomes the body of the word.
|
||||
|
||||
## Definitions Are Shared
|
||||
|
||||
When you define a word in one step, it becomes available to all other steps. This is how you share code across your pattern. Define your synths, rhythms, and utilities once, then use them everywhere.
|
||||
|
||||
Step 0:
|
||||
```forth
|
||||
: bass "saw" s 0.8 gain 800 lpf ;
|
||||
```
|
||||
|
||||
Step 4:
|
||||
```forth
|
||||
c2 note bass .
|
||||
```
|
||||
|
||||
Step 8:
|
||||
```forth
|
||||
g2 note bass .
|
||||
```
|
||||
|
||||
The `bass` word carries the sound design. Each step just adds a note and plays.
|
||||
|
||||
## Redefining Words
|
||||
|
||||
You can redefine any word, including built-in ones:
|
||||
|
||||
```forth
|
||||
: dup drop ;
|
||||
```
|
||||
|
||||
Now `dup` does the opposite of what it used to do. This is powerful but dangerous. Redefining core words can break things in subtle ways.
|
||||
|
||||
You can even redefine numbers:
|
||||
|
||||
```forth
|
||||
: 2 4 ;
|
||||
```
|
||||
|
||||
Now `2` pushes `4` onto the stack. The number two no longer exists in your session. This is a classic Forth demonstration: nothing is sacred, everything can be redefined.
|
||||
|
||||
## Practical Uses
|
||||
|
||||
**Synth definitions** save you from repeating sound design:
|
||||
|
||||
```forth
|
||||
: pad "sine" s 0.3 gain 2 attack 0.5 verb ;
|
||||
```
|
||||
|
||||
**Transpositions** and musical helpers:
|
||||
|
||||
```forth
|
||||
: octup 12 + ;
|
||||
: octdn 12 - ;
|
||||
```
|
||||
|
||||
## Words That Emit
|
||||
|
||||
A word can contain `.` to emit sounds directly:
|
||||
|
||||
```forth
|
||||
: kick "kick" s . ;
|
||||
: hat "hat" s 0.4 gain . ;
|
||||
```
|
||||
|
||||
Then a step becomes trivial:
|
||||
|
||||
```forth
|
||||
kick hat
|
||||
```
|
||||
|
||||
Two sounds, two words, no clutter.
|
||||
|
||||
## Stack Effects
|
||||
|
||||
When you create a word, think about what it expects on the stack and what it leaves behind. The word `double` expects one number and leaves one number. The word `kick` expects nothing and leaves nothing (it emits a sound as a side effect). Well-designed words have clear stack effects. This makes them easy to combine.
|
||||
38
docs/forth/dictionary.md
Normal file
38
docs/forth/dictionary.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# The Dictionary
|
||||
|
||||
Cagire includes a built-in dictionary of all the internal Forth words. Press `Ctrl+Up` to reach the **Dict** view.
|
||||
|
||||
## Using the Dictionary
|
||||
|
||||
The dictionary shows every available word organized by category:
|
||||
|
||||
- **Stack**: Manipulation words like `dup`, `swap`, `drop`.
|
||||
- **Arithmetic**: Math operations.
|
||||
- **Sound**: Sound sources and emission.
|
||||
- **Filter**, **Envelope**, **Effects**: Sound shaping.
|
||||
- **MIDI**: External MIDI control (`chan`, `cc`, `emit`, `clock`, etc.).
|
||||
- **Context**: Sequencer state like `step`, `beat`, `tempo`.
|
||||
- And many more...
|
||||
|
||||
This tutorial will not teach you how to use all words. The syntax is very uniform and you can quickly learn a new word when necessary. We encourage you to explore as you play, this is the best way to learn. The tutorial will remain focused on various topics that require you to apply knowledge to a given task or specific context.
|
||||
|
||||
## Navigation
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Tab` | Switch between categories and words |
|
||||
| `↑/↓` or `j/k` | Navigate items |
|
||||
| `PgUp/PgDn` | Page through lists |
|
||||
| `/` or `Ctrl+F` | Search |
|
||||
| `Esc` | Clear search |
|
||||
|
||||
Each word entry shows:
|
||||
|
||||
- **Name** and aliases
|
||||
- **Stack effect**: `( before -- after )`
|
||||
- **Description**: What the word does
|
||||
- **Example**: How to use it
|
||||
|
||||
Press `/` to search across all words. The search matches word names, aliases, and descriptions. Press `Esc` to clear and return to browsing.
|
||||
|
||||
Use the dictionary while writing scripts to check stack effects and study their behavior. Some words also come with shorter aliases (e.g., `sound` → `s`). You will learn aliases quite naturally, because aliases are usually reserved for very common words.
|
||||
250
docs/forth/oddities.md
Normal file
250
docs/forth/oddities.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Oddities
|
||||
|
||||
Cagire's Forth is not a classic Forth. It borrows the core ideas (stack-based evaluation, postfix notation, word definitions) but adds modern features and domain-specific extensions. If you know traditional Forth, here are the differences.
|
||||
|
||||
## Comments
|
||||
|
||||
Classic Forth uses parentheses for comments:
|
||||
|
||||
```forth
|
||||
( this is a comment )
|
||||
```
|
||||
|
||||
Cagire uses double semicolons:
|
||||
|
||||
```forth
|
||||
;; this is a comment
|
||||
```
|
||||
|
||||
Everything after `;;` until the end of the line is ignored.
|
||||
|
||||
## Quotations
|
||||
|
||||
Classic Forth has no quotations. Code is not a value you can pass around.
|
||||
|
||||
Cagire has first-class quotations using curly braces:
|
||||
|
||||
```forth
|
||||
{ dup + }
|
||||
```
|
||||
|
||||
This pushes a block of code onto the stack. You can store it, pass it to other words, and execute it later. Quotations enable conditionals, probability, and cycling.
|
||||
|
||||
## Conditionals
|
||||
|
||||
Classic Forth uses `IF ... ELSE ... THEN`:
|
||||
|
||||
```forth
|
||||
x 0 > IF 1 ELSE -1 THEN
|
||||
```
|
||||
|
||||
Cagire supports this syntax but also provides quotation-based conditionals:
|
||||
|
||||
```forth
|
||||
{ 1 } { -1 } x 0 > ifelse
|
||||
```
|
||||
|
||||
The words `?` and `!?` execute a quotation based on a condition:
|
||||
|
||||
```forth
|
||||
{ "kick" s . } coin ? ;; execute if coin is 1
|
||||
{ "snare" s . } coin !? ;; execute if coin is 0
|
||||
```
|
||||
|
||||
## Strings
|
||||
|
||||
Classic Forth has limited string support. You print strings with `."`:
|
||||
|
||||
```forth
|
||||
." Hello World"
|
||||
```
|
||||
|
||||
Cagire has first-class strings:
|
||||
|
||||
```forth
|
||||
"hello"
|
||||
```
|
||||
|
||||
This pushes a string value onto the stack. Strings are used for sound names, sample names, and variable keys. You often do not need quotes at all. Any unrecognized word becomes a string automatically:
|
||||
|
||||
```forth
|
||||
kick s . ;; "kick" is not a word, so it becomes the string "kick"
|
||||
myweirdname ;; pushes "myweirdname" onto the stack
|
||||
```
|
||||
|
||||
This makes scripts cleaner. You only need quotes when the string contains spaces or conflicts with a real word.
|
||||
|
||||
## Variables
|
||||
|
||||
Classic Forth declares variables explicitly:
|
||||
|
||||
```forth
|
||||
VARIABLE x
|
||||
10 x !
|
||||
x @
|
||||
```
|
||||
|
||||
Cagire uses prefix syntax:
|
||||
|
||||
```forth
|
||||
10 !x ;; store 10 in x
|
||||
@x ;; fetch x (returns 0 if undefined)
|
||||
10 ,x ;; store 10 in x, keep on stack
|
||||
```
|
||||
|
||||
No declaration needed. Variables spring into existence when you store to them. `,x` stores and keeps the value on the stack.
|
||||
|
||||
## Floating Point
|
||||
|
||||
Classic Forth (in its original form) has no floating point. Numbers are integers. Floating point was added later as an optional extension with separate words. Cagire has native floating point:
|
||||
|
||||
```forth
|
||||
3.14159
|
||||
0.5 0.3 + ;; 0.8
|
||||
```
|
||||
|
||||
Integers and floats mix freely. Division always produces a float.
|
||||
|
||||
## Loops
|
||||
|
||||
Classic Forth has `DO ... LOOP`:
|
||||
|
||||
```forth
|
||||
10 0 DO I . LOOP
|
||||
```
|
||||
|
||||
Cagire uses a quotation-based loop with `times`:
|
||||
|
||||
```forth
|
||||
4 { @i . } times ;; prints 0 1 2 3
|
||||
```
|
||||
|
||||
The loop counter is stored in the variable `i`, accessed with `@i`. This fits Cagire's style where control flow uses quotations.
|
||||
|
||||
```forth
|
||||
4 { @i 4 / at hat s . } times ;; hat at 0, 0.25, 0.5, 0.75
|
||||
4 { c4 @i + note sine s . } times ;; ascending notes
|
||||
```
|
||||
|
||||
For generating sequences without side effects, use `..` or `gen`:
|
||||
|
||||
```forth
|
||||
1 5 .. ;; pushes 1 2 3 4 5
|
||||
{ dup * } 4 gen ;; pushes 0 1 4 9 (squares)
|
||||
```
|
||||
|
||||
## The Command Register
|
||||
|
||||
This is completely unique to Cagire. Traditional Forth programs print text. Cagire programs build sound commands.
|
||||
|
||||
The command register accumulates a sound name and parameters:
|
||||
|
||||
```forth
|
||||
"sine" sound ;; set sound
|
||||
440 freq ;; add parameter
|
||||
0.5 gain ;; add parameter
|
||||
. ;; emit and clear
|
||||
```
|
||||
|
||||
Nothing is sent to the audio engine until you emit with `.`. This is unlike any classic Forth.
|
||||
|
||||
## Context Words
|
||||
|
||||
Cagire provides words that read the current sequencer state:
|
||||
|
||||
```forth
|
||||
step ;; current step index (0-127)
|
||||
beat ;; current beat position
|
||||
iter ;; pattern iteration count
|
||||
tempo ;; current BPM
|
||||
phase ;; position in bar (0-1)
|
||||
```
|
||||
|
||||
These have no equivalent in classic Forth. They connect your script to the sequencer's timeline.
|
||||
|
||||
## Probability
|
||||
|
||||
Classic Forth is deterministic. Cagire has built-in randomness:
|
||||
|
||||
```forth
|
||||
{ "snare" s . } 50 prob ;; 50% chance
|
||||
{ "clap" s . } 0.25 chance ;; 25% chance
|
||||
{ "hat" s . } often ;; 75% chance
|
||||
{ "rim" s . } sometimes ;; 50% chance
|
||||
{ "tom" s . } rarely ;; 25% chance
|
||||
```
|
||||
|
||||
These words take a quotation and execute it probabilistically.
|
||||
|
||||
## Periodic Execution
|
||||
|
||||
Execute a quotation on specific iterations:
|
||||
|
||||
```forth
|
||||
{ "snare" s . } 4 every ;; every 4th pattern iteration
|
||||
{ "hat" s . } 3 8 bjork ;; Euclidean: 3 hits across 8 step runs
|
||||
{ "hat" s . } 5 8 pbjork ;; Euclidean: 5 hits across 8 pattern iterations
|
||||
```
|
||||
|
||||
`every` checks the pattern iteration count. On iteration 0, 4, 8, 12... the quotation runs. On all other iterations it is skipped.
|
||||
|
||||
`bjork` and `pbjork` use Bjorklund's algorithm to distribute k hits as evenly as possible across n positions. `bjork` counts by step runs, `pbjork` counts by pattern iterations. Classic Euclidean rhythms: tresillo (3,8), cinquillo (5,8), son clave (5,16).
|
||||
|
||||
## Cycling
|
||||
|
||||
Cagire has built-in support for cycling through values. Push values onto the stack, then select one based on pattern state:
|
||||
|
||||
```forth
|
||||
60 64 67 3 cycle note
|
||||
```
|
||||
|
||||
Each time the step runs, a different note is selected. The `3` tells `cycle` how many values to pick from.
|
||||
|
||||
You can also use quotations if you need to execute code:
|
||||
|
||||
```forth
|
||||
{ c4 note } { e4 note } { g4 note } 3 cycle
|
||||
```
|
||||
|
||||
When the selected value is a quotation, it gets executed. When it is a plain value, it gets pushed onto the stack.
|
||||
|
||||
Two cycling words exist:
|
||||
|
||||
- `cycle` - selects based on `runs` (how many times this step has played)
|
||||
- `pcycle` - selects based on `iter` (how many times the pattern has looped)
|
||||
|
||||
The difference between `cycle` and `pcycle` matters when patterns have different lengths. `cycle` counts per-step, `pcycle` counts per-pattern.
|
||||
|
||||
## Polyphonic Parameters
|
||||
|
||||
Parameter words like `note`, `freq`, and `gain` consume the entire stack. If you push multiple values before a param word, you get polyphony:
|
||||
|
||||
```forth
|
||||
60 64 67 note sine s . ;; emits 3 voices with notes 60, 64, 67
|
||||
```
|
||||
|
||||
This works for any param and for the sound word itself:
|
||||
|
||||
```forth
|
||||
440 880 freq sine tri s . ;; 2 voices: sine at 440, tri at 880
|
||||
```
|
||||
|
||||
When params have different lengths, shorter lists cycle:
|
||||
|
||||
```forth
|
||||
60 64 67 note ;; 3 notes
|
||||
0.5 1.0 gain ;; 2 gains (cycles: 0.5, 1.0, 0.5)
|
||||
sine s . ;; emits 3 voices
|
||||
```
|
||||
|
||||
Polyphony multiplies with `at` deltas:
|
||||
|
||||
```forth
|
||||
0 0.5 at ;; 2 time points
|
||||
60 64 note ;; 2 notes
|
||||
sine s . ;; emits 4 voices (2 notes × 2 times)
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Cagire's Forth is a domain-specific language for music. It keeps Forth's elegance (stack, postfix, definitions) but adapts it for live coding.
|
||||
51
docs/forth/prelude.md
Normal file
51
docs/forth/prelude.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# The Prelude
|
||||
|
||||
When you define a word in a step, it becomes available to all steps. But when you close and reopen the project, the dictionary is empty again. Words defined in steps only exist after those steps run.
|
||||
|
||||
The **prelude** solves this. It's a project-wide script that runs automatically when playback starts and when you load a project.
|
||||
|
||||
## Accessing the Prelude
|
||||
|
||||
Press `d` to open the prelude editor. Press `Esc` to save and evaluate. Press `D` (Shift+d) to re-evaluate the prelude without opening the editor.
|
||||
|
||||
## What It's For
|
||||
|
||||
Define words that should be available everywhere, always:
|
||||
|
||||
```forth
|
||||
: kick "kick" s 0.9 gain . ;
|
||||
: hat "hat" s 0.4 gain . ;
|
||||
: bass "saw" s 0.7 gain 200 lpf . ;
|
||||
```
|
||||
|
||||
Now every step in your project can use `kick`, `hat`, and `bass` from the first beat.
|
||||
|
||||
## When It Runs
|
||||
|
||||
The prelude evaluates:
|
||||
|
||||
1. When you press Space to start playback (if stopped)
|
||||
2. When you load a project
|
||||
3. When you press `D` manually
|
||||
|
||||
It does not run on every step, only once at these moments. This makes it ideal for setup code: word definitions, initial variable values, seed resets.
|
||||
|
||||
## Practical Example
|
||||
|
||||
A prelude for a techno project:
|
||||
|
||||
```forth
|
||||
: k "kick" s 1.2 attack . ;
|
||||
: sn "snare" s 0.6 gain 0.02 attack . ;
|
||||
: hh "hat" s 0.3 gain 8000 hpf . ;
|
||||
: sub "sine" s 0.8 gain 150 lpf . ;
|
||||
0 seed
|
||||
```
|
||||
|
||||
Step scripts become trivial:
|
||||
|
||||
```forth
|
||||
c1 note k sub
|
||||
```
|
||||
|
||||
The sound design lives in the prelude. Steps focus on rhythm and melody.
|
||||
95
docs/forth/stack.md
Normal file
95
docs/forth/stack.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# The Stack
|
||||
|
||||
The stack is the heart of Forth. Every value you type goes onto the stack. Every word you call takes values from the stack and puts results back. There are no variables in the traditional sense, just this pile of values that grows and shrinks as your program runs.
|
||||
|
||||
## Pushing Values
|
||||
|
||||
When you type a number or a string, it goes on top of the stack:
|
||||
|
||||
| Input | Stack (top on right) |
|
||||
|-------|---------------------|
|
||||
| `3` | `3` |
|
||||
| `4` | `3 4` |
|
||||
| `5` | `3 4 5` |
|
||||
|
||||
The stack grows to the right. The rightmost value is the top.
|
||||
|
||||
## Words Consume and Produce
|
||||
|
||||
Words take values from the top and push results back. The `+` word pops two numbers and pushes their sum:
|
||||
|
||||
| Input | Stack |
|
||||
|-------|-------|
|
||||
| `3` | `3` |
|
||||
| `4` | `3 4` |
|
||||
| `+` | `7` |
|
||||
|
||||
This is why Forth uses postfix notation: operands come first, then the operator.
|
||||
|
||||
## Stack Notation
|
||||
|
||||
Documentation describes what words do using stack effect notation:
|
||||
|
||||
```
|
||||
( before -- after )
|
||||
```
|
||||
|
||||
The word `+` has the effect `( a b -- sum )`. It takes two values and leaves one.
|
||||
The word `dup` has the effect `( a -- a a )`. It takes one value and leaves two.
|
||||
|
||||
## Thinking in Stack
|
||||
|
||||
The key to Forth is learning to visualize the stack as you write. Consider this program:
|
||||
|
||||
| Input | Stack | What happens |
|
||||
|-------|-------|--------------|
|
||||
| `3` | `3` | Push 3 |
|
||||
| `4` | `3 4` | Push 4 |
|
||||
| `+` | `7` | Add them |
|
||||
| `2` | `7 2` | Push 2 |
|
||||
| `*` | `14` | Multiply |
|
||||
|
||||
This computes `(3 + 4) * 2`. The parentheses are implicit in the order of operations. You can use line breaks and white spaces to keep organized, and the editor will also show the stack at each step for you if you ask it nicely :)
|
||||
|
||||
## Rearranging Values
|
||||
|
||||
Sometimes you need values in a different order. Stack manipulation words like `dup`, `swap`, `drop`, and `over` let you shuffle things around. You will find them in the dictionary. Here is a common pattern. You want to use a value twice:
|
||||
|
||||
| Input | Stack |
|
||||
|-------|-------|
|
||||
| `3` | `3` |
|
||||
| `dup` | `3 3` |
|
||||
| `+` | `6` |
|
||||
|
||||
The word `dup` duplicates the top, so `3 dup +` doubles the number.
|
||||
|
||||
Another pattern. You have two values but need them swapped:
|
||||
|
||||
| Input | Stack |
|
||||
|-------|-------|
|
||||
| `3` | `3` |
|
||||
| `4` | `3 4` |
|
||||
| `swap` | `4 3` |
|
||||
| `-` | `1` |
|
||||
|
||||
Without `swap`, `3 4 -` would compute `3 - 4 = -1`. With `swap`, you get `4 - 3 = 1`.
|
||||
|
||||
## Stack Errors
|
||||
|
||||
Two things can go wrong with the stack:
|
||||
|
||||
* **Stack underflow** happens when a word needs more values than the stack has. If you write `+` with only one number on the stack, there is nothing to add. The script stops with an error.
|
||||
|
||||
```forth
|
||||
3 + ;; error: stack underflow
|
||||
```
|
||||
|
||||
The fix is simple: make sure you push enough values before calling a word. Check the stack effect in the dictionary if you are unsure.
|
||||
|
||||
* **Stack overflow** is the opposite: too many values left on the stack. This is less critical but indicates sloppy code. If your script leaves unused values behind, you probably made a mistake somewhere.
|
||||
|
||||
```forth
|
||||
3 4 5 + . ;; plays a sound, but 3 is still on the stack
|
||||
```
|
||||
|
||||
The `3` was never used. Either it should not be there, or you forgot a word that consumes it.
|
||||
Reference in New Issue
Block a user