Feat: refactoring codebase

This commit is contained in:
2026-02-16 16:00:57 +01:00
parent c749ed6f85
commit b60703aa16
49 changed files with 1852 additions and 1853 deletions

View File

@@ -0,0 +1,82 @@
# Audio-Rate Modulation
Any parameter can be modulated continuously using modulation words. Instead of a fixed value, these words produce a modulation string that the engine interprets as a moving signal.
All time values are in **steps**, just like `attack`, `decay`, and `release`. At 120 BPM with speed 1, one step is 0.125 seconds. Writing `4 lfo` means a 4-step period.
## LFOs
Oscillate a parameter between two values.
```forth
saw s 200 4000 4 lfo lpf . ( sweep filter over 4 steps )
saw s 0.3 0.7 2 tlfo pan . ( triangle pan over 2 steps )
```
| Word | Shape | Output |
|------|-------|--------|
| `lfo` | Sine | `min~max:period` |
| `tlfo` | Triangle | `min~max:periodt` |
| `wlfo` | Sawtooth | `min~max:periodw` |
| `qlfo` | Square | `min~max:periodq` |
Stack effect: `( min max period -- str )`
## Slides
Transition from one value to another over a duration.
```forth
saw s 0 1 0.5 slide gain . ( fade in over half a step )
saw s 200 4000 8 sslide lpf . ( smooth sweep over 8 steps )
```
| Word | Curve | Output |
|------|-------|--------|
| `slide` | Linear | `start>end:dur` |
| `expslide` | Exponential | `start>end:dure` |
| `sslide` | Smooth (S-curve) | `start>end:durs` |
Stack effect: `( start end dur -- str )`
## Random
Randomize a parameter within a range, retriggering at a given period.
```forth
saw s 200 4000 2 jit lpf . ( new random value every 2 steps )
saw s 200 4000 2 sjit lpf . ( same but smoothly interpolated )
saw s 200 4000 1 drunk lpf . ( random walk, each step )
```
| Word | Behavior | Output |
|------|----------|--------|
| `jit` | Sample & hold | `min?max:period` |
| `sjit` | Smooth interpolation | `min?max:periods` |
| `drunk` | Random walk | `min?max:periodd` |
Stack effect: `( min max period -- str )`
## Envelopes
Define a multi-segment envelope for a parameter. Provide a start value, then pairs of target and duration.
```forth
saw s 0 1 0.1 0.7 0.5 0 8 env gain .
```
This creates: start at `0`, rise to `1` in `0.1` steps, drop to `0.7` in `0.5` steps, fall to `0` in `8` steps.
Stack effect: `( start target1 dur1 [target2 dur2 ...] -- str )`
## Combining
Modulation words return strings, so they compose naturally with the rest of the language. Use them anywhere a parameter value is expected.
```forth
saw s
200 4000 4 lfo lpf
0.3 0.7 8 tlfo pan
0 1 0.1 0.7 0.5 0 8 env gain
.
```

65
docs/engine/distortion.md Normal file
View File

