Skip to main content

sva_samples/machine/
mod.rs

1// Concern: runs one node renderer a sample at a time, whole or span after span | Non-concern: the op array's own shape (ops.rs), the tree the engine hands over | IO: (NodeRenderer, Ctx) -> Buffer
2
3pub mod ops;
4pub mod renderer;
5pub mod tape;
6
7use crate::buffer::Buffer;
8use crate::error::SampleError;
9use crate::filters::FilterSite;
10use crate::physics::{Solver, site};
11use ops::{Layout, Op, lower};
12use renderer::{NodeRenderer, Site};
13use tape::{Tape, Window};
14
15pub use ops::Layout as MachineLayout;
16
17/// The op array, one width per slot, and the call sites the run opens state for.
18struct Program {
19    ops: Vec<Op>,
20    widths: Vec<usize>,
21    sites: Vec<Site>,
22    pub width: usize,
23}
24
25/// `self_planes` is this node's own output so far, planar: component `c` owns
26/// `[c*len, (c+1)*len)`, and only the first `written` samples are founded. `run` refounds
27/// that mark every sample, so no block sizes the loop.
28pub struct Ctx<'a> {
29    pub rate: u32,
30    pub origin_secs: f64,
31    pub len: usize,
32    pub reads: &'a [Window<'a>],
33    pub self_planes: &'a [f64],
34    pub written: usize,
35}
36
37impl NodeRenderer {
38    fn compile(&self, layout: &Layout) -> Result<Program, SampleError> {
39        let (mut ops, mut widths) = (Vec::new(), Vec::new());
40        let width = lower(self, layout, &mut ops, &mut widths)?;
41        Ok(Program {
42            ops,
43            widths,
44            sites: layout.sites.clone(),
45            width,
46        })
47    }
48
49    /// A loop reads what this run wrote; a caller's own `self_planes` replaces it.
50    pub fn run(&self, layout: &Layout, ctx: &Ctx) -> Result<Buffer, SampleError> {
51        let mut machine = Machine::open(self, layout, ctx.rate, ctx.origin_secs)?;
52        let mut own = Tape::new(machine.width(), ctx.len);
53        let past = match ctx.self_planes.is_empty() {
54            true => Past::Own,
55            false => Past::Fixed {
56                planes: ctx.self_planes,
57                len: ctx.len,
58                written: ctx.written,
59            },
60        };
61        machine.steps(ctx.len, ctx.reads, &past, &mut own)?;
62        let mut out = Buffer::of_planes(ctx.rate, own.into_planes());
63        out.origin_secs = ctx.origin_secs;
64        Ok(out)
65    }
66}
67
68#[derive(Clone)]
69enum State {
70    Filter(FilterSite),
71    Physics(Box<dyn Solver>),
72}
73
74impl Clone for Box<dyn Solver> {
75    fn clone(&self) -> Box<dyn Solver> {
76        self.boxed()
77    }
78}
79
80/// A filter site carries one lane per component of its widest argument, which the compiled
81/// slot width already states.
82fn open(p: &Program, rate: u32) -> Result<Vec<State>, SampleError> {
83    let mut lanes = vec![1usize; p.sites.len()];
84    for (slot, op) in p.ops.iter().enumerate() {
85        if let Op::Filter(id) = op {
86            lanes[id.0 as usize] = p.widths[slot];
87        }
88    }
89    p.sites
90        .iter()
91        .zip(lanes)
92        .map(|(s, width)| {
93            Ok(match s {
94                Site::Filter(shape) => State::Filter(FilterSite::new(
95                    *shape,
96                    width,
97                    &[0.0],
98                    &[0.0],
99                    &[0.0],
100                    f64::from(rate),
101                )),
102                Site::Physics(params) => State::Physics(site(params, rate)?),
103            })
104        })
105        .collect()
106}
107
108/// One value slot per op and a stack of the indices waiting. Nothing allocates in the loop.
109struct Stack {
110    values: Vec<Vec<f64>>,
111    pending: Vec<usize>,
112}
113
114impl Stack {
115    fn of(widths: &[usize]) -> Stack {
116        Stack {
117            values: widths.iter().map(|&w| vec![0.0; w]).collect(),
118            pending: Vec::with_capacity(widths.len()),
119        }
120    }
121}
122
123/// Component `c` of an operand that may be mono where its neighbour is wide.
124fn part(v: &[f64], c: usize) -> f64 {
125    v[c % v.len()]
126}
127
128enum Past<'a> {
129    Own,
130    Fixed {
131        planes: &'a [f64],
132        len: usize,
133        written: usize,
134    },
135}
136
137/// One compiled node and every call site's state, run over any span of the grid in order.
138/// A span continues exactly where the last ended, so blocks write the samples one run would.
139pub struct Machine {
140    program: Program,
141    states: Vec<State>,
142    stack: Stack,
143    rate: u32,
144    origin_secs: f64,
145}
146
147#[derive(Clone)]
148pub struct MachineState {
149    sites: Vec<Site>,
150    states: Vec<State>,
151}
152
153impl Machine {
154    pub fn open(
155        renderer: &NodeRenderer,
156        layout: &Layout,
157        rate: u32,
158        origin_secs: f64,
159    ) -> Result<Machine, SampleError> {
160        let program = renderer.compile(layout)?;
161        let states = open(&program, rate)?;
162        let stack = Stack::of(&program.widths);
163        Ok(Machine {
164            program,
165            states,
166            stack,
167            rate,
168            origin_secs,
169        })
170    }
171
172    pub fn width(&self) -> usize {
173        self.program.width
174    }
175
176    pub fn run_to(
177        &mut self,
178        to: usize,
179        reads: &[Window],
180        own: &mut Tape,
181    ) -> Result<(), SampleError> {
182        for state in &mut self.states {
183            if let State::Filter(filter) = state {
184                filter.forget_frames();
185            }
186        }
187        self.steps(to, reads, &Past::Own, own)
188    }
189
190    fn steps(
191        &mut self,
192        to: usize,
193        reads: &[Window],
194        past: &Past,
195        own: &mut Tape,
196    ) -> Result<(), SampleError> {
197        let sr = f64::from(self.rate);
198        let p = &self.program;
199        for i in own.end()..to {
200            let t = self.origin_secs + i as f64 / sr;
201            let here = Here {
202                reads,
203                past,
204                own,
205                i,
206                t,
207                sr,
208            };
209            step(p, &here, &mut self.states, &mut self.stack)?;
210            let top = &self.stack.values[*self
211                .stack
212                .pending
213                .last()
214                .expect("a renderer leaves one value")];
215            for c in 0..p.width {
216                own.push(c, part(top, c));
217            }
218        }
219        Ok(())
220    }
221
222    /// Call site `site`'s solver as it stands now, where that site is one.
223    pub fn solver(&self, site: usize) -> Option<&dyn Solver> {
224        match self.states.get(site)? {
225            State::Physics(solver) => Some(solver.as_ref()),
226            State::Filter(_) => None,
227        }
228    }
229
230    pub fn filter(&self, site: usize) -> Option<&FilterSite> {
231        match self.states.get(site)? {
232            State::Filter(filter) => Some(filter),
233            State::Physics(_) => None,
234        }
235    }
236
237    pub fn state(&self) -> MachineState {
238        MachineState {
239            sites: self.program.sites.clone(),
240            states: self.states.clone(),
241        }
242    }
243
244    /// Takes `held`'s state site by site. A solver whose release alone moved keeps its own
245    /// parameters and takes only the motion; any other difference refuses.
246    pub fn carry(&mut self, held: &MachineState) -> Result<(), SampleError> {
247        if held.sites.len() != self.program.sites.len() {
248            return Err(SampleError::StateMismatch);
249        }
250        for (at, (mine, theirs)) in self.program.sites.iter().zip(&held.sites).enumerate() {
251            let taken = match (mine, theirs, &mut self.states[at], &held.states[at]) {
252                _ if mine == theirs => {
253                    self.states[at] = held.states[at].clone();
254                    true
255                }
256                (
257                    Site::Physics(a),
258                    Site::Physics(b),
259                    State::Physics(solver),
260                    State::Physics(motion),
261                ) if a.differs_in_release_alone(b) => solver.take_motion(motion.as_ref()),
262                _ => false,
263            };
264            if !taken {
265                return Err(SampleError::StateMismatch);
266            }
267        }
268        Ok(())
269    }
270}
271
272struct Here<'a> {
273    reads: &'a [Window<'a>],
274    past: &'a Past<'a>,
275    own: &'a Tape,
276    i: usize,
277    t: f64,
278    sr: f64,
279}
280
281/// One sample of the whole renderer. Postfix order puts every operand's slot before the slot
282/// that consumes it, so `split_at_mut` hands out the reads and the one write at once.
283fn step(
284    p: &Program,
285    here: &Here,
286    states: &mut [State],
287    stack: &mut Stack,
288) -> Result<(), SampleError> {
289    stack.pending.clear();
290    for (slot, op) in p.ops.iter().enumerate() {
291        let at = stack.pending.len() - arity_of(op);
292        let (done, rest) = stack.values.split_at_mut(slot);
293        fill(op, done, &stack.pending[at..], &mut rest[0], here, states)?;
294        stack.pending.truncate(at);
295        stack.pending.push(slot);
296    }
297    Ok(())
298}
299
300fn arity_of(op: &Op) -> usize {
301    match op {
302        Op::Const(_) | Op::Time | Op::Read { .. } | Op::SelfAt { .. } | Op::Physics(_) => 0,
303        Op::Map(_) | Op::Crop { .. } | Op::Channel(_) => 1,
304        Op::Sub | Op::Div | Op::Pow | Op::Zip(_) => 2,
305        Op::Add(n) | Op::Mul(n) | Op::Join(n) => *n,
306        Op::Filter(_) => 4,
307    }
308}
309
310fn fill(
311    op: &Op,
312    done: &[Vec<f64>],
313    srcs: &[usize],
314    result: &mut [f64],
315    here: &Here,
316    states: &mut [State],
317) -> Result<(), SampleError> {
318    let (i, t, sr) = (here.i, here.t, here.sr);
319    let arg = |k: usize| done[srcs[k]].as_slice();
320    match op {
321        Op::Const(v) => result[0] = *v,
322        Op::Time => result[0] = t,
323        Op::Read { id, shift } => {
324            let window = here.reads[id.0 as usize];
325            let at = i as i64 + shift;
326            for (c, slot) in result.iter_mut().enumerate() {
327                *slot = window.at(c, at);
328            }
329        }
330        Op::SelfAt { steps } => {
331            let at = i as i64 - i64::from(*steps);
332            for (c, slot) in result.iter_mut().enumerate() {
333                *slot = match here.past {
334                    Past::Own => here.own.window().at(c, at),
335                    Past::Fixed {
336                        planes,
337                        len,
338                        written,
339                    } => usize::try_from(at)
340                        .ok()
341                        .filter(|k| k < written)
342                        .and_then(|k| planes.get(c * len + k).copied())
343                        .unwrap_or(0.0),
344                };
345            }
346        }
347        Op::Add(_) | Op::Mul(_) => {
348            let product = matches!(op, Op::Mul(_));
349            for (c, slot) in result.iter_mut().enumerate() {
350                *slot =
351                    srcs.iter()
352                        .enumerate()
353                        .fold(f64::from(u8::from(product)), |acc, (k, _)| {
354                            if product {
355                                acc * part(arg(k), c)
356                            } else {
357                                acc + part(arg(k), c)
358                            }
359                        });
360            }
361        }
362        Op::Sub | Op::Div | Op::Pow | Op::Zip(_) => {
363            for (c, slot) in result.iter_mut().enumerate() {
364                let (a, b) = (part(arg(0), c), part(arg(1), c));
365                *slot = match op {
366                    Op::Sub => a - b,
367                    Op::Div => a / b,
368                    Op::Pow => a.powf(b),
369                    Op::Zip(f) => f.apply(a, b),
370                    _ => unreachable!("the arm's own guard"),
371                };
372            }
373        }
374        Op::Map(f) => {
375            for (c, slot) in result.iter_mut().enumerate() {
376                *slot = f.apply(part(arg(0), c));
377            }
378        }
379        Op::Crop { a, b, rise, fall } => {
380            let gain = crate::collapse::crop_gain(t, *a, *b, *rise, *fall);
381            for (c, slot) in result.iter_mut().enumerate() {
382                *slot = match gain {
383                    0.0 => 0.0,
384                    gain => part(arg(0), c) * gain,
385                };
386            }
387        }
388        Op::Join(_) => {
389            let mut c = 0;
390            for k in 0..srcs.len() {
391                for &v in arg(k) {
392                    result[c] = v;
393                    c += 1;
394                }
395            }
396        }
397        Op::Channel(k) => result[0] = arg(0)[*k],
398        Op::Filter(id) => {
399            let State::Filter(filter) = &mut states[id.0 as usize] else {
400                unreachable!("a filter op names a filter site")
401            };
402            filter.process(arg(0), arg(1), arg(2), arg(3), result, sr, i);
403        }
404        Op::Physics(id) => {
405            let State::Physics(solver) = &mut states[id.0 as usize] else {
406                unreachable!("a physics op names a physics site")
407            };
408            result[0] = solver.step()?;
409        }
410    }
411    Ok(())
412}