Skip to main content

alpha2_audio/
graph.rs

1#![allow(clippy::needless_range_loop)]
2
3use crate::{ChannelLayout, ProcessCtx, Processor, StateBlob, StateError};
4
5/// Why a [`ProcessorChain`] refuses a processor.
6#[derive(Copy, Clone, Debug, Eq, PartialEq)]
7#[non_exhaustive]
8pub enum ChainError {
9    /// The processor reads a second bus after its own channels
10    /// ([`Processor::inputs_per_output`]): a sidechain, or the other side of
11    /// a crossfade. A chain gives each node only its own channels.
12    MultipleInputs,
13}
14
15impl core::fmt::Display for ChainError {
16    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17        formatter.write_str(match self {
18            Self::MultipleInputs => {
19                "a processor chain takes only processors that read one input per output"
20            }
21        })
22    }
23}
24
25impl std::error::Error for ChainError {}
26
27/// Where a [`ProcessorChain`] refused a saved state, and why. The chain is
28/// left as it was.
29#[derive(Copy, Clone, Debug, Eq, PartialEq)]
30#[non_exhaustive]
31pub struct ChainStateError {
32    /// The node that refused its part, counted from the first; `None` for
33    /// the chain's own: its node count, framing, or bypass record.
34    pub node: Option<usize>,
35    /// Why.
36    pub error: StateError,
37}
38
39impl ChainStateError {
40    const fn chain(error: StateError) -> Self {
41        Self { node: None, error }
42    }
43}
44
45impl core::fmt::Display for ChainStateError {
46    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
47        match self.node {
48            Some(node) => write!(formatter, "node {node}: {}", self.error),
49            None => write!(formatter, "the chain: {}", self.error),
50        }
51    }
52}
53
54impl std::error::Error for ChainStateError {
55    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
56        Some(&self.error)
57    }
58}
59
60/// Where an [`AudioGraph`] refused a saved state, and why. The graph is
61/// left as it was.
62#[derive(Copy, Clone, Debug, Eq, PartialEq)]
63#[non_exhaustive]
64pub enum GraphStateError {
65    /// The state was saved by a graph with another number of buses.
66    Topology,
67    /// A bus's clip chain refused its part.
68    ClipChain {
69        /// The bus.
70        bus: usize,
71        /// Where in the chain, and why.
72        error: ChainStateError,
73    },
74    /// A track bus refused its part.
75    TrackBus {
76        /// The bus.
77        bus: usize,
78        /// Where in the chain, and why.
79        error: ChainStateError,
80    },
81    /// The master chain refused its part.
82    Master(ChainStateError),
83    /// The audio a bus's alignment holds does not fit it: the buses'
84    /// latencies differ from the saving graph's.
85    Alignment {
86        /// The bus.
87        bus: usize,
88        /// Why.
89        error: StateError,
90    },
91}
92
93impl core::fmt::Display for GraphStateError {
94    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
95        match self {
96            Self::Topology => {
97                formatter.write_str("the saved state was made by a graph with other buses")
98            }
99            Self::ClipChain { bus, error } => write!(formatter, "clip chain {bus}, {error}"),
100            Self::TrackBus { bus, error } => write!(formatter, "track bus {bus}, {error}"),
101            Self::Master(error) => write!(formatter, "master, {error}"),
102            Self::Alignment { bus, error } => write!(formatter, "bus {bus} alignment: {error}"),
103        }
104    }
105}
106
107impl std::error::Error for GraphStateError {}
108
109/// How far a chain has been prepared since its processors last changed.
110#[derive(Copy, Clone, Debug, Eq, PartialEq)]
111enum Prepared {
112    /// Never, or a processor was pushed since: [`ProcessorChain::process`]
113    /// and [`ProcessorChain::process_in_place`] pass their input through.
114    No,
115    /// By [`ProcessorChain::prepare_nodes`]: only
116    /// [`ProcessorChain::process_in_place`] runs the nodes.
117    Nodes,
118    /// By [`ProcessorChain::prepare`].
119    Full,
120}
121
122/// Ordered processor chain with all realtime scratch allocated by `prepare`.
123///
124/// A chain runs its nodes only once prepared: before its first `prepare`,
125/// and after a `push` until the next one, it passes its input through.
126///
127/// Each node gets the context of the audio it receives: the chain input's,
128/// moved back by the latency of the nodes before it, so a sample-addressed
129/// node (a fade, automation) after a lookahead acts on the audio it scales.
130pub struct ProcessorChain {
131    processors: Vec<Box<dyn Processor>>,
132    layout: ChannelLayout,
133    scratch_a: [Vec<f32>; 2],
134    scratch_b: [Vec<f32>; 2],
135    bypass: bool,
136    prepared: Prepared,
137    max_block: usize,
138    /// Whether the chain keeps [`Self::dry`]: from its first
139    /// [`Self::set_bypass`] on.
140    bypassable: bool,
141    /// The chain's input over its last [`Self::latency_samples`], which a
142    /// bypassed chain plays.
143    dry: Delay,
144}
145
146impl ProcessorChain {
147    /// Creates an empty chain for a validated layout.
148    #[must_use]
149    pub fn new(layout: ChannelLayout) -> Self {
150        Self {
151            processors: Vec::new(),
152            layout,
153            scratch_a: [Vec::new(), Vec::new()],
154            scratch_b: [Vec::new(), Vec::new()],
155            bypass: false,
156            prepared: Prepared::No,
157            max_block: 0,
158            bypassable: false,
159            dry: Delay::default(),
160        }
161    }
162    /// Adds a processor outside the realtime path. The chain passes its
163    /// input through until it is prepared again.
164    ///
165    /// # Errors
166    /// Returns [`ChainError::MultipleInputs`], and leaves the chain as it
167    /// was, for a processor that reads more than one input per output.
168    pub fn push(&mut self, processor: impl Processor + 'static) -> Result<(), ChainError> {
169        if processor.inputs_per_output() != 1 {
170            return Err(ChainError::MultipleInputs);
171        }
172        self.processors.push(Box::new(processor));
173        self.prepared = Prepared::No;
174        Ok(())
175    }
176    /// Bypasses the whole chain, or ends the bypass, outside the realtime
177    /// path.
178    ///
179    /// A bypassed chain plays its input delayed by its
180    /// [`Self::latency_samples`], which it keeps reporting, so a host that
181    /// compensates the latency stays aligned; and its nodes keep running on
182    /// the input, so ending the bypass plays what they make of the current
183    /// audio, never audio from before the bypass. The delayed input comes
184    /// from a record the chain keeps from its first call here on (either
185    /// way; it allocates it then, or at the next prepare): bypassed sooner
186    /// after that than its latency, it plays silence for the part it has no
187    /// record of. A chain never bypassed keeps no record and costs nothing.
188    pub fn set_bypass(&mut self, bypass: bool) {
189        self.bypass = bypass;
190        if !self.bypassable {
191            self.bypassable = true;
192            if self.prepared != Prepared::No {
193                self.dry
194                    .resize(self.delay_samples(), self.layout.channels());
195            }
196        }
197        // `process_in_place` runs bypassed nodes into scratch of the chain's own.
198        if bypass && self.prepared == Prepared::Nodes {
199            self.allocate_scratch(false);
200        }
201    }
202    /// Whether [`Self::process`] copies its input to its output unchanged
203    /// and touches nothing else, so a host may skip it: the chain is empty,
204    /// or not prepared since its last push. (A bypassed chain still runs its
205    /// nodes.)
206    #[must_use]
207    pub fn passes_through(&self) -> bool {
208        self.processors.is_empty() || self.prepared == Prepared::No
209    }
210    /// Whether the chain has been prepared since its processors last
211    /// changed, so it runs them.
212    #[must_use]
213    pub fn is_prepared(&self) -> bool {
214        self.prepared != Prepared::No
215    }
216    /// Allocates scratch and prepares every node.
217    pub fn prepare(&mut self, sample_rate: u32, max_block: usize) {
218        for processor in &mut self.processors {
219            processor.prepare(sample_rate, max_block);
220        }
221        // At least one sample, so a block of any length goes through in pieces.
222        self.max_block = max_block.max(1);
223        self.prepared = Prepared::Full;
224        if self.processors.is_empty() {
225            self.scratch_a.iter_mut().for_each(Vec::clear);
226            self.scratch_b.iter_mut().for_each(Vec::clear);
227            self.dry.resize(0, 0);
228            return;
229        }
230        self.allocate_scratch(true);
231        self.dry
232            .resize(self.delay_samples(), self.layout.channels());
233    }
234    /// Prepares every node but leaves the chain without scratch of its own:
235    /// a chain run only through [`Self::process_in_place`] works in its
236    /// caller's buffers, so many such chains cost only their nodes' state
237    /// (a bypassed one also a block of scratch). [`Self::process`] passes
238    /// its input through until [`Self::prepare`].
239    pub fn prepare_nodes(&mut self, sample_rate: u32, max_block: usize) {
240        for processor in &mut self.processors {
241            processor.prepare(sample_rate, max_block);
242        }
243        self.max_block = max_block.max(1);
244        for channel in self.scratch_a.iter_mut().chain(&mut self.scratch_b) {
245            *channel = Vec::new();
246        }
247        self.prepared = Prepared::Nodes;
248        if self.bypass {
249            self.allocate_scratch(false);
250        }
251        self.dry
252            .resize(self.delay_samples(), self.layout.channels());
253    }
254    /// A block of scratch per channel: `scratch_a`, and with `both`
255    /// `scratch_b` too.
256    fn allocate_scratch(&mut self, both: bool) {
257        for channel in 0..self.layout.channels() {
258            self.scratch_a[channel].resize(self.max_block, 0.0);
259            if both {
260                self.scratch_b[channel].resize(self.max_block, 0.0);
261            }
262        }
263    }
264    /// How long a record of its input the chain keeps for bypass.
265    fn delay_samples(&self) -> usize {
266        if self.bypassable {
267            usize::try_from(self.latency_samples()).unwrap_or(usize::MAX)
268        } else {
269            0
270        }
271    }
272    /// Processes the first `frames` samples of each `buffer` channel in
273    /// place without allocating, the way [`Self::process`] would: `spare`
274    /// holds each node's output before it becomes the next node's input, so
275    /// the two may trade allocations. Both need `frames` samples per channel
276    /// of the layout.
277    pub fn process_in_place(
278        &mut self,
279        ctx: &ProcessCtx,
280        buffer: &mut [Vec<f32>; 2],
281        spare: &mut [Vec<f32>; 2],
282        frames: usize,
283    ) {
284        let channels = self.layout.channels();
285        if self.processors.is_empty() || self.prepared == Prepared::No {
286            return;
287        }
288        let frames = buffer
289            .iter()
290            .take(channels)
291            .chain(spare.iter().take(channels))
292            .map(Vec::len)
293            .fold(frames, usize::min);
294        if !self.bypass {
295            for channel in 0..channels {
296                self.dry.remember(channel, &buffer[channel][..frames]);
297            }
298            self.dry.advance(frames);
299            let mut upstream = 0_u32;
300            for processor in &mut self.processors {
301                let node_ctx = ctx.offset_by(-i64::from(upstream));
302                process_processor(
303                    processor.as_mut(),
304                    &node_ctx,
305                    channels,
306                    (buffer, 0..frames),
307                    (spare, 0..frames),
308                );
309                core::mem::swap(buffer, spare);
310                upstream = upstream.saturating_add(processor.latency_samples());
311            }
312            return;
313        }
314        // Bypassed: the nodes run from the buffer through `spare` and the chain's own scratch, by
315        // turns, and the buffer becomes the delayed input. In pieces of the scratch, which holds a
316        // prepared block.
317        let piece = self.scratch_a[0].len().max(1);
318        let mut start = 0;
319        while start < frames {
320            let end = (start + piece).min(frames);
321            let count = end - start;
322            let piece_ctx = ctx.offset_by(i64::try_from(start).unwrap_or(i64::MAX));
323            let mut upstream = 0_u32;
324            for (index, processor) in self.processors.iter_mut().enumerate() {
325                let node_ctx = piece_ctx.offset_by(-i64::from(upstream));
326                if index == 0 {
327                    process_processor(
328                        processor.as_mut(),
329                        &node_ctx,
330                        channels,
331                        (buffer, start..end),
332                        (spare, start..end),
333                    );
334                } else if index % 2 == 1 {
335                    process_processor(
336                        processor.as_mut(),
337                        &node_ctx,
338                        channels,
339                        (spare, start..end),
340                        (&mut self.scratch_a, 0..count),
341                    );
342                } else {
343                    process_processor(
344                        processor.as_mut(),
345                        &node_ctx,
346                        channels,
347                        (&self.scratch_a, 0..count),
348                        (spare, start..end),
349                    );
350                }
351                upstream = upstream.saturating_add(processor.latency_samples());
352            }
353            for channel in 0..channels {
354                self.dry
355                    .delay_in_place(channel, &mut buffer[channel][start..end]);
356            }
357            self.dry.advance(count);
358            start = end;
359        }
360    }
361    /// Sum of serial node latencies; none while the chain is not prepared
362    /// and so passes its input through.
363    #[must_use]
364    pub fn latency_samples(&self) -> u32 {
365        if self.prepared == Prepared::No {
366            return 0;
367        }
368        self.processors.iter().fold(0_u32, |total, processor| {
369            total.saturating_add(processor.latency_samples())
370        })
371    }
372    /// Sum of serial processor settling horizons, excluding their delays.
373    #[must_use]
374    pub fn history_samples(&self) -> u32 {
375        self.processors.iter().fold(0_u32, |total, processor| {
376            total.saturating_add(processor.history_samples())
377        })
378    }
379    /// Clears all histories.
380    pub fn reset(&mut self) {
381        for processor in &mut self.processors {
382            processor.reset();
383        }
384        for channel in &mut self.scratch_a {
385            channel.fill(0.0);
386        }
387        for channel in &mut self.scratch_b {
388            channel.fill(0.0);
389        }
390        self.dry.reset();
391    }
392    /// Processes one block without allocating: the first `n` samples of each
393    /// output channel of the layout, for `n` the shortest input or output
394    /// channel. A block longer than the prepared `max_block` runs in pieces,
395    /// as consecutive blocks would. Without an input or output channel for
396    /// every channel of the layout there is nothing to process, and the
397    /// outputs given are silenced.
398    pub fn process(&mut self, ctx: &ProcessCtx, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
399        let channels = self.layout.channels();
400        if inputs.len() < channels || outputs.len() < channels {
401            for output in outputs.iter_mut() {
402                output.fill(0.0);
403            }
404            return;
405        }
406        let frames = inputs
407            .iter()
408            .take(channels)
409            .map(|v| v.len())
410            .chain(outputs.iter().take(channels).map(|v| v.len()))
411            .min()
412            .unwrap_or(0);
413        if self.processors.is_empty() || self.prepared != Prepared::Full {
414            for channel in 0..channels {
415                outputs[channel][..frames].copy_from_slice(&inputs[channel][..frames]);
416            }
417            return;
418        }
419        let piece = self.scratch_a[0].len();
420        let mut start = 0;
421        while start < frames {
422            let end = (start + piece).min(frames);
423            let count = end - start;
424            for channel in 0..channels {
425                self.scratch_a[channel][..count].copy_from_slice(&inputs[channel][start..end]);
426            }
427            let piece_ctx = ctx.offset_by(i64::try_from(start).unwrap_or(i64::MAX));
428            let mut upstream = 0_u32;
429            for processor in &mut self.processors {
430                let node_ctx = piece_ctx.offset_by(-i64::from(upstream));
431                process_processor(
432                    processor.as_mut(),
433                    &node_ctx,
434                    channels,
435                    (&self.scratch_a, 0..count),
436                    (&mut self.scratch_b, 0..count),
437                );
438                core::mem::swap(&mut self.scratch_a, &mut self.scratch_b);
439                upstream = upstream.saturating_add(processor.latency_samples());
440            }
441            for channel in 0..channels {
442                if self.bypass {
443                    self.dry.delay(
444                        channel,
445                        &inputs[channel][start..end],
446                        &mut outputs[channel][start..end],
447                    );
448                } else {
449                    self.dry.remember(channel, &inputs[channel][start..end]);
450                    outputs[channel][start..end].copy_from_slice(&self.scratch_a[channel][..count]);
451                }
452            }
453            self.dry.advance(count);
454            start = end;
455        }
456    }
457    /// Serializes each processor state with length framing, then the record
458    /// of the input a bypass plays.
459    #[must_use]
460    pub fn save_state(&self) -> StateBlob {
461        let mut bytes = Vec::new();
462        bytes.extend_from_slice(
463            &(u32::try_from(self.processors.len()).unwrap_or(u32::MAX)).to_le_bytes(),
464        );
465        for state in self
466            .processors
467            .iter()
468            .map(|processor| processor.save_state())
469            .chain(core::iter::once(self.dry.save_state()))
470        {
471            bytes.extend_from_slice(
472                &(u64::try_from(state.as_bytes().len()).unwrap_or(u64::MAX)).to_le_bytes(),
473            );
474            bytes.extend_from_slice(state.as_bytes());
475        }
476        StateBlob::new(bytes)
477    }
478    /// Restores a state saved by a chain of the same nodes, prepared alike,
479    /// whole or not at all: when a node refuses its part, the nodes before
480    /// it get their own state back.
481    ///
482    /// A bypass record the saving chain did not keep (never bypassed) leaves
483    /// this one's empty, as a first bypass finds it; one this chain does not
484    /// keep is dropped.
485    ///
486    /// # Errors
487    /// Returns where and why the state was refused: the node count or
488    /// framing differ, or the record has another length
489    /// ([`ChainStateError::node`] `None`), or a node refused its part.
490    pub fn load_state(&mut self, state: &StateBlob) -> Result<(), ChainStateError> {
491        let (nodes, record) = Self::split_state(state.as_bytes(), self.processors.len())
492            .ok_or(ChainStateError::chain(StateError::Mismatch))?;
493        let record = self
494            .dry
495            .check_record(&record)
496            .map_err(ChainStateError::chain)?;
497        let snapshot = self
498            .processors
499            .iter()
500            .map(|processor| processor.save_state())
501            .collect::<Vec<_>>();
502        for (index, part) in nodes.iter().enumerate() {
503            if let Err(error) = self.processors[index].load_state(part) {
504                undo(&mut self.processors[..index], &snapshot);
505                return Err(ChainStateError {
506                    node: Some(index),
507                    error,
508                });
509            }
510        }
511        self.dry.load_record(record);
512        Ok(())
513    }
514    /// The history of the node at `index` alone, as its
515    /// [`Processor::save_state`] makes it; `None` past the last node.
516    ///
517    /// With [`Self::load_node_state`] a host that rebuilds a chain with an
518    /// edited node hands each node it kept, or replaced with one of the same
519    /// kind, the history of the one before it.
520    #[must_use]
521    pub fn node_state(&self, index: usize) -> Option<StateBlob> {
522        self.processors
523            .get(index)
524            .map(|processor| processor.save_state())
525    }
526    /// Restores the node at `index` from a state [`Self::node_state`] took,
527    /// whole or not at all.
528    ///
529    /// # Errors
530    /// Returns [`StateError::Mismatch`] past the last node, and otherwise
531    /// what the node's [`Processor::load_state`] refuses, leaving it as it
532    /// was.
533    pub fn load_node_state(&mut self, index: usize, state: &StateBlob) -> Result<(), StateError> {
534        self.processors
535            .get_mut(index)
536            .ok_or(StateError::Mismatch)?
537            .load_state(state)
538    }
539    /// A chain state's parts: `count` node states, and the bypass record.
540    /// `None` when the count or framing differ.
541    fn split_state(bytes: &[u8], count: usize) -> Option<(Vec<StateBlob>, StateBlob)> {
542        let saved = u32::from_le_bytes(bytes.get(0..4)?.try_into().ok()?);
543        if usize::try_from(saved).ok()? != count {
544            return None;
545        }
546        let mut offset = 4;
547        let mut parts = Vec::with_capacity(count + 1);
548        for _ in 0..=count {
549            let length = usize::try_from(u64::from_le_bytes(
550                bytes.get(offset..offset + 8)?.try_into().ok()?,
551            ))
552            .ok()?;
553            let end = (offset + 8).checked_add(length)?;
554            parts.push(StateBlob::new(bytes.get(offset + 8..end)?.to_vec()));
555            offset = end;
556        }
557        let record = parts.pop()?;
558        (offset == bytes.len()).then_some((parts, record))
559    }
560}
561
562/// Gives `processors` back their `snapshot`, states each saved itself, after
563/// a later node refused its part. Such a state always loads back
564/// ([`Processor::load_state`]); a processor that breaks that is reset.
565fn undo(processors: &mut [Box<dyn Processor>], snapshot: &[StateBlob]) {
566    for (processor, state) in processors.iter_mut().zip(snapshot) {
567        if processor.load_state(state).is_err() {
568            processor.reset();
569        }
570    }
571}
572
573/// Runs `processor` from `input`'s `range` into `output`'s, both the same
574/// length, on the layout's first `channels`.
575fn process_processor(
576    processor: &mut dyn Processor,
577    ctx: &ProcessCtx,
578    channels: usize,
579    (input, from): (&[Vec<f32>; 2], core::ops::Range<usize>),
580    (output, to): (&mut [Vec<f32>; 2], core::ops::Range<usize>),
581) {
582    match channels {
583        1 => processor.process(ctx, &[&input[0][from]], &mut [&mut output[0][to]]),
584        2 => {
585            let (left, right) = output.split_at_mut(1);
586            processor.process(
587                ctx,
588                &[&input[0][from.clone()], &input[1][from]],
589                &mut [&mut left[0][to.clone()], &mut right[0][to]],
590            );
591        }
592        _ => {}
593    }
594}
595
596/// A delay of a fixed number of samples on each channel of a layout, kept as
597/// a ring. Every channel shares the cursor: run each channel's piece, then
598/// [`Self::advance`] once.
599#[derive(Clone, Debug, Default)]
600struct Delay {
601    lines: [Vec<f32>; 2],
602    cursor: usize,
603}
604
605impl Delay {
606    /// Empties the delay and makes it `samples` long on `channels` channels.
607    fn resize(&mut self, samples: usize, channels: usize) {
608        for (channel, line) in self.lines.iter_mut().enumerate() {
609            line.clear();
610            if channel < channels {
611                line.resize(samples, 0.0);
612            } else {
613                line.shrink_to_fit();
614            }
615        }
616        self.cursor = 0;
617    }
618    fn reset(&mut self) {
619        for line in &mut self.lines {
620            line.fill(0.0);
621        }
622        self.cursor = 0;
623    }
624    /// Writes `input` delayed into `output`, the same length.
625    fn delay(&mut self, channel: usize, input: &[f32], output: &mut [f32]) {
626        let line = &mut self.lines[channel];
627        if line.is_empty() {
628            output.copy_from_slice(input);
629            return;
630        }
631        let mut cursor = self.cursor;
632        for (input, output) in input.iter().zip(output.iter_mut()) {
633            *output = line[cursor];
634            line[cursor] = *input;
635            cursor += 1;
636            if cursor == line.len() {
637                cursor = 0;
638            }
639        }
640    }
641    /// Replaces `samples` with themselves delayed.
642    fn delay_in_place(&mut self, channel: usize, samples: &mut [f32]) {
643        let line = &mut self.lines[channel];
644        if line.is_empty() {
645            return;
646        }
647        let mut cursor = self.cursor;
648        for sample in samples {
649            core::mem::swap(&mut line[cursor], sample);
650            cursor += 1;
651            if cursor == line.len() {
652                cursor = 0;
653            }
654        }
655    }
656    /// Records `input` as [`Self::delay`] would, without writing it out.
657    fn remember(&mut self, channel: usize, input: &[f32]) {
658        let line = &mut self.lines[channel];
659        let length = line.len();
660        if length == 0 {
661            return;
662        }
663        let skip = input.len().saturating_sub(length);
664        let mut cursor = (self.cursor + skip % length) % length;
665        for &sample in &input[skip..] {
666            line[cursor] = sample;
667            cursor += 1;
668            if cursor == length {
669                cursor = 0;
670            }
671        }
672    }
673    /// Moves every channel on by `frames` samples.
674    fn advance(&mut self, frames: usize) {
675        let length = self.lines[0].len();
676        if length != 0 {
677            self.cursor = (self.cursor + frames % length) % length;
678        }
679    }
680    fn save_state(&self) -> StateBlob {
681        let mut bytes = Vec::with_capacity(8 + self.lines.iter().map(Vec::len).sum::<usize>() * 4);
682        bytes.extend_from_slice(&(u64::try_from(self.cursor).unwrap_or(0)).to_le_bytes());
683        for value in self.lines.iter().flatten() {
684            bytes.extend_from_slice(&value.to_le_bytes());
685        }
686        StateBlob::new(bytes)
687    }
688    /// The cursor of a state saved by a delay of the same length and
689    /// channels. Any sample fits: a delay plays each once, keeping nothing
690    /// of it, as it plays whatever audio it is given.
691    fn check(&self, state: &StateBlob) -> Result<usize, StateError> {
692        let bytes = state.as_bytes();
693        let samples = self.lines.iter().map(Vec::len).sum::<usize>();
694        if bytes.len() != 8 + samples * 4 {
695            return Err(StateError::Mismatch);
696        }
697        let cursor = usize::try_from(u64::from_le_bytes(
698            bytes[..8].try_into().expect("fixed range"),
699        ))
700        .map_err(|_| StateError::Invalid)?;
701        if cursor >= self.lines[0].len().max(1) {
702            return Err(StateError::Invalid);
703        }
704        Ok(cursor)
705    }
706    /// Restores a state [`Self::check`] took, at the cursor it returned.
707    fn load(&mut self, state: &StateBlob, cursor: usize) {
708        let values = state.as_bytes()[8..]
709            .as_chunks::<4>()
710            .0
711            .iter()
712            .map(|word| f32::from_le_bytes(*word));
713        for (target, value) in self.lines.iter_mut().flatten().zip(values) {
714            *target = value;
715        }
716        self.cursor = cursor;
717    }
718    /// How a chain's bypass record takes a saved one ([`Self::load_record`]):
719    /// whole when both are kept, emptied when only this one is, not at all
720    /// when this one is not kept.
721    fn check_record<'a>(&self, state: &'a StateBlob) -> Result<Record<'a>, StateError> {
722        if self.lines[0].is_empty() {
723            Ok(Record::Ignore)
724        } else if state.as_bytes().len() == 8 {
725            Ok(Record::Empty)
726        } else {
727            Ok(Record::Load(state, self.check(state)?))
728        }
729    }
730    fn load_record(&mut self, record: Record<'_>) {
731        match record {
732            Record::Ignore => {}
733            Record::Empty => self.reset(),
734            Record::Load(state, cursor) => self.load(state, cursor),
735        }
736    }
737}
738
739/// What a chain's bypass record does with a saved one.
740#[derive(Copy, Clone)]
741enum Record<'a> {
742    Ignore,
743    Empty,
744    Load(&'a StateBlob, usize),
745}
746
747/// Clip chain -> track bus -> master bus graph with deterministic latency aggregation.
748///
749/// Buses whose chains differ in latency mix aligned: each bus is delayed to
750/// the longest clip-and-track latency before the mix, so the graph's output
751/// is its input delayed by exactly [`Self::latency_samples`], whichever
752/// chain a lookahead sits in. Each chain gets the context of the audio it
753/// receives.
754pub struct AudioGraph {
755    layout: ChannelLayout,
756    clip_chains: Vec<ProcessorChain>,
757    track_buses: Vec<ProcessorChain>,
758    master: ProcessorChain,
759    /// Per bus, the delay that brings it to the longest bus's latency.
760    alignment: Vec<Delay>,
761    mix: [Vec<f32>; 2],
762    bus_output: [Vec<f32>; 2],
763    track_output: [Vec<f32>; 2],
764    max_block: usize,
765}
766
767/// Complete graph history captured between offline chunks.
768#[derive(Clone, Debug, Default, Eq, PartialEq)]
769pub struct AudioGraphState {
770    clip_chains: Vec<StateBlob>,
771    track_buses: Vec<StateBlob>,
772    master: StateBlob,
773    alignment: Vec<StateBlob>,
774}
775
776impl AudioGraph {
777    /// Creates one clip chain and one track bus per input bus.
778    #[must_use]
779    pub fn new(layout: ChannelLayout, buses: usize) -> Self {
780        Self {
781            layout,
782            clip_chains: (0..buses).map(|_| ProcessorChain::new(layout)).collect(),
783            track_buses: (0..buses).map(|_| ProcessorChain::new(layout)).collect(),
784            master: ProcessorChain::new(layout),
785            alignment: (0..buses).map(|_| Delay::default()).collect(),
786            mix: [Vec::new(), Vec::new()],
787            bus_output: [Vec::new(), Vec::new()],
788            track_output: [Vec::new(), Vec::new()],
789            max_block: 0,
790        }
791    }
792    /// Channel layout every bus and the master run in.
793    #[must_use]
794    pub const fn layout(&self) -> ChannelLayout {
795        self.layout
796    }
797    /// Number of input buses, each with its own clip chain and track bus.
798    #[must_use]
799    pub fn buses(&self) -> usize {
800        self.clip_chains.len()
801    }
802    /// Mutable clip effect chain of `bus`; `None` past the last bus.
803    pub fn clip_chain_mut(&mut self, bus: usize) -> Option<&mut ProcessorChain> {
804        self.clip_chains.get_mut(bus)
805    }
806    /// Mutable track effect chain of `bus`; `None` past the last bus.
807    pub fn track_bus_mut(&mut self, bus: usize) -> Option<&mut ProcessorChain> {
808        self.track_buses.get_mut(bus)
809    }
810    /// Mutable master effect chain.
811    pub fn master_mut(&mut self) -> &mut ProcessorChain {
812        &mut self.master
813    }
814    /// Prepares every allocation before realtime processing.
815    pub fn prepare(&mut self, sample_rate: u32, max_block: usize) {
816        // At least one sample, so a block of any length goes through in pieces.
817        self.max_block = max_block.max(1);
818        for chain in self
819            .clip_chains
820            .iter_mut()
821            .chain(&mut self.track_buses)
822            .chain(core::iter::once(&mut self.master))
823        {
824            chain.prepare(sample_rate, self.max_block);
825        }
826        for channel in 0..2 {
827            self.mix[channel].resize(self.max_block, 0.0);
828            self.bus_output[channel].resize(self.max_block, 0.0);
829            self.track_output[channel].resize(self.max_block, 0.0);
830        }
831        let longest = self.bus_latency();
832        for bus in 0..self.alignment.len() {
833            let pad = longest - self.path_latency(bus);
834            self.alignment[bus].resize(
835                usize::try_from(pad).unwrap_or(usize::MAX),
836                self.layout.channels(),
837            );
838        }
839    }
840    /// The latency of bus `bus` through its clip chain and track bus.
841    fn path_latency(&self, bus: usize) -> u32 {
842        self.clip_chains[bus]
843            .latency_samples()
844            .saturating_add(self.track_buses[bus].latency_samples())
845    }
846    /// The longest latency of any bus through its clip chain and track bus.
847    fn bus_latency(&self) -> u32 {
848        (0..self.clip_chains.len())
849            .map(|bus| self.path_latency(bus))
850            .max()
851            .unwrap_or(0)
852    }
853    /// Maximum serial latency among tracks plus master latency: how late
854    /// the graph's output is, every bus aligned to it.
855    #[must_use]
856    pub fn latency_samples(&self) -> u32 {
857        self.bus_latency()
858            .saturating_add(self.master.latency_samples())
859    }
860    /// Processes flattened planar bus inputs (`bus0-L, bus0-R, bus1-L, ...`)
861    /// into the first `n` samples of each output channel, for `n` the
862    /// shortest output channel; a shorter bus is padded with silence, and a
863    /// block longer than the prepared `max_block` runs in pieces. Without an
864    /// input channel for every channel of every bus, an output channel for
865    /// every channel of the layout, or a [`Self::prepare`] first, there is
866    /// nothing to mix, and the outputs given are silenced.
867    pub fn process(&mut self, ctx: &ProcessCtx, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
868        let channels = self.layout.channels();
869        if inputs.len() < self.clip_chains.len() * channels
870            || outputs.len() < channels
871            || self.max_block == 0
872        {
873            for output in outputs.iter_mut() {
874                output.fill(0.0);
875            }
876            return;
877        }
878        let frames = outputs
879            .iter()
880            .take(channels)
881            .map(|v| v.len())
882            .min()
883            .unwrap_or(0);
884        let mut start = 0;
885        while start < frames {
886            let end = (start + self.max_block).min(frames);
887            let ctx = ctx.offset_by(i64::try_from(start).unwrap_or(i64::MAX));
888            self.process_piece(&ctx, inputs, outputs, start, end);
889            start = end;
890        }
891    }
892
893    /// Mixes samples `start..end` of the block, at most `max_block` of them.
894    fn process_piece(
895        &mut self,
896        ctx: &ProcessCtx,
897        inputs: &[&[f32]],
898        outputs: &mut [&mut [f32]],
899        start: usize,
900        end: usize,
901    ) {
902        let channels = self.layout.channels();
903        let frames = end - start;
904        for channel in 0..channels {
905            self.mix[channel][..frames].fill(0.0);
906        }
907        for bus in 0..self.clip_chains.len() {
908            let base = bus * channels;
909            let piece = |channel: usize| {
910                let input = inputs[base + channel];
911                &input[start.min(input.len())..end.min(input.len())]
912            };
913            let bus_inputs: [&[f32]; 2] = [piece(0), piece(channels - 1)];
914            process_chain_to_internal(
915                &mut self.clip_chains[bus],
916                ctx,
917                channels,
918                frames,
919                &bus_inputs[..channels],
920                &mut self.bus_output,
921            );
922            let bus_refs: [&[f32]; 2] =
923                [&self.bus_output[0][..frames], &self.bus_output[1][..frames]];
924            // The track bus gets audio as late as the clip chain made it.
925            let track_ctx = ctx.offset_by(-i64::from(self.clip_chains[bus].latency_samples()));
926            process_chain_to_internal(
927                &mut self.track_buses[bus],
928                &track_ctx,
929                channels,
930                frames,
931                &bus_refs[..channels],
932                &mut self.track_output,
933            );
934            let alignment = &mut self.alignment[bus];
935            for channel in 0..channels {
936                alignment.delay_in_place(channel, &mut self.track_output[channel][..frames]);
937            }
938            alignment.advance(frames);
939            for channel in 0..channels {
940                for frame in 0..frames {
941                    self.mix[channel][frame] += self.track_output[channel][frame];
942                }
943            }
944        }
945        let mix_refs: [&[f32]; 2] = [&self.mix[0][..frames], &self.mix[1][..frames]];
946        // Every bus reaches the master as late as the longest.
947        let ctx = ctx.offset_by(-i64::from(self.bus_latency()));
948        let (left, right) = outputs.split_at_mut(1);
949        match channels {
950            1 => self
951                .master
952                .process(&ctx, &mix_refs[..1], &mut [&mut left[0][start..end]]),
953            _ => self.master.process(
954                &ctx,
955                &mix_refs[..2],
956                &mut [&mut left[0][start..end], &mut right[0][start..end]],
957            ),
958        }
959    }
960    /// Clears every graph node.
961    pub fn reset(&mut self) {
962        for chain in self
963            .clip_chains
964            .iter_mut()
965            .chain(&mut self.track_buses)
966            .chain(core::iter::once(&mut self.master))
967        {
968            chain.reset();
969        }
970        for alignment in &mut self.alignment {
971            alignment.reset();
972        }
973    }
974
975    /// Captures every clip, track, and master processor history, and the
976    /// audio the buses' alignment holds.
977    #[must_use]
978    pub fn save_state(&self) -> AudioGraphState {
979        AudioGraphState {
980            clip_chains: self
981                .clip_chains
982                .iter()
983                .map(ProcessorChain::save_state)
984                .collect(),
985            track_buses: self
986                .track_buses
987                .iter()
988                .map(ProcessorChain::save_state)
989                .collect(),
990            master: self.master.save_state(),
991            alignment: self.alignment.iter().map(Delay::save_state).collect(),
992        }
993    }
994
995    /// Restores a state saved by a graph of the same buses and chains,
996    /// prepared alike, whole or not at all: when a chain refuses its part,
997    /// the chains before it get their own state back.
998    ///
999    /// # Errors
1000    /// Returns where and why the state was refused: the bus counts differ,
1001    /// a chain refused its part, or a bus's alignment does not fit (the
1002    /// buses' latencies differ from the saving graph's).
1003    pub fn load_state(&mut self, state: &AudioGraphState) -> Result<(), GraphStateError> {
1004        let buses = self.clip_chains.len();
1005        if state.clip_chains.len() != buses
1006            || state.track_buses.len() != buses
1007            || state.alignment.len() != buses
1008        {
1009            return Err(GraphStateError::Topology);
1010        }
1011        let mut cursors = Vec::with_capacity(buses);
1012        for (bus, (alignment, saved)) in self.alignment.iter().zip(&state.alignment).enumerate() {
1013            cursors.push(
1014                alignment
1015                    .check(saved)
1016                    .map_err(|error| GraphStateError::Alignment { bus, error })?,
1017            );
1018        }
1019        let snapshot = self
1020            .chains()
1021            .map(ProcessorChain::save_state)
1022            .collect::<Vec<_>>();
1023        let saved = state
1024            .clip_chains
1025            .iter()
1026            .chain(&state.track_buses)
1027            .chain(core::iter::once(&state.master));
1028        let refused =
1029            self.chains_mut()
1030                .zip(saved)
1031                .enumerate()
1032                .find_map(|(index, (chain, saved))| {
1033                    chain.load_state(saved).err().map(|error| (index, error))
1034                });
1035        if let Some((index, error)) = refused {
1036            for (chain, state) in self.chains_mut().zip(&snapshot).take(index) {
1037                if chain.load_state(state).is_err() {
1038                    chain.reset();
1039                }
1040            }
1041            return Err(if index < buses {
1042                GraphStateError::ClipChain { bus: index, error }
1043            } else if index < 2 * buses {
1044                GraphStateError::TrackBus {
1045                    bus: index - buses,
1046                    error,
1047                }
1048            } else {
1049                GraphStateError::Master(error)
1050            });
1051        }
1052        for ((alignment, saved), cursor) in
1053            self.alignment.iter_mut().zip(&state.alignment).zip(cursors)
1054        {
1055            alignment.load(saved, cursor);
1056        }
1057        Ok(())
1058    }
1059
1060    /// Every chain: the clip chains, the track buses, then the master.
1061    fn chains(&self) -> impl Iterator<Item = &ProcessorChain> {
1062        self.clip_chains
1063            .iter()
1064            .chain(&self.track_buses)
1065            .chain(core::iter::once(&self.master))
1066    }
1067
1068    fn chains_mut(&mut self) -> impl Iterator<Item = &mut ProcessorChain> {
1069        self.clip_chains
1070            .iter_mut()
1071            .chain(&mut self.track_buses)
1072            .chain(core::iter::once(&mut self.master))
1073    }
1074}
1075
1076fn process_chain_to_internal(
1077    chain: &mut ProcessorChain,
1078    ctx: &ProcessCtx,
1079    channels: usize,
1080    frames: usize,
1081    inputs: &[&[f32]],
1082    output: &mut [Vec<f32>; 2],
1083) {
1084    // A chain only writes as many frames as its shortest input. Clear the complete graph block so a
1085    // short bus is padded with silence rather than retaining the previous bus's scratch tail.
1086    for channel in output.iter_mut().take(channels) {
1087        channel[..frames].fill(0.0);
1088    }
1089    match channels {
1090        1 => chain.process(ctx, inputs, &mut [&mut output[0][..frames]]),
1091        2 => {
1092            let (left, right) = output.split_at_mut(1);
1093            chain.process(
1094                ctx,
1095                inputs,
1096                &mut [&mut left[0][..frames], &mut right[0][..frames]],
1097            );
1098        }
1099        _ => {}
1100    }
1101}