@@ -0,0 +1,65 @@
# Distortion
Distortion effects add harmonics by nonlinearly shaping the waveform.
## Saturation
Soft saturation using the transfer function `x / (1 + k|x|)`.
```forth
saw 2 distort .
saw 8 distort 0.5 distortvol . ( with volume compensation )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `distort` | 0+ | Saturation amount |
| `distortvol` | 0-1 | Output volume |
## Wavefolding
Wavefolding reflects the signal when it exceeds ±1, using `sin(x × amount × π/2)`.
```forth
sine 4 fold .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `fold` | 0+ | Fold amount |
## Wavewrapping
Wavewrapping applies modulo to wrap the signal into the -1 to 1 range.
```forth
saw 3 wrap .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `wrap` | 0+ | Number of wraps |
## Bit Crushing
Bit crushing quantizes the signal to fewer amplitude levels.
```forth
snare 6 crush . ( 6-bit = 32 levels )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `crush` | 1-16 | Bit depth |
## Sample Rate Reduction
Sample rate reduction holds each sample for multiple output samples.
```forth
hat 4 coarse . ( 1/4 effective sample rate )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `coarse` | 1+ | Reduction factor (1 = bypass) |

131
docs/engine/filters.md Normal file
View File

@@ -0,0 +1,131 @@
# Filters
Filters attenuate frequencies above or below a cutoff point.
## Lowpass Filter
The lowpass filter (`lpf`) attenuates frequencies above the cutoff.
```forth
saw 1000 lpf . ( cut above 1000 Hz )
saw 500 lpf 0.8 lpq . ( with resonance )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `lpf` | Hz | Cutoff frequency |
| `lpq` | 0-1 | Resonance (peak at cutoff) |
## Highpass Filter
The highpass filter (`hpf`) attenuates frequencies below the cutoff.
```forth
kick 200 hpf . ( cut below 200 Hz )
pad 400 hpf 0.3 hpq . ( with resonance )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `hpf` | Hz | Cutoff frequency |
| `hpq` | 0-1 | Resonance |
## Bandpass Filter
The bandpass filter (`bpf`) attenuates frequencies outside a band around the center frequency.
```forth
noise 1000 bpf 0.7 bpq . ( narrow band around 1000 Hz )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `bpf` | Hz | Center frequency |
| `bpq` | 0-1 | Resonance (narrower band) |
## Filter Slope
The `ftype` parameter sets the filter slope (rolloff steepness).
| Value | Slope |
|-------|-------|
| `1` | 12 dB/octave |
| `2` | 24 dB/octave (default) |
| `3` | 48 dB/octave |
```forth
saw 800 lpf 3 ftype . ( 48 dB/oct lowpass )
```
## Filter Envelope
Filters can be modulated by an ADSR envelope. The envelope multiplies the base cutoff:
```
final_cutoff = lpf + (lpe × envelope × lpf)
```
When the envelope is at 1.0 and `lpe` is 1.0, the cutoff doubles. When the envelope is at 0, the cutoff equals `lpf`.
```forth
saw 200 lpf 2 lpe 0.01 lpa 0.3 lpd . ( cutoff sweeps from 600 Hz down to 200 Hz )
```
| Parameter | Description |
|-----------|-------------|
| `lpe` | Envelope depth (multiplier, 1.0 = double cutoff at peak) |
| `lpa` | Attack time in seconds |
| `lpd` | Decay time in seconds |
| `lps` | Sustain level (0-1) |
| `lpr` | Release time in seconds |
The same pattern works for highpass (`hpe`, `hpa`, etc.) and bandpass (`bpe`, `bpa`, etc.).
## Ladder Filters
Ladder filters use a different algorithm (Moog-style) with self-oscillation at high resonance.
```forth
saw 800 llpf 0.7 llpq . ( ladder lowpass )
saw 300 lhpf 0.5 lhpq . ( ladder highpass )
saw 1000 lbpf 0.8 lbpq . ( ladder bandpass )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `llpf` | Hz | Ladder lowpass cutoff |
| `llpq` | 0-1 | Ladder lowpass resonance |
| `lhpf` | Hz | Ladder highpass cutoff |
| `lhpq` | 0-1 | Ladder highpass resonance |
| `lbpf` | Hz | Ladder bandpass cutoff |
| `lbpq` | 0-1 | Ladder bandpass resonance |
Ladder filters share the lowpass envelope parameters (`lpe`, `lpa`, etc.).
## EQ
The 3-band EQ applies shelf and peak filters at fixed frequencies.
```forth
kick 3 eqlo -2 eqhi . ( +3 dB at 200 Hz, -2 dB at 5000 Hz )
snare 2 eqmid . ( +2 dB at 1000 Hz )
```
| Parameter | Frequency | Type |
|-----------|-----------|------|
| `eqlo` | 200 Hz | Low shelf (dB) |
| `eqmid` | 1000 Hz | Peak (dB) |
| `eqhi` | 5000 Hz | High shelf (dB) |
## Tilt EQ
Tilt EQ applies a high shelf at 800 Hz with up to ±6 dB gain.
```forth
pad -0.5 tilt . ( -3 dB above 800 Hz )
hat 0.5 tilt . ( +3 dB above 800 Hz )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `tilt` | -1 to 1 | High shelf gain (-1 = -6 dB, 0 = flat, 1 = +6 dB) |

