use crate::locked::*;
use cranelift_jit::JITModule;
use std::cell::RefCell;
use std::collections::HashMap;
use std::mem;
use std::rc::Rc;
use std::sync::Arc;
use synfx_dsp::AtomicFloat;
pub const BUFFER_DEFAULT_SIZE: usize = 16;
pub(crate) const AUX_VAR_COUNT: usize = 3;
pub(crate) const AUX_VAR_IDX_SRATE: usize = 0;
pub(crate) const AUX_VAR_IDX_ISRATE: usize = 1;
pub(crate) const AUX_VAR_IDX_RESET: usize = 2;
pub enum DSPNodeContextError {
UnknownTable(usize),
WrongTableSize { tbl_idx: usize, new_size: usize, old_size: usize },
}
#[derive(Debug, Clone)]
pub struct DSPContextConfig {
pub atom_count: usize,
pub buffer_count: usize,
pub tables: Vec<Arc<Vec<f32>>>,
}
impl Default for DSPContextConfig {
fn default() -> Self {
let mut tables = vec![];
for _ in 0..16 {
tables.push(Arc::new(vec![0.0; 1]));
}
Self { atom_count: 32, buffer_count: 16, tables }
}
}
pub struct DSPNodeContext {
pub(crate) config: DSPContextConfig,
state: *mut DSPState,
persistent_var_index: usize,
persistent_var_map: HashMap<String, usize>,
node_states: HashMap<u64, Box<DSPNodeState>>,
generation: u64,
next_dsp_fun: Option<Box<DSPFunction>>,
debug_enabled: bool,
pub(crate) cranelift_ir_dump: String,
atoms: Vec<Arc<AtomicFloat>>,
buffer_lengths: Vec<usize>,
pub(crate) buffer_declare: Vec<usize>,
}
impl DSPNodeContext {
fn new() -> Self {
Self::new_with_config(DSPContextConfig::default())
}
fn new_with_config(config: DSPContextConfig) -> Self {
let mut atoms = vec![];
atoms.resize_with(config.atom_count, || Arc::new(AtomicFloat::new(0.0)));
let atoms_state = atoms.clone();
let mut buffer_lengths = vec![];
let mut buffers = vec![];
for _ in 0..config.buffer_count {
buffers.push(vec![0.0; BUFFER_DEFAULT_SIZE]);
buffer_lengths.push(BUFFER_DEFAULT_SIZE);
}
let buffers = LockedMutPtrs::new(buffers);
let buffer_declare = buffer_lengths.clone();
let tables = LockedPtrs::new(config.tables.clone());
Self {
config,
state: Box::into_raw(Box::new(DSPState {
x: 0.0,
y: 0.0,
srate: 44100.0,
israte: 1.0 / 44100.0,
atoms: atoms_state,
buffers,
tables,
})),
node_states: HashMap::new(),
generation: 0,
next_dsp_fun: None,
persistent_var_map: HashMap::new(),
persistent_var_index: 0,
debug_enabled: false,
cranelift_ir_dump: String::from(""),
atoms,
buffer_lengths,
buffer_declare,
}
}
pub fn new_ref() -> Rc<RefCell<Self>> {
Rc::new(RefCell::new(Self::new()))
}
pub(crate) fn init_dsp_function(&mut self) {
self.generation += 1;
self.next_dsp_fun = Some(Box::new(DSPFunction::new(self.state, self.generation)));
}
pub fn set_debug(&mut self, enabled: bool) {
self.debug_enabled = enabled;
}
pub fn debug_enabled(&self) -> bool {
self.debug_enabled
}
pub fn get_ir_dump(&self) -> &str {
&self.cranelift_ir_dump
}
pub fn atom(&self, idx: usize) -> Option<Arc<AtomicFloat>> {
self.atoms.get(idx).cloned()
}
pub fn get_persistent_variable_index_by_name(&self, pers_var_name: &str) -> Option<usize> {
self.persistent_var_map.get(pers_var_name).map(|i| *i)
}
pub(crate) fn get_persistent_variable_index(
&mut self,
pers_var_name: &str,
) -> Result<usize, String> {
let index = if let Some(index) = self.persistent_var_map.get(pers_var_name) {
*index
} else {
let index = self.persistent_var_index;
self.persistent_var_index += 1;
self.persistent_var_map.insert(pers_var_name.to_string(), index);
index
};
if let Some(next_dsp_fun) = &mut self.next_dsp_fun {
next_dsp_fun.touch_persistent_var_index(index);
Ok(index)
} else {
Err("No DSPFunction in DSPNodeContext".to_string())
}
}
pub fn send_table(
&mut self,
tbl_idx: usize,
table: Arc<Vec<f64>>,
) -> Result<(), DSPNodeContextError> {
let config_tbl_len = 0;
Err(DSPNodeContextError::WrongTableSize {
tbl_idx,
new_size: table.len(),
old_size: config_tbl_len,
})
}
pub(crate) fn add_dsp_node_instance(
&mut self,
node_type: Arc<dyn DSPNodeType>,
dsp_node_uid: u64,
) -> Result<usize, String> {
if let Some(next_dsp_fun) = &mut self.next_dsp_fun {
if next_dsp_fun.has_dsp_node_state_uid(dsp_node_uid) {
return Err(format!(
"node_state_uid has been used multiple times in same AST: {}",
dsp_node_uid
));
}
if !self.node_states.contains_key(&dsp_node_uid) {
self.node_states.insert(
dsp_node_uid,
Box::new(DSPNodeState::new(dsp_node_uid, node_type.clone())),
);
}
if let Some(state) = self.node_states.get_mut(&dsp_node_uid) {
if state.node_type().name() != node_type.name() {
return Err(format!(
"Different DSPNodeType for uid {}: {} != {}",
dsp_node_uid,
state.node_type().name(),
node_type.name()
));
}
Ok(next_dsp_fun.install(state))
} else {
Err(format!("NodeState does not exist, but it should... bad! {}", dsp_node_uid))
}
} else {
Err("No DSPFunction in DSPNodeContext".to_string())
}
}
pub(crate) fn finalize_dsp_function(
&mut self,
function_ptr: *const u8,
module: JITModule,
) -> Option<Box<DSPFunction>> {
if let Some(mut next_dsp_fun) = self.next_dsp_fun.take() {
for (i, (len, declare)) in
self.buffer_lengths.iter().zip(self.buffer_declare.iter()).enumerate()
{
if *len != *declare {
next_dsp_fun.add_buffer_update(i, *declare);
}
}
for (len, declare) in self.buffer_lengths.iter_mut().zip(self.buffer_declare.iter_mut())
{
*len = *declare;
}
next_dsp_fun.set_function_ptr(function_ptr, module);
for (_, node_state) in self.node_states.iter_mut() {
node_state.set_initialized();
}
Some(next_dsp_fun)
} else {
None
}
}
pub fn cleanup_dsp_fun_after_user(&mut self, _fun: Box<DSPFunction>) {
}
pub fn free(&mut self) {
if !self.state.is_null() {
let _ = unsafe { Box::from_raw(self.state) };
self.state = std::ptr::null_mut();
}
}
}
impl Drop for DSPNodeContext {
fn drop(&mut self) {
if !self.state.is_null() {
eprintln!("WBlockDSP JIT DSPNodeContext not cleaned up on exit. Forgot to call free() or keep it alive long enough?");
}
}
}
pub struct DSPNodeTypeLibrary {
type_by_name: HashMap<String, Arc<dyn DSPNodeType>>,
types: Vec<Arc<dyn DSPNodeType>>,
}
impl DSPNodeTypeLibrary {
pub fn new() -> Self {
Self { types: vec![], type_by_name: HashMap::new() }
}
pub fn add(&mut self, typ: Arc<dyn DSPNodeType>) {
self.types.push(typ.clone());
self.type_by_name.insert(typ.name().to_string(), typ);
}
pub fn get_type_by_name(&self, typ_name: &str) -> Option<Arc<dyn DSPNodeType>> {
self.type_by_name.get(typ_name).cloned()
}
pub fn for_each<T, F: FnMut(&Arc<dyn DSPNodeType>) -> Result<(), T>>(
&self,
mut f: F,
) -> Result<(), T> {
for t in self.types.iter() {
f(t)?;
}
Ok(())
}
}
#[macro_export]
macro_rules! stateful_dsp_node_type {
($node_type: ident, $struct_type: ident =>
$func_name: ident $jit_name: literal $signature: literal
doc $doc: literal
inputs $($idx: literal $inp: literal)*
outputs $($idxo: literal $out: literal)*) => {
struct $node_type;
impl $node_type {
fn new_ref() -> std::sync::Arc<Self> {
std::sync::Arc::new(Self {})
}
}
impl DSPNodeType for $node_type {
fn name(&self) -> &str {
$jit_name
}
fn function_ptr(&self) -> *const u8 {
$func_name as *const u8
}
fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
match $signature.chars().nth(i) {
Some('v') => Some(DSPNodeSigBit::Value),
Some('D') => Some(DSPNodeSigBit::DSPStatePtr),
Some('S') => Some(DSPNodeSigBit::NodeStatePtr),
Some('M') => Some(DSPNodeSigBit::MultReturnPtr),
_ => None,
}
}
fn has_return_value(&self) -> bool {
$signature.find("r").is_some()
}
fn reset_state(&self, dsp_state: *mut DSPState, state_ptr: *mut u8) {
let ptr = state_ptr as *mut $struct_type;
unsafe {
(*ptr).reset(&mut (*dsp_state));
}
}
fn allocate_state(&self) -> Option<*mut u8> {
Some(Box::into_raw(Box::new($struct_type::default())) as *mut u8)
}
fn deallocate_state(&self, ptr: *mut u8) {
let _ = unsafe { Box::from_raw(ptr as *mut $struct_type) };
}
fn documentation(&self) -> &str {
$doc
}
fn input_names(&self, index: usize) -> Option<&str> {
match index {
$($idx => Some($inp),)*
_ => None
}
}
fn input_index_by_name(&self, name: &str) -> Option<usize> {
match name {
$($inp => Some($idx),)*
_ => None
}
}
fn output_names(&self, index: usize) -> Option<&str> {
match index {
$($idxo => Some($out),)*
_ => None
}
}
fn output_index_by_name(&self, name: &str) -> Option<usize> {
match name {
$($out => Some($idxo),)*
_ => None
}
}
}
};
}
#[macro_export]
macro_rules! stateless_dsp_node_type {
($node_type: ident =>
$func_name: ident $jit_name: literal $signature: literal
doc $doc: literal
inputs $($idx: literal $inp: literal)*
outputs $($idxo: literal $out: literal)*) => {
#[derive(Default)]
struct $node_type;
impl $node_type {
#[allow(dead_code)]
fn new_ref() -> std::sync::Arc<Self> {
std::sync::Arc::new(Self {})
}
}
impl DSPNodeType for $node_type {
fn name(&self) -> &str {
$jit_name
}
fn function_ptr(&self) -> *const u8 {
$func_name as *const u8
}
fn signature(&self, i: usize) -> Option<DSPNodeSigBit> {
match $signature.chars().nth(i) {
Some('v') => Some(DSPNodeSigBit::Value),
Some('D') => Some(DSPNodeSigBit::DSPStatePtr),
Some('M') => Some(DSPNodeSigBit::MultReturnPtr),
_ => None,
}
}
fn has_return_value(&self) -> bool {
$signature.find("r").is_some()
}
fn documentation(&self) -> &str {
$doc
}
fn input_names(&self, index: usize) -> Option<&str> {
match index {
$($idx => Some($inp),)*
_ => None
}
}
fn input_index_by_name(&self, name: &str) -> Option<usize> {
match name {
$($inp => Some($idx),)*
_ => None
}
}
fn output_names(&self, index: usize) -> Option<&str> {
match index {
$($idxo => Some($out),)*
_ => None
}
}
fn output_index_by_name(&self, name: &str) -> Option<usize> {
match name {
$($out => Some($idxo),)*
_ => None
}
}
}
};
}
pub struct DSPFunction {
state: *mut DSPState,
node_state_types: Vec<Arc<dyn DSPNodeType>>,
node_states: Vec<*mut u8>,
node_state_init_reset: Vec<usize>,
node_state_uids: Vec<u64>,
dsp_ctx_generation: u64,
module: Option<JITModule>,
persistent_vars: Vec<f64>,
buffer_updates: Option<Vec<(usize, Vec<f64>)>>,
buffer_updates_done: bool,
aux_vars: [f64; AUX_VAR_COUNT],
resetted: bool,
function: Option<
fn(
f64,
f64,
f64,
f64,
f64,
f64,
*mut f64,
*mut f64,
*mut f64,
*mut DSPState,
*const *mut u8,
*mut f64,
*mut f64,
*const *mut f64,
*const u64,
*const *const f32,
*const u64,
) -> f64,
>,
}
unsafe impl Send for DSPFunction {}
unsafe impl Sync for DSPFunction {}
impl DSPFunction {
pub(crate) fn new(state: *mut DSPState, dsp_ctx_generation: u64) -> Self {
Self {
state,
node_state_types: vec![],
node_states: vec![],
node_state_init_reset: vec![],
node_state_uids: vec![],
persistent_vars: vec![],
aux_vars: [0.0; AUX_VAR_COUNT],
function: None,
dsp_ctx_generation,
module: None,
resetted: false,
buffer_updates: Some(vec![]),
buffer_updates_done: true,
}
}
pub(crate) fn set_function_ptr(&mut self, function: *const u8, module: JITModule) {
self.module = Some(module);
self.function = Some(unsafe {
mem::transmute::<
_,
fn(
f64,
f64,
f64,
f64,
f64,
f64,
*mut f64,
*mut f64,
*mut f64,
*mut DSPState,
*const *mut u8,
*mut f64,
*mut f64,
*const *mut f64,
*const u64,
*const *const f32,
*const u64,
) -> f64,
>(function)
});
}
pub(crate) fn add_buffer_update(&mut self, buf_idx: usize, length: usize) {
if let Some(updates) = &mut self.buffer_updates {
updates.push((buf_idx, vec![0.0; length]));
}
self.buffer_updates_done = false;
}
pub fn init(&mut self, srate: f64, previous_function: Option<&DSPFunction>) {
if let Some(previous_function) = previous_function {
let prev_len = previous_function.persistent_vars.len();
let now_len = self.persistent_vars.len();
let len = prev_len.min(now_len);
self.persistent_vars[0..len].copy_from_slice(&previous_function.persistent_vars[0..len])
} else {
self.resetted = true;
}
if !self.buffer_updates_done {
if let Some(mut updates) = self.buffer_updates.take() {
for (idx, new_vec) in updates.iter_mut() {
let _ = self.swap_buffer(*idx, new_vec, true);
}
self.buffer_updates = Some(updates);
}
self.buffer_updates_done = true;
}
unsafe {
(*self.state).srate = srate;
(*self.state).israte = 1.0 / srate;
}
self.aux_vars[AUX_VAR_IDX_SRATE] = srate;
self.aux_vars[AUX_VAR_IDX_ISRATE] = 1.0 / srate;
for idx in self.node_state_init_reset.iter() {
let typ = &self.node_state_types[*idx as usize];
let ptr = self.node_states[*idx as usize];
typ.reset_state(self.state, ptr);
}
}
pub fn swap_buffer(
&mut self,
index: usize,
new_buf: &mut Vec<f64>,
preserve_old_samples: bool,
) -> Result<(), ()> {
unsafe {
if index >= (*self.state).buffers.len() {
return Err(());
}
if preserve_old_samples {
let old_len = (*self.state).buffers.element_len(index);
let old_vec = (*self.state).buffers.pointers()[index];
let min_len = old_len.min(new_buf.len());
std::ptr::copy_nonoverlapping(old_vec, new_buf.as_mut_ptr(), min_len);
}
let _ = (*self.state).buffers.swap_element(index, new_buf);
}
Ok(())
}
pub fn swap_table(
&mut self,
index: usize,
new_table: &mut Arc<Vec<f32>>,
) -> Result<(), ()> {
unsafe {
if index >= (*self.state).tables.len() {
return Err(());
}
let _ = (*self.state).tables.swap_element(index, new_table);
}
Ok(())
}
pub fn set_sample_rate(&mut self, srate: f64) {
unsafe {
(*self.state).srate = srate;
(*self.state).israte = 1.0 / srate;
}
self.aux_vars[AUX_VAR_IDX_SRATE] = srate;
self.aux_vars[AUX_VAR_IDX_ISRATE] = 1.0 / srate;
self.reset();
}
pub fn reset(&mut self) {
self.resetted = true;
for (typ, ptr) in self.node_state_types.iter().zip(self.node_states.iter_mut()) {
typ.reset_state(self.state, *ptr);
}
self.persistent_vars.fill(0.0);
}
pub fn get_dsp_state_ptr(&self) -> *mut DSPState {
self.state
}
pub unsafe fn with_dsp_state<R, F: FnMut(*mut DSPState) -> R>(&mut self, mut f: F) -> R {
f(self.get_dsp_state_ptr())
}
#[allow(clippy::result_unit_err)]
pub unsafe fn with_node_state<T, R, F: FnMut(*mut T) -> R>(
&mut self,
node_state_uid: u64,
mut f: F,
) -> Result<R, ()> {
if let Some(state_ptr) = self.get_node_state_ptr(node_state_uid) {
Ok(f(state_ptr as *mut T))
} else {
Err(())
}
}
pub fn get_node_state_ptr(&self, node_state_uid: u64) -> Option<*mut u8> {
for (i, uid) in self.node_state_uids.iter().enumerate() {
if *uid == node_state_uid {
return Some(self.node_states[i]);
}
}
None
}
pub fn exec_2in_2out(&mut self, in1: f64, in2: f64) -> (f64, f64, f64) {
let mut s1 = 0.0;
let mut s2 = 0.0;
let r = self.exec(in1, in2, 0.0, 0.0, 0.0, 0.0, &mut s1, &mut s2);
(s1, s2, r)
}
#[allow(clippy::too_many_arguments)]
pub fn exec(
&mut self,
in1: f64,
in2: f64,
alpha: f64,
beta: f64,
delta: f64,
gamma: f64,
sig1: &mut f64,
sig2: &mut f64,
) -> f64 {
{
self.aux_vars[AUX_VAR_IDX_RESET] = if self.resetted {
self.resetted = false;
1.0
} else {
0.0
};
}
let states_ptr: *const *mut u8 = self.node_states.as_mut_ptr();
let pers_vars_ptr: *mut f64 = self.persistent_vars.as_mut_ptr();
let aux_vars: *mut f64 = self.aux_vars.as_mut_ptr();
let bufs: *const *mut f64 = unsafe { (*self.state).buffers.pointers().as_ptr() };
let buf_lens: *const u64 = unsafe { (*self.state).buffers.lens().as_ptr() };
let tables: *const *const f32 = unsafe { (*self.state).tables.pointers().as_ptr() };
let table_lens: *const u64 = unsafe { (*self.state).tables.lens().as_ptr() };
let mut multi_returns = [0.0; 5];
(unsafe { self.function.unwrap_unchecked() })(
in1,
in2,
alpha,
beta,
delta,
gamma,
sig1,
sig2,
aux_vars,
self.state,
states_ptr,
pers_vars_ptr,
(&mut multi_returns) as *mut f64,
bufs,
buf_lens,
tables,
table_lens,
)
}
pub(crate) fn install(&mut self, node_state: &mut DSPNodeState) -> usize {
let idx = self.node_states.len();
node_state.mark(self.dsp_ctx_generation, idx);
self.node_states.push(node_state.ptr());
self.node_state_types.push(node_state.node_type());
self.node_state_uids.push(node_state.uid());
if !node_state.is_initialized() {
self.node_state_init_reset.push(idx);
}
idx
}
pub(crate) fn touch_persistent_var_index(&mut self, idx: usize) {
if idx >= self.persistent_vars.len() {
self.persistent_vars.resize(idx + 1, 0.0);
}
}
pub fn access_persistent_var(&mut self, idx: usize) -> Option<&mut f64> {
self.persistent_vars.get_mut(idx)
}
pub fn has_dsp_node_state_uid(&self, uid: u64) -> bool {
for i in self.node_state_uids.iter() {
if *i == uid {
return true;
}
}
false
}
}
impl Drop for DSPFunction {
fn drop(&mut self) {
unsafe {
if let Some(module) = self.module.take() {
module.free_memory();
}
};
}
}
pub struct DSPState {
pub x: f64,
pub y: f64,
pub srate: f64,
pub israte: f64,
pub atoms: Vec<Arc<AtomicFloat>>,
pub buffers: LockedMutPtrs<Vec<f64>, f64>,
pub tables: LockedPtrs<Arc<Vec<f32>>, f32>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DSPNodeSigBit {
Value,
DSPStatePtr,
NodeStatePtr,
MultReturnPtr,
}
pub trait DSPNodeType: Sync + Send {
fn name(&self) -> &str;
fn documentation(&self) -> &str {
"undocumented"
}
fn input_names(&self, _index: usize) -> Option<&str> {
None
}
fn output_names(&self, _index: usize) -> Option<&str> {
None
}
fn input_index_by_name(&self, name: &str) -> Option<usize> {
let mut i = 0;
while let Some(iname) = self.input_names(i) {
if iname == name {
return Some(i);
}
i += 1;
}
None
}
fn output_index_by_name(&self, name: &str) -> Option<usize> {
let mut i = 0;
while let Some(oname) = self.output_names(i) {
if oname == name {
return Some(i);
}
i += 1;
}
None
}
fn input_count(&self) -> usize {
let mut i = 0;
while self.input_names(i).is_some() {
i += 1;
}
i
}
fn output_count(&self) -> usize {
let mut i = 0;
while self.output_names(i).is_some() {
i += 1;
}
i
}
fn is_stateful(&self) -> bool {
let mut i = 0;
while let Some(sig) = self.signature(i) {
if let DSPNodeSigBit::NodeStatePtr = sig {
return true;
}
i += 1;
}
false
}
fn function_ptr(&self) -> *const u8;
fn signature(&self, _i: usize) -> Option<DSPNodeSigBit> {
None
}
fn has_return_value(&self) -> bool;
fn reset_state(&self, _dsp_state: *mut DSPState, _state_ptr: *mut u8) {}
fn allocate_state(&self) -> Option<*mut u8> {
None
}
fn deallocate_state(&self, _ptr: *mut u8) {}
}
pub(crate) struct DSPNodeState {
uid: u64,
node_type: Arc<dyn DSPNodeType>,
ptr: *mut u8,
generation: u64,
function_index: usize,
initialized: bool,
}
impl DSPNodeState {
pub(crate) fn new(uid: u64, node_type: Arc<dyn DSPNodeType>) -> Self {
Self {
uid,
node_type: node_type.clone(),
ptr: node_type.allocate_state().expect("DSPNodeState created for stateful node type"),
generation: 0,
function_index: 0,
initialized: false,
}
}
pub(crate) fn uid(&self) -> u64 {
self.uid
}
pub(crate) fn mark(&mut self, gen: u64, index: usize) {
self.generation = gen;
self.function_index = index;
}
pub(crate) fn is_initialized(&self) -> bool {
self.initialized
}
pub(crate) fn set_initialized(&mut self) {
self.initialized = true;
}
pub(crate) fn ptr(&self) -> *mut u8 {
self.ptr
}
pub(crate) fn node_type(&self) -> Arc<dyn DSPNodeType> {
self.node_type.clone()
}
}
impl Drop for DSPNodeState {
fn drop(&mut self) {
self.node_type.deallocate_state(self.ptr);
self.ptr = std::ptr::null_mut();
}
}