diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 241934a..ca9174b 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -16,6 +16,8 @@ mod node_tseq; mod node_sampl; #[allow(non_upper_case_globals)] mod node_fbwr_fbrd; +#[allow(non_upper_case_globals)] +mod node_ad; pub mod tracker; mod satom; @@ -38,6 +40,7 @@ use crate::fa_tseq_cmode; use crate::fa_sampl_dclick; use crate::fa_sampl_pmode; use crate::fa_sampl_dir; +use crate::fa_ad_mult; use node_amp::Amp; use node_sin::Sin; @@ -47,6 +50,7 @@ use node_tseq::TSeq; use node_sampl::Sampl; use node_fbwr_fbrd::FbWr; use node_fbwr_fbrd::FbRd; +use node_ad::Ad; pub const MIDI_MAX_FREQ : f32 = 13289.75; @@ -276,6 +280,15 @@ macro_rules! r_ms { ($x: expr, $coarse: expr) => { } } } +/// The rounding function for milliseconds knobs +macro_rules! r_ems { ($x: expr, $coarse: expr) => { + if $coarse { + n_env!((d_env!($x)).round()) + } else { + n_env!((d_env!($x) * 10.0).round() / 10.0) + } +} } + /// The default steps function: macro_rules! stp_d { () => { (20.0, 100.0) } } /// The UI steps to control parameters with a finer fine control: @@ -302,7 +315,13 @@ macro_rules! f_freq { ($formatter: expr, $v: expr, $denorm_v: expr) => { } } macro_rules! f_ms { ($formatter: expr, $v: expr, $denorm_v: expr) => { - write!($formatter, "{:5.2}ms", $denorm_v) + if $denorm_v >= 1000.0 { + write!($formatter, "{:6.0}ms", $denorm_v) + } else if $denorm_v >= 100.0 { + write!($formatter, "{:5.1}ms", $denorm_v) + } else { + write!($formatter, "{:5.2}ms", $denorm_v) + } } } macro_rules! f_det { ($formatter: expr, $v: expr, $denorm_v: expr) => { @@ -326,6 +345,9 @@ define_exp!{n_att d_att 0.0, 1.0} define_exp!{n_declick d_declick 0.0, 50.0} +define_exp!{n_env d_env 0.0, 1000.0} + + // A note about the input-indicies: // // Atoms and Input parameters share the same global ID space @@ -402,6 +424,14 @@ macro_rules! node_list { fbrd => FbRd UIType::Generic UICategory::IOUtil (0 atv n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0) [0 sig], + ad => Ad UIType::Generic UICategory::CV + (0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0) + (1 atk n_env d_env r_ems f_ms stp_m 0.0, 1.0, 3.0) + (2 dcy n_env d_env r_ems f_ms stp_m 0.0, 1.0, 10.0) + (3 ashp n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) + (4 dshp n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) + {5 0 mult setting(0) fa_ad_mult 0 2} + [0 sig], test => Test UIType::Generic UICategory::IOUtil (0 f n_id d_id r_id f_def stp_d 0.0, 1.0, 0.5) {1 0 s setting(0) fa_test_s 0 10}, diff --git a/src/dsp/node_ad.rs b/src/dsp/node_ad.rs new file mode 100644 index 0000000..95e2e64 --- /dev/null +++ b/src/dsp/node_ad.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2021 Weird Constructor +// This is a part of HexoDSP. Released under (A)GPLv3 or any later. +// See README.md and COPYING for details. + +use crate::nodes::{NodeAudioContext, NodeExecContext}; +use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals}; + +#[macro_export] +macro_rules! fa_ad_mult { ($formatter: expr, $v: expr, $denorm_v: expr) => { { + let s = + match ($v.round() as usize) { + 0 => "x1", + 1 => "x10", + 2 => "x100", + _ => "?", + }; + write!($formatter, "{}", s) +} } } + +/// A simple amplifier +#[derive(Debug, Clone)] +pub struct Ad { +} + +impl Ad { + pub fn new(_nid: &NodeId) -> Self { + Self { + } + } + pub const inp : &'static str = + "Ad inp\nSignal input. If you don't connect this, and set this to 1.0 \ + this will act as envelope signal generator. But you can also just \ + route a signal directly through this of course.\nRange: (-1..1)\n"; + pub const atk : &'static str = + "Ad atk\nAttack time of the envelope. You can extend the maximum \ + range of this with the 'mult' setting.\nRange: (0..1)\n"; + pub const dcy : &'static str = + "Ad atk\nDecay time of the envelope. You can extend the maximum \ + range of this with the 'mult' setting.\nRange: (0..1)\n"; + pub const ashp : &'static str = + "Ad ashp\nAttack shape. This allows you to change the shape \ + of the attack stage from a logarithmic, to a linear and to an \ + exponential shape.\nRange: (0..1)\n"; + pub const dshp : &'static str = + "Ad dshp\nDecay shape. This allows you to change the shape \ + of the decay stage from a logarithmic, to a linear and to an \ + exponential shape.\nRange: (0..1)\n"; + pub const mult : &'static str = + "Ad mult\nAttack and Decay time range multiplier. \ + This will extend the maximum range of the 'atk' and 'dcy' parameters."; + pub const sig : &'static str = + "Ad sig\nEnvelope signal output. If a signal is sent to the 'inp' port, \ + you will receive an attenuated signal here. If you set 'inp' to a \ + fixed value (for instance 1.0), this will output an envelope signal \ + in the range 0.0 to 'inp' (1.0).\nRange: (-1..1)\n"; + pub const DESC : &'static str = +r#"Attack-Decay Envelope + +This is a simple envelope offering an attack time and decay time with shape parameter. +"#; + pub const HELP : &'static str = +r#"Ad - Attack-Decay Envelope + +"#; + +} + +impl DspNode for Ad { + fn outputs() -> usize { 1 } + + fn set_sample_rate(&mut self, _srate: f32) { } + fn reset(&mut self) { } + + #[inline] + fn process( + &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, + atoms: &[SAtom], _params: &[ProcBuf], inputs: &[ProcBuf], + outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals) + { + use crate::dsp::{out, inp, denorm, denorm_v, inp_dir, at}; + + let gain = inp::Amp::gain(inputs); + let att = inp::Amp::att(inputs); + let inp = inp::Amp::inp(inputs); + let out = out::Amp::sig(outputs); + let neg = at::Amp::neg_att(atoms); + + let last_frame = ctx.nframes() - 1; + + let last_val = + if neg.i() > 0 { + for frame in 0..ctx.nframes() { + out.write(frame, + inp.read(frame) + * denorm_v::Amp::att( + inp_dir::Amp::att(att, frame) + .max(0.0)) + * denorm::Amp::gain(gain, frame)); + } + + inp.read(last_frame) + * denorm_v::Amp::att( + inp_dir::Amp::att(att, last_frame) + .max(0.0)) + * denorm::Amp::gain(gain, last_frame) + + } else { + for frame in 0..ctx.nframes() { + out.write(frame, + inp.read(frame) + * denorm_v::Amp::att( + inp_dir::Amp::att(att, frame).abs()) + * denorm::Amp::gain(gain, frame)); + } + + inp.read(last_frame) + * denorm_v::Amp::att( + inp_dir::Amp::att(att, last_frame).abs()) + * denorm::Amp::gain(gain, last_frame) + }; + + ctx_vals[0].set(last_val); + } +} diff --git a/src/dsp/node_fbwr_fbrd.rs b/src/dsp/node_fbwr_fbrd.rs index d666b2d..8ad881a 100644 --- a/src/dsp/node_fbwr_fbrd.rs +++ b/src/dsp/node_fbwr_fbrd.rs @@ -46,7 +46,7 @@ is running at. } impl DspNode for FbWr { - fn outputs() -> usize { 1 } + fn outputs() -> usize { 0 } fn set_sample_rate(&mut self, _srate: f32) { } fn reset(&mut self) { }