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, #[id = "source"] pub source: EnumParam, #[id = "level"] pub level: FloatParam, #[id = "split"] pub split: FloatParam, #[id = "dynamics"] pub dynamics: FloatParam, #[id = "drive"] pub drive: 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., " %"), 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(), 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; 9], ) { let floats: [(&FloatParam, f32, f32); 8] = [ (&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), ]; 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(8, 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.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)); }