1use rill_core::builtin::MultichannelBlockBuiltin;
6use rill_core::math::Transcendental;
7#[cfg(feature = "router")]
8use rill_core::traits::MultichannelAlgorithm;
9use rill_core::traits::{Algorithm, ParamValue, ProcessResult};
10
11use crate::builtin::{BlockBuiltin, SampleBuiltin};
12use crate::error::CompileError;
13use crate::ir::{Ir, ParamDef};
14use crate::schedule::{build_schedule, Schedule};
15
16pub(crate) enum BuiltinInst<T: Transcendental> {
18 Sample(Box<dyn SampleBuiltin<T>>),
20 Block(Box<dyn BlockBuiltin<T>>),
22 #[allow(dead_code)]
24 MultichannelBlock(Box<dyn MultichannelBlockBuiltin<T>>),
25}
26
27pub struct RillProgram<T: Transcendental> {
29 pub(crate) ir: Ir,
30 pub(crate) schedule: Schedule,
31 pub(crate) state: Vec<f64>,
33 pub(crate) state_next: Vec<f64>,
35 pub(crate) delays: Vec<DelayRing>,
37 pub(crate) block_regs: Vec<Vec<T>>,
39 pub(crate) regs_scalar: Vec<f64>,
41 pub(crate) builtins: Vec<BuiltinInst<T>>,
43 pub(crate) params: Vec<ParamValue>,
45 pub(crate) params_dirty: Vec<bool>,
47 pub(crate) params_meta: Vec<ParamDef>,
49}
50
51pub(crate) struct DelayRing {
53 pub(crate) buf: Vec<f64>,
54 pub(crate) head: usize,
55}
56
57impl DelayRing {
58 pub(crate) fn new(len: usize) -> Self {
59 Self {
60 buf: vec![0.0; len.max(1)],
61 head: 0,
62 }
63 }
64 pub(crate) fn read(&self) -> f64 {
65 self.buf[self.head]
66 }
67 pub(crate) fn write(&mut self, v: f64) {
68 self.buf[self.head] = v;
69 self.head = (self.head + 1) % self.buf.len();
70 }
71}
72
73impl<T: Transcendental> RillProgram<T> {
74 pub fn new(ir: Ir) -> Self {
78 let state = vec![0.0; ir.state.state_slots];
79 let state_next = state.clone();
80 let delays = ir
81 .state
82 .delay_lens
83 .iter()
84 .map(|&l| DelayRing::new(l))
85 .collect();
86 let block_regs = vec![Vec::new(); ir.num_regs];
87 let regs_scalar = vec![0.0; ir.num_regs];
88 let schedule = build_schedule(&ir);
89 let params_meta = ir.params.clone();
90 let params: Vec<ParamValue> = ir
91 .params
92 .iter()
93 .map(|p| ParamValue::Float(p.default as f32))
94 .collect();
95 let params_dirty = vec![false; params.len()];
96 Self {
97 ir,
98 schedule,
99 state,
100 state_next,
101 delays,
102 block_regs,
103 regs_scalar,
104 builtins: Vec::new(),
105 params,
106 params_dirty,
107 params_meta,
108 }
109 }
110
111 pub fn new_with(
118 ir: Ir,
119 registry: &crate::builtin::Registry<T>,
120 sample_rate: f32,
121 ) -> Result<Self, CompileError> {
122 let mut builtins = Vec::with_capacity(ir.builtins.len());
123 for bi in &ir.builtins {
124 let entry = registry.get(&bi.name).ok_or_else(|| {
125 CompileError::Unsupported(format!("unknown built-in '{}'", bi.name))
126 })?;
127 match bi.kind {
128 crate::builtin::BuiltinKind::Sample => {
129 let mut b = entry
130 .build_sample(&bi.params, sample_rate)
131 .expect("registry build_sample failed for sample builtin");
132 b.init(sample_rate);
133 builtins.push(BuiltinInst::Sample(b));
134 }
135 crate::builtin::BuiltinKind::Block => {
136 let mut b = entry
137 .build_block(&bi.params, sample_rate)
138 .expect("registry build_block failed for block builtin");
139 Algorithm::init(b.as_mut(), sample_rate);
140 builtins.push(BuiltinInst::Block(b));
141 }
142 }
143 }
144 let state = vec![0.0; ir.state.state_slots];
145 let state_next = state.clone();
146 let delays = ir
147 .state
148 .delay_lens
149 .iter()
150 .map(|&l| DelayRing::new(l))
151 .collect();
152 let block_regs = vec![Vec::new(); ir.num_regs];
153 let regs_scalar = vec![0.0; ir.num_regs];
154 let schedule = build_schedule(&ir);
155 let params_meta = ir.params.clone();
156 let params: Vec<ParamValue> = ir
157 .params
158 .iter()
159 .map(|p| ParamValue::Float(p.default as f32))
160 .collect();
161 let params_dirty = vec![false; params.len()];
162 Ok(Self {
163 ir,
164 schedule,
165 state,
166 state_next,
167 delays,
168 block_regs,
169 regs_scalar,
170 builtins,
171 params,
172 params_dirty,
173 params_meta,
174 })
175 }
176
177 pub(crate) fn ensure_block_len(&mut self, n: usize) {
179 for r in &mut self.block_regs {
180 if r.len() < n {
181 r.resize(n, T::ZERO);
182 }
183 }
184 }
185
186 pub fn param_index(&self, name: &str) -> Option<usize> {
188 self.params_meta.iter().position(|p| p.name == name)
189 }
190
191 pub fn set_param(&mut self, idx: usize, value: ParamValue) {
193 if let Some(def) = self.params_meta.get(idx) {
194 let clamped = match &value {
195 ParamValue::Float(v) => {
196 ParamValue::Float((*v as f64).clamp(def.min, def.max) as f32)
197 }
198 ParamValue::Int(v) if *v as f64 >= def.min && (*v as f64) <= def.max => value,
199 _ => value,
200 };
201 self.params[idx] = clamped;
202 if let Some(d) = self.params_dirty.get_mut(idx) {
203 *d = true;
204 }
205 }
206 }
207
208 pub fn param(&self, idx: usize) -> ParamValue {
210 self.params
211 .get(idx)
212 .cloned()
213 .unwrap_or(ParamValue::Float(0.0))
214 }
215
216 pub fn params_meta(&self) -> &[ParamDef] {
218 &self.params_meta
219 }
220
221 pub fn process_reference(
224 &mut self,
225 input: Option<&[T]>,
226 output: &mut [T],
227 ) -> ProcessResult<()> {
228 crate::backend::interp::run_block_reference(self, input, output);
229 Ok(())
230 }
231
232 pub fn init(&mut self, sample_rate: f32) {
234 for b in &mut self.builtins {
235 match b {
236 BuiltinInst::Sample(inst) => inst.init(sample_rate),
237 BuiltinInst::Block(inst) => Algorithm::init(inst.as_mut(), sample_rate),
238 BuiltinInst::MultichannelBlock(_) => {}
239 }
240 }
241 }
242}
243
244impl<T: Transcendental> Algorithm<T> for RillProgram<T> {
245 fn process(&mut self, input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
246 crate::backend::interp::run_block_hybrid(self, input, output);
247 Ok(())
248 }
249
250 fn reset(&mut self) {
251 for s in &mut self.state {
252 *s = 0.0;
253 }
254 for s in &mut self.state_next {
255 *s = 0.0;
256 }
257 for d in &mut self.delays {
258 for v in &mut d.buf {
259 *v = 0.0;
260 }
261 d.head = 0;
262 }
263 for b in &mut self.builtins {
264 match b {
265 BuiltinInst::Sample(inst) => inst.reset(),
266 BuiltinInst::Block(inst) => Algorithm::reset(inst.as_mut()),
267 BuiltinInst::MultichannelBlock(_) => {}
268 }
269 }
270 }
271}
272
273#[cfg(feature = "router")]
274impl<T: Transcendental> MultichannelAlgorithm<T> for RillProgram<T> {
275 fn num_inputs(&self) -> usize {
276 self.ir.num_inputs
277 }
278
279 fn num_outputs(&self) -> usize {
280 self.ir.num_outputs
281 }
282
283 fn process(&mut self, inputs: &[&[T]], outputs: &mut [&mut [T]]) -> ProcessResult<()> {
284 let n_in = inputs.len();
285 let n_out = outputs.len();
286 let buf_size = if n_out > 0 { outputs[0].len() } else { 0 };
287
288 if n_in <= 1 && n_out == 1 {
289 let input = if n_in == 0 { None } else { Some(inputs[0]) };
290 return Algorithm::process(self, input, outputs[0]);
291 }
292
293 crate::backend::interp::push_builtin_params(self);
294 for sample_idx in 0..buf_size {
295 let in_sample = if n_in > 0 {
296 inputs[0][sample_idx].to_f64()
297 } else {
298 0.0
299 };
300 let y = crate::backend::interp::eval_sample_scalar(self, in_sample);
301 if n_out > 0 {
302 outputs[0][sample_idx] = T::from_f64(y);
303 }
304 }
305 Ok(())
306 }
307
308 fn reset(&mut self) {
309 Algorithm::reset(self);
310 }
311}
312
313impl<T: Transcendental> BlockBuiltin<T> for RillProgram<T> {
314 fn set_param(&mut self, index: usize, value: &ParamValue) {
315 self.set_param(index, value.clone());
316 }
317}