diff --git a/src/block_compiler.rs b/src/block_compiler.rs index fbdd2c9..4fcd1a7 100644 --- a/src/block_compiler.rs +++ b/src/block_compiler.rs @@ -96,7 +96,7 @@ enum BlkASTNode { }, Literal { value: f64, - } + }, } impl BlkASTNode { @@ -169,13 +169,7 @@ impl BlkASTNode { lbl: &str, childs: Vec<(Option, BlkASTRef)>, ) -> BlkASTRef { - Rc::new(BlkASTNode::Node { - id, - out, - typ: typ.to_string(), - lbl: lbl.to_string(), - childs, - }) + Rc::new(BlkASTNode::Node { id, out, typ: typ.to_string(), lbl: lbl.to_string(), childs }) } } @@ -187,6 +181,9 @@ pub enum BlkJITCompileError { ASTMissingOutputLabel(usize), NoTmpVarForOutput(usize, String), BadLiteralNumber(String), + NodeWithoutID(String), + UnknownType(String), + TooManyInputs(String, usize), } pub struct Block2JITCompiler { @@ -260,9 +257,7 @@ impl Block2JITCompiler { Err(BlkJITCompileError::BadTree(node.clone())) } } - "value" => { - Ok(BlkASTNode::new_literal(&node.0.borrow().lbl)?) - } + "value" => Ok(BlkASTNode::new_literal(&node.0.borrow().lbl)?), "set" => { if let Some((_in, out, first)) = node.first_child() { let out = if out.len() > 0 { Some(out) } else { None }; @@ -283,7 +278,6 @@ impl Block2JITCompiler { area.push(BlkASTNode::new_get(0, &tmp_var)); self.store_idout_var(id, "", &tmp_var); Ok(BlkASTNode::new_area(area)) - } else { Err(BlkJITCompileError::BadTree(node.clone())) } @@ -327,11 +321,7 @@ impl Block2JITCompiler { &tmp_var, BlkASTNode::new_get(0, &format!("%{}", i)), )); - self.store_idout_var( - id, - &oname, - &tmp_var, - ); + self.store_idout_var(id, &oname, &tmp_var); } else { return Err(BlkJITCompileError::NoOutputAtIdx(optype.to_string(), i)); } @@ -371,20 +361,44 @@ impl Block2JITCompiler { stmt.push(self.bjit2jit(&c)?); } Ok(stmts(&stmt[..])) - }, + } BlkASTNode::Set { var, expr } => { let e = self.bjit2jit(&expr)?; Ok(assign(var, e)) } - BlkASTNode::Get { id, var: varname } => { - Ok(var(varname)) + BlkASTNode::Get { id, var: varname } => Ok(var(varname)), + BlkASTNode::Node { id, out, typ, lbl, childs } => match &typ[..] { + "if" => Err(BlkJITCompileError::UnknownError), + "zero" => Ok(literal(0.0)), + node => { + if *id == 0 { + return Err(BlkJITCompileError::NodeWithoutID(typ.to_string())); + } + + let lang = self.lang.clone(); + + let mut args = vec![]; + + if let Some(inputs) = lang.borrow().get_type_inputs(typ) { + if childs.len() > inputs.len() { + return Err(BlkJITCompileError::TooManyInputs(typ.to_string(), *id)); + } + + for input_name in inputs.iter() { + for (inp, c) in childs.iter() { + if inp == input_name { + args.push(self.bjit2jit(&c)?); + } + } + } + } else { + return Err(BlkJITCompileError::UnknownType(typ.to_string())); + } + + Ok(call(typ, *id as u64, &args[..])) + } }, - BlkASTNode::Node { id, out, typ, lbl, childs } => { - Err(BlkJITCompileError::UnknownError) - } - BlkASTNode::Literal { value } => { - Ok(literal(*value)) - } + BlkASTNode::Literal { value } => Ok(literal(*value)), } } @@ -416,21 +430,23 @@ mod test { }; } - use synfx_dsp_jit::{get_standard_library, DSPNodeContext, JIT, ASTFun, DSPFunction}; + use synfx_dsp_jit::{get_standard_library, ASTFun, DSPFunction, DSPNodeContext, JIT}; - fn new_jit_fun(mut f: F) -> (Rc>, Box) { + fn new_jit_fun( + mut f: F, + ) -> (Rc>, Box) { use crate::block_compiler::{BlkJITCompileError, Block2JITCompiler}; use crate::blocklang::BlockFun; use crate::blocklang_def; - let lang = blocklang_def::setup_hxdsp_block_language(); + let lib = get_standard_library(); + let lang = blocklang_def::setup_hxdsp_block_language(lib.clone()); let mut bf = BlockFun::new(lang.clone()); f(&mut bf); let mut compiler = Block2JITCompiler::new(bf.block_language()); let ast = compiler.compile(&bf).expect("blk2jit compiles"); - let lib = get_standard_library(); let ctx = DSPNodeContext::new_ref(); let jit = JIT::new(lib, ctx.clone()); let mut fun = jit.compile(ASTFun::new(ast)).expect("jit compiles"); @@ -440,7 +456,6 @@ mod test { (ctx, fun) } - #[test] fn check_blocklang_sig1() { let (ctx, mut fun) = new_jit_fun(|bf| { @@ -460,4 +475,36 @@ mod test { ctx.borrow_mut().free(); } + #[test] + fn check_blocklang_accum_shift() { + let (ctx, mut fun) = new_jit_fun(|bf| { + bf.instanciate_at(0, 1, 1, "accum", None); + bf.shift_port(0, 1, 1, 1, false); + bf.instanciate_at(0, 0, 2, "value", Some("0.01".to_string())); + bf.instanciate_at(0, 0, 1, "get", Some("*reset".to_string())); + }); + + fun.exec_2in_2out(0.0, 0.0); + fun.exec_2in_2out(0.0, 0.0); + fun.exec_2in_2out(0.0, 0.0); + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + assert_float_eq!(ret, 0.04); + + let reset_idx = ctx.borrow().get_persistent_variable_index_by_name("*reset").unwrap(); + fun.access_persistent_var(reset_idx).map(|reset| *reset = 1.0); + + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + assert_float_eq!(ret, 0.0); + + fun.access_persistent_var(reset_idx).map(|reset| *reset = 0.0); + + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + let (s1, s2, ret) = fun.exec_2in_2out(0.0, 0.0); + assert_float_eq!(ret, 0.05); + + ctx.borrow_mut().free(); + } } diff --git a/src/blocklang.rs b/src/blocklang.rs index e46d80c..1a6c715 100644 --- a/src/blocklang.rs +++ b/src/blocklang.rs @@ -907,15 +907,21 @@ impl BlockLanguage { Some(&typ.outputs) } + pub fn get_type_inputs(&self, typ: &str) -> Option<&[Option]> { + let typ = self.types.get(typ)?; + Some(&typ.inputs) + } + pub fn get_output_name_at_index(&self, typ: &str, idx: usize) -> Option { - let outs = self.get_type_outputs(typ)?; - let mut i = 0; - for o in outs.iter() { - if let Some(outname) = o { - if i == idx { - return Some(outname.to_string()); + if let Some(outs) = self.get_type_outputs(typ) { + let mut i = 0; + for o in outs.iter() { + if let Some(outname) = o { + if i == idx { + return Some(outname.to_string()); + } + i += 1; } - i += 1; } } diff --git a/src/blocklang_def.rs b/src/blocklang_def.rs index 5cc7762..20a35e8 100644 --- a/src/blocklang_def.rs +++ b/src/blocklang_def.rs @@ -2,11 +2,14 @@ // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. -use crate::blocklang::{BlockLanguage, BlockUserInput, BlockType}; +use crate::blocklang::{BlockLanguage, BlockType, BlockUserInput}; use std::cell::RefCell; use std::rc::Rc; +use synfx_dsp_jit::DSPNodeTypeLibrary; -pub fn setup_hxdsp_block_language() -> Rc> { +pub fn setup_hxdsp_block_language( + dsp_lib: Rc>, +) -> Rc> { let mut lang = BlockLanguage::new(); lang.define(BlockType { @@ -17,222 +20,248 @@ pub fn setup_hxdsp_block_language() -> Rc> { outputs: vec![Some("".to_string())], area_count: 0, user_input: BlockUserInput::None, - description: "A phasor, returns a saw tooth wave to scan through things or use as modulator.".to_string(), + description: + "A phasor, returns a saw tooth wave to scan through things or use as modulator." + .to_string(), color: 2, }); lang.define(BlockType { - category: "literals".to_string(), - name: "zero".to_string(), - rows: 1, - inputs: vec![], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "The 0.0 value".to_string(), - color: 1, + category: "literals".to_string(), + name: "zero".to_string(), + rows: 1, + inputs: vec![], + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "The 0.0 value".to_string(), + color: 1, }); lang.define(BlockType { - category: "literals".to_string(), - name: "π".to_string(), - rows: 1, - inputs: vec![], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "The PI number".to_string(), - color: 1, + category: "literals".to_string(), + name: "π".to_string(), + rows: 1, + inputs: vec![], + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "The PI number".to_string(), + color: 1, }); lang.define(BlockType { - category: "literals".to_string(), - name: "2π".to_string(), - rows: 1, - inputs: vec![], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "2 * PI == TAU".to_string(), - color: 1, + category: "literals".to_string(), + name: "2π".to_string(), + rows: 1, + inputs: vec![], + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "2 * PI == TAU".to_string(), + color: 1, }); lang.define(BlockType { - category: "literals".to_string(), - name: "SR".to_string(), - rows: 1, - inputs: vec![], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "The sample rate".to_string(), - color: 1, + category: "literals".to_string(), + name: "value".to_string(), + rows: 1, + inputs: vec![], + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::Float, + description: "A literal value, typed in by the user.".to_string(), + color: 1, }); lang.define(BlockType { - category: "literals".to_string(), - name: "value".to_string(), - rows: 1, - inputs: vec![], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::Float, - description: "A literal value, typed in by the user.".to_string(), - color: 1, + category: "routing".to_string(), + name: "->".to_string(), + rows: 1, + inputs: vec![Some("".to_string())], + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "Forwards the value one block".to_string(), + color: 6, }); lang.define(BlockType { - category: "routing".to_string(), - name: "->".to_string(), - rows: 1, - inputs: vec![Some("".to_string())], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Forwards the value one block".to_string(), - color: 6, + category: "routing".to_string(), + name: "->2".to_string(), + rows: 2, + inputs: vec![Some("".to_string())], + outputs: vec![Some("".to_string()), Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "Forwards the value one block and sends it to multiple destinations" + .to_string(), + color: 6, }); lang.define(BlockType { - category: "routing".to_string(), - name: "->2".to_string(), - rows: 2, - inputs: vec![Some("".to_string())], - outputs: vec![Some("".to_string()), Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Forwards the value one block and sends it to multiple destinations".to_string(), - color: 6, + category: "routing".to_string(), + name: "->3".to_string(), + rows: 3, + inputs: vec![Some("".to_string())], + outputs: vec![Some("".to_string()), Some("".to_string()), Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "Forwards the value one block and sends it to multiple destinations" + .to_string(), + color: 6, }); lang.define(BlockType { - category: "routing".to_string(), - name: "->3".to_string(), - rows: 3, - inputs: vec![Some("".to_string())], - outputs: vec![Some("".to_string()), Some("".to_string()), Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Forwards the value one block and sends it to multiple destinations".to_string(), - color: 6, + category: "variables".to_string(), + name: "set".to_string(), + rows: 1, + inputs: vec![Some("".to_string())], + outputs: vec![], + area_count: 0, + user_input: BlockUserInput::Identifier, + description: "Stores into a variable".to_string(), + color: 2, }); lang.define(BlockType { - category: "variables".to_string(), - name: "set".to_string(), - rows: 1, - inputs: vec![Some("".to_string())], - outputs: vec![], - area_count: 0, - user_input: BlockUserInput::Identifier, - description: "Stores into a variable".to_string(), - color: 2, + category: "variables".to_string(), + name: "get".to_string(), + rows: 1, + inputs: vec![], + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::Identifier, + description: "Loads a variable".to_string(), + color: 12, }); lang.define(BlockType { - category: "variables".to_string(), - name: "get".to_string(), - rows: 1, - inputs: vec![], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::Identifier, - description: "Loads a variable".to_string(), - color: 12, + category: "variables".to_string(), + name: "if".to_string(), + rows: 1, + inputs: vec![Some("".to_string())], + outputs: vec![Some("".to_string())], + area_count: 2, + user_input: BlockUserInput::None, + description: "Divides the controlflow based on a true (>= 0.5) \ + or false (< 0.5) input value." + .to_string(), + color: 0, }); - lang.define(BlockType { - category: "variables".to_string(), - name: "if".to_string(), - rows: 1, - inputs: vec![Some("".to_string())], - outputs: vec![Some("".to_string())], - area_count: 2, - user_input: BlockUserInput::None, - description: "Divides the controlflow based on a true (>= 0.5) \ - or false (< 0.5) input value.".to_string(), - color: 0, - }); + // lang.define(BlockType { + // category: "nodes".to_string(), + // name: "1pole".to_string(), + // rows: 2, + // inputs: vec![Some("in".to_string()), Some("f".to_string())], + // outputs: vec![Some("lp".to_string()), Some("hp".to_string())], + // area_count: 0, + // user_input: BlockUserInput::None, + // description: "Runs a simple one pole filter on the input".to_string(), + // color: 8, + // }); + // + // lang.define(BlockType { + // category: "nodes".to_string(), + // name: "svf".to_string(), + // rows: 3, + // inputs: vec![Some("in".to_string()), Some("f".to_string()), Some("r".to_string())], + // outputs: vec![Some("lp".to_string()), Some("bp".to_string()), Some("hp".to_string())], + // area_count: 0, + // user_input: BlockUserInput::None, + // description: "Runs a state variable filter on the input".to_string(), + // color: 8, + // }); + // + // lang.define(BlockType { + // category: "functions".to_string(), + // name: "sin".to_string(), + // rows: 1, + // inputs: vec![Some("".to_string())], + // outputs: vec![Some("".to_string())], + // area_count: 0, + // user_input: BlockUserInput::None, + // description: "Calculates the sine of the input".to_string(), + // color: 16, + // }); + // + // lang.define(BlockType { + // category: "nodes".to_string(), + // name: "delay".to_string(), + // rows: 2, + // inputs: vec![Some("in".to_string()), Some("t".to_string())], + // outputs: vec![Some("".to_string())], + // area_count: 0, + // user_input: BlockUserInput::None, + // description: "Runs a linearly interpolated delay on the input".to_string(), + // color: 8, + // }); lang.define(BlockType { - category: "nodes".to_string(), - name: "1pole".to_string(), - rows: 2, - inputs: vec![Some("in".to_string()), Some("f".to_string())], - outputs: vec![Some("lp".to_string()), Some("hp".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Runs a simple one pole filter on the input".to_string(), - color: 8, - }); - - lang.define(BlockType { - category: "nodes".to_string(), - name: "svf".to_string(), - rows: 3, - inputs: vec![Some("in".to_string()), Some("f".to_string()), Some("r".to_string())], - outputs: vec![Some("lp".to_string()), Some("bp".to_string()), Some("hp".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Runs a state variable filter on the input".to_string(), - color: 8, - }); - - lang.define(BlockType { - category: "functions".to_string(), - name: "sin".to_string(), - rows: 1, - inputs: vec![Some("".to_string())], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Calculates the sine of the input".to_string(), - color: 16, - }); - - lang.define(BlockType { - category: "nodes".to_string(), - name: "delay".to_string(), - rows: 2, - inputs: vec![Some("in".to_string()), Some("t".to_string())], - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Runs a linearly interpolated delay on the input".to_string(), - color: 8, - }); - - lang.define(BlockType { - category: "arithmetics".to_string(), - name: "/%".to_string(), - rows: 2, - inputs: vec![Some("a".to_string()), Some("b".to_string())], - outputs: vec![Some("div".to_string()), Some("rem".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "Computes the integer division and remainder of a / b".to_string(), - color: 8, + category: "arithmetics".to_string(), + name: "/%".to_string(), + rows: 2, + inputs: vec![Some("a".to_string()), Some("b".to_string())], + outputs: vec![Some("div".to_string()), Some("rem".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "Computes the integer division and remainder of a / b".to_string(), + color: 8, }); for fun_name in &["+", "-", "*", "/"] { lang.define(BlockType { - category: "arithmetics".to_string(), - name: fun_name.to_string(), - rows: 2, - inputs: - if fun_name == &"-" || fun_name == &"/" { - vec![Some("a".to_string()), Some("b".to_string())] - } else { - vec![Some("".to_string()), Some("".to_string())] - }, - outputs: vec![Some("".to_string())], - area_count: 0, - user_input: BlockUserInput::None, - description: "A binary arithmetics operation".to_string(), - color: 4, + category: "arithmetics".to_string(), + name: fun_name.to_string(), + rows: 2, + inputs: if fun_name == &"-" || fun_name == &"/" { + vec![Some("a".to_string()), Some("b".to_string())] + } else { + vec![Some("".to_string()), Some("".to_string())] + }, + outputs: vec![Some("".to_string())], + area_count: 0, + user_input: BlockUserInput::None, + description: "A binary arithmetics operation".to_string(), + color: 4, }); } + dsp_lib.borrow().for_each(|node_type| -> Result<(), ()> { + let max_ports = node_type.input_count().max(node_type.output_count()); + let is_stateful = node_type.is_stateful(); + + let mut inputs = vec![]; + let mut outputs = vec![]; + + let mut i = 0; + while let Some(name) = node_type.input_names(i) { + inputs.push(Some(name[0..(name.len().min(2))].to_string())); + i += 1; + } + + let mut i = 0; + while let Some(name) = node_type.output_names(i) { + outputs.push(Some(name[0..(name.len().min(2))].to_string())); + i += 1; + } + + lang.define(BlockType { + category: if is_stateful { "nodes".to_string() } else { "functions".to_string() }, + name: node_type.name().to_string(), + rows: max_ports, + area_count: 0, + user_input: BlockUserInput::None, + description: node_type.documentation().to_string(), + color: if is_stateful { 8 } else { 16 }, + inputs, + outputs, + }); + + Ok(()) + }).expect("seriously no error here"); + lang.define_identifier("in1"); lang.define_identifier("in2"); lang.define_identifier("israte"); diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index 55217b2..73163b6 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -281,7 +281,9 @@ impl NodeConfigurator { let mut scopes = vec![]; scopes.resize_with(MAX_SCOPES, || ScopeHandle::new_shared()); - let lang = blocklang_def::setup_hxdsp_block_language(); + let code_engines = vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES]; + + let lang = blocklang_def::setup_hxdsp_block_language(code_engines[0].get_lib()); let mut block_functions = vec![]; block_functions.resize_with(MAX_AVAIL_CODE_ENGINES, || { (0, Arc::new(Mutex::new(BlockFun::new(lang.clone())))) @@ -304,7 +306,7 @@ impl NodeConfigurator { node2idx: HashMap::new(), trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS], #[cfg(feature = "synfx-dsp-jit")] - code_engines: vec![CodeEngine::new(); MAX_AVAIL_CODE_ENGINES], + code_engines, block_functions, scopes, }, diff --git a/src/wblockdsp.rs b/src/wblockdsp.rs index 0e97cfa..60b9b86 100644 --- a/src/wblockdsp.rs +++ b/src/wblockdsp.rs @@ -45,6 +45,10 @@ impl CodeEngine { Self { lib, dsp_ctx: DSPNodeContext::new_ref(), update_prod, return_cons } } + pub fn get_lib(&self) -> Rc> { + self.lib.clone() + } + pub fn upload(&mut self, ast: Box) -> Result<(), JITCompileError> { let jit = JIT::new(self.lib.clone(), self.dsp_ctx.clone()); let fun = jit.compile(ASTFun::new(ast))?; diff --git a/tests/node_code.rs b/tests/node_code.rs index 25577a1..0e699fb 100644 --- a/tests/node_code.rs +++ b/tests/node_code.rs @@ -38,3 +38,4 @@ fn check_node_code_1() { let res = run_for_ms(&mut node_exec, 25.0); assert_decimated_feq!(res.0, 50, vec![0.3; 10]); } +