feat(anvil): add Ghost harmonics and Phone listen

Ghost adds the sub's harmonics 2–10 above Ghost from, so phones hear the
note. The band above the split is measured per harmonic and only missing
harmonics are filled, so added ones cannot cancel the patch's own. The sub
level does not change. Phone monitors the output in mono, 400 Hz–7 kHz.
This commit is contained in:
nvrl committed 2026-11-06 13:46:15 +01:00
1 parent 4f1e237d66
commit e7f3346077
12 files changed
+489 -50

No files matched your search

+24 -3
View File
@@ -28,6 +28,10 @@ pub struct AnvilParams {
pub dynamics: FloatParam,
#[id = "drive"]
pub drive: FloatParam,
#[id = "ghost"]
pub ghost: FloatParam,
#[id = "ghost_from"]
pub ghost_from: FloatParam,
#[id = "octave"]
pub octave: IntParam,
#[id = "glide"]
@@ -66,6 +70,17 @@ impl Default for AnvilParams {
.with_unit(" Hz"),
dynamics: linear("Dynamics", d.dynamics, 0., 100., " %"),
drive: linear("Drive", d.drive, 0., 100., " %"),
ghost: linear("Ghost", d.ghost, 0., 100., " %"),
ghost_from: FloatParam::new(
"Ghost from",
d.ghost_from,
FloatRange::Skewed {
min: 150.,
max: 600.,
factor: FloatRange::skew_factor(-1.),
},
)
.with_unit(" Hz"),
octave: IntParam::new("Octave", d.octave, IntRange::Linear { min: -2, max: 1 }),
glide: linear("Glide", d.glide, 0., 200., " ms"),
phase: linear("Phase", d.phase, 0., 360., "°"),
@@ -89,6 +104,8 @@ impl AnvilParams {
split: self.split.value(),
dynamics: self.dynamics.value(),
drive: self.drive.value(),
ghost: self.ghost.value(),
ghost_from: self.ghost_from.value(),
octave: self.octave.value(),
glide: self.glide.value(),
phase: self.phase.value(),
@@ -108,9 +125,9 @@ impl AnvilParams {
old: &Settings,
new: &Settings,
dragging: bool,
open: &mut [bool; 9],
open: &mut [bool; 11],
) {
let floats: [(&FloatParam, f32, f32); 8] = [
let floats: [(&FloatParam, f32, f32); 10] = [
(&self.level, old.level, new.level),
(&self.split, old.split, new.split),
(&self.dynamics, old.dynamics, new.dynamics),
@@ -119,6 +136,8 @@ impl AnvilParams {
(&self.phase, old.phase, new.phase),
(&self.gate, old.gate, new.gate),
(&self.keep, old.keep, new.keep),
(&self.ghost, old.ghost, new.ghost),
(&self.ghost_from, old.ghost_from, new.ghost_from),
];
let mut continuous = |i: usize, changed: bool, set: &dyn Fn()| {
if changed {
@@ -136,7 +155,7 @@ impl AnvilParams {
for (i, (p, a, b)) in floats.into_iter().enumerate() {
continuous(i, a != b, &|| setter.set_parameter(p, b));
}
continuous(8, old.output != new.output, &|| {
continuous(10, old.output != new.output, &|| {
setter.set_parameter(&self.output, new.output)
});
let once = |changed: bool, f: &dyn Fn()| {
@@ -180,6 +199,8 @@ impl AnvilParams {
&self.phase,
&self.gate,
&self.keep,
&self.ghost,
&self.ghost_from,
&self.output,
][i]
}