55
docs/engine/intro.md Normal file
View File

@@ -0,0 +1,55 @@
# Introduction
Cagire includes an audio engine called `Doux`. No external software is needed to make sound. `Doux` is an opinionated, semi-modular synthesizer. It was designed for live coding environments and works by receiving command strings that describe sounds. Despite its fixed architecture,`Doux` is extremely versatile and will likely cover most of the audio needs of a live coder.
## How It Works
When you write a Forth script and emit (`.`), the script produces a command string. This command travels to the audio engine, which interprets it and creates a voice. The voice plays until its envelope finishes or until it is killed by another voice. You can also spawn infinite voices, but you will need to manage their lifecycle manually, otherwise they will never stop.
```forth
saw s c4 note 0.8 gain 0.3 verb .
```
## Voices
Each `emit` (`.`) creates or manages a voice by sending parameters. Voices are independent sound generators with their own oscillator, envelope, and effects. The engine can run many voices at once (up to `128`, default `32`). When you exceed the voice limit, the oldest voice is stolen (a process called _round robin scheduling_). You can monitor voice usage on the Engine page:
- **Active voices**: how many are playing right now.
- **Peak voices**: the maximum reached since last reset.
Press `r` on the Engine page to reset the peak counter.
## Parameters
After selecting a sound source, you add parameters. Each parameter word takes a value from the stack and stores it in the command register:
```forth
saw s
c4 note ;; pitch
0.5 gain ;; volume
0.1 attack ;; envelope attack time
2000 lpf ;; lowpass filter at 2kHz
0.3 verb ;; reverb mix
.
```
Parameters can appear in any order. They accumulate until you emit. You can clear the register using the `clear` word.
## Controlling Existing Voices
You can emit without a sound name. In this case, no new voice is created. Instead, the parameters are sent to control an existing voice. Use `voice` with an ID to target a specific voice:
```forth
0 voice 500 freq . ;; change frequency on voice 0
```
This is useful for modulating long-running or infinite voices. Set up a drone on one step with a known voice ID, then tweak its parameters from other steps.
## Hush and Panic
Two emergency controls exist on the Engine page:
- `h` - **Hush**: gracefully fade out all voices
- `p` - **Panic**: immediately kill all voices
Use hush when things get too loud. Use panic when things go wrong.

132
docs/engine/modulation.md Normal file
View File

