diff --git a/src/dsp/helpers.rs b/src/dsp/helpers.rs index 52a7043..49b8971 100644 --- a/src/dsp/helpers.rs +++ b/src/dsp/helpers.rs @@ -2134,6 +2134,111 @@ impl TriSawLFO { } } +#[derive(Debug, Clone)] +pub struct Quantizer { + /// All keys, containing the min/max octave! + keys: Vec, + /// Only the used keys with their pitches from the UI + used_keys: [f32; 12], + /// A value combination of the arguments to `update_keys`. + input_params: u64, + /// The number of used keys from the mask. + mask_key_count: u16, + /// The last key for the pitch that was returned by `process`. + last_key: u8, +} + +const QUANT_TUNE_TO_A4 : f32 = (9.0 / 12.0) * 0.1; + +impl Quantizer { + pub fn new() -> Self { + Self { + keys: vec![0.0; 12 * 10], + used_keys: [0.0; 12], + mask_key_count: 0, + input_params: 0, + last_key: 0, + } + } + + #[inline] + pub fn last_key_pitch(&self) -> f32 { + self.used_keys[ + self.last_key as usize + % (self.mask_key_count as usize)] + + QUANT_TUNE_TO_A4 + } + + #[inline] + pub fn has_no_keys(&self) -> bool { + self.keys.is_empty() + } + + #[inline] + pub fn update_keys(&mut self, mask: i64, min_oct: i64, max_oct: i64) { + let inp_params = + (mask as u64) + | ((min_oct as u64) << 8) + | ((max_oct as u64) << 16); + + if self.input_params == inp_params { + return; + } + + self.input_params = inp_params; + + let mut mask_count = 0; + + for i in 0..9 { + if mask & (0x1 << i) > 0 { + self.used_keys[mask_count] = ((i as f32 / 12.0) * 0.1) - QUANT_TUNE_TO_A4; + mask_count += 1; + } + } + + for i in 9..12 { + let key_pitch_idx = (i + 9 + 12) % 12; + if mask & (0x1 << i) > 0 { + self.used_keys[mask_count] = (i as f32 / 12.0) * 0.1 - QUANT_TUNE_TO_A4; + mask_count += 1; + } + } + + self.keys.clear(); + + let min_oct = min_oct as usize; + for o in 0..min_oct { + let o = min_oct - o; + + for i in 0..mask_count { + self.keys.push(self.used_keys[i] - (o as f32) * 0.1); + } + } + + for i in 0..mask_count { + self.keys.push(self.used_keys[i]); + } + + let max_oct = max_oct as usize; + for o in 1..=max_oct { + for i in 0..mask_count { + self.keys.push(self.used_keys[i] + (o as f32) * 0.1); + } + } + + self.mask_key_count = mask_count as u16; + } + + #[inline] + pub fn signal_to_pitch(&mut self, inp: f32) -> f32 { + let len = self.keys.len(); + let key = (inp.clamp(0.0, 0.9999) * (len as f32)).floor(); + let key = key as usize % len; + self.last_key = key as u8; + self.keys[key] + } +} + #[macro_export] macro_rules! fa_distort { ($formatter: expr, $v: expr, $denorm_v: expr) => { { let s = diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 7d79ad7..6d9b057 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -739,7 +739,7 @@ macro_rules! node_list { [0 sig], quant => Quant UIType::Generic UICategory::CV (0 inp n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0) - (1 oct n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (1 oct n_id d_id r_s f_def stp_d -1.0, 1.0, 0.0) {2 0 keys setting(0) fa_quant 0 0} {3 1 omin setting(0) fa_quant_omin 0 4} {4 2 omax setting(0) fa_quant_omax 0 4} diff --git a/src/dsp/node_quant.rs b/src/dsp/node_quant.rs index 8cf403a..8acf5cf 100644 --- a/src/dsp/node_quant.rs +++ b/src/dsp/node_quant.rs @@ -4,7 +4,7 @@ use crate::nodes::{NodeAudioContext, NodeExecContext}; use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext}; -use crate::dsp::helpers::{Trigger}; +use crate::dsp::helpers::Quantizer; #[macro_export] macro_rules! fa_quant { ($formatter: expr, $v: expr, $denorm_v: expr) => { { @@ -42,11 +42,13 @@ macro_rules! fa_quant_omax { ($formatter: expr, $v: expr, $denorm_v: expr) => { /// A 9 channel signal multiplexer #[derive(Debug, Clone)] pub struct Quant { + quant: Quantizer, } impl Quant { pub fn new(_nid: &NodeId) -> Self { Self { + quant: Quantizer::new(), } } pub const inp : &'static str = @@ -93,53 +95,9 @@ impl DspNode for Quant { let omin = at::Quant::omin(atoms); let omax = at::Quant::omax(atoms); - let mut key_count = 0; - let mut used_keys = [0.0; 12]; + self.quant.update_keys(keys.i(), omin.i(), omax.i()); - let mask = keys.i(); - let tune_to_a4 = (9.0 / 12.0) * 0.1; - for i in 0..9 { - if mask & (0x1 << i) > 0 { - used_keys[key_count] = ((i as f32 / 12.0) * 0.1) - tune_to_a4; - key_count += 1; - } - } - - for i in 9..12 { - let key_pitch_idx = (i + 9 + 12) % 12; - if mask & (0x1 << i) > 0 { - used_keys[key_count] = (i as f32 / 12.0) * 0.1 - tune_to_a4; - key_count += 1; - } - } - - let mut all_keys = [0.0; 12 * 10]; // -4 and +4 octaves + 1 center - let mut max_all = 0; - - let omin = omin.i() as usize; - for o in 0..omin { - let o = omin - o; - - for i in 0..key_count { - all_keys[max_all] = used_keys[i] - (o as f32) * 0.1; - max_all += 1; - } - } - - for i in 0..key_count { - all_keys[max_all] = used_keys[i]; - max_all += 1; - } - - let omax = omax.i() as usize; - for o in 1..=omax { - for i in 0..key_count { - all_keys[max_all] = used_keys[i] + (o as f32) * 0.1; - max_all += 1; - } - } - - if max_all == 0 { + if self.quant.has_no_keys() { for frame in 0..ctx.nframes() { out.write( frame, @@ -154,18 +112,15 @@ impl DspNode for Quant { let mut last_key = 0; for frame in 0..ctx.nframes() { - let key = - (denorm::Quant::inp(inp, frame) * (max_all as f32)) - .floor(); - let key = key as usize % max_all; - let pitch = all_keys[key]; - last_key = key; + let pitch = + self.quant.signal_to_pitch( + denorm::Quant::inp(inp, frame)); out.write(frame, pitch + denorm::Quant::oct(oct, frame)); } - let last_pitch = used_keys[last_key as usize % key_count]; - ctx_vals[1].set((last_pitch + tune_to_a4) * 10.0 + 0.0001); - ctx_vals[0].set(((last_pitch + tune_to_a4) * 10.0 - 0.5) * 2.0); + let last_pitch = self.quant.last_key_pitch(); + ctx_vals[1].set(last_pitch * 10.0 + 0.0001); + ctx_vals[0].set((last_pitch * 10.0 - 0.5) * 2.0); } } }