WIP: prepare the ground for audio rate modulation
This commit is contained in:
@@ -106,6 +106,11 @@ pub enum Op {
|
||||
EuclidRot,
|
||||
Times,
|
||||
Chord(&'static [i64]),
|
||||
// Audio-rate modulation DSL
|
||||
ModLfo(u8),
|
||||
ModSlide(u8),
|
||||
ModRnd(u8),
|
||||
ModEnv,
|
||||
// MIDI
|
||||
MidiEmit,
|
||||
GetMidiCC,
|
||||
|
||||
@@ -1103,6 +1103,52 @@ impl Forth {
|
||||
}
|
||||
}
|
||||
|
||||
Op::ModLfo(shape) => {
|
||||
let period = stack.pop().ok_or("stack underflow")?.as_float()? * ctx.step_duration();
|
||||
let max = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let min = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let suffix = match shape { 1 => "t", 2 => "w", 3 => "q", _ => "" };
|
||||
let s = format!("{min}~{max}:{period}{suffix}");
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
Op::ModSlide(curve) => {
|
||||
let dur = stack.pop().ok_or("stack underflow")?.as_float()? * ctx.step_duration();
|
||||
let end = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let start = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let suffix = match curve { 1 => "e", 2 => "s", _ => "" };
|
||||
let s = format!("{start}>{end}:{dur}{suffix}");
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
Op::ModRnd(dist) => {
|
||||
let period = stack.pop().ok_or("stack underflow")?.as_float()? * ctx.step_duration();
|
||||
let max = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let min = stack.pop().ok_or("stack underflow")?.as_float()?;
|
||||
let suffix = match dist { 1 => "s", 2 => "d", _ => "" };
|
||||
let s = format!("{min}?{max}:{period}{suffix}");
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
Op::ModEnv => {
|
||||
if stack.is_empty() {
|
||||
return Err("stack underflow".into());
|
||||
}
|
||||
let values = std::mem::take(stack);
|
||||
let mut floats = Vec::with_capacity(values.len());
|
||||
for v in &values {
|
||||
floats.push(v.as_float()?);
|
||||
}
|
||||
if floats.len() < 3 || (floats.len() - 1) % 2 != 0 {
|
||||
return Err("env expects: start target1 dur1 [target2 dur2 ...]".into());
|
||||
}
|
||||
let step_dur = ctx.step_duration();
|
||||
use std::fmt::Write;
|
||||
let mut s = String::new();
|
||||
let _ = write!(&mut s, "{}", floats[0]);
|
||||
for pair in floats[1..].chunks(2) {
|
||||
let _ = write!(&mut s, ">{}:{}", pair[0], pair[1] * step_dur);
|
||||
}
|
||||
stack.push(Value::Str(s.into(), None));
|
||||
}
|
||||
|
||||
// MIDI operations
|
||||
Op::MidiEmit => {
|
||||
let (_, params) = cmd.snapshot().unwrap_or((None, &[]));
|
||||
|
||||
@@ -104,6 +104,17 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
|
||||
"mstop" => Op::MidiStop,
|
||||
"mcont" => Op::MidiContinue,
|
||||
"forget" => Op::Forget,
|
||||
"lfo" => Op::ModLfo(0),
|
||||
"tlfo" => Op::ModLfo(1),
|
||||
"wlfo" => Op::ModLfo(2),
|
||||
"qlfo" => Op::ModLfo(3),
|
||||
"slide" => Op::ModSlide(0),
|
||||
"expslide" => Op::ModSlide(1),
|
||||
"sslide" => Op::ModSlide(2),
|
||||
"jit" => Op::ModRnd(0),
|
||||
"sjit" => Op::ModRnd(1),
|
||||
"drunk" => Op::ModRnd(2),
|
||||
"env" => Op::ModEnv,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -659,4 +659,115 @@ pub(super) const WORDS: &[Word] = &[
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
// Audio-rate Modulation DSL
|
||||
Word {
|
||||
name: "lfo",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Sine oscillation: min~max:period",
|
||||
example: "200 4000 2 lfo lpf",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "tlfo",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Triangle oscillation: min~max:periodt",
|
||||
example: "0.3 0.7 0.5 tlfo pan",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "wlfo",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Sawtooth oscillation: min~max:periodw",
|
||||
example: "200 4000 1 wlfo lpf",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "qlfo",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Square oscillation: min~max:periodq",
|
||||
example: "0.0 1.0 0.25 qlfo gain",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "slide",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(start end dur -- str)",
|
||||
desc: "Linear transition: start>end:dur",
|
||||
example: "0 1 0.01 slide gain",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "expslide",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(start end dur -- str)",
|
||||
desc: "Exponential transition: start>end:dure",
|
||||
example: "0 1 0.5 expslide gain",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "sslide",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(start end dur -- str)",
|
||||
desc: "Smooth transition: start>end:durs",
|
||||
example: "200 800 1 sslide lpf",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "jit",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Random hold: min?max:period",
|
||||
example: "200 4000 0.5 jit lpf",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "sjit",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Smooth random: min?max:periods",
|
||||
example: "200 4000 0.5 sjit lpf",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "drunk",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(min max period -- str)",
|
||||
desc: "Drunk walk: min?max:periodd",
|
||||
example: "200 4000 0.5 drunk lpf",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
Word {
|
||||
name: "env",
|
||||
aliases: &[],
|
||||
category: "Audio Modulation",
|
||||
stack: "(start t1 d1 ... -- str)",
|
||||
desc: "Multi-segment envelope: start>t1:d1>...",
|
||||
example: "0 1 0.01 0.7 0.1 0 2 env gain",
|
||||
compile: Simple,
|
||||
varargs: false,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user