Skip to main content

rill_lang/
program.rs

1//! `RillProgram<T>` — a compiled rill-lang program that implements
2//! [`rill_core::Algorithm`]. Owns its IR, schedule, and pre-allocated state;
3//! `process()` performs no heap allocation after warm-up.
4
5use rill_core::math::Transcendental;
6use rill_core::traits::{Algorithm, ProcessResult};
7
8use crate::builtin::{BlockBuiltin, SampleBuiltin};
9use crate::error::CompileError;
10use crate::ir::{Ir, ParamDef};
11use crate::schedule::{build_schedule, Schedule};
12
13/// A runtime built-in instance, indexed directly by IR `instance` fields.
14pub(crate) enum BuiltinInst<T: Transcendental> {
15    /// A per-sample stateful built-in.
16    Sample(Box<dyn SampleBuiltin<T>>),
17    /// An opaque whole-buffer built-in.
18    Block(Box<dyn BlockBuiltin<T>>),
19}
20
21/// A compiled program ready to run inside the rill graph.
22pub struct RillProgram<T: Transcendental> {
23    pub(crate) ir: Ir,
24    pub(crate) schedule: Schedule,
25    /// Persistent feedback state (previous-sample values). Length = state_slots.
26    pub(crate) state: Vec<f64>,
27    /// Next-sample feedback writes, applied at sample end.
28    pub(crate) state_next: Vec<f64>,
29    /// Delay lines: ring buffers, one per `@` site.
30    pub(crate) delays: Vec<DelayRing>,
31    /// Whole-buffer register store for the hybrid path (grown to block length).
32    pub(crate) block_regs: Vec<Vec<T>>,
33    /// Scalar register file for the reference (per-sample) path.
34    pub(crate) regs_scalar: Vec<f64>,
35    /// Runtime built-in instances (indexed by `ir.builtins` indices).
36    pub(crate) builtins: Vec<BuiltinInst<T>>,
37    /// Current parameter values, indexed by [`Ir::params`].
38    pub(crate) params: Vec<f64>,
39    /// Parameter metadata (name, default, range).
40    pub(crate) params_meta: Vec<ParamDef>,
41}
42
43/// A fixed-length ring buffer for one `@` delay site.
44pub(crate) struct DelayRing {
45    pub(crate) buf: Vec<f64>,
46    pub(crate) head: usize,
47}
48
49impl DelayRing {
50    pub(crate) fn new(len: usize) -> Self {
51        Self {
52            buf: vec![0.0; len.max(1)],
53            head: 0,
54        }
55    }
56    pub(crate) fn read(&self) -> f64 {
57        self.buf[self.head]
58    }
59    pub(crate) fn write(&mut self, v: f64) {
60        self.buf[self.head] = v;
61        self.head = (self.head + 1) % self.buf.len();
62    }
63}
64
65impl<T: Transcendental> RillProgram<T> {
66    pub(crate) fn new(ir: Ir) -> Self {
67        let state = vec![0.0; ir.state.state_slots];
68        let state_next = state.clone();
69        let delays = ir
70            .state
71            .delay_lens
72            .iter()
73            .map(|&l| DelayRing::new(l))
74            .collect();
75        let block_regs = vec![Vec::new(); ir.num_regs];
76        let regs_scalar = vec![0.0; ir.num_regs];
77        let schedule = build_schedule(&ir);
78        let params_meta = ir.params.clone();
79        let params = ir.params.iter().map(|p| p.default).collect();
80        Self {
81            ir,
82            schedule,
83            state,
84            state_next,
85            delays,
86            block_regs,
87            regs_scalar,
88            builtins: Vec::new(),
89            params,
90            params_meta,
91        }
92    }
93
94    pub(crate) fn new_with(
95        ir: Ir,
96        registry: &crate::builtin::Registry<T>,
97        sample_rate: f32,
98    ) -> Result<Self, CompileError> {
99        let mut builtins = Vec::with_capacity(ir.builtins.len());
100        for bi in &ir.builtins {
101            let entry = registry.get(&bi.name).ok_or_else(|| {
102                CompileError::Unsupported(format!("unknown built-in '{}'", bi.name))
103            })?;
104            match bi.kind {
105                crate::builtin::BuiltinKind::Sample => {
106                    let mut b = entry
107                        .build_sample(&bi.params, sample_rate)
108                        .expect("registry build_sample failed for sample builtin");
109                    b.init(sample_rate);
110                    builtins.push(BuiltinInst::Sample(b));
111                }
112                crate::builtin::BuiltinKind::Block => {
113                    let mut b = entry
114                        .build_block(&bi.params, sample_rate)
115                        .expect("registry build_block failed for block builtin");
116                    Algorithm::init(b.as_mut(), sample_rate);
117                    builtins.push(BuiltinInst::Block(b));
118                }
119            }
120        }
121        let state = vec![0.0; ir.state.state_slots];
122        let state_next = state.clone();
123        let delays = ir
124            .state
125            .delay_lens
126            .iter()
127            .map(|&l| DelayRing::new(l))
128            .collect();
129        let block_regs = vec![Vec::new(); ir.num_regs];
130        let regs_scalar = vec![0.0; ir.num_regs];
131        let schedule = build_schedule(&ir);
132        let params_meta = ir.params.clone();
133        let params = ir.params.iter().map(|p| p.default).collect();
134        Ok(Self {
135            ir,
136            schedule,
137            state,
138            state_next,
139            delays,
140            block_regs,
141            regs_scalar,
142            builtins,
143            params,
144            params_meta,
145        })
146    }
147
148    /// Ensure every block register can hold `n` samples (grows + reuses).
149    pub(crate) fn ensure_block_len(&mut self, n: usize) {
150        for r in &mut self.block_regs {
151            if r.len() < n {
152                r.resize(n, T::ZERO);
153            }
154        }
155    }
156
157    /// Index of a named parameter, if present.
158    pub fn param_index(&self, name: &str) -> Option<usize> {
159        self.params_meta.iter().position(|p| p.name == name)
160    }
161
162    /// Set a parameter by index (clamped to its range). RT-safe (plain store).
163    pub fn set_param(&mut self, idx: usize, value: f64) {
164        if let Some(def) = self.params_meta.get(idx) {
165            self.params[idx] = value.clamp(def.min, def.max);
166        }
167    }
168
169    /// Current value of a parameter by index.
170    pub fn param(&self, idx: usize) -> f64 {
171        self.params.get(idx).copied().unwrap_or(0.0)
172    }
173
174    /// Metadata for all parameters (name, default, range).
175    pub fn params_meta(&self) -> &[ParamDef] {
176        &self.params_meta
177    }
178
179    /// Reference implementation: the MVP per-sample interpreter. Used by tests
180    /// as a numerical oracle; not the production path.
181    pub fn process_reference(
182        &mut self,
183        input: Option<&[T]>,
184        output: &mut [T],
185    ) -> ProcessResult<()> {
186        crate::backend::interp::run_block_reference(self, input, output);
187        Ok(())
188    }
189
190    /// Forward initialisation to all built-in instances.
191    pub fn init(&mut self, sample_rate: f32) {
192        for b in &mut self.builtins {
193            match b {
194                BuiltinInst::Sample(inst) => inst.init(sample_rate),
195                BuiltinInst::Block(inst) => Algorithm::init(inst.as_mut(), sample_rate),
196            }
197        }
198    }
199}
200
201impl<T: Transcendental> Algorithm<T> for RillProgram<T> {
202    fn process(&mut self, input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
203        crate::backend::interp::run_block_hybrid(self, input, output);
204        Ok(())
205    }
206
207    fn reset(&mut self) {
208        for s in &mut self.state {
209            *s = 0.0;
210        }
211        for s in &mut self.state_next {
212            *s = 0.0;
213        }
214        for d in &mut self.delays {
215            for v in &mut d.buf {
216                *v = 0.0;
217            }
218            d.head = 0;
219        }
220        for b in &mut self.builtins {
221            match b {
222                BuiltinInst::Sample(inst) => inst.reset(),
223                BuiltinInst::Block(inst) => Algorithm::reset(inst.as_mut()),
224            }
225        }
226    }
227}