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.
214 lines
7.1 KiB
Rust
214 lines
7.1 KiB
Rust
use anvil_core::{Settings, Source};
|
|
use nih_plug::prelude::*;
|
|
use nih_plug_egui::EguiState;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Enum, Debug, PartialEq, Eq, Clone, Copy)]
|
|
pub enum SourceParam {
|
|
#[id = "audio"]
|
|
Audio,
|
|
#[id = "midi"]
|
|
#[name = "MIDI"]
|
|
Midi,
|
|
}
|
|
fn linear(name: &str, v: f32, min: f32, max: f32, unit: &'static str) -> FloatParam {
|
|
FloatParam::new(name, v, FloatRange::Linear { min, max }).with_unit(unit)
|
|
}
|
|
#[derive(Params)]
|
|
pub struct AnvilParams {
|
|
#[persist = "editor"]
|
|
pub editor: Arc<EguiState>,
|
|
#[id = "source"]
|
|
pub source: EnumParam<SourceParam>,
|
|
#[id = "level"]
|
|
pub level: FloatParam,
|
|
#[id = "split"]
|
|
pub split: FloatParam,
|
|
#[id = "dynamics"]
|
|
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"]
|
|
pub glide: FloatParam,
|
|
#[id = "phase"]
|
|
pub phase: FloatParam,
|
|
#[id = "gate"]
|
|
pub gate: FloatParam,
|
|
#[id = "keep"]
|
|
pub keep: FloatParam,
|
|
#[id = "output"]
|
|
pub output: FloatParam,
|
|
#[id = "retrigger"]
|
|
pub retrigger: BoolParam,
|
|
#[id = "snap"]
|
|
pub snap: BoolParam,
|
|
#[id = "bypass"]
|
|
pub bypass: BoolParam,
|
|
}
|
|
impl Default for AnvilParams {
|
|
fn default() -> Self {
|
|
let d = Settings::default();
|
|
Self {
|
|
editor: EguiState::from_size(anvil_ui::WIDTH, anvil_ui::HEIGHT),
|
|
source: EnumParam::new("Pitch source", SourceParam::Audio),
|
|
level: linear("Sub level", d.level, -30., 0., " dBFS"),
|
|
split: FloatParam::new(
|
|
"Split",
|
|
d.split,
|
|
FloatRange::Skewed {
|
|
min: 40.,
|
|
max: 200.,
|
|
factor: FloatRange::skew_factor(-1.),
|
|
},
|
|
)
|
|
.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., "°"),
|
|
gate: linear("Gate", d.gate, -60., -20., " dB"),
|
|
keep: linear("Keep", d.keep, 0., 100., " %"),
|
|
output: linear("Output", d.output, -12., 12., " dB"),
|
|
retrigger: BoolParam::new("Retrigger", d.retrigger),
|
|
snap: BoolParam::new("Snap to semitone", d.snap),
|
|
bypass: BoolParam::new("Bypass", false).make_bypass(),
|
|
}
|
|
}
|
|
}
|
|
impl AnvilParams {
|
|
pub fn settings(&self) -> Settings {
|
|
Settings {
|
|
source: match self.source.value() {
|
|
SourceParam::Audio => Source::Audio,
|
|
SourceParam::Midi => Source::Midi,
|
|
},
|
|
level: self.level.value(),
|
|
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(),
|
|
gate: self.gate.value(),
|
|
keep: self.keep.value(),
|
|
output: self.output.value(),
|
|
retrigger: self.retrigger.value(),
|
|
snap: self.snap.value(),
|
|
bypass: self.bypass.value(),
|
|
}
|
|
}
|
|
/// Pushes every changed editor value to the host. Continuous controls hold one
|
|
/// gesture while the pointer is down; switches are set in a single gesture.
|
|
pub fn apply(
|
|
&self,
|
|
setter: &ParamSetter,
|
|
old: &Settings,
|
|
new: &Settings,
|
|
dragging: bool,
|
|
open: &mut [bool; 11],
|
|
) {
|
|
let floats: [(&FloatParam, f32, f32); 10] = [
|
|
(&self.level, old.level, new.level),
|
|
(&self.split, old.split, new.split),
|
|
(&self.dynamics, old.dynamics, new.dynamics),
|
|
(&self.drive, old.drive, new.drive),
|
|
(&self.glide, old.glide, new.glide),
|
|
(&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 {
|
|
if !open[i] {
|
|
open[i] = true;
|
|
set_begin(setter, self, i);
|
|
}
|
|
set();
|
|
}
|
|
if !dragging && open[i] {
|
|
set_end(setter, self, i);
|
|
open[i] = false;
|
|
}
|
|
};
|
|
for (i, (p, a, b)) in floats.into_iter().enumerate() {
|
|
continuous(i, a != b, &|| setter.set_parameter(p, b));
|
|
}
|
|
continuous(10, old.output != new.output, &|| {
|
|
setter.set_parameter(&self.output, new.output)
|
|
});
|
|
let once = |changed: bool, f: &dyn Fn()| {
|
|
if changed {
|
|
f();
|
|
}
|
|
};
|
|
once(new.octave != old.octave, &|| {
|
|
setter.begin_set_parameter(&self.octave);
|
|
setter.set_parameter(&self.octave, new.octave);
|
|
setter.end_set_parameter(&self.octave);
|
|
});
|
|
for (p, a, b) in [
|
|
(&self.retrigger, old.retrigger, new.retrigger),
|
|
(&self.snap, old.snap, new.snap),
|
|
(&self.bypass, old.bypass, new.bypass),
|
|
] {
|
|
once(a != b, &|| {
|
|
setter.begin_set_parameter(p);
|
|
setter.set_parameter(p, b);
|
|
setter.end_set_parameter(p);
|
|
});
|
|
}
|
|
once(new.source != old.source, &|| {
|
|
let v = match new.source {
|
|
Source::Audio => SourceParam::Audio,
|
|
Source::Midi => SourceParam::Midi,
|
|
};
|
|
setter.begin_set_parameter(&self.source);
|
|
setter.set_parameter(&self.source, v);
|
|
setter.end_set_parameter(&self.source);
|
|
});
|
|
}
|
|
fn continuous(&self, i: usize) -> &FloatParam {
|
|
[
|
|
&self.level,
|
|
&self.split,
|
|
&self.dynamics,
|
|
&self.drive,
|
|
&self.glide,
|
|
&self.phase,
|
|
&self.gate,
|
|
&self.keep,
|
|
&self.ghost,
|
|
&self.ghost_from,
|
|
&self.output,
|
|
][i]
|
|
}
|
|
}
|
|
fn set_begin(setter: &ParamSetter, p: &AnvilParams, i: usize) {
|
|
setter.begin_set_parameter(p.continuous(i));
|
|
}
|
|
fn set_end(setter: &ParamSetter, p: &AnvilParams, i: usize) {
|
|
setter.end_set_parameter(p.continuous(i));
|
|
}
|