Added trigger outputs to the quantizers
This commit is contained in:
parent
a496bb1b04
commit
8fa9ce35e9
4 changed files with 95 additions and 20 deletions
|
@ -577,6 +577,44 @@ impl Default for TrigSignal {
|
||||||
fn default() -> Self { Self::new() }
|
fn default() -> Self { Self::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ChangeTrig {
|
||||||
|
ts: TrigSignal,
|
||||||
|
last: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChangeTrig {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
ts: TrigSignal::new(),
|
||||||
|
last: -100.0, // some random value :-)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.ts.reset();
|
||||||
|
self.last = -100.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sample_rate(&mut self, srate: f32) {
|
||||||
|
self.ts.set_sample_rate(srate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn next(&mut self, inp: f32) -> f32 {
|
||||||
|
if (inp - self.last).abs() > std::f32::EPSILON {
|
||||||
|
self.ts.trigger();
|
||||||
|
self.last = inp;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.ts.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ChangeTrig {
|
||||||
|
fn default() -> Self { Self::new() }
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Trigger {
|
pub struct Trigger {
|
||||||
triggered: bool,
|
triggered: bool,
|
||||||
|
|
|
@ -747,14 +747,16 @@ macro_rules! node_list {
|
||||||
(0 freq n_pit d_pit r_id f_freq stp_d -1.0, 0.5647131, 440.0)
|
(0 freq n_pit d_pit r_id f_freq stp_d -1.0, 0.5647131, 440.0)
|
||||||
(1 oct n_id d_id r_s 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}
|
{2 0 keys setting(0) fa_quant 0 0}
|
||||||
[0 sig],
|
[0 sig]
|
||||||
|
[1 t],
|
||||||
cqnt => CQnt UIType::Generic UICategory::CV
|
cqnt => CQnt UIType::Generic UICategory::CV
|
||||||
(0 inp n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0)
|
(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_s 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_cqnt 0 0}
|
{2 0 keys setting(0) fa_cqnt 0 0}
|
||||||
{3 1 omin setting(0) fa_cqnt_omin 0 4}
|
{3 1 omin setting(0) fa_cqnt_omin 0 4}
|
||||||
{4 2 omax setting(0) fa_cqnt_omax 0 4}
|
{4 2 omax setting(0) fa_cqnt_omax 0 4}
|
||||||
[0 sig],
|
[0 sig]
|
||||||
|
[1 t],
|
||||||
tseq => TSeq UIType::Generic UICategory::Mod
|
tseq => TSeq UIType::Generic UICategory::Mod
|
||||||
(0 clock n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0)
|
(0 clock n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0)
|
||||||
(1 trig n_id n_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
(1 trig n_id n_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext};
|
||||||
use crate::dsp::helpers::CtrlPitchQuantizer;
|
use crate::dsp::helpers::{CtrlPitchQuantizer, ChangeTrig};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! fa_cqnt { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
macro_rules! fa_cqnt { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
||||||
|
@ -43,12 +43,14 @@ macro_rules! fa_cqnt_omax { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CQnt {
|
pub struct CQnt {
|
||||||
quant: Box<CtrlPitchQuantizer>,
|
quant: Box<CtrlPitchQuantizer>,
|
||||||
|
change_trig: ChangeTrig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CQnt {
|
impl CQnt {
|
||||||
pub fn new(_nid: &NodeId) -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
quant: Box::new(CtrlPitchQuantizer::new()),
|
quant: Box::new(CtrlPitchQuantizer::new()),
|
||||||
|
change_trig: ChangeTrig::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub const inp : &'static str =
|
pub const inp : &'static str =
|
||||||
|
@ -61,6 +63,10 @@ impl CQnt {
|
||||||
"CQnt omax\n\nRange: (-1..1)";
|
"CQnt omax\n\nRange: (-1..1)";
|
||||||
pub const sig : &'static str =
|
pub const sig : &'static str =
|
||||||
"CQnt sig\n\nRange: (-1..1)";
|
"CQnt sig\n\nRange: (-1..1)";
|
||||||
|
pub const t : &'static str =
|
||||||
|
"CQnt t\nEverytime the quantizer snaps to a new pitch, it will \
|
||||||
|
emit a short trigger on this signal output. This is useful \
|
||||||
|
to trigger for example an envelope.";
|
||||||
pub const keys : &'static str =
|
pub const keys : &'static str =
|
||||||
"CQnt keys\n";
|
"CQnt keys\n";
|
||||||
pub const DESC : &'static str =
|
pub const DESC : &'static str =
|
||||||
|
@ -80,8 +86,14 @@ like the 'Quant' node.
|
||||||
impl DspNode for CQnt {
|
impl DspNode for CQnt {
|
||||||
fn outputs() -> usize { 1 }
|
fn outputs() -> usize { 1 }
|
||||||
|
|
||||||
fn set_sample_rate(&mut self, _srate: f32) { }
|
fn set_sample_rate(&mut self, srate: f32) {
|
||||||
fn reset(&mut self) { }
|
self.change_trig.set_sample_rate(srate);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.change_trig.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn process<T: NodeAudioContext>(
|
fn process<T: NodeAudioContext>(
|
||||||
|
@ -90,11 +102,12 @@ impl DspNode for CQnt {
|
||||||
atoms: &[SAtom], inputs: &[ProcBuf],
|
atoms: &[SAtom], inputs: &[ProcBuf],
|
||||||
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
|
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
|
||||||
{
|
{
|
||||||
use crate::dsp::{at, out, inp, denorm};
|
use crate::dsp::{at, out_buf, inp, denorm};
|
||||||
|
|
||||||
let inp = inp::CQnt::inp(inputs);
|
let inp = inp::CQnt::inp(inputs);
|
||||||
let oct = inp::CQnt::oct(inputs);
|
let oct = inp::CQnt::oct(inputs);
|
||||||
let out = out::CQnt::sig(outputs);
|
let mut out = out_buf::CQnt::sig(outputs);
|
||||||
|
let mut t = out_buf::CQnt::t(outputs);
|
||||||
let keys = at::CQnt::keys(atoms);
|
let keys = at::CQnt::keys(atoms);
|
||||||
let omin = at::CQnt::omin(atoms);
|
let omin = at::CQnt::omin(atoms);
|
||||||
let omax = at::CQnt::omax(atoms);
|
let omax = at::CQnt::omax(atoms);
|
||||||
|
@ -107,6 +120,8 @@ impl DspNode for CQnt {
|
||||||
let pitch =
|
let pitch =
|
||||||
self.quant.signal_to_pitch(
|
self.quant.signal_to_pitch(
|
||||||
denorm::CQnt::inp(inp, frame));
|
denorm::CQnt::inp(inp, frame));
|
||||||
|
|
||||||
|
t.write(frame, self.change_trig.next(pitch));
|
||||||
out.write(frame, pitch + denorm::CQnt::oct(oct, frame));
|
out.write(frame, pitch + denorm::CQnt::oct(oct, frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext};
|
use crate::dsp::{NodeId, SAtom, ProcBuf, DspNode, LedPhaseVals, NodeContext};
|
||||||
use crate::dsp::helpers::Quantizer;
|
use crate::dsp::helpers::{Quantizer, ChangeTrig};
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! fa_quant { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
macro_rules! fa_quant { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
||||||
|
@ -15,22 +15,34 @@ macro_rules! fa_quant { ($formatter: expr, $v: expr, $denorm_v: expr) => { {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Quant {
|
pub struct Quant {
|
||||||
quant: Box<Quantizer>,
|
quant: Box<Quantizer>,
|
||||||
|
change_trig: ChangeTrig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Quant {
|
impl Quant {
|
||||||
pub fn new(_nid: &NodeId) -> Self {
|
pub fn new(_nid: &NodeId) -> Self {
|
||||||
Self {
|
Self {
|
||||||
quant: Box::new(Quantizer::new()),
|
quant: Box::new(Quantizer::new()),
|
||||||
|
change_trig: ChangeTrig::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub const freq : &'static str =
|
pub const freq : &'static str =
|
||||||
"Quant freq\n\nRange: (0..1)";
|
"Quant freq\nAny signal that is to be pitch quantized.\nRange: (-1..1)";
|
||||||
pub const oct : &'static str =
|
pub const oct : &'static str =
|
||||||
"Quant oct\n\nRange: (-1..1)";
|
"Quant oct\nPitch offset, the knob is snapping to octave offsets. \
|
||||||
|
Feed signal values snapped to 0.1 multiples for exact octave offsets.\
|
||||||
|
\nRange: (-1..1)";
|
||||||
pub const sig : &'static str =
|
pub const sig : &'static str =
|
||||||
"Quant sig\n\nRange: (-1..1)";
|
"Quant sig\nThe quantized output signal that is rounded to \
|
||||||
|
the next selected note pitch within the octave of the \
|
||||||
|
original input to 'freq'.\nRange: (-1..1)";
|
||||||
pub const keys : &'static str =
|
pub const keys : &'static str =
|
||||||
"Quant keys\n";
|
"Quant keys\nSelect the notes you want to snap to here. \
|
||||||
|
If no notes are selected, the quantizer will snap the \
|
||||||
|
incoming signal to any closest note.";
|
||||||
|
pub const t : &'static str =
|
||||||
|
"Quant t\nEverytime the quantizer snaps to a new pitch, it will \
|
||||||
|
emit a short trigger on this signal output. This is useful \
|
||||||
|
to trigger for example an envelope.";
|
||||||
pub const DESC : &'static str =
|
pub const DESC : &'static str =
|
||||||
r#"Pitch Quantizer
|
r#"Pitch Quantizer
|
||||||
|
|
||||||
|
@ -52,8 +64,13 @@ please see also the 'CQnt' node.
|
||||||
impl DspNode for Quant {
|
impl DspNode for Quant {
|
||||||
fn outputs() -> usize { 1 }
|
fn outputs() -> usize { 1 }
|
||||||
|
|
||||||
fn set_sample_rate(&mut self, _srate: f32) { }
|
fn set_sample_rate(&mut self, srate: f32) {
|
||||||
fn reset(&mut self) { }
|
self.change_trig.set_sample_rate(srate);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.change_trig.reset();
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn process<T: NodeAudioContext>(
|
fn process<T: NodeAudioContext>(
|
||||||
|
@ -62,17 +79,20 @@ impl DspNode for Quant {
|
||||||
atoms: &[SAtom], inputs: &[ProcBuf],
|
atoms: &[SAtom], inputs: &[ProcBuf],
|
||||||
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
|
outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals)
|
||||||
{
|
{
|
||||||
use crate::dsp::{at, out, inp, denorm};
|
use crate::dsp::{at, out_buf, inp, denorm};
|
||||||
|
|
||||||
let freq = inp::Quant::freq(inputs);
|
let freq = inp::Quant::freq(inputs);
|
||||||
let oct = inp::Quant::oct(inputs);
|
let oct = inp::Quant::oct(inputs);
|
||||||
let keys = at::Quant::keys(atoms);
|
let keys = at::Quant::keys(atoms);
|
||||||
let out = out::Quant::sig(outputs);
|
let mut out = out_buf::CQnt::sig(outputs);
|
||||||
|
let mut t = out_buf::CQnt::t(outputs);
|
||||||
|
|
||||||
self.quant.set_keys(keys.i());
|
self.quant.set_keys(keys.i());
|
||||||
|
|
||||||
for frame in 0..ctx.nframes() {
|
for frame in 0..ctx.nframes() {
|
||||||
let pitch = self.quant.process(freq.read(frame));
|
let pitch = self.quant.process(freq.read(frame));
|
||||||
|
|
||||||
|
t.write(frame, self.change_trig.next(pitch));
|
||||||
out.write(frame, pitch + denorm::Quant::oct(oct, frame));
|
out.write(frame, pitch + denorm::Quant::oct(oct, frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue