// Copyright (c) 2022 Weird Constructor // This file is a part of HexoDSP. Released under GPL-3.0-or-later. // See README.md and COPYING for details. use synfx_dsp_jit::*; use ringbuf::{Consumer, Producer, RingBuffer}; use std::cell::RefCell; use std::rc::Rc; const MAX_RINGBUF_SIZE: usize = 128; enum CodeUpdateMsg { UpdateFun(Box), } enum CodeReturnMsg { DestroyFun(Box), } pub struct CodeEngine { dsp_ctx: Rc>, lib: Rc>, update_prod: Producer, return_cons: Consumer, } impl Clone for CodeEngine { fn clone(&self) -> Self { CodeEngine::new() } } impl CodeEngine { pub fn new() -> Self { let rb = RingBuffer::new(MAX_RINGBUF_SIZE); let (update_prod, _update_cons) = rb.split(); let rb = RingBuffer::new(MAX_RINGBUF_SIZE); let (_return_prod, return_cons) = rb.split(); let lib = get_standard_library(); 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))?; let _ = self.update_prod.push(CodeUpdateMsg::UpdateFun(fun)); Ok(()) } pub fn cleanup(&self, fun: Box) { self.dsp_ctx.borrow_mut().cleanup_dsp_fun_after_user(fun); } pub fn query_returns(&mut self) { while let Some(msg) = self.return_cons.pop() { match msg { CodeReturnMsg::DestroyFun(fun) => { self.cleanup(fun); } } } } pub fn get_backend(&mut self) -> CodeEngineBackend { let rb = RingBuffer::new(MAX_RINGBUF_SIZE); let (update_prod, update_cons) = rb.split(); let rb = RingBuffer::new(MAX_RINGBUF_SIZE); let (return_prod, return_cons) = rb.split(); self.update_prod = update_prod; self.return_cons = return_cons; let function = get_nop_function(self.lib.clone(), self.dsp_ctx.clone()); CodeEngineBackend::new(function, update_cons, return_prod) } } impl Drop for CodeEngine { fn drop(&mut self) { self.dsp_ctx.borrow_mut().free(); } } pub struct CodeEngineBackend { sample_rate: f32, function: Box, update_cons: Consumer, return_prod: Producer, } impl CodeEngineBackend { fn new( function: Box, update_cons: Consumer, return_prod: Producer, ) -> Self { Self { sample_rate: 0.0, function, update_cons, return_prod } } #[inline] pub fn process( &mut self, in1: f32, in2: f32, a: f32, b: f32, d: f32, g: f32, ) -> (f32, f32, f32) { let mut s1 = 0.0_f64; let mut s2 = 0.0_f64; let res = self .function .exec(in1 as f64, in2 as f64, a as f64, b as f64, d as f64, g as f64, &mut s1, &mut s2); (s1 as f32, s2 as f32, res as f32) } pub fn swap_fun(&mut self, srate: f32, mut fun: Box) -> Box { std::mem::swap(&mut self.function, &mut fun); self.function.init(srate as f64, Some(&fun)); fun } pub fn set_sample_rate(&mut self, srate: f32) { self.sample_rate = srate; self.function.set_sample_rate(srate as f64); } pub fn clear(&mut self) { self.function.reset(); } pub fn process_updates(&mut self) { while let Some(msg) = self.update_cons.pop() { match msg { CodeUpdateMsg::UpdateFun(mut fun) => { std::mem::swap(&mut self.function, &mut fun); self.function.init(self.sample_rate as f64, Some(&fun)); let _ = self.return_prod.push(CodeReturnMsg::DestroyFun(fun)); } } } } }