Feat: polyphony + iterator reset

This commit is contained in:
2026-02-02 00:33:46 +01:00
parent 8452033473
commit ccce0df79d
9 changed files with 132 additions and 145 deletions

View File

@@ -193,21 +193,42 @@ You can also use quotations if you need to execute code:
When the selected value is a quotation, it gets executed. When it is a plain value, it gets pushed onto the stack.
Three cycling words exist:
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)
- `tcycle` - creates a cycle list that resolves at emit time
The difference between `cycle` and `pcycle` matters when patterns have different lengths. `cycle` counts per-step, `pcycle` counts per-pattern.
`tcycle` is special. It does not select immediately. Instead it creates a value that cycles when emitted:
## 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
0.3 0.5 0.7 3 tcycle gain
60 64 67 note sine s . ;; emits 3 voices with notes 60, 64, 67
```
If you emit multiple times in one step (using `at`), each emit gets the next value from the cycle.
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