rill_lang/
graph_compiler.rs1use std::collections::HashMap;
4
5use rill_core::buffer::{Buffer, FixedBuffer};
6use rill_core::math::Transcendental;
7use rill_core::traits::{Algorithm, MultichannelAlgorithm, ProcessResult};
8
9use crate::builtin::Registry;
10use crate::graph_ir::{EdgeKind, GraphIr};
11
12pub enum AlgorithmVariant<T: Transcendental> {
14 Siso(Box<dyn crate::builtin::BlockBuiltin<T>>),
16 Mimo(Box<dyn rill_core::builtin::MultichannelBlockBuiltin<T>>),
18}
19
20pub struct NodeClosure<T: Transcendental, const BUF_SIZE: usize> {
22 pub(crate) algo: AlgorithmVariant<T>,
23 input_indices: Vec<usize>,
24 output_indices: Vec<usize>,
25 input_slices: Vec<&'static [T]>,
27 output_slices: Vec<&'static mut [T]>,
28}
29
30impl<T: Transcendental, const BUF_SIZE: usize> NodeClosure<T, BUF_SIZE> {
31 #[allow(unsafe_code)]
33 pub fn execute(&mut self, buffers: &mut [FixedBuffer<T, BUF_SIZE>]) -> ProcessResult<()> {
34 match &mut self.algo {
35 AlgorithmVariant::Siso(algo) => {
36 let bufs_ptr = buffers.as_mut_ptr();
37 let input = if self.input_indices.is_empty() {
38 None
39 } else {
40 let in_buf = unsafe { &*bufs_ptr.add(self.input_indices[0]) };
41 Some(in_buf.as_slice())
42 };
43 let out_buf = unsafe { &mut *bufs_ptr.add(self.output_indices[0]) };
44 Algorithm::process(algo.as_mut(), input, out_buf.as_mut_slice())?;
45 }
46 AlgorithmVariant::Mimo(algo) => {
47 let bufs_ptr = buffers.as_mut_ptr();
48 self.input_slices.clear();
49 for &idx in &self.input_indices {
50 let buf = unsafe { &*bufs_ptr.add(idx) };
51 self.input_slices
52 .push(unsafe { std::mem::transmute::<&[T], &'static [T]>(buf.as_slice()) });
53 }
54 self.output_slices.clear();
55 for &idx in &self.output_indices {
56 let buf = unsafe { &mut *bufs_ptr.add(idx) };
57 self.output_slices.push(unsafe {
58 std::mem::transmute::<&mut [T], &'static mut [T]>(buf.as_mut_slice())
59 });
60 }
61 MultichannelAlgorithm::process(
62 algo.as_mut(),
63 &self.input_slices,
64 &mut self.output_slices,
65 )?;
66 }
67 }
68 Ok(())
69 }
70
71 pub fn set_param(&mut self, index: usize, value: &rill_core::traits::ParamValue) {
73 match &mut self.algo {
74 AlgorithmVariant::Siso(algo) => algo.set_param(index, value),
75 AlgorithmVariant::Mimo(algo) => algo.set_param(index, value),
76 }
77 }
78
79 pub fn reset(&mut self) {
81 match &mut self.algo {
82 AlgorithmVariant::Siso(algo) => Algorithm::reset(algo.as_mut()),
83 AlgorithmVariant::Mimo(algo) => MultichannelAlgorithm::reset(algo.as_mut()),
84 }
85 }
86}
87
88pub struct CompiledGraph<T: Transcendental, const BUF_SIZE: usize> {
90 pub buffers: Vec<FixedBuffer<T, BUF_SIZE>>,
92 pub nodes: Vec<NodeClosure<T, BUF_SIZE>>,
94 pub inputs: usize,
96 pub outputs: usize,
98 pub output_mapping: Vec<usize>,
100 pub node_names: Vec<String>,
102 pub node_param_names: Vec<Vec<String>>,
104}
105
106pub fn compile<T: Transcendental, const BUF_SIZE: usize>(
108 ir: &GraphIr,
109 registry: &Registry<T>,
110 sample_rate: f32,
111) -> Result<CompiledGraph<T, BUF_SIZE>, String> {
112 let mut edge_buffers: HashMap<(String, usize, String, usize), usize> = HashMap::new();
114 let mut buffer_counter: usize = ir.inputs;
115 let mut output_bufs_per_node: Vec<Vec<usize>> = Vec::new();
116
117 for name in &ir.topo_order {
118 let node = ir.nodes.get(name).unwrap();
119 let mut output_bufs = Vec::new();
120 for _port in 0..node.arity.1 {
121 let buf = buffer_counter;
122 buffer_counter += 1;
123 output_bufs.push(buf);
124 }
125 output_bufs_per_node.push(output_bufs.clone());
126
127 for edge in &ir.edges {
128 if edge.from_node == *name && edge.kind == EdgeKind::Signal {
129 edge_buffers.insert(
130 (
131 edge.from_node.clone(),
132 edge.from_port,
133 edge.to_node.clone(),
134 edge.to_port,
135 ),
136 output_bufs[edge.from_port],
137 );
138 }
139 }
140 }
141
142 let n_bufs = buffer_counter;
143
144 let mut nodes: Vec<NodeClosure<T, BUF_SIZE>> = Vec::new();
146 let mut node_names: Vec<String> = Vec::new();
147 let mut node_param_names_sets: Vec<Vec<String>> = Vec::new();
148
149 for (idx, name) in ir.topo_order.iter().enumerate() {
150 let node = ir.nodes.get(name).unwrap();
151
152 let mut input_bufs: Vec<usize> = Vec::new();
153 for edge in &ir.edges {
154 if edge.to_node == *name && edge.kind == EdgeKind::Signal {
155 let key = (
156 edge.from_node.clone(),
157 edge.from_port,
158 edge.to_node.clone(),
159 edge.to_port,
160 );
161 if let Some(&buf) = edge_buffers.get(&key) {
162 if input_bufs.len() <= edge.to_port {
163 input_bufs.resize(edge.to_port + 1, 0);
164 }
165 input_bufs[edge.to_port] = buf;
166 }
167 }
168 }
169
170 if input_bufs.is_empty() && idx < ir.inputs {
171 for port in 0..node.arity.0.min(ir.inputs) {
172 input_bufs.push(port);
173 }
174 }
175
176 let output_bufs = output_bufs_per_node[idx].clone();
177 let n_in = input_bufs.len();
178 let n_out = output_bufs.len();
179
180 let param_names: Vec<String> = node.ir.params.iter().map(|p| p.name.clone()).collect();
181 node_param_names_sets.push(param_names);
182
183 let algo = if n_in <= 1 && n_out == 1 {
184 let prog =
185 crate::program::RillProgram::<T>::new_with(node.ir.clone(), registry, sample_rate)
186 .map_err(|e| format!("program creation: {e}"))?;
187 AlgorithmVariant::Siso(Box::new(prog))
188 } else {
189 let bi = node
190 .ir
191 .builtins
192 .first()
193 .ok_or_else(|| format!("MIMO node '{}' has no builtins", name))?;
194 let entry = registry
195 .get(&bi.name)
196 .ok_or_else(|| format!("unknown builtin: {}", bi.name))?;
197
198 let mimo = entry
199 .build_multichannel_block(&bi.params, sample_rate)
200 .ok_or_else(|| {
201 format!(
202 "failed to build multichannel block: {}. Is it registered via register_multichannel_block?",
203 bi.name
204 )
205 })?;
206 AlgorithmVariant::Mimo(mimo)
207 };
208
209 nodes.push(NodeClosure {
210 algo,
211 input_indices: input_bufs,
212 output_indices: output_bufs,
213 input_slices: Vec::with_capacity(n_in),
214 output_slices: Vec::with_capacity(n_out),
215 });
216 node_names.push(name.clone());
217 }
218
219 let mut output_mapping = Vec::new();
221 for name in &ir.topo_order {
222 let is_leaf = !ir
223 .edges
224 .iter()
225 .any(|e| e.from_node == *name && e.kind == EdgeKind::Signal);
226 if is_leaf {
227 let idx = ir.topo_order.iter().position(|n| n == name).unwrap();
228 for &buf in &output_bufs_per_node[idx] {
229 output_mapping.push(buf);
230 }
231 }
232 }
233
234 let buffers = vec![FixedBuffer::<T, BUF_SIZE>::new(); n_bufs];
235
236 Ok(CompiledGraph {
237 buffers,
238 nodes,
239 inputs: ir.inputs,
240 outputs: ir.outputs,
241 output_mapping,
242 node_names,
243 node_param_names: node_param_names_sets,
244 })
245}