@@ -0,0 +1,132 @@
# Modulation
Modulation effects vary parameters over time using LFOs or envelopes.
## Vibrato
Vibrato modulates pitch with an LFO.
```forth
saw 5 vib 0.5 vibmod . ( 5 Hz, 0.5 semitone depth )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `vib` | Hz | LFO rate |
| `vibmod` | semitones | Modulation depth |
| `vibshape` | shape | LFO waveform (sine, tri, saw, square) |
## Pitch Envelope
The pitch envelope applies an ADSR to the oscillator frequency.
```forth
sine 100 freq 24 penv 0.001 patt 0.1 pdec .
```
| Parameter | Description |
|-----------|-------------|
| `penv` | Envelope depth in semitones |
| `patt` | Attack time in seconds |
| `pdec` | Decay time in seconds |
| `psus` | Sustain level (0-1) |
| `prel` | Release time in seconds |
## Glide
Glide interpolates between pitch changes over time.
```forth
saw c4 0.1 glide . ( 100ms glide )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `glide` | seconds | Glide time |
## FM Synthesis
FM modulates the carrier frequency with a modulator oscillator.
```forth
sine 440 freq 2 fm 2 fmh . ( modulator at 2× carrier frequency )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `fm` | 0+ | Modulation index |
| `fmh` | ratio | Harmonic ratio (modulator / carrier) |
| `fmshape` | shape | Modulator waveform |
FM has its own envelope (`fme`, `fma`, `fmd`, `fms`, `fmr`).
## Amplitude Modulation
AM multiplies the signal by an LFO.
```forth
pad 4 am 0.5 amdepth . ( 4 Hz tremolo )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `am` | Hz | LFO rate |
| `amdepth` | 0-1 | Modulation depth |
| `amshape` | shape | LFO waveform |
## Ring Modulation
Ring modulation multiplies two signals, producing sum and difference frequencies.
```forth
saw 150 rm 0.8 rmdepth . ( ring mod at 150 Hz )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `rm` | Hz | Modulator frequency |
| `rmdepth` | 0-1 | Modulation depth |
| `rmshape` | shape | Modulator waveform |
## Phaser
Phaser sweeps notches through the frequency spectrum using allpass filters.
```forth
pad 0.5 phaser 0.6 phaserdepth .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `phaser` | Hz | Sweep rate |
| `phaserdepth` | 0-1 | Sweep depth |
| `phasersweep` | cents | Sweep range |
| `phasercenter` | Hz | Center frequency |
## Flanger
Flanger mixes the signal with a short modulated delay (0.5-10ms).
```forth
pad 0.3 flanger 0.7 flangerdepth 0.5 flangerfeedback .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `flanger` | Hz | Modulation rate |
| `flangerdepth` | 0-1 | Modulation depth |
| `flangerfeedback` | 0-0.95 | Feedback amount |
## Chorus
Chorus uses multiple modulated delay lines with 120° phase offset for stereo width.
```forth
pad 1 chorus 0.4 chorusdepth 20 chorusdelay .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `chorus` | Hz | Modulation rate |
| `chorusdepth` | 0-1 | Modulation depth |
| `chorusdelay` | ms | Base delay time |

126
docs/engine/samples.md Normal file
View File

@@ -0,0 +1,126 @@
# Samples
The `sample` source plays audio files from disk with pitch tracking.
## Loading Samples
There are two ways to load samples:
* **From the app:** Navigate to the Engine view and find the Samples section. Press `A` to open a file browser, then select a folder containing your samples. Press `D` to remove the last added path.
* **From the command line:** Use the `-s` flag when launching Cagire:
```
cagire -s ~/samples -s ~/more-samples
```
The engine scans these directories and builds a registry of available samples. Samples load in the background without blocking audio. Supported file formats are `.wav`, `.mp3`, `.ogg`, `.flac` and `.aiff`.
## Folder Organization
```
samples/
├── kick.wav → "kick"
├── snare.wav → "snare"
└── hats/
├── closed.wav → "hats" n 0
├── open.wav → "hats" n 1
└── pedal.wav → "hats" n 2
```
Folders at the root of your directory are used as the name of a sample bank. Folders create sample banks where each file gets an index. Files are sorted alphabetically and assigned indices starting from `0`.
## Playing Samples
```forth
kick sound . ( play kick sample )
hats sound 2 n . ( play third hat sample )
snare sound 0.5 speed . ( play snare at half speed )
```
## Parameters
| Parameter | Range | Description |
|-----------|-------|-------------|
| `n` | 0+ | Sample index within a folder (wraps around) |
| `begin` | 0-1 | Playback start position |
| `end` | 0-1 | Playback end position |
| `speed` | any | Playback speed multiplier |
| `freq` | Hz | Base frequency for pitch tracking |
| `fit` | seconds | Stretch/compress sample to fit duration |
| `cut` | 0+ | Choke group |
## Slicing with Begin/End
The `begin` and `end` parameters define what portion of the sample plays. Values are normalized from 0 (start) to 1 (end).
```forth
kick sound 0.25 begin 0.75 end . ( play middle half )
kick sound 0.5 begin . ( play second half )
kick sound 0.5 end . ( play first half )
```
If begin is greater than end, they swap automatically.
## Speed and Pitch
The `speed` parameter affects both tempo and pitch. A speed of 2 plays twice as fast and an octave higher.
```forth
snare sound 2 speed . ( double speed, octave up )
snare sound 0.5 speed . ( half speed, octave down )
snare sound -1 speed . ( play in reverse )
```
For pitched playback, use `freq` or note names. The sample's base frequency defaults to middle C (261.626 Hz).
```forth
kick sound 440 freq . ( play at A4 )
kick sound c4 . ( play at C4 )
```
Negative speed will reverse the sample and play it backwards.
```forth
crow sound -1 speed . ( play backwards at nominal speed )
crow sound -4 speed . ( play backwards, 4 times faster )
```
## Fitting to Duration
The `fit` parameter stretches or compresses a sample to match a target duration in seconds. This adjusts speed automatically.
```forth
kick sound 0.25 fit . ( fit kick into quarter second )
snare sound beat fit . ( fit snare to one beat )
```
## Choke Groups
The `cut` parameter assigns a sample to a choke group. When a new sample with the same cut value plays, it kills any currently playing samples in that group.
```forth
hihat_closed sound 1 cut . ( choke group 1 )
hihat_open sound 1 cut . ( kills closed hat, starts open )
```
This is essential for realistic hi-hat behavior where open and closed hats shouldn't overlap.
## Bank Variations
Add `_suffix` to sample names to create variations that share the same base name.
```
samples/
├── kick.wav
├── kick_hard.wav
├── kick_soft.wav
```
Select variations with the `bank` parameter:
```forth
kick sound . ( plays kick.wav )
kick sound hard bank . ( plays kick_hard.wav )
kick sound soft bank . ( plays kick_soft.wav )
```

