pub trait DSPNodeType: Sync + Send {
Show 15 methods // Required methods fn name(&self) -> &str; fn function_ptr(&self) -> *const u8; fn has_return_value(&self) -> bool; // Provided methods fn documentation(&self) -> &str { ... } fn input_names(&self, _index: usize) -> Option<&str> { ... } fn output_names(&self, _index: usize) -> Option<&str> { ... } fn input_index_by_name(&self, name: &str) -> Option<usize> { ... } fn output_index_by_name(&self, name: &str) -> Option<usize> { ... } fn input_count(&self) -> usize { ... } fn output_count(&self) -> usize { ... } fn is_stateful(&self) -> bool { ... } fn signature(&self, _i: usize) -> Option<DSPNodeSigBit> { ... } fn reset_state(&self, _dsp_state: *mut DSPState, _state_ptr: *mut u8) { ... } fn allocate_state(&self) -> Option<*mut u8> { ... } fn deallocate_state(&self, _ptr: *mut u8) { ... }
}
Expand description

This trait allows you to define your own DSP stateful and stateless primitives. Among defining a few important properties for the compiler, it handles allocation and deallocation of the state that belongs to a DSPNodeType.

Stateless DSP Nodes/Primitives

Here is a simple example how to define a stateless DSP function:

 use std::rc::Rc;
 use std::cell::RefCell;
 use synfx_dsp_jit::{DSPNodeType, DSPNodeSigBit, DSPNodeTypeLibrary};

 let lib = Rc::new(RefCell::new(DSPNodeTypeLibrary::new()));

 pub struct MyPrimitive;

 extern "C" fn my_primitive_function(a: f64, b: f64) -> f64 {
    (2.0 * a * b.cos()).sin()
 }

 impl DSPNodeType for MyPrimitive {
     // make a name, so you can refer to it via `ASTNode::Call("my_prim", ...)`.
     fn name(&self) -> &str { "my_prim" }

     // Provide a pointer:
     fn function_ptr(&self) -> *const u8 { my_primitive_function as *const u8 }

     // Define the function signature for the JIT compiler:
     fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
         match i {
             0 | 1 => Some(DSPNodeSigBit::Value),
             _ => None, // Return None to signal we only take 2 parameters
         }
     }

     // Tell the JIT compiler that you return a value:
     fn has_return_value(&self) -> bool { true }

     // The other trait functions do not need to be provided, because this is
     // a stateless primitive.
 }

 lib.borrow_mut().add(std::sync::Arc::new(MyPrimitive {}));

 use synfx_dsp_jit::{ASTFun, JIT, DSPNodeContext};
 let ctx = DSPNodeContext::new_ref();
 let jit = JIT::new(lib.clone(), ctx.clone());

 use synfx_dsp_jit::build::*;
 let mut fun = jit.compile(ASTFun::new(
     op_add(call("my_prim", 0, &[var("in1"), var("in2")]), literal(10.0))))
     .expect("no compile error");

 fun.init(44100.0, None);

 let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 1.5);

 assert!((ret - 10.1410029).abs() < 0.000001);

 ctx.borrow_mut().free();

Stateful DSP Nodes/Primitives

Here is a simple example how to define a stateful DSP function, in this example just an accumulator.

