Skip to main content

rill_sampler/
player.rs

1use rill_core::time::ClockTick;
2use rill_core::traits::{
3    Algorithm, SignalNode, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId,
4    Port, 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
73            .set_loop_start(self.loop_start);
74        self.left.set_loop_end(self.loop_end);
75        self.left
76            .set_loop_mode(self.loop_mode);
77        self.left
78            .set_playback_rate(self.rate);
79        self.left.set_cubic(self.cubic);
80
81        if let Some(right_data) = sample.right {
82            let mut right_player = SamplePlayer::new(right_data);
83            right_player.set_loop_start(self.loop_start);
84            right_player.set_loop_end(self.loop_end);
85            right_player.set_loop_mode(self.loop_mode);
86            right_player.set_playback_rate(self.rate);
87            right_player.set_cubic(self.cubic);
88            self.right = Some(right_player);
89
90            if self.outputs.len() < 2 {
91                self.outputs
92                    .push(Port::output(NodeId(0), 1, "right"));
93            }
94        } else {
95            self.right = None;
96            self.outputs.truncate(1);
97        }
98    }
99
100    /// Start / stop playback.
101    pub fn play(&mut self) {
102        self.gate = true;
103        self.left.set_gate(true);
104        if let Some(ref mut r) = self.right {
105            r.set_gate(true);
106        }
107    }
108
109    pub fn stop(&mut self) {
110        self.gate = false;
111        self.left.set_gate(false);
112        if let Some(ref mut r) = self.right {
113            r.set_gate(false);
114        }
115    }
116
117    fn param_to_t(value: ParamValue) -> Option<T> {
118        match value {
119            ParamValue::Float(f) => Some(T::from_f32(f)),
120            ParamValue::Int(i) => Some(T::from_f32(i as f32)),
121            _ => None,
122        }
123    }
124
125    fn t_to_param(value: T) -> ParamValue {
126        ParamValue::Float(value.to_f32())
127    }
128}
129
130impl<T: Transcendental, const BUF_SIZE: usize> Default for SamplePlayerNode<T, BUF_SIZE> {
131    fn default() -> Self {
132        Self::new()
133    }
134}
135
136impl<T: Transcendental, const BUF_SIZE: usize> SignalNode<T, BUF_SIZE>
137    for SamplePlayerNode<T, BUF_SIZE>
138{
139    fn metadata(&self) -> NodeMetadata {
140        NodeMetadata {
141            name: "SamplePlayer".to_string(),
142            type_name: None,
143            category: NodeCategory::Source,
144            description: "Sample playback node with loop modes and stereo".to_string(),
145            author: "Rill".to_string(),
146            version: env!("CARGO_PKG_VERSION").to_string(),
147            signal_inputs: 0,
148            signal_outputs: if self.right.is_some() { 2 } else { 1 },
149            control_inputs: 0,
150            control_outputs: 0,
151            clock_inputs: 0,
152            clock_outputs: 0,
153            feedback_ports: 0,
154            parameters: vec![],
155        }
156    }
157
158    fn init(&mut self, sample_rate: f32) {
159        self.left.init(sample_rate);
160        if let Some(ref mut r) = self.right {
161            r.init(sample_rate);
162        }
163        self.state = Some(NodeState::new(sample_rate));
164    }
165
166    fn reset(&mut self) {
167        self.left.reset();
168        if let Some(ref mut r) = self.right {
169            r.reset();
170        }
171        self.gate = false;
172        if let Some(state) = &mut self.state {
173            state.reset();
174        }
175    }
176
177    fn get_parameter(&self, id: &ParameterId) -> Option<ParamValue> {
178        match id.as_str() {
179            "gate" => Some(ParamValue::Bool(self.gate)),
180            "rate" => Some(ParamValue::Float(self.rate as f32)),
181            "loop_mode" => {
182                let s = match self.loop_mode {
183                    LoopMode::OneShot => "oneshot",
184                    LoopMode::Forward => "forward",
185                    LoopMode::PingPong => "pingpong",
186                };
187                Some(ParamValue::Choice(s.into()))
188            }
189            "start" => {
190                let len = self.left.len().max(1) as f64;
191                Some(ParamValue::Float((self.loop_start / len) as f32))
192            }
193            "end" => {
194                let len = self.left.len().max(1) as f64;
195                Some(ParamValue::Float((self.loop_end / len) as f32))
196            }
197            "amplitude" => Some(Self::t_to_param(self.amplitude)),
198            "interpolation" => Some(ParamValue::Choice(
199                if self.cubic { "cubic" } else { "linear" }.into(),
200            )),
201            "position" => Some(ParamValue::Float(self.left.phase().to_f32())),
202            _ => None,
203        }
204    }
205
206    fn set_parameter(&mut self, id: &ParameterId, value: ParamValue) -> ProcessResult<()> {
207        let len = self.left.len().max(1) as f64;
208        match id.as_str() {
209            "gate" => {
210                if let ParamValue::Bool(b) = value {
211                    self.gate = b;
212                    self.left.set_gate(b);
213                    if let Some(ref mut r) = self.right {
214                        r.set_gate(b);
215                    }
216                    Ok(())
217                } else {
218                    Err(ProcessError::Parameter("Expected bool".into()))
219                }
220            }
221            "rate" => {
222                if let Some(r) = Self::param_to_t(value) {
223                    self.rate = r.to_f64().clamp(0.0, 4.0);
224                    self.left.set_playback_rate(self.rate);
225                    if let Some(ref mut rp) = self.right {
226                        rp.set_playback_rate(self.rate);
227                    }
228                    Ok(())
229                } else {
230                    Err(ProcessError::Parameter("Expected float".into()))
231                }
232            }
233            "loop_mode" => {
234                if let ParamValue::Choice(s) = &value {
235                    self.loop_mode = match s.as_str() {
236                        "forward" => LoopMode::Forward,
237                        "pingpong" => LoopMode::PingPong,
238                        _ => LoopMode::OneShot,
239                    };
240                    self.left.set_loop_mode(self.loop_mode);
241                    if let Some(ref mut r) = self.right {
242                        r.set_loop_mode(self.loop_mode);
243                    }
244                    Ok(())
245                } else {
246                    Err(ProcessError::Parameter("Expected choice".into()))
247                }
248            }
249            "start" => {
250                if let Some(s) = Self::param_to_t(value) {
251                    self.loop_start = (s.to_f64() * len).clamp(0.0, self.loop_end);
252                    self.left.set_loop_start(self.loop_start);
253                    if let Some(ref mut r) = self.right {
254                        r.set_loop_start(self.loop_start);
255                    }
256                    Ok(())
257                } else {
258                    Err(ProcessError::Parameter("Expected float".into()))
259                }
260            }
261            "end" => {
262                if let Some(e) = Self::param_to_t(value) {
263                    self.loop_end = (e.to_f64() * len).clamp(self.loop_start, len);
264                    self.left.set_loop_end(self.loop_end);
265                    if let Some(ref mut r) = self.right {
266                        r.set_loop_end(self.loop_end);
267                    }
268                    Ok(())
269                } else {
270                    Err(ProcessError::Parameter("Expected float".into()))
271                }
272            }
273            "amplitude" => {
274                if let Some(a) = Self::param_to_t(value) {
275                    self.amplitude = a.clamp(T::ZERO, T::from_f32(1.0));
276                    Ok(())
277                } else {
278                    Err(ProcessError::Parameter("Expected float".into()))
279                }
280            }
281            "interpolation" => {
282                if let ParamValue::Choice(s) = &value {
283                    self.cubic = s == "cubic";
284                    self.left.set_cubic(self.cubic);
285                    if let Some(ref mut r) = self.right {
286                        r.set_cubic(self.cubic);
287                    }
288                    Ok(())
289                } else {
290                    Err(ProcessError::Parameter("Expected choice".into()))
291                }
292            }
293            _ => Err(ProcessError::Parameter(format!(
294                "Unknown parameter: {}",
295                id
296            ))),
297        }
298    }
299
300    fn id(&self) -> NodeId {
301        NodeId(0)
302    }
303
304    fn set_id(&mut self, _id: NodeId) {}
305
306    fn input_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
307        None
308    }
309
310    fn input_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
311        None
312    }
313
314    fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
315        self.outputs.get(index)
316    }
317
318    fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
319        self.outputs.get_mut(index)
320    }
321
322    fn control_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
323        None
324    }
325
326    fn control_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
327        None
328    }
329
330    fn state(&self) -> &NodeState<T, BUF_SIZE> {
331        self.state.as_ref().unwrap()
332    }
333
334    fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
335        self.state.as_mut().unwrap()
336    }
337
338    fn num_signal_inputs(&self) -> usize {
339        0
340    }
341
342    fn num_signal_outputs(&self) -> usize {
343        self.outputs.len()
344    }
345}
346
347impl<T: Transcendental, const BUF_SIZE: usize> Source<T, BUF_SIZE>
348    for SamplePlayerNode<T, BUF_SIZE>
349{
350    fn generate(
351        &mut self,
352        clock: &ClockTick,
353        _control_inputs: &[T],
354        _clock_inputs: &[ClockTick],
355    ) -> ProcessResult<()> {
356        let amp = self.amplitude;
357
358        let mut temp = [T::ZERO; BUF_SIZE];
359        self.left
360            .process(None, &mut temp[..], &rill_core::traits::ActionContext::new(clock))?;
361        if amp != T::from_f32(1.0) {
362            for s in temp.iter_mut() {
363                *s = *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
371                .process(None, &mut right_temp[..], &rill_core::traits::ActionContext::new(clock))?;
372            if amp != T::from_f32(1.0) {
373                for s in right_temp.iter_mut() {
374                    *s = *s * amp;
375                }
376            }
377            if self.outputs.len() > 1 {
378                *self.outputs[1].buffer.as_mut_array() = right_temp;
379            }
380        }
381
382        Ok(())
383    }
384}