synfx_dsp_jit/
context.rs

1// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
2// This file is a part of synfx-dsp-jit. Released under GPL-3.0-or-later.
3// See README.md and COPYING for details.
4
5use crate::locked::*;
6use cranelift_jit::JITModule;
7use std::cell::RefCell;
8use std::collections::HashMap;
9use std::mem;
10use std::rc::Rc;
11use std::sync::Arc;
12use synfx_dsp::AtomicFloat;
13
14/// Default size of undeclared buffers.
15pub const BUFFER_DEFAULT_SIZE: usize = 16;
16
17/// Auxilary variables to access directly from the machine code.
18pub(crate) const AUX_VAR_COUNT: usize = 3;
19
20pub(crate) const AUX_VAR_IDX_SRATE: usize = 0;
21pub(crate) const AUX_VAR_IDX_ISRATE: usize = 1;
22pub(crate) const AUX_VAR_IDX_RESET: usize = 2;
23
24pub enum DSPNodeContextError {
25    UnknownTable(usize),
26    WrongTableSize { tbl_idx: usize, new_size: usize, old_size: usize },
27}
28
29/// Configures the environment that will be available to the [DSPFunction]
30/// that is provided by [DSPNodeContext].
31///
32/// This could for instance be the number of atoms to be used by `atomr`/`atomw`, the
33/// number and length of buffers or the audio samples...
34#[derive(Debug, Clone)]
35pub struct DSPContextConfig {
36    /// The number of atoms available to `atomr`/`atomw`.
37    pub atom_count: usize,
38    /// The number of buffers available to `bufr`/`bufw`.
39    pub buffer_count: usize,
40    /// The number of available tables for the `tblr`/`tblw` operations.
41    /// The tables can be swapped out at runtime using the [DSPNodeContext::send_table] method.
42    pub tables: Vec<Arc<Vec<f32>>>,
43}
44
45impl Default for DSPContextConfig {
46    fn default() -> Self {
47        let mut tables = vec![];
48        for _ in 0..16 {
49            tables.push(Arc::new(vec![0.0; 1]));
50        }
51        Self { atom_count: 32, buffer_count: 16, tables }
52    }
53}
54
55/// This table holds all the DSP state including the state of the individual DSP nodes
56/// that were created by the [crate::jit::DSPFunctionTranslator].
57pub struct DSPNodeContext {
58    /// The environment configuration for the [DSPFunction] to operate in.
59    pub(crate) config: DSPContextConfig,
60    /// The global DSP state that is passed to all stateful DSP nodes.
61    state: *mut DSPState,
62    /// Persistent variables:
63    persistent_var_index: usize,
64    /// An assignment of persistent variables to their index in the `persistent_vars` vector.
65    persistent_var_map: HashMap<String, usize>,
66    /// A map of unique DSP node instances (identified by dsp_node_uid) that need private state.
67    node_states: HashMap<u64, Box<DSPNodeState>>,
68    /// A generation counter to determine whether some [DSPNodeState] instances in `node_states`
69    /// can be cleaned up.
70    generation: u64,
71    /// Contains the currently compiled [DSPFunction].
72    next_dsp_fun: Option<Box<DSPFunction>>,
73    /// If enabled, some extra data will be collected.
74    debug_enabled: bool,
75    /// If [DSPNodeContext::set_debug] is enabled, this contains the most recently compiled piece
76    /// of cranelift intermedite representation. You can receive this via [DSPNodeContext::get_ir_dump].
77    pub(crate) cranelift_ir_dump: String,
78    /// An array of atomic floats to exchange data with the live real time thread.
79    /// These AtomicFloats will be shared via the [DSPState] structure and read/written using
80    /// the `atomw` and `atomr` nodes.
81    atoms: Vec<Arc<AtomicFloat>>,
82    /// Holds the current buffer lengths, they are updated
83    /// in [DSPNodeContext::finalize_dsp_function].
84    buffer_lengths: Vec<usize>,
85    /// Holds the most recently declared buffer lengths, these are used to determine
86    /// if we need to send a buffer update with the [DSPFunction]
87    /// in [DSPNodeContext::finalize_dsp_function].
88    pub(crate) buffer_declare: Vec<usize>,
89}
90
91impl DSPNodeContext {
92    fn new() -> Self {
93        Self::new_with_config(DSPContextConfig::default())
94    }
95
96    fn new_with_config(config: DSPContextConfig) -> Self {
97        let mut atoms = vec![];
98        atoms.resize_with(config.atom_count, || Arc::new(AtomicFloat::new(0.0)));
99        let atoms_state = atoms.clone();
100
101        let mut buffer_lengths = vec![];
102        let mut buffers = vec![];
103        for _ in 0..config.buffer_count {
104            buffers.push(vec![0.0; BUFFER_DEFAULT_SIZE]);
105            buffer_lengths.push(BUFFER_DEFAULT_SIZE);
106        }
107        let buffers = LockedMutPtrs::new(buffers);
108        let buffer_declare = buffer_lengths.clone();
109
110        let tables = LockedPtrs::new(config.tables.clone());
111
112        Self {
113            config,
114            state: Box::into_raw(Box::new(DSPState {
115                x: 0.0,
116                y: 0.0,
117                srate: 44100.0,
118                israte: 1.0 / 44100.0,
119                atoms: atoms_state,
120                buffers,
121                tables,
122            })),
123            node_states: HashMap::new(),
124            generation: 0,
125            next_dsp_fun: None,
126            persistent_var_map: HashMap::new(),
127            persistent_var_index: 0,
128            debug_enabled: false,
129            cranelift_ir_dump: String::from(""),
130            atoms,
131            buffer_lengths,
132            buffer_declare,
133        }
134    }
135
136    /// Creates a new [DSPNodeContext] that you can pass into [crate::JIT::new].
137    pub fn new_ref() -> Rc<RefCell<Self>> {
138        Rc::new(RefCell::new(Self::new()))
139    }
140
141    pub(crate) fn init_dsp_function(&mut self) {
142        self.generation += 1;
143        self.next_dsp_fun = Some(Box::new(DSPFunction::new(self.state, self.generation)));
144    }
145
146    /// Enabled debug information collection. See also [DSPNodeContext::get_ir_dump].
147    pub fn set_debug(&mut self, enabled: bool) {
148        self.debug_enabled = enabled;
149    }
150
151    /// Returns if debug is enabled.
152    pub fn debug_enabled(&self) -> bool {
153        self.debug_enabled
154    }
155
156    /// If [DSPNodeContext::set_debug] is enabled, this will return the most recent
157    /// IR code for the most recently compiled [DSPFunction].
158    pub fn get_ir_dump(&self) -> &str {
159        &self.cranelift_ir_dump
160    }
161
162    /// Returns you a reference to the specified atom connected with the DSP backend.
163    /// These atoms can be read and written in the [DSPFunction] using the `atomr` and `atomw`
164    /// nodes.
165    pub fn atom(&self, idx: usize) -> Option<Arc<AtomicFloat>> {
166        self.atoms.get(idx).cloned()
167    }
168
169    /// Retrieve the index into the most recently compiled [DSPFunction].
170    /// To be used by [DSPFunction::access_persistent_var].
171    pub fn get_persistent_variable_index_by_name(&self, pers_var_name: &str) -> Option<usize> {
172        self.persistent_var_map.get(pers_var_name).map(|i| *i)
173    }
174
175    /// Retrieve the index into the persistent variable vector passed in as "&pv".
176    pub(crate) fn get_persistent_variable_index(
177        &mut self,
178        pers_var_name: &str,
179    ) -> Result<usize, String> {
180        let index = if let Some(index) = self.persistent_var_map.get(pers_var_name) {
181            *index
182        } else {
183            let index = self.persistent_var_index;
184            self.persistent_var_index += 1;
185            self.persistent_var_map.insert(pers_var_name.to_string(), index);
186            index
187        };
188
189        if let Some(next_dsp_fun) = &mut self.next_dsp_fun {
190            next_dsp_fun.touch_persistent_var_index(index);
191            Ok(index)
192        } else {
193            Err("No DSPFunction in DSPNodeContext".to_string())
194        }
195    }
196
197    /// Tries to send a new table to the backend. You have to make sure the table
198    /// has exactly the same size as the previous table given in the [DSPContextConfig].
199    /// Otherwise a [DSPNodeContextError] is returned.
200    pub fn send_table(
201        &mut self,
202        tbl_idx: usize,
203        table: Arc<Vec<f64>>,
204    ) -> Result<(), DSPNodeContextError> {
205        let config_tbl_len = 0;
206
207        // Err(DSPNodeContextError::UnknwonTable(tbl_idx)
208
209        Err(DSPNodeContextError::WrongTableSize {
210            tbl_idx,
211            new_size: table.len(),
212            old_size: config_tbl_len,
213        })
214    }
215
216    /// Adds a [DSPNodeState] to the currently compiled [DSPFunction] and returns
217    /// the index into the node state vector in the [DSPFunction], so that the JIT
218    /// code can index into that vector to find the right state pointer.
219    pub(crate) fn add_dsp_node_instance(
220        &mut self,
221        node_type: Arc<dyn DSPNodeType>,
222        dsp_node_uid: u64,
223    ) -> Result<usize, String> {
224        if let Some(next_dsp_fun) = &mut self.next_dsp_fun {
225            if next_dsp_fun.has_dsp_node_state_uid(dsp_node_uid) {
226                return Err(format!(
227                    "node_state_uid has been used multiple times in same AST: {}",
228                    dsp_node_uid
229                ));
230            }
231
232            if !self.node_states.contains_key(&dsp_node_uid) {
233                self.node_states.insert(
234                    dsp_node_uid,
235                    Box::new(DSPNodeState::new(dsp_node_uid, node_type.clone())),
236                );
237            }
238
239            if let Some(state) = self.node_states.get_mut(&dsp_node_uid) {
240                if state.node_type().name() != node_type.name() {
241                    return Err(format!(
242                        "Different DSPNodeType for uid {}: {} != {}",
243                        dsp_node_uid,
244                        state.node_type().name(),
245                        node_type.name()
246                    ));
247                }
248
249                Ok(next_dsp_fun.install(state))
250            } else {
251                Err(format!("NodeState does not exist, but it should... bad! {}", dsp_node_uid))
252            }
253        } else {
254            Err("No DSPFunction in DSPNodeContext".to_string())
255        }
256    }
257
258    pub(crate) fn finalize_dsp_function(
259        &mut self,
260        function_ptr: *const u8,
261        module: JITModule,
262    ) -> Option<Box<DSPFunction>> {
263        if let Some(mut next_dsp_fun) = self.next_dsp_fun.take() {
264            for (i, (len, declare)) in
265                self.buffer_lengths.iter().zip(self.buffer_declare.iter()).enumerate()
266            {
267                if *len != *declare {
268                    next_dsp_fun.add_buffer_update(i, *declare);
269                }
270            }
271
272            for (len, declare) in self.buffer_lengths.iter_mut().zip(self.buffer_declare.iter_mut())
273            {
274                *len = *declare;
275            }
276
277            next_dsp_fun.set_function_ptr(function_ptr, module);
278
279            for (_, node_state) in self.node_states.iter_mut() {
280                node_state.set_initialized();
281            }
282
283            Some(next_dsp_fun)
284        } else {
285            None
286        }
287    }
288
289    /// If you received a [DSPFunction] back from the audio thread, you should
290    /// pass it into this function. It will make sure to purge old unused [DSPNodeState] instances.
291    pub fn cleanup_dsp_fun_after_user(&mut self, _fun: Box<DSPFunction>) {
292        // TODO: Garbage collect and free unused node state!
293        //       But this must happen by the backend/frontend thread separation.
294        //       Best would be to provide DSPNodeContext::cleaup_dsp_function_after_use(DSPFunction).
295    }
296
297    /// You must call this after all [DSPFunction] instances compiled with this state are done executing.
298    /// If you don't call this, you might get a memory leak.
299    /// The API is a bit manual at this point, because usually [DSPFunction]
300    /// will be executed on a different thread, and synchronizing this would come with
301    /// additional overhead that I wanted to save.
302    pub fn free(&mut self) {
303        if !self.state.is_null() {
304            let _ = unsafe { Box::from_raw(self.state) };
305            self.state = std::ptr::null_mut();
306        }
307    }
308}
309
310impl Drop for DSPNodeContext {
311    fn drop(&mut self) {
312        if !self.state.is_null() {
313            eprintln!("WBlockDSP JIT DSPNodeContext not cleaned up on exit. Forgot to call free() or keep it alive long enough?");
314        }
315    }
316}
317
318/// This structure holds all the [DSPNodeType] definitions and provides
319/// them to the [crate::JIT] and [crate::jit::DSPFunctionTranslator].
320pub struct DSPNodeTypeLibrary {
321    type_by_name: HashMap<String, Arc<dyn DSPNodeType>>,
322    types: Vec<Arc<dyn DSPNodeType>>,
323}
324
325impl DSPNodeTypeLibrary {
326    /// Create a new instance of this.
327    pub fn new() -> Self {
328        Self { types: vec![], type_by_name: HashMap::new() }
329    }
330
331    /// Add the given [DSPNodeType] to this library.
332    pub fn add(&mut self, typ: Arc<dyn DSPNodeType>) {
333        self.types.push(typ.clone());
334        self.type_by_name.insert(typ.name().to_string(), typ);
335    }
336
337    /// Retrieves a [DSPNodeType] by it's name.
338    pub fn get_type_by_name(&self, typ_name: &str) -> Option<Arc<dyn DSPNodeType>> {
339        self.type_by_name.get(typ_name).cloned()
340    }
341
342    /// Iterate through all types in the Library:
343    ///
344    ///```
345    /// use synfx_dsp_jit::*;
346    ///
347    /// let lib = DSPNodeTypeLibrary::new();
348    /// // ...
349    /// lib.for_each(|typ| -> Result<(), ()> {
350    ///     println!("Type available: {}", typ.name());
351    ///     Ok(())
352    /// }).expect("no error");
353    ///```
354    pub fn for_each<T, F: FnMut(&Arc<dyn DSPNodeType>) -> Result<(), T>>(
355        &self,
356        mut f: F,
357    ) -> Result<(), T> {
358        for t in self.types.iter() {
359            f(t)?;
360        }
361        Ok(())
362    }
363}
364
365/// This macro can help you defining new stateful DSP nodes.
366///
367///```
368/// use synfx_dsp_jit::*;
369///
370/// struct MyDSPNode {
371///     value: f64,
372/// }
373///
374/// impl MyDSPNode {
375///     fn reset(&mut self, _state: &mut DSPState) {
376///         *self = Self::default();
377///     }
378/// }
379///
380/// impl Default for MyDSPNode {
381///     fn default() -> Self {
382///         Self { value: 0.0 }
383///     }
384/// }
385///
386/// extern "C" fn process_my_dsp_node(my_state: *mut MyDSPNode) -> f64 {
387///     let mut my_state = unsafe { &mut *my_state };
388///     my_state.value += 1.0;
389///     my_state.value
390/// }
391///
392/// // DIYNodeType is the type that is newly defined here, that one you
393/// // pass to DSPNodeTypeLibrary::add
394/// synfx_dsp_jit::stateful_dsp_node_type! {
395///     DIYNodeType, MyDSPNode => process_my_dsp_node "my_dsp" "Sr"
396///     doc
397///     "This is a simple counter. It counts by increments of 1.0 everytime it's called."
398///     inputs
399///     outputs
400///     0 "sum"
401/// }
402///
403/// // Then use the type by adding it:
404/// fn make_library() -> DSPNodeTypeLibrary {
405///     let mut lib = DSPNodeTypeLibrary::new();
406///     lib.add(DIYNodeType::new_ref());
407///     lib
408/// }
409///```
410///
411/// You might've guessed, `process_my_dsp_node` is the function identifier in the Rust
412/// code. The `"my_dsp"` is the name you can use to refer to this in [crate::ASTNode::Call]:
413/// `ASTNode::Call("my_dsp".to_string(), 1, ...)`.
414/// **Attention:** Make sure to provide unique state IDs here!
415///
416/// The `"Sr"` is a string that specifies the signature of the function. Following characters
417/// are available:
418///
419/// - "v" - A floating point value
420/// - "D" - The global [crate::DSPState] pointer
421/// - "S" - The node specific state pointer (`MyDSPNode`)
422/// - "M" - A pointer to the multi return value array, of type `*mut [f64; 5]`. These can be accessed
423/// by the variables "%1" to "%5" after the call.
424/// - "r" - Must be specified as last one, defines that this function returns something.
425///
426#[macro_export]
427macro_rules! stateful_dsp_node_type {
428    ($node_type: ident, $struct_type: ident =>
429     $func_name: ident $jit_name: literal $signature: literal
430     doc $doc: literal
431     inputs $($idx: literal $inp: literal)*
432     outputs $($idxo: literal $out: literal)*) => {
433        struct $node_type;
434        impl $node_type {
435            fn new_ref() -> std::sync::Arc<Self> {
436                std::sync::Arc::new(Self {})
437            }
438        }
439        impl DSPNodeType for $node_type {
440            fn name(&self) -> &str {
441                $jit_name
442            }
443
444            fn function_ptr(&self) -> *const u8 {
445                $func_name as *const u8
446            }
447
448            fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
449                match $signature.chars().nth(i) {
450                    Some('v') => Some(DSPNodeSigBit::Value),
451                    Some('D') => Some(DSPNodeSigBit::DSPStatePtr),
452                    Some('S') => Some(DSPNodeSigBit::NodeStatePtr),
453                    Some('M') => Some(DSPNodeSigBit::MultReturnPtr),
454                    _ => None,
455                }
456            }
457
458            fn has_return_value(&self) -> bool {
459                $signature.find("r").is_some()
460            }
461
462            fn reset_state(&self, dsp_state: *mut DSPState, state_ptr: *mut u8) {
463                let ptr = state_ptr as *mut $struct_type;
464                unsafe {
465                    (*ptr).reset(&mut (*dsp_state));
466                }
467            }
468
469            fn allocate_state(&self) -> Option<*mut u8> {
470                Some(Box::into_raw(Box::new($struct_type::default())) as *mut u8)
471            }
472
473            fn deallocate_state(&self, ptr: *mut u8) {
474                let _ = unsafe { Box::from_raw(ptr as *mut $struct_type) };
475            }
476
477            fn documentation(&self) -> &str {
478                $doc
479            }
480
481            fn input_names(&self, index: usize) -> Option<&str> {
482                match index {
483                    $($idx => Some($inp),)*
484                    _ => None
485                }
486            }
487
488            fn input_index_by_name(&self, name: &str) -> Option<usize> {
489                match name {
490                    $($inp => Some($idx),)*
491                    _ => None
492                }
493            }
494
495            fn output_names(&self, index: usize) -> Option<&str> {
496                match index {
497                    $($idxo => Some($out),)*
498                    _ => None
499                }
500            }
501
502            fn output_index_by_name(&self, name: &str) -> Option<usize> {
503                match name {
504                    $($out => Some($idxo),)*
505                    _ => None
506                }
507            }
508        }
509    };
510}
511
512/// This macro can help you defining new stateless DSP nodes.
513///
514///```
515/// use synfx_dsp_jit::*;
516///
517/// extern "C" fn process_mul2(v: f64) -> f64 {
518///     v * 2.0
519/// }
520///
521/// synfx_dsp_jit::stateless_dsp_node_type! {
522///     Mul2NodeType => process_mul2 "mul2" "vr"
523///     doc
524///     "A simple multiplication by 2.0. Using '*' is simpler thought..."
525///     inputs
526///     0 ""
527///     outputs
528///     0 ""
529/// }
530///
531/// // Then use the type by adding it:
532/// fn make_library() -> DSPNodeTypeLibrary {
533///     let mut lib = DSPNodeTypeLibrary::new();
534///     lib.add(Mul2NodeType::new_ref());
535///     lib
536/// }
537///```
538///
539/// The `"vr"` is a string that specifies the signature of the function. Following characters
540/// are available:
541///
542/// - "v" - A floating point value
543/// - "D" - The global [crate::DSPState] pointer
544/// - "M" - A pointer to the multi return value array, of type `*mut [f64; 5]`. These can be accessed
545/// by the variables "%1" to "%5" after the call.
546/// - "r" - Must be specified as last one, defines that this function returns something.
547///
548#[macro_export]
549macro_rules! stateless_dsp_node_type {
550    ($node_type: ident =>
551     $func_name: ident $jit_name: literal $signature: literal
552     doc $doc: literal
553     inputs $($idx: literal $inp: literal)*
554     outputs $($idxo: literal $out: literal)*) => {
555        #[derive(Default)]
556        struct $node_type;
557        impl $node_type {
558            #[allow(dead_code)]
559            fn new_ref() -> std::sync::Arc<Self> {
560                std::sync::Arc::new(Self {})
561            }
562        }
563        impl DSPNodeType for $node_type {
564            fn name(&self) -> &str {
565                $jit_name
566            }
567
568            fn function_ptr(&self) -> *const u8 {
569                $func_name as *const u8
570            }
571
572            fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
573                match $signature.chars().nth(i) {
574                    Some('v') => Some(DSPNodeSigBit::Value),
575                    Some('D') => Some(DSPNodeSigBit::DSPStatePtr),
576                    Some('M') => Some(DSPNodeSigBit::MultReturnPtr),
577                    _ => None,
578                }
579            }
580
581            fn has_return_value(&self) -> bool {
582                $signature.find("r").is_some()
583            }
584
585            fn documentation(&self) -> &str {
586                $doc
587            }
588
589            fn input_names(&self, index: usize) -> Option<&str> {
590                match index {
591                    $($idx => Some($inp),)*
592                    _ => None
593                }
594            }
595
596            fn input_index_by_name(&self, name: &str) -> Option<usize> {
597                match name {
598                    $($inp => Some($idx),)*
599                    _ => None
600                }
601            }
602
603            fn output_names(&self, index: usize) -> Option<&str> {
604                match index {
605                    $($idxo => Some($out),)*
606                    _ => None
607                }
608            }
609
610            fn output_index_by_name(&self, name: &str) -> Option<usize> {
611                match name {
612                    $($out => Some($idxo),)*
613                    _ => None
614                }
615            }
616        }
617    };
618}
619
620/// This is the result of the JIT compiled [crate::ast::ASTNode] tree.
621/// You can send this structure to the audio backend thread and execute it
622/// using [DSPFunction::exec].
623///
624/// To execute this [DSPFunction] properly, you have to call [DSPFunction::init]
625/// once the newly allocated structure is received by the DSP executing thread.
626///
627/// If the sample rate changes or the stateful DSP stuff must be resetted,
628/// you should call [DSPFunction::reset] or [DSPFunction::set_sample_rate].
629/// Of course also only on the DSP executing thread.
630pub struct DSPFunction {
631    state: *mut DSPState,
632    /// Contains the types of the corresponding `node_states`. The [DSPNodeType] is
633    /// necessary to reset the state pointed to by the pointers in `node_states`.
634    node_state_types: Vec<Arc<dyn DSPNodeType>>,
635    /// Contains the actual pointers to the state that was constructed by the corresponding [DSPNodeState].
636    node_states: Vec<*mut u8>,
637    /// Constains indices into `node_states`, so that they can be reset/initialized by [DSPFunction::init].
638    /// Only contains recently added (as determined by [DSPNodeContext]) and uninitialized state indices.
639    node_state_init_reset: Vec<usize>,
640    /// Keeps the node_state_uid of the [DSPNodeState] pieces used already in this
641    /// function. It's for error detection when building this [DSPFunction], to prevent
642    /// the user from evaluating a stateful DSP node multiple times.
643    node_state_uids: Vec<u64>,
644    /// Generation of the corresponding [DSPNodeContext].
645    dsp_ctx_generation: u64,
646    /// The JITModule that is the home for the `function` pointer. It must be kept alive
647    /// as long as the `function` pointer is in use.
648    module: Option<JITModule>,
649    /// Storage of persistent variables:
650    persistent_vars: Vec<f64>,
651    /// Buffer updates for the buffers in [DSPState], these are determined and set
652    /// in [DSPNodeContext::finalize_dsp_function].
653    buffer_updates: Option<Vec<(usize, Vec<f64>)>>,
654    /// This is just a flag as precaution, in case init() is accidentally called
655    /// multiple times.
656    buffer_updates_done: bool,
657    /// Auxilary variables to access directly from the machine code. Holds information such as
658    /// the sample rate or the inverse of the sample rate.
659    aux_vars: [f64; AUX_VAR_COUNT],
660    /// Is true directly after reset.
661    resetted: bool,
662    function: Option<
663        fn(
664            f64,
665            f64,
666            f64,
667            f64,
668            f64,
669            f64,
670            *mut f64,
671            *mut f64,
672            *mut f64,
673            *mut DSPState,
674            *const *mut u8,
675            *mut f64,
676            *mut f64,
677            *const *mut f64,
678            *const u64,
679            *const *const f32,
680            *const u64,
681        ) -> f64,
682    >,
683}
684
685unsafe impl Send for DSPFunction {}
686unsafe impl Sync for DSPFunction {}
687
688impl DSPFunction {
689    /// Used by [DSPNodeContext] to create a new instance of this.
690    pub(crate) fn new(state: *mut DSPState, dsp_ctx_generation: u64) -> Self {
691        Self {
692            state,
693            node_state_types: vec![],
694            node_states: vec![],
695            node_state_init_reset: vec![],
696            node_state_uids: vec![],
697            persistent_vars: vec![],
698            aux_vars: [0.0; AUX_VAR_COUNT],
699            function: None,
700            dsp_ctx_generation,
701            module: None,
702            resetted: false,
703            buffer_updates: Some(vec![]),
704            buffer_updates_done: true,
705        }
706    }
707
708    /// At the end of the compilation the [crate::JIT] will put the resulting function
709    /// pointer into this function.
710    pub(crate) fn set_function_ptr(&mut self, function: *const u8, module: JITModule) {
711        self.module = Some(module);
712        self.function = Some(unsafe {
713            mem::transmute::<
714                _,
715                fn(
716                    f64,
717                    f64,
718                    f64,
719                    f64,
720                    f64,
721                    f64,
722                    *mut f64,
723                    *mut f64,
724                    *mut f64,
725                    *mut DSPState,
726                    *const *mut u8,
727                    *mut f64,
728                    *mut f64,
729                    *const *mut f64,
730                    *const u64,
731                    *const *const f32,
732                    *const u64,
733                ) -> f64,
734            >(function)
735        });
736    }
737
738    /// Appends a buffer update to this [DSPFunction], to update the buffers
739    /// according to [crate::ast::ASTNode::BufDeclare]. Buffers are only updated
740    /// if they get a new length though.
741    pub(crate) fn add_buffer_update(&mut self, buf_idx: usize, length: usize) {
742        if let Some(updates) = &mut self.buffer_updates {
743            updates.push((buf_idx, vec![0.0; length]));
744        }
745        self.buffer_updates_done = false;
746    }
747
748    /// This function must be called before [DSPFunction::exec]!
749    /// otherwise your states might not be properly initialized or preserved.
750    ///
751    /// If you recompiled a function, pass the old one on the audio thread to
752    /// the `previous_function` parameter here. It will take care of preserving
753    /// state, such as persistent variables (those that start with "*": `crate::build::var("*abc")`).
754    pub fn init(&mut self, srate: f64, previous_function: Option<&DSPFunction>) {
755        if let Some(previous_function) = previous_function {
756            let prev_len = previous_function.persistent_vars.len();
757            let now_len = self.persistent_vars.len();
758            let len = prev_len.min(now_len);
759            self.persistent_vars[0..len].copy_from_slice(&previous_function.persistent_vars[0..len])
760        } else {
761            self.resetted = true;
762        }
763
764        if !self.buffer_updates_done {
765            if let Some(mut updates) = self.buffer_updates.take() {
766                for (idx, new_vec) in updates.iter_mut() {
767                    let _ = self.swap_buffer(*idx, new_vec, true);
768                }
769                self.buffer_updates = Some(updates);
770            }
771            self.buffer_updates_done = true;
772        }
773
774        unsafe {
775            (*self.state).srate = srate;
776            (*self.state).israte = 1.0 / srate;
777        }
778        self.aux_vars[AUX_VAR_IDX_SRATE] = srate;
779        self.aux_vars[AUX_VAR_IDX_ISRATE] = 1.0 / srate;
780
781        for idx in self.node_state_init_reset.iter() {
782            let typ = &self.node_state_types[*idx as usize];
783            let ptr = self.node_states[*idx as usize];
784            typ.reset_state(self.state, ptr);
785        }
786    }
787
788    /// Swaps out the buffer at the given index with the new buffer. The contents
789    /// of the Vec will be swapped with the current contents of the buffer, unless
790    /// you specify `preserve_old_samples` which will try to preserve as many samples
791    /// from the previous buffer as possible.
792    pub fn swap_buffer(
793        &mut self,
794        index: usize,
795        new_buf: &mut Vec<f64>,
796        preserve_old_samples: bool,
797    ) -> Result<(), ()> {
798        unsafe {
799            if index >= (*self.state).buffers.len() {
800                return Err(());
801            }
802            if preserve_old_samples {
803                let old_len = (*self.state).buffers.element_len(index);
804                let old_vec = (*self.state).buffers.pointers()[index];
805                let min_len = old_len.min(new_buf.len());
806                std::ptr::copy_nonoverlapping(old_vec, new_buf.as_mut_ptr(), min_len);
807            }
808            let _ = (*self.state).buffers.swap_element(index, new_buf);
809        }
810        Ok(())
811    }
812
813    /// Swaps out the table at the given index with the new table.
814    pub fn swap_table(
815        &mut self,
816        index: usize,
817        new_table: &mut Arc<Vec<f32>>,
818    ) -> Result<(), ()> {
819        unsafe {
820            if index >= (*self.state).tables.len() {
821                return Err(());
822            }
823            let _ = (*self.state).tables.swap_element(index, new_table);
824        }
825        Ok(())
826    }
827
828    /// If the audio thread changes the sampling rate, call this function, it will update
829    /// the [DSPState] and reset all [DSPNodeState]s.
830    pub fn set_sample_rate(&mut self, srate: f64) {
831        unsafe {
832            (*self.state).srate = srate;
833            (*self.state).israte = 1.0 / srate;
834        }
835        self.aux_vars[AUX_VAR_IDX_SRATE] = srate;
836        self.aux_vars[AUX_VAR_IDX_ISRATE] = 1.0 / srate;
837
838        self.reset();
839    }
840
841    /// If the DSP state needs to be resetted, call this on the audio thread.
842    pub fn reset(&mut self) {
843        self.resetted = true;
844        for (typ, ptr) in self.node_state_types.iter().zip(self.node_states.iter_mut()) {
845            typ.reset_state(self.state, *ptr);
846        }
847        self.persistent_vars.fill(0.0);
848    }
849
850    /// Use this to retrieve a pointer to the [DSPState] to access it between
851    /// calls to [DSPFunction::exec].
852    pub fn get_dsp_state_ptr(&self) -> *mut DSPState {
853        self.state
854    }
855
856    /// Use this to access the [DSPState] pointer between calls to [DSPFunction::exec].
857    ///
858    /// # Safety
859    ///
860    /// You must not create multiple aliasing references from that DSP state!
861    pub unsafe fn with_dsp_state<R, F: FnMut(*mut DSPState) -> R>(&mut self, mut f: F) -> R {
862        f(self.get_dsp_state_ptr())
863    }
864
865    /// Use this to access the state of a specific DSP node state pointer between
866    /// calls to [DSPFunction::exec].
867    ///
868    /// The `node_state_uid` and the type you pass here must match! It's your responsibility
869    /// to make sure this works!
870    ///
871    /// # Safety
872    ///
873    /// You absolutely must know which ID has which [DSPNodeType], otherwise this will badly go wrong!
874    ///
875    ///```
876    /// use synfx_dsp_jit::*;
877    /// use synfx_dsp_jit::build::*;
878    /// use synfx_dsp_jit::stdlib::AccumNodeState;
879    ///
880    /// let (ctx, mut fun) = instant_compile_ast(call("accum", 21, &[var("in1"), literal(0.0)])).unwrap();
881    ///
882    /// fun.init(44100.0, None);
883    /// // Accumulate 42.0 here:
884    /// fun.exec_2in_2out(21.0, 0.0);
885    /// fun.exec_2in_2out(21.0, 0.0);
886    ///
887    /// unsafe {
888    ///     // Check 42.0 and set 99.0
889    ///     fun.with_node_state(21, |state: *mut AccumNodeState| {
890    ///         assert!(((*state).value - 42.0).abs() < 0.0001);
891    ///         (*state).value = 99.0;
892    ///     })
893    /// };
894    ///
895    /// // Accumulate up to 100.0 here:
896    /// let (_, _, ret) = fun.exec_2in_2out(1.0, 0.0);
897    /// assert!((ret - 100.0).abs() < 0.0001);
898    ///
899    /// ctx.borrow_mut().free();
900    ///```
901    #[allow(clippy::result_unit_err)]
902    pub unsafe fn with_node_state<T, R, F: FnMut(*mut T) -> R>(
903        &mut self,
904        node_state_uid: u64,
905        mut f: F,
906    ) -> Result<R, ()> {
907        if let Some(state_ptr) = self.get_node_state_ptr(node_state_uid) {
908            Ok(f(state_ptr as *mut T))
909        } else {
910            Err(())
911        }
912    }
913
914    /// Retrieves the DSP node state pointer for a certain unique node state id.
915    ///
916    /// # Safety
917    ///
918    /// You are responsible afterwards for knowing what type the actual pointer is of.
919    pub fn get_node_state_ptr(&self, node_state_uid: u64) -> Option<*mut u8> {
920        for (i, uid) in self.node_state_uids.iter().enumerate() {
921            if *uid == node_state_uid {
922                return Some(self.node_states[i]);
923            }
924        }
925
926        None
927    }
928
929    /// Helper function, it lets you specify only the contents of the parameters
930    /// `"in1"` and `"in2"`. It also returns you the values for `"&sig1"` and `"&sig2"`
931    /// after execution. The third value is the return value of the compiled expression.
932    pub fn exec_2in_2out(&mut self, in1: f64, in2: f64) -> (f64, f64, f64) {
933        let mut s1 = 0.0;
934        let mut s2 = 0.0;
935        let r = self.exec(in1, in2, 0.0, 0.0, 0.0, 0.0, &mut s1, &mut s2);
936        (s1, s2, r)
937    }
938
939    /// Executes the machine code and provides the following parameters in order:
940    /// `"in1", "in2", "alpha", "beta", "delta", "gamma", "&sig1", "&sig2"`
941    ///
942    /// It returns the return value of the computation. For addition outputs you can
943    /// write to `"&sig1"` or `"&sig2"` with for instance: `assign(var("&sig1"), literal(10.0))`.
944    #[allow(clippy::too_many_arguments)]
945    pub fn exec(
946        &mut self,
947        in1: f64,
948        in2: f64,
949        alpha: f64,
950        beta: f64,
951        delta: f64,
952        gamma: f64,
953        sig1: &mut f64,
954        sig2: &mut f64,
955    ) -> f64 {
956        {
957            self.aux_vars[AUX_VAR_IDX_RESET] = if self.resetted {
958                self.resetted = false;
959                1.0
960            } else {
961                0.0
962            };
963        }
964        let states_ptr: *const *mut u8 = self.node_states.as_mut_ptr();
965        let pers_vars_ptr: *mut f64 = self.persistent_vars.as_mut_ptr();
966        let aux_vars: *mut f64 = self.aux_vars.as_mut_ptr();
967        let bufs: *const *mut f64 = unsafe { (*self.state).buffers.pointers().as_ptr() };
968        let buf_lens: *const u64 = unsafe { (*self.state).buffers.lens().as_ptr() };
969        let tables: *const *const f32 = unsafe { (*self.state).tables.pointers().as_ptr() };
970        let table_lens: *const u64 = unsafe { (*self.state).tables.lens().as_ptr() };
971        let mut multi_returns = [0.0; 5];
972
973        (unsafe { self.function.unwrap_unchecked() })(
974            in1,
975            in2,
976            alpha,
977            beta,
978            delta,
979            gamma,
980            sig1,
981            sig2,
982            aux_vars,
983            self.state,
984            states_ptr,
985            pers_vars_ptr,
986            (&mut multi_returns) as *mut f64,
987            bufs,
988            buf_lens,
989            tables,
990            table_lens,
991        )
992    }
993
994    pub(crate) fn install(&mut self, node_state: &mut DSPNodeState) -> usize {
995        let idx = self.node_states.len();
996        node_state.mark(self.dsp_ctx_generation, idx);
997
998        self.node_states.push(node_state.ptr());
999        self.node_state_types.push(node_state.node_type());
1000        self.node_state_uids.push(node_state.uid());
1001
1002        if !node_state.is_initialized() {
1003            self.node_state_init_reset.push(idx);
1004        }
1005
1006        idx
1007    }
1008
1009    pub(crate) fn touch_persistent_var_index(&mut self, idx: usize) {
1010        if idx >= self.persistent_vars.len() {
1011            self.persistent_vars.resize(idx + 1, 0.0);
1012        }
1013    }
1014
1015    /// Gives you access to the persistent variables. To get the index of the
1016    /// persistent variable you must use [DSPNodeContext::get_persistent_variable_index_by_name].
1017    pub fn access_persistent_var(&mut self, idx: usize) -> Option<&mut f64> {
1018        self.persistent_vars.get_mut(idx)
1019    }
1020
1021    /// Checks if the DSP function actually has the state for a certain unique DSP node state ID.
1022    pub fn has_dsp_node_state_uid(&self, uid: u64) -> bool {
1023        for i in self.node_state_uids.iter() {
1024            if *i == uid {
1025                return true;
1026            }
1027        }
1028
1029        false
1030    }
1031}
1032
1033impl Drop for DSPFunction {
1034    fn drop(&mut self) {
1035        unsafe {
1036            if let Some(module) = self.module.take() {
1037                module.free_memory();
1038            }
1039        };
1040    }
1041}
1042
1043/// The global DSP state that all stateful [DSPNodeType] DSP nodes share.
1044pub struct DSPState {
1045    pub x: f64,
1046    pub y: f64,
1047    pub srate: f64,
1048    pub israte: f64,
1049    pub atoms: Vec<Arc<AtomicFloat>>,
1050    pub buffers: LockedMutPtrs<Vec<f64>, f64>,
1051    pub tables: LockedPtrs<Arc<Vec<f32>>, f32>,
1052}
1053
1054/// An enum to specify the position of value and [DSPState] and [DSPNodeState] parameters
1055/// for the JIT compiler.
1056#[derive(Debug, Clone, Copy, PartialEq)]
1057pub enum DSPNodeSigBit {
1058    /// Signature placeholder for f64
1059    Value,
1060    /// Signature placeholder for the [DSPState] pointer
1061    DSPStatePtr,
1062    /// Signature placeholder for the [DSPNodeState] pointer that belongs to this node
1063    NodeStatePtr,
1064    /// Signature placeholder for a pointer to the multi return value array (max size is 5! `*mut [f64; 5]`)
1065    MultReturnPtr,
1066}
1067
1068/// This trait allows you to define your own DSP stateful and stateless primitives.
1069/// Among defining a few important properties for the compiler, it handles allocation and
1070/// deallocation of the state that belongs to a DSPNodeType.
1071///
1072/// ## Stateless DSP Nodes/Primitives
1073///
1074/// Here is a simple example how to define a stateless DSP function:
1075///
1076///```
1077/// use std::rc::Rc;
1078/// use std::cell::RefCell;
1079/// use synfx_dsp_jit::{DSPNodeType, DSPNodeSigBit, DSPNodeTypeLibrary};
1080///
1081/// let lib = Rc::new(RefCell::new(DSPNodeTypeLibrary::new()));
1082///
1083/// pub struct MyPrimitive;
1084///
1085/// extern "C" fn my_primitive_function(a: f64, b: f64) -> f64 {
1086///    (2.0 * a * b.cos()).sin()
1087/// }
1088///
1089/// impl DSPNodeType for MyPrimitive {
1090///     // make a name, so you can refer to it via `ASTNode::Call("my_prim", ...)`.
1091///     fn name(&self) -> &str { "my_prim" }
1092///
1093///     // Provide a pointer:
1094///     fn function_ptr(&self) -> *const u8 { my_primitive_function as *const u8 }
1095///
1096///     // Define the function signature for the JIT compiler:
1097///     fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
1098///         match i {
1099///             0 | 1 => Some(DSPNodeSigBit::Value),
1100///             _ => None, // Return None to signal we only take 2 parameters
1101///         }
1102///     }
1103///
1104///     // Tell the JIT compiler that you return a value:
1105///     fn has_return_value(&self) -> bool { true }
1106///
1107///     // The other trait functions do not need to be provided, because this is
1108///     // a stateless primitive.
1109/// }
1110///
1111/// lib.borrow_mut().add(std::sync::Arc::new(MyPrimitive {}));
1112///
1113/// use synfx_dsp_jit::{ASTFun, JIT, DSPNodeContext};
1114/// let ctx = DSPNodeContext::new_ref();
1115/// let jit = JIT::new(lib.clone(), ctx.clone());
1116///
1117/// use synfx_dsp_jit::build::*;
1118/// let mut fun = jit.compile(ASTFun::new(
1119///     op_add(call("my_prim", 0, &[var("in1"), var("in2")]), literal(10.0))))
1120///     .expect("no compile error");
1121///
1122/// fun.init(44100.0, None);
1123///
1124/// let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 1.5);
1125///
1126/// assert!((ret - 10.1410029).abs() < 0.000001);
1127///
1128/// ctx.borrow_mut().free();
1129///```
1130///
1131/// ## Stateful DSP Nodes/Primitives
1132///
1133/// Here is a simple example how to define a stateful DSP function,
1134/// in this example just an accumulator.
1135///
1136/// There is a little helper macro that might help you: [crate::stateful_dsp_node_type]
1137///
1138///```
1139/// use std::rc::Rc;
1140/// use std::cell::RefCell;
1141/// use synfx_dsp_jit::{DSPNodeType, DSPState, DSPNodeSigBit, DSPNodeTypeLibrary};
1142///
1143/// let lib = Rc::new(RefCell::new(DSPNodeTypeLibrary::new()));
1144///
1145/// pub struct MyPrimitive;
1146///
1147/// struct MyPrimAccumulator {
1148///     count: f64,
1149/// }
1150///
1151/// // Be careful defining the signature of this primitive, there is no safety net here!
1152/// // Check twice with DSPNodeType::signature()!
1153/// extern "C" fn my_primitive_accum(add: f64, state: *mut u8) -> f64 {
1154///     let state = unsafe { &mut *(state as *mut MyPrimAccumulator) };
1155///     state.count += add;
1156///     state.count
1157/// }
1158///
1159/// impl DSPNodeType for MyPrimitive {
1160///     // make a name, so you can refer to it via `ASTNode::Call("my_prim", ...)`.
1161///     fn name(&self) -> &str { "accum" }
1162///
1163///     // Provide a pointer:
1164///     fn function_ptr(&self) -> *const u8 { my_primitive_accum as *const u8 }
1165///
1166///     // Define the function signature for the JIT compiler. Be really careful though,
1167///     // There is no safety net here.
1168///     fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
1169///         match i {
1170///             0 => Some(DSPNodeSigBit::Value),
1171///             1 => Some(DSPNodeSigBit::NodeStatePtr),
1172///             _ => None, // Return None to signal we only take 1 parameter
1173///         }
1174///     }
1175///
1176///     // Tell the JIT compiler that you return a value:
1177///     fn has_return_value(&self) -> bool { true }
1178///
1179///     // Specify how to reset the state:
1180///     fn reset_state(&self, _dsp_state: *mut DSPState, state_ptr: *mut u8) {
1181///         unsafe { (*(state_ptr as *mut MyPrimAccumulator)).count = 0.0 };
1182///     }
1183///
1184///     // Allocate our state:
1185///     fn allocate_state(&self) -> Option<*mut u8> {
1186///         Some(Box::into_raw(Box::new(MyPrimAccumulator { count: 0.0 })) as *mut u8)
1187///     }
1188///
1189///     // Deallocate our state:
1190///     fn deallocate_state(&self, ptr: *mut u8) {
1191///         let _ = unsafe { Box::from_raw(ptr as *mut MyPrimAccumulator) };
1192///     }
1193/// }
1194///
1195/// lib.borrow_mut().add(std::sync::Arc::new(MyPrimitive {}));
1196///
1197/// use synfx_dsp_jit::{ASTFun, JIT, DSPNodeContext};
1198/// let ctx = DSPNodeContext::new_ref();
1199/// let jit = JIT::new(lib.clone(), ctx.clone());
1200///
1201/// use synfx_dsp_jit::build::*;
1202/// let mut fun =
1203///     jit.compile(ASTFun::new(call("accum", 0, &[var("in1")]))).expect("no compile error");
1204///
1205/// fun.init(44100.0, None);
1206///
1207/// let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
1208/// assert!((ret - 1.0).abs() < 0.000001);
1209///
1210/// let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
1211/// assert!((ret - 2.0).abs() < 0.000001);
1212///
1213/// let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
1214/// assert!((ret - 3.0).abs() < 0.000001);
1215///
1216/// // You can cause a reset eg. with fun.set_sample_rate() or fun.reset():
1217/// fun.reset();
1218///
1219/// // Counting will restart:
1220/// let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
1221/// assert!((ret - 1.0).abs() < 0.000001);
1222///
1223/// ctx.borrow_mut().free();
1224///```
1225pub trait DSPNodeType: Sync + Send {
1226    /// The name of this DSP node, by this name it can be called from
1227    /// the [crate::ast::ASTFun].
1228    fn name(&self) -> &str;
1229
1230    /// Document what this node does and how to use it.
1231    /// Format should be in Markdown.
1232    ///
1233    /// Documenting the node will make it easier for library implementors
1234    /// and even eventual end users to figure out what this node
1235    /// does and how to use it.
1236    ///
1237    /// For instance, this text should define what the input and output
1238    /// parameters do. And also define which value ranges these operate in.
1239    fn documentation(&self) -> &str {
1240        "undocumented"
1241    }
1242
1243    /// Returns the name of each input port of this node.
1244    /// Choose descriptive but short names.
1245    /// These names will be used by compiler frontends to identify the ports,
1246    /// and it will make it easier to stay compatible if indices change.
1247    fn input_names(&self, _index: usize) -> Option<&str> {
1248        None
1249    }
1250
1251    /// Returns the name of each output port of this node.
1252    /// Choose descriptive but short names.
1253    /// These names will be used by compiler frontends to identify the ports,
1254    /// and it will make it easier to stay compatible if indices change.
1255    fn output_names(&self, _index: usize) -> Option<&str> {
1256        None
1257    }
1258
1259    /// Returns the index of the output by it's name.
1260    fn input_index_by_name(&self, name: &str) -> Option<usize> {
1261        let mut i = 0;
1262
1263        while let Some(iname) = self.input_names(i) {
1264            if iname == name {
1265                return Some(i);
1266            }
1267            i += 1;
1268        }
1269
1270        None
1271    }
1272
1273    /// Returns the index of the output by it's name.
1274    fn output_index_by_name(&self, name: &str) -> Option<usize> {
1275        let mut i = 0;
1276
1277        while let Some(oname) = self.output_names(i) {
1278            if oname == name {
1279                return Some(i);
1280            }
1281            i += 1;
1282        }
1283
1284        None
1285    }
1286
1287    /// Number of input ports
1288    fn input_count(&self) -> usize {
1289        let mut i = 0;
1290        while self.input_names(i).is_some() {
1291            i += 1;
1292        }
1293        i
1294    }
1295
1296    /// Number of output ports
1297    fn output_count(&self) -> usize {
1298        let mut i = 0;
1299        while self.output_names(i).is_some() {
1300            i += 1;
1301        }
1302        i
1303    }
1304
1305    /// Returns true if this node type requires state.
1306    fn is_stateful(&self) -> bool {
1307        let mut i = 0;
1308        while let Some(sig) = self.signature(i) {
1309            if let DSPNodeSigBit::NodeStatePtr = sig {
1310                return true;
1311            }
1312
1313            i += 1;
1314        }
1315
1316        false
1317    }
1318
1319    /// The function pointer that should be inserted.
1320    fn function_ptr(&self) -> *const u8;
1321
1322    /// Should return the signature type for input parameter `i`.
1323    fn signature(&self, _i: usize) -> Option<DSPNodeSigBit> {
1324        None
1325    }
1326
1327    /// Should return true if the function for [DSPNodeType::function_ptr]
1328    /// returns something.
1329    fn has_return_value(&self) -> bool;
1330
1331    /// Will be called when the node state should be resetted.
1332    /// This should be used to store the sample rate for instance or
1333    /// do other sample rate dependent recomputations.
1334    /// Also things delay lines should zero their buffers.
1335    fn reset_state(&self, _dsp_state: *mut DSPState, _state_ptr: *mut u8) {}
1336
1337    /// Allocates a new piece of state for this [DSPNodeType].
1338    /// Must be deallocated using [DSPNodeType::deallocate_state].
1339    fn allocate_state(&self) -> Option<*mut u8> {
1340        None
1341    }
1342
1343    /// Deallocates the private state of this [DSPNodeType].
1344    fn deallocate_state(&self, _ptr: *mut u8) {}
1345}
1346
1347/// A handle to manage the state of a DSP node
1348/// that was created while the [crate::jit::DSPFunctionTranslator] compiled the given AST
1349/// to machine code. The AST needs to take care to refer to the same piece
1350/// of state with the same type across different compilations of the AST with the
1351/// same [DSPNodeContext].
1352///
1353/// It holds a pointer to the state of a single DSP node. The internal state
1354/// pointer will be shared with the execution thread that will execute the
1355/// complete DSP function/graph.
1356///
1357/// You will not have to allocate and manage this manually, see also [DSPFunction].
1358pub(crate) struct DSPNodeState {
1359    /// The node_state_uid that identifies this piece of state uniquely across multiple
1360    /// ASTs.
1361    uid: u64,
1362    /// Holds the type of this piece of state.
1363    node_type: Arc<dyn DSPNodeType>,
1364    /// A pointer to the allocated piece of state. It will be shared
1365    /// with the execution thread. So you must not touch the data that is referenced
1366    /// here.
1367    ptr: *mut u8,
1368    /// A generation counter that is used by [DSPNodeContext] to determine
1369    /// if a piece of state is not used anymore.
1370    generation: u64,
1371    /// The current index into the most recent [DSPFunction] that was
1372    /// constructed by [DSPNodeContext].
1373    function_index: usize,
1374    /// A flag that stores if this DSPNodeState instance was already initialized.
1375    /// It is set by [DSPNodeContext] if a finished [DSPFunction] was successfully compiled.
1376    initialized: bool,
1377}
1378
1379impl DSPNodeState {
1380    /// Creates a fresh piece of DSP node state.
1381    pub(crate) fn new(uid: u64, node_type: Arc<dyn DSPNodeType>) -> Self {
1382        Self {
1383            uid,
1384            node_type: node_type.clone(),
1385            ptr: node_type.allocate_state().expect("DSPNodeState created for stateful node type"),
1386            generation: 0,
1387            function_index: 0,
1388            initialized: false,
1389        }
1390    }
1391
1392    /// Returns the unique ID of this piece of DSP node state.
1393    pub(crate) fn uid(&self) -> u64 {
1394        self.uid
1395    }
1396
1397    /// Marks this piece of DSP state as used and deposits the
1398    /// index into the current [DSPFunction].
1399    pub(crate) fn mark(&mut self, gen: u64, index: usize) {
1400        self.generation = gen;
1401        self.function_index = index;
1402    }
1403
1404    /// Checks if the [DSPNodeState] was initialized by the most recently compiled [DSPFunction]
1405    pub(crate) fn is_initialized(&self) -> bool {
1406        self.initialized
1407    }
1408
1409    /// Sets that the [DSPNodeState] is initialized.
1410    ///
1411    /// This happens once the [DSPNodeContext] finished compiling a [DSPFunction].
1412    /// The user of the [DSPNodeContext] or rather the [crate::JIT] needs to make sure to
1413    /// actually really call [DSPFunction::init] of course. Otherwise this state tracking
1414    /// all falls apart. But this happens across different threads, so the synchronizing effort
1415    /// for this is not worth it (regarding development time) at the moment I think.
1416    pub(crate) fn set_initialized(&mut self) {
1417        self.initialized = true;
1418    }
1419
1420    /// Returns the state pointer for this DSPNodeState instance.
1421    /// Primarily used by [DSPFunction::install].
1422    pub(crate) fn ptr(&self) -> *mut u8 {
1423        self.ptr
1424    }
1425
1426    /// Returns the [DSPNodeType] for this [DSPNodeState].
1427    pub(crate) fn node_type(&self) -> Arc<dyn DSPNodeType> {
1428        self.node_type.clone()
1429    }
1430}
1431
1432impl Drop for DSPNodeState {
1433    /// This should only be dropped when the [DSPNodeContext] determined
1434    /// that the pointer that was shared with the execution thread is no longer
1435    /// in use.
1436    fn drop(&mut self) {
1437        self.node_type.deallocate_state(self.ptr);
1438        self.ptr = std::ptr::null_mut();
1439    }
1440}