caw_modules/
sample_playback.rs

1use caw_builder_proc_macros::builder;
2use caw_core::{Buf, Sig, SigCtx, SigT};
3
4pub struct SamplePlayback<P>
5where
6    P: SigT<Item = bool>,
7{
8    // controls whether the sample is currently playing
9    play_gate: P,
10    sample_buffer: Vec<f32>,
11    index: usize,
12    buf: Vec<f32>,
13}
14
15impl<P> SamplePlayback<P>
16where
17    P: SigT<Item = bool>,
18{
19    fn new(play_gate: P, sample_buffer: Vec<f32>) -> Sig<Self> {
20        Sig(Self {
21            play_gate,
22            sample_buffer,
23            index: 0,
24            buf: Vec::new(),
25        })
26    }
27}
28
29impl<P> SigT for SamplePlayback<P>
30where
31    P: SigT<Item = bool>,
32{
33    type Item = f32;
34
35    fn sample(&mut self, ctx: &SigCtx) -> impl Buf<Self::Item> {
36        // Only consider the first value from the "play" signal each frame. This is to simplify
37        // copying samples to the output buffer, but is technically incorrect in some rare edge
38        // cases. This can be revisited later if it causes problems.
39        if self.play_gate.sample(ctx).iter().next().unwrap() {
40            let next_index =
41                (self.index + ctx.num_samples).min(self.sample_buffer.len());
42            let samples_this_frame =
43                &self.sample_buffer[self.index..next_index];
44            self.index = next_index;
45            self.buf.resize(samples_this_frame.len(), 0.0);
46            self.buf.copy_from_slice(samples_this_frame);
47        } else {
48            // This silences the module when the play signal is false.
49            self.buf.clear();
50        }
51        // Pad the buffer with 0s so on the last frame the buffer is still the requested size.
52        self.buf.resize(ctx.num_samples, 0.0);
53        &self.buf
54    }
55}
56
57builder! {
58    #[constructor = "sample_playback"]
59    #[constructor_doc = "Play back samples from a buffer"]
60    #[build_fn = "SamplePlayback::new"]
61    #[build_ty = "Sig<SamplePlayback<P>>"]
62    #[generic_setter_type_name = "X"]
63    pub struct PropsBuilder {
64        #[generic_with_constraint = "SigT<Item = bool>"]
65        #[generic_name = "P"]
66        #[default = true]
67        play_gate: bool,
68        sample_buffer: Vec<f32>,
69    }
70}