250 lines
6.5 KiB
Markdown
250 lines
6.5 KiB
Markdown
# 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)
|
||
```
|
||
|
||
No declaration needed. Variables spring into existence when you store to them.
|
||
|
||
## 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.
|