1use crate::dsp::{Node, ProcBuf, SAtom};
6use std::cell::RefCell;
7use triple_buffer::{Input, Output, TripleBuffer};
8
9thread_local! {
10 pub static NODE_PROG_ID_COUNTER: RefCell<usize> = RefCell::new(1);
11}
12
13#[derive(Debug, Clone)]
14pub struct ModOp {
15 amount: f32,
16 modbuf: ProcBuf,
17 outbuf: ProcBuf,
18 inbuf: ProcBuf,
19}
20
21impl Drop for ModOp {
22 fn drop(&mut self) {
23 self.modbuf.free();
24 }
25}
26
27impl ModOp {
28 pub fn new() -> Self {
29 Self {
30 amount: 0.0,
31 modbuf: ProcBuf::new(),
32 outbuf: ProcBuf::null(),
33 inbuf: ProcBuf::null(),
34 }
35 }
36
37 pub fn set_amt(&mut self, amt: f32) {
38 self.amount = amt;
39 }
40
41 pub fn lock(&mut self, inbuf: ProcBuf, outbuf: ProcBuf) -> ProcBuf {
42 self.inbuf = inbuf;
43 self.outbuf = outbuf;
44 self.modbuf
45 }
46
47 pub fn unlock(&mut self) {
48 self.outbuf = ProcBuf::null();
49 self.inbuf = ProcBuf::null();
50 }
51
52 #[inline]
53 pub fn process(&mut self, nframes: usize) {
54 let modbuf = &mut self.modbuf;
55 let inbuf = &mut self.inbuf;
56 let outbuf = &mut self.outbuf;
57
58 if inbuf.is_null() {
59 return;
60 }
61
62 for frame in 0..nframes {
63 modbuf.write(frame, inbuf.read(frame) + (self.amount * outbuf.read(frame)));
64 }
65 }
66}
67
68#[derive(Debug, Clone)]
71pub struct NodeOp {
72 pub idx: u8,
74 pub node: Node,
76 pub out_idxlen: (usize, usize),
78 pub in_idxlen: (usize, usize),
80 pub at_idxlen: (usize, usize),
82 pub mod_idxlen: (usize, usize),
84 pub inputs: Vec<(usize, usize, Option<usize>)>,
88 pub in_connected: u64,
91 pub out_connected: u64,
94}
95
96impl NodeOp {
97 pub fn in_idx_belongs_to_nodeop(&self, idx: usize) -> bool {
98 idx >= self.in_idxlen.0 && idx < self.in_idxlen.1
99 }
100
101 pub fn set_in_idx_connected_flag(&mut self, global_idx: usize) {
102 if !self.in_idx_belongs_to_nodeop(global_idx) {
103 return;
104 }
105
106 let local_idx = global_idx - self.in_idxlen.0;
107 self.in_connected |= 0x1 << local_idx;
108 }
109
110 pub fn out_idx_belongs_to_nodeop(&self, idx: usize) -> bool {
111 idx >= self.out_idxlen.0 && idx < self.out_idxlen.1
112 }
113
114 pub fn set_out_idx_connected_flag(&mut self, global_idx: usize) {
115 if !self.out_idx_belongs_to_nodeop(global_idx) {
116 return;
117 }
118
119 let local_idx = global_idx - self.out_idxlen.0;
120 self.out_connected |= 0x1 << local_idx;
121 }
122}
123
124impl std::fmt::Display for NodeOp {
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(
127 f,
128 "Op(i={} out=({}-{}|{:x}) in=({}-{}|{:x}) at=({}-{}) mod=({}-{})",
129 self.idx,
130 self.out_idxlen.0,
131 self.out_idxlen.1,
132 self.out_connected,
133 self.in_idxlen.0,
134 self.in_idxlen.1,
135 self.in_connected,
136 self.at_idxlen.0,
137 self.at_idxlen.1,
138 self.mod_idxlen.0,
139 self.mod_idxlen.1
140 )?;
141
142 for i in self.inputs.iter() {
143 write!(f, " cpy=(o{} => i{})", i.0, i.1)?;
144 }
145
146 for i in self.inputs.iter() {
147 if let Some(idx) = i.2 {
148 write!(f, " mod={}", idx)?;
149 }
150 }
151
152 write!(f, ")")
153 }
154}
155
156#[derive(Debug)]
159pub struct NodeProg {
160 pub inp: Vec<ProcBuf>,
164
165 pub cur_inp: Vec<ProcBuf>,
168
169 pub out: Vec<ProcBuf>,
171
172 pub params: Vec<f32>,
175
176 pub atoms: Vec<SAtom>,
179
180 pub prog: Vec<NodeOp>,
183
184 pub modops: Vec<ModOp>,
186
187 pub locked_buffers: bool,
192
193 pub out_feedback: Input<Vec<f32>>,
196
197 pub out_fb_cons: Option<Output<Vec<f32>>>,
199
200 pub unique_id: usize,
203}
204
205impl Drop for NodeProg {
206 fn drop(&mut self) {
207 for buf in self.inp.iter_mut() {
208 buf.free();
209 }
210
211 for buf in self.out.iter_mut() {
212 buf.free();
213 }
214 }
215}
216
217fn new_node_prog_id() -> usize {
218 NODE_PROG_ID_COUNTER.with(|cnt| {
219 let unique_id = *cnt.borrow();
220 *cnt.borrow_mut() += 1;
221 unique_id
222 })
223}
224
225impl NodeProg {
226 pub fn empty() -> Self {
227 let out_fb = vec![];
228 let tb = TripleBuffer::new(out_fb);
229 let (input_fb, output_fb) = tb.split();
230 Self {
231 out: vec![],
232 inp: vec![],
233 cur_inp: vec![],
234 params: vec![],
235 atoms: vec![],
236 prog: vec![],
237 modops: vec![],
238 out_feedback: input_fb,
239 out_fb_cons: Some(output_fb),
240 locked_buffers: false,
241 unique_id: new_node_prog_id(),
242 }
243 }
244
245 pub fn new(out_len: usize, inp_len: usize, at_len: usize, mod_len: usize) -> Self {
246 let mut out = vec![];
247 out.resize_with(out_len, ProcBuf::new);
248
249 let out_fb = vec![0.0; out_len];
250 let tb = TripleBuffer::new(out_fb);
251 let (input_fb, output_fb) = tb.split();
252
253 let mut inp = vec![];
254 inp.resize_with(inp_len, ProcBuf::new);
255 let mut cur_inp = vec![];
256 cur_inp.resize_with(inp_len, ProcBuf::null);
257
258 let mut params = vec![];
259 params.resize(inp_len, 0.0);
260 let mut atoms = vec![];
261 atoms.resize(at_len, SAtom::setting(0));
262 let mut modops = vec![];
263 modops.resize_with(mod_len, ModOp::new);
264
265 Self {
266 out,
267 inp,
268 cur_inp,
269 params,
270 atoms,
271 modops,
272 prog: vec![],
273 out_feedback: input_fb,
274 out_fb_cons: Some(output_fb),
275 locked_buffers: false,
276 unique_id: new_node_prog_id(),
277 }
278 }
279
280 pub fn take_feedback_consumer(&mut self) -> Option<Output<Vec<f32>>> {
281 self.out_fb_cons.take()
282 }
283
284 pub fn params_mut(&mut self) -> &mut [f32] {
285 &mut self.params
286 }
287
288 pub fn atoms_mut(&mut self) -> &mut [SAtom] {
289 &mut self.atoms
290 }
291
292 pub fn modops_mut(&mut self) -> &mut [ModOp] {
293 &mut self.modops
294 }
295
296 pub fn append_op(&mut self, mut node_op: NodeOp) {
297 for n_op in self.prog.iter_mut() {
298 if n_op.idx == node_op.idx {
299 return;
300 }
301 }
302
303 node_op.out_connected = 0x0;
304 node_op.in_connected = 0x0;
305 self.prog.push(node_op);
306 }
307
308 pub fn append_edge(
309 &mut self,
310 node_op: NodeOp,
311 inp_index: usize,
312 out_index: usize,
313 mod_index: Option<usize>,
314 ) {
315 for n_op in self.prog.iter_mut() {
316 if n_op.out_idx_belongs_to_nodeop(out_index) {
317 n_op.set_out_idx_connected_flag(out_index);
318 }
319 }
320
321 for n_op in self.prog.iter_mut() {
322 if n_op.idx == node_op.idx {
323 n_op.set_in_idx_connected_flag(inp_index);
324 n_op.inputs.push((out_index, inp_index, mod_index));
325 return;
326 }
327 }
328 }
329
330 pub fn initialize_input_buffers(&mut self) {
334 for param_idx in 0..self.params.len() {
335 let param_val = self.params[param_idx];
336 self.inp[param_idx].fill(param_val);
337 }
338 }
339
340 pub fn swap_previous_outputs(&mut self, prev_prog: &mut NodeProg) {
341 if self.locked_buffers {
342 self.unlock_buffers();
343 }
344
345 if prev_prog.locked_buffers {
346 prev_prog.unlock_buffers();
347 }
348
349 for (old_inp_pb, new_inp_pb) in prev_prog.inp.iter_mut().zip(self.inp.iter_mut()) {
352 std::mem::swap(old_inp_pb, new_inp_pb);
353 }
354 }
355
356 pub fn unlock_buffers(&mut self) {
357 for buf in self.cur_inp.iter_mut() {
358 *buf = ProcBuf::null();
359 }
360 for modop in self.modops.iter_mut() {
361 modop.unlock();
362 }
363 self.locked_buffers = false;
364 }
365
366 pub fn assign_outputs(&mut self) {
367 for op in self.prog.iter() {
368 let input_bufs = &mut self.cur_inp;
384 let out_bufs = &mut self.out;
385
386 let inp = op.in_idxlen;
387
388 input_bufs[inp.0..inp.1].copy_from_slice(&self.inp[inp.0..inp.1]);
390
391 for io in op.inputs.iter() {
393 input_bufs[io.1] = out_bufs[io.0];
394
395 if let Some(idx) = io.2 {
396 input_bufs[io.1] = self.modops[idx].lock(self.inp[io.1], out_bufs[io.0]);
397 }
398 }
399 }
400
401 self.locked_buffers = true;
402 }
403}