diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index 219c707..9193b58 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -537,6 +537,8 @@ mod node_tseq; mod node_tslfo; #[allow(non_upper_case_globals)] mod node_vosc; +#[allow(non_upper_case_globals)] +mod node_code; pub mod biquad; pub mod dattorro; @@ -607,6 +609,7 @@ use node_sin::Sin; use node_smap::SMap; use node_test::Test; use node_tseq::TSeq; +use node_code::Code; use node_tslfo::TsLFO; use node_vosc::VOsc; @@ -1367,11 +1370,21 @@ macro_rules! node_list { [9 gat4] [10 gat5] [11 gat6], + code => Code UIType::Generic UICategory::Signal + (0 in1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (1 in2 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (2 alpha n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (3 beta n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (4 delta n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (5 gamma n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + [0 sig] + [1 sig1] + [2 sig2], sampl => Sampl UIType::Generic UICategory::Osc (0 freq n_pit d_pit r_fq f_def stp_d -1.0, 0.564713133, 440.0) - (1 trig n_id n_id r_id f_def stp_d -1.0, 1.0, 0.0) - (2 offs n_id n_id r_id f_def stp_d 0.0, 1.0, 0.0) - (3 len n_id n_id r_id f_def stp_d 0.0, 1.0, 1.0) + (1 trig n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) + (2 offs n_id d_id r_id f_def stp_d 0.0, 1.0, 0.0) + (3 len n_id d_id r_id f_def stp_d 0.0, 1.0, 1.0) (4 dcms n_declick d_declick r_dc_ms f_ms stp_m 0.0, 1.0, 3.0) (5 det n_det d_det r_det f_det stp_f -0.2, 0.2, 0.0) {6 0 sample audio_unloaded("") sample f_def 0 0} diff --git a/src/dsp/node_code.rs b/src/dsp/node_code.rs new file mode 100644 index 0000000..0f00176 --- /dev/null +++ b/src/dsp/node_code.rs @@ -0,0 +1,109 @@ +// Copyright (c) 2021 Weird Constructor +// This file is a part of HexoDSP. Released under GPL-3.0-or-later. +// See README.md and COPYING for details. + +use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; +use crate::nodes::{NodeAudioContext, NodeExecContext}; +use crate::wblockdsp::CodeEngineBackend; + +use crate::dsp::MAX_BLOCK_SIZE; + +/// A WBlockDSP code execution node for JIT'ed DSP code +pub struct Code { + backend: Option>, + srate: f64, +} + +impl std::fmt::Debug for Code { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "Code") + } +} + +impl Clone for Code { + fn clone(&self) -> Self { + Self::new(&NodeId::Nop) + } +} + +impl Code { + pub fn new(_nid: &NodeId) -> Self { + Self { + backend: None, + srate: 48000.0, + } + } + + pub fn set_backend(&mut self, backend: CodeEngineBackend) { + self.backend = Some(Box::new(backend)); + } + + pub const in1: &'static str = "Code in1\nInput Signal 1\nRange: (-1..1)\n"; + pub const in2: &'static str = "Code in2\nInput Signal 1\nRange: (-1..1)\n"; + pub const alpha: &'static str = "Code alpha\nInput Parameter Alpha\nRange: (-1..1)\n"; + pub const beta: &'static str = "Code alpha\nInput Parameter Alpha\nRange: (-1..1)\n"; + pub const delta: &'static str = "Code alpha\nInput Parameter Alpha\nRange: (-1..1)\n"; + pub const gamma: &'static str = "Code alpha\nInput Parameter Alpha\nRange: (-1..1)\n"; + pub const sig: &'static str = "Code sig\nReturn output\nRange: (-1..1)\n"; + pub const sig1: &'static str = "Code sig1\nSignal channel 1 output\nRange: (-1..1)\n"; + pub const sig2: &'static str = "Code sig2\nSignal channel 2 output\nRange: (-1..1)\n"; + + pub const DESC: &'static str = "WBlockDSP Code Execution\n\n\ + This node executes just in time compiled code as fast as machine code. \ + Use this to implement real time DSP code yourself."; + pub const HELP: &'static str = r#"WBlockDSP Code Execution + +Do it! +"#; +} + +impl DspNode for Code { + fn outputs() -> usize { + 3 + } + + fn set_sample_rate(&mut self, srate: f32) { + self.srate = srate as f64; + if let Some(backend) = self.backend.as_mut() { + backend.set_sample_rate(srate); + } + } + + fn reset(&mut self) { + if let Some(backend) = self.backend.as_mut() { + backend.clear(); + } + } + + #[inline] + fn process( + &mut self, + ctx: &mut T, + _ectx: &mut NodeExecContext, + _nctx: &NodeContext, + atoms: &[SAtom], + inputs: &[ProcBuf], + outputs: &mut [ProcBuf], + ctx_vals: LedPhaseVals, + ) { + use crate::dsp::{at, denorm, inp, out}; +// let clock = inp::TSeq::clock(inputs); +// let trig = inp::TSeq::trig(inputs); +// let cmode = at::TSeq::cmode(atoms); + + let backend = if let Some(backend) = &mut self.backend { + backend + } else { + return; + }; + + backend.process_updates(); + + for frame in 0..ctx.nframes() { + let (s1, s2, ret) = backend.process(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + } + + ctx_vals[0].set(0.0); + ctx_vals[1].set(0.0); + } +} diff --git a/src/nodes/mod.rs b/src/nodes/mod.rs index 06e6e55..fb3f7a0 100644 --- a/src/nodes/mod.rs +++ b/src/nodes/mod.rs @@ -8,6 +8,7 @@ pub const SCOPE_SAMPLES: usize = 512; pub const MAX_INPUTS: usize = 32; pub const MAX_SMOOTHERS: usize = 36 + 4; // 6 * 6 modulator inputs + 4 UI Knobs pub const MAX_AVAIL_TRACKERS: usize = 128; +pub const MAX_AVAIL_CODE_ENGINES: usize = 32; pub const MAX_FB_DELAYS: usize = 256; // 256 feedback delays, thats roughly 1.2MB RAM pub const FB_DELAY_TIME_US: usize = 3140; // 3.14ms (should be enough for MAX_BLOCK_SIZE) // This means, until 384000 sample rate the times are accurate. diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index addc9ef..bc4b3d0 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -4,7 +4,7 @@ use super::{ FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, MAX_AVAIL_TRACKERS, - MAX_INPUTS, MAX_SCOPES, UNUSED_MONITOR_IDX, + MAX_INPUTS, MAX_SCOPES, UNUSED_MONITOR_IDX, MAX_AVAIL_CODE_ENGINES }; use crate::dsp::tracker::{PatternData, Tracker}; use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom}; @@ -182,6 +182,9 @@ pub struct NodeConfigurator { pub(crate) trackers: Vec, /// Holding the scope buffers: pub(crate) scopes: Vec>, + /// Holding the WBlockDSP code engine backends: + #[cfg(feature = "wblockdsp")] + pub(crate) code_engines: Vec, /// The shared parts of the [NodeConfigurator] /// and the [crate::nodes::NodeExecutor]. pub(crate) shared: SharedNodeConf, @@ -287,6 +290,7 @@ impl NodeConfigurator { atom_values: std::collections::HashMap::new(), node2idx: HashMap::new(), trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS], + code_engines: vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES], scopes, }, shared_exec, diff --git a/src/wblockdsp.rs b/src/wblockdsp.rs index cbb5bd9..115be8a 100644 --- a/src/wblockdsp.rs +++ b/src/wblockdsp.rs @@ -24,9 +24,13 @@ pub struct CodeEngine { dsp_ctx: Rc>, lib: Rc>, update_prod: Producer, - update_cons: Option>, return_cons: Consumer, - return_prod: Option>, +} + +impl Clone for CodeEngine { + fn clone(&self) -> Self { + CodeEngine::new() + } } impl CodeEngine { @@ -42,8 +46,6 @@ impl CodeEngine { lib, dsp_ctx: DSPNodeContext::new_ref(), update_prod, - update_cons: Some(update_cons), - return_prod: Some(return_prod), return_cons, } } @@ -75,15 +77,17 @@ impl CodeEngine { } } - pub fn get_backend(&mut self) -> Option { - if let Some(update_cons) = self.update_cons.take() { - if let Some(return_prod) = self.return_prod.take() { - let function = get_nop_function(self.lib.clone(), self.dsp_ctx.clone()); - return Some(CodeEngineBackend::new(function, update_cons, return_prod)); - } - } + pub fn get_backend(&mut self) -> CodeEngineBackend { + let rb = RingBuffer::new(MAX_RINGBUF_SIZE); + let (update_prod, update_cons) = rb.split(); + let rb = RingBuffer::new(MAX_RINGBUF_SIZE); + let (return_prod, return_cons) = rb.split(); - None + self.update_prod = update_prod; + self.return_cons = return_cons; + + let function = get_nop_function(self.lib.clone(), self.dsp_ctx.clone()); + CodeEngineBackend::new(function, update_cons, return_prod) } }