125
docs/engine/settings.md Normal file
View File

@@ -0,0 +1,125 @@
# Settings
The audio engine can be configured through the Engine page or via command-line arguments. Settings are saved and restored between sessions.
## Engine Page
Press `Ctrl+Right` until you reach the Engine page. Here you can see the engine status and adjust settings.
### Display
The right side of the page shows visualizations:
- **Scope**: oscilloscope showing the audio waveform
- **Spectrum**: 32-band frequency analyzer
### Settings
Navigate with arrow keys, adjust values with left/right:
- **Output Device**: where sound goes (speakers, headphones, interface).
- **Input Device**: what audio input source to use (microphone, line-in, etc.).
- **Channels**: number of output channels (2 for stereo).
- **Buffer Size**: audio buffer in samples (64-4096).
- **Max Voices**: polyphony limit (1-128, default 32).
### Buffer Size
Smaller buffers mean lower latency but higher CPU load. Larger buffers are safer but feel sluggish.
| Buffer | Latency at 44.1kHz |
|--------|-------------------|
| 64 | ~1.5ms |
| 128 | ~3ms |
| 256 | ~6ms |
| 512 | ~12ms |
| 1024 | ~23ms |
Start with 512. Lower it if you need tighter timing. Raise it if you hear glitches.
## Samples
The engine indexes audio files from your sample directories. Add directories with `A`, remove with `D`. The sample count shows how many files are indexed.
### Supported Formats
- WAV (.wav)
- MP3 (.mp3)
- OGG Vorbis (.ogg)
- FLAC (.flac)
- AIFF (.aiff, .aif)
- AAC (.aac)
- M4A (.m4a)
### Lazy Loading
Samples are not loaded into memory at startup. They are decoded on demand when first played. This means you can have thousands of samples indexed without using much RAM until you actually use them.
### Folder Organization
The scanner looks at top-level files and one level of subdirectories:
```
samples/
├── kick.wav -> "kick"
├── snare.wav -> "snare"
├── hats/
│ ├── closed.wav -> "hats" n 0
│ ├── open.wav -> "hats" n 1
│ └── pedal.wav -> "hats" n 2
└── breaks/
├── amen.wav -> "breaks" n 0
└── think.wav -> "breaks" n 1
```
Top-level files are named by their filename (without extension). Files inside folders are sorted alphabetically and numbered starting from 0.
### Playing Samples
Reference samples by name:
```forth
kick s . ;; play kick.wav
snare s 0.5 gain . ;; play snare at half volume
```
For samples in folders, use `n` to select which one:
```forth
hats s 0 n . ;; play hats/closed.wav (index 0)
hats s 1 n . ;; play hats/open.wav (index 1)
hats s 2 n . ;; play hats/pedal.wav (index 2)
```
The index wraps around. If you have 3 samples and request `5 n`, you get index 2 (because 5 % 3 = 2).
### Sample Variations with Bank
The `bank` parameter lets you organize variations:
```
samples/
├── kick.wav -> default
├── kick_a.wav -> bank "a"
├── kick_b.wav -> bank "b"
└── kick_hard.wav -> bank "hard"
```
```forth
kick s . ;; plays kick.wav
kick s a bank . ;; plays kick_a.wav
kick s hard bank . ;; plays kick_hard.wav
```
If the banked version does not exist, it falls back to the default.
## Troubleshooting
* **No sound**: Check output device selection.
* Try the test sound (`t`) on Engine page).
* **Glitches/crackling**: Increase buffer size, restart the Engine.
* **High CPU**: Reduce max voices. Disable scope/spectrum. Increase buffer size.
* **Samples not found**: Check sample directories on Engine page. Filenames are case-sensitive on some systems.

