use crate::stateful_dsp_node_type;
use crate::stateless_dsp_node_type;
use crate::{DSPNodeSigBit, DSPNodeType, DSPNodeTypeLibrary, DSPState};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
use synfx_dsp::Trigger;
pub struct AccumNodeState {
pub value: f64,
}
impl AccumNodeState {
fn reset(&mut self, _state: &mut DSPState) {
*self = Self::default();
}
}
impl Default for AccumNodeState {
fn default() -> Self {
Self { value: 0.0 }
}
}
extern "C" fn process_accum_nod(v: f64, r: f64, state: *mut AccumNodeState) -> f64 {
let state = unsafe { &mut *state };
if r > 0.5 {
state.value = 0.0;
} else {
state.value += v;
}
state.value
}
stateful_dsp_node_type! {
AccumNodeType, AccumNodeState => process_accum_nod "accum" "vvSr"
doc
"This is a simple accumulator. It sums up it's input and returns it. \
You can reset it's state if you pass a value >= 0.5 into 'reset'."
inputs
0 "input"
1 "reset"
outputs
0 "sum"
}
pub struct PhaseNodeState {
pub israte: f64,
pub value: f64,
pub trig: Trigger,
}
impl PhaseNodeState {
fn reset(&mut self, state: &mut DSPState) {
*self = Self::default();
self.israte = state.israte;
}
}
impl Default for PhaseNodeState {
fn default() -> Self {
Self { israte: 1.0 / 44100.0, value: 0.0, trig: Trigger::new() }
}
}
extern "C" fn process_phase(freq: f64, reset: f64, state: *mut PhaseNodeState) -> f64 {
let state = unsafe { &mut *state };
if state.trig.check_trigger(reset as f32) {
state.value = 0.0;
}
let value = state.value;
state.value += freq * state.israte;
state.value = state.value.fract();
value
}
stateful_dsp_node_type! {
PhaseNodeType, PhaseNodeState => process_phase "phase" "vvSr"
doc
"A very simple oscillator that outputs a rising sawtooth wave to 'phase' (range 0.0 to 1.0) with the \
frequency 'freq' (range 0.0 to 22050.0). The phase will be reset to 0.0 when a rising edge is detected on 'reset'."
inputs
0 "freq"
1 "reset"
outputs
0 "phase"
}
extern "C" fn process_sin(v: f64) -> f64 {
v.sin()
}
stateless_dsp_node_type! {
SinNodeType => process_sin "sin" "vr"
doc
"This is a sine function. Input is in radians."
inputs
0 ""
outputs
0 ""
}
extern "C" fn process_divrem(a: f64, b: f64, retvars: *mut [f64; 5]) -> f64 {
unsafe {
(*retvars)[0] = a % b;
}
a / b
}
stateless_dsp_node_type! {
DivRemNodeType => process_divrem "/%" "vvMr"
doc
"Computes the float division and remainder of a / b"
inputs
0 "a"
1 "b"
outputs
0 "div"
1 "rem"
}
extern "C" fn process_atomr(idx: f64, dsp_state: *mut DSPState) -> f64 {
let atoms = unsafe { &mut (*dsp_state).atoms };
let i = idx.floor() as usize % atoms.len();
atoms[i].get() as f64
}
stateless_dsp_node_type! {
AtomRNodeType => process_atomr "atomr" "vDr"
doc
"This node reads from the specified 'index' in the 512 long array of \
shared atomic floats. If index >= 512, it will wrap around."
inputs
0 "index"
outputs
0 "value"
}
extern "C" fn process_atomr_lin(idx: f64, dsp_state: *mut DSPState) -> f64 {
let atoms = unsafe { &mut (*dsp_state).atoms };
let i1 = idx.floor() as usize % atoms.len();
let i2 = (i1 + 1) % atoms.len();
let f = idx.fract();
let a = atoms[i1].get() as f64;
let b = atoms[i2].get() as f64;
a * (1.0 - f) + f * b
}
stateless_dsp_node_type! {
AtomRLinNodeType => process_atomr_lin "atomr~" "vDr"
doc
"This node reads linearily interpolated from the specified 'index' in the 512 long array of \
shared atomic floats. If index >= 512, it will wrap around."
inputs
0 "index"
outputs
0 "value"
}
extern "C" fn process_atomw(idx: f64, v: f64, dsp_state: *mut DSPState) -> f64 {
let atoms = unsafe { &mut (*dsp_state).atoms };
let i = idx.floor() as usize % atoms.len();
atoms[i].set(v as f32);
v
}
stateless_dsp_node_type! {
AtomWNodeType => process_atomw "atomw" "vvDr"
doc
"This node writes 'vlue' to the specified 'index' in the 512 long array of \
shared atomic floats. If index >= 512, it will wrap around. It passes 'value' through, \
so that you can easily plug this node in some signal path."
inputs
0 "index"
1 "value"
outputs
0 "value"
}
pub struct SHLNodeState {
pub value: f64,
}
impl SHLNodeState {
fn reset(&mut self, _state: &mut DSPState) {
*self = Self::default();
}
}
impl Default for SHLNodeState {
fn default() -> Self {
Self { value: 0.0 }
}
}
extern "C" fn process_sh_l(i: f64, set: f64, state: *mut SHNodeState) -> f64 {
let state = unsafe { &mut *state };
if set > 0.5 {
state.value = i;
}
state.value
}
stateful_dsp_node_type! {
SHLNodeType, SHLNodeState => process_sh_l "s&h~" "vvSr"
doc
"This is a sample & hold node that samples it's 'input' as long as 'set' is above 0.5"
inputs
0 "input"
1 "set"
outputs
0 "value"
}
pub struct SHNodeState {
pub trig: Trigger,
pub value: f64,
}
impl SHNodeState {
fn reset(&mut self, _state: &mut DSPState) {
*self = Self::default();
}
}
impl Default for SHNodeState {
fn default() -> Self {
Self { trig: Trigger::new(), value: 0.0 }
}
}
extern "C" fn process_sh(i: f64, set: f64, state: *mut SHNodeState) -> f64 {
let state = unsafe { &mut *state };
if state.trig.check_trigger(set as f32) {
state.value = i;
}
state.value
}
stateful_dsp_node_type! {
SHNodeType, SHNodeState => process_sh "s&h" "vvSr"
doc
"This is a sample & hold node that samples it's 'input' on a rising edge \
on the 'set' input using a Schmitt trigger with high/low thresholds at 0.5/0.25."
inputs
0 "input"
1 "set"
outputs
0 "value"
}
pub fn get_standard_library() -> Rc<RefCell<DSPNodeTypeLibrary>> {
let lib = Rc::new(RefCell::new(DSPNodeTypeLibrary::new()));
lib.borrow_mut().add(Arc::new(SinNodeType::default()));
lib.borrow_mut().add(AccumNodeType::new_ref());
lib.borrow_mut().add(DivRemNodeType::new_ref());
lib.borrow_mut().add(PhaseNodeType::new_ref());
lib.borrow_mut().add(AtomWNodeType::new_ref());
lib.borrow_mut().add(AtomRNodeType::new_ref());
lib.borrow_mut().add(AtomRLinNodeType::new_ref());
lib.borrow_mut().add(SHNodeType::new_ref());
lib.borrow_mut().add(SHLNodeType::new_ref());
lib
}