Feat: adding LPG
This commit is contained in:
@@ -135,6 +135,7 @@ pub enum Op {
|
|||||||
ModEnv,
|
ModEnv,
|
||||||
ModEnvAd,
|
ModEnvAd,
|
||||||
ModEnvAdr,
|
ModEnvAdr,
|
||||||
|
Lpg,
|
||||||
// Global params
|
// Global params
|
||||||
EmitAll,
|
EmitAll,
|
||||||
ClearGlobal,
|
ClearGlobal,
|
||||||
|
|||||||
@@ -1481,6 +1481,22 @@ impl Forth {
|
|||||||
stack.push(Value::Str(s.into(), None));
|
stack.push(Value::Str(s.into(), None));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Op::Lpg => {
|
||||||
|
let depth = pop_float(stack)?.clamp(0.0, 1.0);
|
||||||
|
let max = pop_float(stack)?;
|
||||||
|
let min = pop_float(stack)?;
|
||||||
|
let effective_max = min + (max - min) * depth;
|
||||||
|
let sd = ctx.step_duration();
|
||||||
|
let a = cmd_param_float(cmd, "attack").unwrap_or(0.0) * sd;
|
||||||
|
let d = cmd_param_float(cmd, "decay").unwrap_or(1.0) * sd;
|
||||||
|
let s = cmd_param_float(cmd, "sustain").unwrap_or(0.0);
|
||||||
|
let r = cmd_param_float(cmd, "release").unwrap_or(0.0) * sd;
|
||||||
|
use std::fmt::Write;
|
||||||
|
let mut mod_str = String::new();
|
||||||
|
let _ = write!(&mut mod_str, "{min}^{effective_max}:{a}:{d}:{s}:{r}");
|
||||||
|
cmd.set_param("lpf", Value::Str(mod_str.into(), None));
|
||||||
|
}
|
||||||
|
|
||||||
// MIDI operations
|
// MIDI operations
|
||||||
Op::MidiEmit => {
|
Op::MidiEmit => {
|
||||||
let (_, params) = cmd.snapshot().unwrap_or((None, &[]));
|
let (_, params) = cmd.snapshot().unwrap_or((None, &[]));
|
||||||
@@ -1726,6 +1742,14 @@ fn extract_dev_param(params: &[(&str, Value)]) -> u8 {
|
|||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cmd_param_float(cmd: &CmdRegister, name: &str) -> Option<f64> {
|
||||||
|
cmd.params()
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.find(|(k, _)| *k == name)
|
||||||
|
.and_then(|(_, v)| v.as_float().ok())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_tempo_scaled_param(name: &str) -> bool {
|
fn is_tempo_scaled_param(name: &str) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
name,
|
name,
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
|
|||||||
"ead" => Op::ModEnvAd,
|
"ead" => Op::ModEnvAd,
|
||||||
"eadr" => Op::ModEnvAdr,
|
"eadr" => Op::ModEnvAdr,
|
||||||
"eadsr" | "env" => Op::ModEnv,
|
"eadsr" | "env" => Op::ModEnv,
|
||||||
|
"lpg" => Op::Lpg,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -862,4 +862,14 @@ pub(super) const WORDS: &[Word] = &[
|
|||||||
compile: Simple,
|
compile: Simple,
|
||||||
varargs: false,
|
varargs: false,
|
||||||
},
|
},
|
||||||
|
Word {
|
||||||
|
name: "lpg",
|
||||||
|
aliases: &[],
|
||||||
|
category: "Audio Modulation",
|
||||||
|
stack: "(min max depth --)",
|
||||||
|
desc: "Low pass gate: pairs amp envelope with lpf modulation",
|
||||||
|
example: "0.01 0.1 ad 200 8000 1 lpg .",
|
||||||
|
compile: Simple,
|
||||||
|
varargs: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -57,17 +57,53 @@ saw snd 200 4000 1 drunk lpf . ( random walk, each step )
|
|||||||
|
|
||||||
Stack effect: `( min max period -- str )`
|
Stack effect: `( min max period -- str )`
|
||||||
|
|
||||||
## Envelopes
|
## Envelope Modulation
|
||||||
|
|
||||||
Define a multi-segment envelope for a parameter. Provide a start value, then pairs of target and duration.
|
Apply an envelope to any parameter. The `env` word is the complete form: it sweeps from `min` to `max` following a full attack, decay, sustain, release shape. All times are in steps.
|
||||||
|
|
||||||
```forth
|
```forth
|
||||||
saw snd 0 1 0.1 0.7 0.5 0 8 env gain .
|
saw snd 200 8000 0.01 0.1 0.5 0.3 env lpf .
|
||||||
```
|
```
|
||||||
|
|
||||||
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: `( min max attack decay sustain release -- str )`
|
||||||
|
|
||||||
Stack effect: `( start target1 dur1 [target2 dur2 ...] -- str )`
|
This is the building block. From it, three shorthands drop the parameters you don't need:
|
||||||
|
|
||||||
|
| Word | Stack | What it does |
|
||||||
|
|------|-------|-------------|
|
||||||
|
| `env` | `( min max a d s r -- str )` | Full envelope (attack, decay, sustain, release) |
|
||||||
|
| `eadr` | `( min max a d r -- str )` | No sustain (sustain = 0) |
|
||||||
|
| `ead` | `( min max a d -- str )` | Percussive (sustain = 0, release = 0) |
|
||||||
|
|
||||||
|
`eadsr` is an alias for `env`.
|
||||||
|
|
||||||
|
```forth
|
||||||
|
saw snd 200 8000 0.01 0.3 ead lpf . ( percussive filter pluck )
|
||||||
|
saw snd 0 5 0.01 0.1 0.3 eadr fm . ( FM depth with release tail )
|
||||||
|
saw snd 200 8000 0.01 0.1 0.5 0.3 env lpf . ( full ADSR on filter )
|
||||||
|
```
|
||||||
|
|
||||||
|
These work on any parameter — `lpf`, `fm`, `gain`, `pan`, `freq`, anything that accepts a value.
|
||||||
|
|
||||||
|
## Low Pass Gate
|
||||||
|
|
||||||
|
The `lpg` word couples the amplitude envelope with a lowpass filter. Set your amp envelope first with `ad` or `adsr`, then `lpg` mirrors it to `lpf`.
|
||||||
|
|
||||||
|
```forth
|
||||||
|
saw snd 0.01 0.1 ad 200 8000 1 lpg . ( percussive LPG )
|
||||||
|
saw snd 0.01 0.1 0.5 0.3 adsr 200 4000 1 lpg . ( sustained LPG )
|
||||||
|
```
|
||||||
|
|
||||||
|
Stack effect: `( min max depth -- )`
|
||||||
|
|
||||||
|
- `min`/`max` — filter frequency range in Hz
|
||||||
|
- `depth` — 0 to 1, scales the filter range (1 = full, 0.5 = halfway)
|
||||||
|
|
||||||
|
```forth
|
||||||
|
saw snd 0.01 0.5 ad 200 8000 0.3 lpg . ( subtle LPG, filter barely opens )
|
||||||
|
```
|
||||||
|
|
||||||
|
`lpg` reads `attack`, `decay`, `sustain`, and `release` from the current sound. If none are set, it defaults to a short percussive shape.
|
||||||
|
|
||||||
## Combining
|
## Combining
|
||||||
|
|
||||||
@@ -77,6 +113,6 @@ Modulation words return strings, so they compose naturally with the rest of the
|
|||||||
saw snd
|
saw snd
|
||||||
200 4000 4 lfo lpf
|
200 4000 4 lfo lpf
|
||||||
0.3 0.7 8 tlfo pan
|
0.3 0.7 8 tlfo pan
|
||||||
0 1 0.1 0.7 0.5 0 8 env gain
|
0 1 0.01 0.1 ead gain
|
||||||
.
|
.
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user