92
docs/engine/sources.md Normal file
View File

@@ -0,0 +1,92 @@
# Sources
The audio engine provides a variety of sound sources. Use the `sound` word (or `s` for short) to select one.
## Basic Oscillators
| Name | Description |
|------|-------------|
| `sine` | Pure sinusoid, smooth and mellow |
| `tri` | Triangle wave, warmer than sine, naturally band-limited |
| `saw` | Bright sawtooth with anti-aliasing, rich in harmonics |
| `zaw` | Raw sawtooth without anti-aliasing, lo-fi character |
| `pulse`, `square` | Variable-width pulse wave with anti-aliasing |
| `pulze`, `zquare` | Raw pulse without anti-aliasing, 8-bit feel |
`pulse` and `pulze` respond to the `pw` parameter (0.0-1.0) for pulse width. At 0.5 you get a square wave.
### Phase Shaping
All oscillators support phase shaping for timbral variation:
| Parameter | Range | Effect |
|-----------|-------|--------|
| `size` | 0-256 | Phase quantization (lo-fi, chiptune). |
| `mult` | 0.25-16 | Phase multiplier (harmonic overtones). |
| `warp` | -1 to 1 | Power curve asymmetry. |
| `mirror` | 0-1 | Phase reflection point. |
These are super useful to get the most out of your oscillators.
### Sub Oscillator
Add a sub oscillator layer to any basic oscillator:
| Parameter | Range | Effect |
|-----------|-------|--------|
| `sub` | 0-1 | Mix level |
| `suboct` | 1-3 | Octaves below main. |
| `subwave` | tri/sine/square | Sub waveform. |
## Noise
| Name | Description |
|------|-------------|
| `white` | Equal energy across all frequencies, bright and hissy. |
| `pink` | -3dB/octave rolloff, equal energy per octave, natural. |
| `brown` | -6dB/octave rolloff, deep rumbling, random walk. |
Noise sources ignore pitch. Use filters to shape the spectrum.
## Live Input
| Name | Description |
|------|-------------|
| `live`, `livein`, `mic` | Live audio input from microphone or line-in |
All filter and effect parameters apply to the input signal.
## Plaits Engines
The Plaits engines come from Mutable Instruments and provide a range of synthesis methods. Beware, these sources can be quite CPU hungry. All share three control parameters (`0.0`-`1.0`):
| Parameter | Controls |
|-----------|----------|
| `harmonics` | Harmonic content, structure, detuning. |
| `timbre` | Brightness, tonal color. |
| `morph` | Smooth transitions between variations. |
### Pitched
| Name | Description |
|------|-------------|
| `modal` | Struck/plucked resonant bodies (strings, plates, tubes). |
| `va`, `analog` | Virtual analog with waveform sync and crossfading. |
| `ws`, `waveshape` | Waveshaper and wavefolder. |
| `fm2` | Two-operator FM synthesis with feedback. |
| `grain` | Granular formant oscillator (vowel-like). |
| `additive` | Harmonic additive synthesis. |
| `wavetable` | Built-in Plaits wavetables (four 8x8 banks). |
| `chord` | Four-note chord generator. |
| `swarm` | Granular cloud of enveloped sawtooths. |
| `pnoise` | Clocked noise through multimode filter. |
### Percussion
| Name | Description |
|------|-------------|
| `kick`, `bass` | 808-style bass drum. |
| `snare` | Analog snare drum with tone/noise balance. |
| `hihat`, `hat` | Metallic 808-style hi-hat. |
Percussions are super hard to use correctly, because you need to tweak their envelope correctly.

