Skip to main content

rill_sampler/
player.rs

1use rill_core::time::ClockTick;
2use rill_core::traits::{
3    Algorithm, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId, Port,
4    SignalNode, Source,
5};
6use rill_core::Transcendental;
7use rill_core::{ProcessError, ProcessResult};
8use rill_core_dsp::generators::{Generator, LoopMode, SamplePlayer};
9use std::marker::PhantomData;
10
11use crate::buffer::SampleBuffer;
12
13/// Sample-playback source node with stereo support.
14///
15/// # Parameters (all automatable via patchbay)
16///
17/// | Name | Type | Range | Description |
18/// |---|---|---|---|
19/// | `"gate"` | Bool | – | Start / stop playback |
20/// | `"rate"` | Float | 0.0–4.0 | Playback speed ratio |
21/// | `"loop_mode"` | Choice | oneshot/forward/pingpong | Loop behaviour |
22/// | `"start"` | Float | 0.0–1.0 | Loop start (normalised) |
23/// | `"end"` | Float | 0.0–1.0 | Loop end (normalised) |
24/// | `"amplitude"` | Float | 0.0–1.0 | Output gain |
25/// | `"interpolation"` | Choice | linear/cubic | Interpolation mode |
26/// | `"position"` | Float | 0.0–1.0 | Current position **(read-only)** |
27///
28/// # Output ports
29/// - Port 0: left channel
30/// - Port 1: right channel (only present when a stereo sample is loaded)
31pub struct SamplePlayerNode<T: Transcendental, const BUF_SIZE: usize> {
32    left: SamplePlayer<T>,
33    right: Option<SamplePlayer<T>>,
34    gate: bool,
35    amplitude: T,
36    rate: f64,
37    loop_mode: LoopMode,
38    loop_start: f64,
39    loop_end: f64,
40    cubic: bool,
41    outputs: Vec<Port<T, BUF_SIZE>>,
42    state: Option<NodeState<T, BUF_SIZE>>,
43    _phantom: PhantomData<[T; BUF_SIZE]>,
44}
45
46impl<T: Transcendental, const BUF_SIZE: usize> SamplePlayerNode<T, BUF_SIZE> {
47    /// Create a new node with an empty sample buffer.
48    pub fn new() -> Self {
49        Self {
50            left: SamplePlayer::new(Vec::new()),
51            right: None,
52            gate: false,
53            amplitude: T::from_f32(1.0),
54            rate: 1.0,
55            loop_mode: LoopMode::OneShot,
56            loop_start: 0.0,
57            loop_end: 0.0,
58            cubic: false,
59            outputs: vec![Port::output(NodeId(0), 0, "left")],
60            state: None,
61            _phantom: PhantomData,
62        }
63    }
64
65    /// Load a sample buffer into the node.
66    pub fn load(&mut self, sample: SampleBuffer<T>) {
67        let len = sample.len() as f64;
68        self.loop_end = len;
69        self.loop_start = 0.0;
70
71        self.left.set_buffer(sample.data);
72        self.left.set_loop_start(self.loop_start);
73        self.left.set_loop_end(self.loop_end);
74        self.left.set_loop_mode(self.loop_mode);
75        self.left.set_playback_rate(self.rate);
76        self.left.set_cubic(self.cubic);
77
78        if let Some(right_data) = sample.right {
79            let mut right_player = SamplePlayer::new(right_data);
80            right_player.set_loop_start(self.loop_start);
81            right_player.set_loop_end(self.loop_end);
82            right_player.set_loop_mode(self.loop_mode);
83            right_player.set_playback_rate(self.rate);
84            right_player.set_cubic(self.cubic);
85            self.right = Some(right_player);
86
87            if self.outputs.len() < 2 {
88                self.outputs.push(Port::output(NodeId(0), 1, "right"));
89            }
90        } else {
91            self.right = None;
92            self.outputs.truncate(1);
93        }
94    }
95
96    /// Start / stop playback.
97    pub fn play(&mut self) {
98        self.gate = true;
99        self.left.set_gate(true);
100        if let Some(ref mut r) = self.right {
101            r.set_gate(true);
102        }
103    }
104
105    /// Stop playback (sets gate to false).
106    pub fn stop(&mut self) {
107        self.gate = false;
108        self.left.set_gate(false);
109        if let Some(ref mut r) = self.right {
110            r.set_gate(false);
111        }
112    }
113
114    fn param_to_t(value: ParamValue) -> Option<T> {
115        match value {
116            ParamValue::Float(f) => Some(T::from_f32(f)),
117            ParamValue::Int(i) => Some(T::from_f32(i as f32)),
118            _ => None,
119        }
120    }
121
122    fn t_to_param(value: T) -> ParamValue {
123        ParamValue::Float(value.to_f32())
124    }
125}
126
127impl<T: Transcendental, const BUF_SIZE: usize> Default for SamplePlayerNode<T, BUF_SIZE> {
128    fn default() -> Self {
129        Self::new()
130    }
131}
132
133impl<T: Transcendental, const BUF_SIZE: usize> SignalNode<T, BUF_SIZE>
134    for SamplePlayerNode<T, BUF_SIZE>
135{
136    fn metadata(&self) -> NodeMetadata {
137        NodeMetadata {
138            name: "SamplePlayer".to_string(),
139            type_name: None,
140            category: NodeCategory::Source,
141            description: "Sample playback node with loop modes and stereo".to_string(),
142            author: "Rill".to_string(),
143            version: env!("CARGO_PKG_VERSION").to_string(),
144            signal_inputs: 0,
145            signal_outputs: if self.right.is_some() { 2 } else { 1 },
146            control_inputs: 0,
147            control_outputs: 0,
148            clock_inputs: 0,
149            clock_outputs: 0,
150            feedback_ports: 0,
151            parameters: vec![],
152        }
153    }
154
155    fn init(&mut self, sample_rate: f32) {
156        self.left.init(sample_rate);
157        if let Some(ref mut r) = self.right {
158            r.init(sample_rate);
159        }
160        self.state = Some(NodeState::new(sample_rate));
161    }
162
163    fn reset(&mut self) {
164        self.left.reset();
165        if let Some(ref mut r) = self.right {
166            r.reset();
167        }
168        self.gate = false;
169        if let Some(state) = &mut self.state {
170            state.reset();
171        }
172    }
173
174    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
175        match id.as_str() {
176            "gate" => Some(ParamValue::Bool(self.gate)),
177            "rate" => Some(ParamValue::Float(self.rate as f32)),
178            "loop_mode" => {
179                let s = match self.loop_mode {
180                    LoopMode::OneShot => "oneshot",
181                    LoopMode::Forward => "forward",
182                    LoopMode::PingPong => "pingpong",
183                };
184                Some(ParamValue::Choice(s.into()))
185            }
186            "start" => {
187                let len = self.left.len().max(1) as f64;
188                Some(ParamValue::Float((self.loop_start / len) as f32))
189            }
190            "end" => {
191                let len = self.left.len().max(1) as f64;
192                Some(ParamValue::Float((self.loop_end / len) as f32))
193            }
194            "amplitude" => Some(Self::t_to_param(self.amplitude)),
195            "interpolation" => Some(ParamValue::Choice(
196                if self.cubic { "cubic" } else { "linear" }.into(),
197            )),
198            "position" => Some(ParamValue::Float(self.left.phase().to_f32())),
199            _ => None,
200        }
201    }
202
203    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
204        let len = self.left.len().max(1) as f64;
205        match id.as_str() {
206            "gate" => {
207                if let ParamValue::Bool(b) = value {
208                    self.gate = b;
209                    self.left.set_gate(b);
210                    if let Some(ref mut r) = self.right {
211                        r.set_gate(b);
212                    }
213                    Ok(())
214                } else {
215                    Err(ProcessError::Parameter("Expected bool".into()))
216                }
217            }
218            "rate" => {
219                if let Some(r) = Self::param_to_t(value) {
220                    self.rate = r.to_f64().clamp(0.0, 4.0);
221                    self.left.set_playback_rate(self.rate);
222                    if let Some(ref mut rp) = self.right {
223                        rp.set_playback_rate(self.rate);
224                    }
225                    Ok(())
226                } else {
227                    Err(ProcessError::Parameter("Expected float".into()))
228                }
229            }
230            "loop_mode" => {
231                if let ParamValue::Choice(s) = &value {
232                    self.loop_mode = match s.as_str() {
233                        "forward" => LoopMode::Forward,
234                        "pingpong" => LoopMode::PingPong,
235                        _ => LoopMode::OneShot,
236                    };
237                    self.left.set_loop_mode(self.loop_mode);
238                    if let Some(ref mut r) = self.right {
239                        r.set_loop_mode(self.loop_mode);
240                    }
241                    Ok(())
242                } else {
243                    Err(ProcessError::Parameter("Expected choice".into()))
244                }
245            }
246            "start" => {
247                if let Some(s) = Self::param_to_t(value) {
248                    self.loop_start = (s.to_f64() * len).clamp(0.0, self.loop_end);
249                    self.left.set_loop_start(self.loop_start);
250                    if let Some(ref mut r) = self.right {
251                        r.set_loop_start(self.loop_start);
252                    }
253                    Ok(())
254                } else {
255                    Err(ProcessError::Parameter("Expected float".into()))
256                }
257            }
258            "end" => {
259                if let Some(e) = Self::param_to_t(value) {
260                    self.loop_end = (e.to_f64() * len).clamp(self.loop_start, len);
261                    self.left.set_loop_end(self.loop_end);
262                    if let Some(ref mut r) = self.right {
263                        r.set_loop_end(self.loop_end);
264                    }
265                    Ok(())
266                } else {
267                    Err(ProcessError::Parameter("Expected float".into()))
268                }
269            }
270            "amplitude" => {
271                if let Some(a) = Self::param_to_t(value) {
272                    self.amplitude = a.clamp(T::ZERO, T::from_f32(1.0));
273                    Ok(())
274                } else {
275                    Err(ProcessError::Parameter("Expected float".into()))
276                }
277            }
278            "interpolation" => {
279                if let ParamValue::Choice(s) = &value {
280                    self.cubic = s == "cubic";
281                    self.left.set_cubic(self.cubic);
282                    if let Some(ref mut r) = self.right {
283                        r.set_cubic(self.cubic);
284                    }
285                    Ok(())
286                } else {
287                    Err(ProcessError::Parameter("Expected choice".into()))
288                }
289            }
290            _ => Err(ProcessError::Parameter(format!(
291                "Unknown parameter: {}",
292                id
293            ))),
294        }
295    }
296
297    fn id(&self) -> NodeId {
298        NodeId(0)
299    }
300
301    fn set_id(&mut self, _id: NodeId) {}
302
303    fn input_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> {
304        None
305    }
306
307    fn input_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
308        None
309    }
310
311    fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
312        self.outputs.get(index)
313    }
314
315    fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
316        self.outputs.get_mut(index)
317    }
318
319    fn control_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> {
320        None
321    }
322
323    fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
324        None
325    }
326
327    fn state(&self) -> &NodeState<T, BUF_SIZE> {
328        self.state.as_ref().unwrap()
329    }
330
331    fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
332        self.state.as_mut().unwrap()
333    }
334
335    fn num_signal_inputs(&self) -> usize {
336        0
337    }
338
339    fn num_signal_outputs(&self) -> usize {
340        self.outputs.len()
341    }
342}
343
344impl<T: Transcendental, const BUF_SIZE: usize> Source<T, BUF_SIZE>
345    for SamplePlayerNode<T, BUF_SIZE>
346{
347    fn generate(
348        &mut self,
349        clock: &ClockTick,
350        _control_inputs: &[T],
351        _clock_inputs: &[ClockTick],
352    ) -> ProcessResult<()> {
353        let amp = self.amplitude;
354
355        let mut temp = [T::ZERO; BUF_SIZE];
356        self.left.process(
357            None,
358            &mut temp[..],
359            &rill_core::traits::ActionContext::new(clock),
360        )?;
361        if amp != T::from_f32(1.0) {
362            for s in temp.iter_mut() {
363                *s *= amp;
364            }
365        }
366        *self.outputs[0].buffer.as_mut_array() = temp;
367
368        if let Some(ref mut right_player) = self.right {
369            let mut right_temp = [T::ZERO; BUF_SIZE];
370            right_player.process(
371                None,
372                &mut right_temp[..],
373                &rill_core::traits::ActionContext::new(clock),
374            )?;
375            if amp != T::from_f32(1.0) {
376                for s in right_temp.iter_mut() {
377                    *s *= amp;
378                }
379            }
380            if self.outputs.len() > 1 {
381                *self.outputs[1].buffer.as_mut_array() = right_temp;
382            }
383        }
384
385        Ok(())
386    }
387}