There is a little helper macro that might help you: crate::stateful_dsp_node_type

 use std::rc::Rc;
 use std::cell::RefCell;
 use synfx_dsp_jit::{DSPNodeType, DSPState, DSPNodeSigBit, DSPNodeTypeLibrary};

 let lib = Rc::new(RefCell::new(DSPNodeTypeLibrary::new()));

 pub struct MyPrimitive;

 struct MyPrimAccumulator {
     count: f64,
 }

 // Be careful defining the signature of this primitive, there is no safety net here!
 // Check twice with DSPNodeType::signature()!
 extern "C" fn my_primitive_accum(add: f64, state: *mut u8) -> f64 {
     let state = unsafe { &mut *(state as *mut MyPrimAccumulator) };
     state.count += add;
     state.count
 }

 impl DSPNodeType for MyPrimitive {
     // make a name, so you can refer to it via `ASTNode::Call("my_prim", ...)`.
     fn name(&self) -> &str { "accum" }

     // Provide a pointer:
     fn function_ptr(&self) -> *const u8 { my_primitive_accum as *const u8 }

     // Define the function signature for the JIT compiler. Be really careful though,
     // There is no safety net here.
     fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
         match i {
             0 => Some(DSPNodeSigBit::Value),
             1 => Some(DSPNodeSigBit::NodeStatePtr),
             _ => None, // Return None to signal we only take 1 parameter
         }
     }

     // Tell the JIT compiler that you return a value:
     fn has_return_value(&self) -> bool { true }

     // Specify how to reset the state:
     fn reset_state(&self, _dsp_state: *mut DSPState, state_ptr: *mut u8) {
         unsafe { (*(state_ptr as *mut MyPrimAccumulator)).count = 0.0 };
     }

     // Allocate our state:
     fn allocate_state(&self) -> Option<*mut u8> {
         Some(Box::into_raw(Box::new(MyPrimAccumulator { count: 0.0 })) as *mut u8)
     }

     // Deallocate our state:
     fn deallocate_state(&self, ptr: *mut u8) {
         let _ = unsafe { Box::from_raw(ptr as *mut MyPrimAccumulator) };
     }
 }

 lib.borrow_mut().add(std::sync::Arc::new(MyPrimitive {}));

 use synfx_dsp_jit::{ASTFun, JIT, DSPNodeContext};
 let ctx = DSPNodeContext::new_ref();
 let jit = JIT::new(lib.clone(), ctx.clone());

 use synfx_dsp_jit::build::*;
 let mut fun =
     jit.compile(ASTFun::new(call("accum", 0, &[var("in1")]))).expect("no compile error");

 fun.init(44100.0, None);

 let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
 assert!((ret - 1.0).abs() < 0.000001);

 let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
 assert!((ret - 2.0).abs() < 0.000001);

 let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
 assert!((ret - 3.0).abs() < 0.000001);

 // You can cause a reset eg. with fun.set_sample_rate() or fun.reset():
 fun.reset();

 // Counting will restart:
 let (_s1, _s2, ret) = fun.exec_2in_2out(1.0, 0.0);
 assert!((ret - 1.0).abs() < 0.000001);

 ctx.borrow_mut().free();

Required Methods§

source

fn name(&self) -> &str

The name of this DSP node, by this name it can be called from the crate::ast::ASTFun.

source

fn function_ptr(&self) -> *const u8

The function pointer that should be inserted.

source

fn has_return_value(&self) -> bool

Should return true if the function for DSPNodeType::function_ptr returns something.

Provided Methods§

source

fn documentation(&self) -> &str

Document what this node does and how to use it. Format should be in Markdown.

Documenting the node will make it easier for library implementors and even eventual end users to figure out what this node does and how to use it.

For instance, this text should define what the input and output parameters do. And also define which value ranges these operate in.

source

fn input_names(&self, _index: usize) -> Option<&str>

Returns the name of each input port of this node. Choose descriptive but short names. These names will be used by compiler frontends to identify the ports, and it will make it easier to stay compatible if indices change.

source

fn output_names(&self, _index: usize) -> Option<&str>

Returns the name of each output port of this node. Choose descriptive but short names. These names will be used by compiler frontends to identify the ports, and it will make it easier to stay compatible if indices change.

source

fn input_index_by_name(&self, name: &str) -> Option<usize>

Returns the index of the output by it’s name.

source

fn output_index_by_name(&self, name: &str) -> Option<usize>

Returns the index of the output by it’s name.

source

fn input_count(&self) -> usize

Number of input ports

source

fn output_count(&self) -> usize

Number of output ports

source

fn is_stateful(&self) -> bool

Returns true if this node type requires state.

source

fn signature(&self, _i: usize) -> Option<DSPNodeSigBit>

Should return the signature type for input parameter i.

source

fn reset_state(&self, _dsp_state: *mut DSPState, _state_ptr: *mut u8)

Will be called when the node state should be resetted. This should be used to store the sample rate for instance or do other sample rate dependent recomputations. Also things delay lines should zero their buffers.

source

fn allocate_state(&self) -> Option<*mut u8>

Allocates a new piece of state for this DSPNodeType. Must be deallocated using DSPNodeType::deallocate_state.

source

fn deallocate_state(&self, _ptr: *mut u8)

Deallocates the private state of this DSPNodeType.

Implementors§