118
docs/engine/space.md Normal file
View File

@@ -0,0 +1,118 @@
# Space & Time
Spatial effects position sounds in the stereo field and add depth through delays and reverbs.
## Pan
Pan positions a sound in the stereo field.
```forth
hat -0.5 pan . ( slightly left )
perc 1 pan . ( hard right )
kick 0 pan . ( center )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `pan` | -1 to 1 | Stereo position (-1 = left, 0 = center, 1 = right) |
## Width
Width controls the stereo spread using mid-side processing.
```forth
pad 1.5 width . ( wider stereo )
pad 0 width . ( mono )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `width` | 0+ | Stereo width (0 = mono, 1 = unchanged, 2 = exaggerated) |
## Haas Effect
The Haas effect delays one channel slightly, creating a sense of stereo width and spatial placement.
```forth
snare 15 haas . ( 15ms delay on right channel )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `haas` | ms | Delay time (1-10ms = subtle width, 10-35ms = distinct echo) |
## Delay
Delay is a send effect that creates echoes. The `delay` parameter sets how much signal is sent to the delay bus.
```forth
snare 0.3 delay 0.25 delaytime 0.5 delayfeedback .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `delay` | 0-1 | Send level |
| `delaytime` | seconds | Delay time |
| `delayfeedback` | 0-0.95 | Feedback amount |
| `delaytype` | type | Delay algorithm |
### Delay Types
| Type | Description |
|------|-------------|
| `standard` | Clean digital repeats |
| `pingpong` | Bounces between left and right |
| `tape` | Each repeat gets darker (analog warmth) |
| `multitap` | 4 taps with swing control via feedback |
```forth
snare 0.4 delay pingpong delaytype .
pad 0.3 delay tape delaytype .
```
## Reverb
Reverb is a send effect that simulates acoustic spaces. The `verb` parameter sets the send level.
```forth
snare 0.2 verb 2 verbdecay .
pad 0.4 verb 4 verbdecay 0.7 verbdamp .
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `verb` | 0-1 | Send level |
| `verbdecay` | seconds | Reverb tail length |
| `verbdamp` | 0-1 | High frequency damping |
| `verbpredelay` | ms | Initial delay before reverb |
| `verbdiff` | 0-1 | Diffusion (smears transients) |
| `verbtype` | type | Reverb algorithm |
### Reverb Types
| Type | Description |
|------|-------------|
| `dattorro` | Plate reverb, bright and metallic shimmer |
| `fdn` | Hall reverb, dense and smooth |
```forth
snare 0.3 verb dattorro verbtype . ( plate )
pad 0.5 verb fdn verbtype . ( hall )
```
## Comb Filter
The comb filter creates resonant pitched delays, useful for Karplus-Strong string synthesis and metallic tones.
```forth
white 0.5 comb 220 combfreq 0.9 combfeedback . ( plucked string )
```
| Parameter | Range | Description |
|-----------|-------|-------------|
| `comb` | 0-1 | Send level |
| `combfreq` | Hz | Resonant frequency |
| `combfeedback` | 0-0.99 | Feedback (higher = longer decay) |
| `combdamp` | 0-1 | High frequency damping |
Higher feedback creates longer, ringing tones. Add damping for more natural string-like decay.

87
docs/engine/wavetable.md Normal file
View File

@@ -0,0 +1,87 @@
# Wavetables
Any sample can be played as a wavetable. When you use the `scan` parameter, the sample automatically becomes a pitched oscillator that can morph between cycles.
## What is a Wavetable?
A wavetable is a series of single-cycle waveforms stored end-to-end in an audio file. Each cycle is a complete waveform that starts and ends at zero crossing, allowing it to loop seamlessly at any pitch.
```
Sample: [cycle 0][cycle 1][cycle 2][cycle 3]
↑ ↑
scan 0 scan 1
```
The oscillator reads through one cycle at audio rate (determining pitch), while `scan` selects which cycle to play. Values between cycles crossfade smoothly, creating timbral morphing.
## Basic Usage
Just add `scan` to any sample and it becomes a wavetable:
```forth
pad 0 scan . ( play pad as wavetable, first cycle )
pad 0.5 scan . ( blend to middle cycles )
pad 440 freq 0 scan . ( play at A4 )
```
Without `scan`, the sample plays normally. With `scan`, it becomes a looping wavetable oscillator.
## Parameters
| Parameter | Range | Description |
|-----------|-------|-------------|
| `scan` | 0-1 | Position in wavetable (0 = first cycle, 1 = last) |
| `wtlen` | samples | Cycle length in samples (0 = entire sample) |
| `scanlfo` | Hz | LFO rate for scan modulation |
| `scandepth` | 0-1 | LFO modulation depth |
| `scanshape` | shape | LFO waveform |
## Cycle Length
The `wtlen` parameter tells the engine how many samples make up one cycle. This must match how the wavetable was created, otherwise you'll hear the wrong pitch or glitchy artifacts.
```forth
pad 0 scan 2048 wtlen . ( Serum-style 2048-sample cycles )
pad 0 scan 1024 wtlen . ( 1024-sample cycles )
```
Common cycle lengths are powers of two: 256, 512, 1024, 2048. Serum uses 2048 samples per cycle. The number of cycles in a wavetable is the total sample length divided by `wtlen`. If `wtlen` is 0 (default), the entire sample is treated as one cycle. The sample still loops as a pitched oscillator, but `scan` has no morphing effect since there's only one cycle.
## Scanning
The `scan` parameter selects which cycle to play:
```forth
pad 0 scan . ( first cycle only )
pad 0.5 scan . ( blend between middle cycles )
pad 1 scan . ( last cycle only )
```
## LFO Modulation
Automate the scan position with a built-in LFO:
```forth
pad 0 scan 2 scanlfo 0.3 scandepth . ( 2 Hz modulation, 30% depth )
```
Available LFO shapes:
| Shape | Description |
|-------|-------------|
| `sine` | Smooth oscillation (default) |
| `tri` | Triangle wave |
| `saw` | Sawtooth, ramps up |
| `square` | Alternates between extremes |
| `sh` | Sample and hold, random steps |
## Creating Wavetables
A proper wavetable file:
- Contains multiple single-cycle waveforms of identical length
- Each cycle starts and ends at zero crossing for seamless looping
- Uses power-of-two cycle lengths (256, 512, 1024, 2048)
- Has cycles that morph smoothly from one to the next
You can find wavetable packs online or create your own in tools like Serum, WaveEdit, or Audacity (using zero-crossing snap). Single-cycle waveforms also work. With `wtlen` set to 0, a single-cycle sample becomes a basic pitched oscillator.

43
docs/engine/words.md Normal file
View File

@@ -0,0 +1,43 @@
# Words & Sounds
Word definitions let you abstract sound design into reusable units.
## Defining Sounds
```forth
: lead "saw" s 0.3 gain 1200 lpf ;
```
Use it with different notes:
```forth
c4 note lead .
e4 note lead .
```
## Self-Contained Words
Include the emit to make the word play directly:
```forth
: kk "kick" s 1 decay . ;
: hh "hihat" s 0.5 gain 0.5 decay . ;
```
Steps become simple:
```forth
kk
0.5 at hh
```
## Effect Presets
```forth
: dark 800 lpf 0.6 lpq ;
: wet 0.7 verb 8 verbdiff ;
```
```forth
c4 note saw s dark wet .
```