1use rill_core::time::ClockTick;
2use rill_core::traits::{
3 Algorithm, Node, NodeCategory, NodeId, NodeMetadata, NodeState, ParamValue, ParameterId, Port,
4 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#[cfg(feature = "wav")]
13use crate::wav::load_wav;
14
15pub struct SamplePlayerNode<T: Transcendental, const BUF_SIZE: usize> {
34 left: SamplePlayer<T>,
35 right: Option<SamplePlayer<T>>,
36 gate: bool,
37 amplitude: T,
38 rate: f64,
39 loop_mode: LoopMode,
40 loop_start: f64,
41 loop_end: f64,
42 cubic: bool,
43 outputs: Vec<Port<T, BUF_SIZE>>,
44 state: Option<NodeState<T, BUF_SIZE>>,
45 _phantom: PhantomData<[T; BUF_SIZE]>,
46}
47
48impl<T: Transcendental, const BUF_SIZE: usize> SamplePlayerNode<T, BUF_SIZE> {
49 pub fn new() -> Self {
51 Self {
52 left: SamplePlayer::new(Vec::new()),
53 right: None,
54 gate: false,
55 amplitude: T::from_f32(1.0),
56 rate: 1.0,
57 loop_mode: LoopMode::OneShot,
58 loop_start: 0.0,
59 loop_end: 0.0,
60 cubic: false,
61 outputs: vec![
62 Port::output(NodeId(0), 0, "left"),
63 Port::output(NodeId(0), 1, "right"),
64 ],
65 state: None,
66 _phantom: PhantomData,
67 }
68 }
69
70 pub fn load(&mut self, sample: SampleBuffer<T>) {
72 let len = sample.len() as f64;
73 self.loop_end = len;
74 self.loop_start = 0.0;
75
76 self.left.set_buffer(sample.data);
77 self.left.set_loop_start(self.loop_start);
78 self.left.set_loop_end(self.loop_end);
79 self.left.set_loop_mode(self.loop_mode);
80 self.left.set_playback_rate(self.rate);
81 self.left.set_cubic(self.cubic);
82
83 if let Some(right_data) = sample.right {
84 let mut right_player = SamplePlayer::new(right_data);
85 right_player.set_loop_start(self.loop_start);
86 right_player.set_loop_end(self.loop_end);
87 right_player.set_loop_mode(self.loop_mode);
88 right_player.set_playback_rate(self.rate);
89 right_player.set_cubic(self.cubic);
90 self.right = Some(right_player);
91
92 if self.outputs.len() < 2 {
93 self.outputs.push(Port::output(NodeId(0), 1, "right"));
94 }
95 } else {
96 self.right = None;
97 self.outputs.truncate(1);
98 }
99 }
100
101 pub fn play(&mut self) {
103 self.gate = true;
104 self.left.set_gate(true);
105 if let Some(ref mut r) = self.right {
106 r.set_gate(true);
107 }
108 }
109
110 pub fn stop(&mut self) {
112 self.gate = false;
113 self.left.set_gate(false);
114 if let Some(ref mut r) = self.right {
115 r.set_gate(false);
116 }
117 }
118
119 fn param_to_t(value: ParamValue) -> Option<T> {
120 match value {
121 ParamValue::Float(f) => Some(T::from_f32(f)),
122 ParamValue::Int(i) => Some(T::from_f32(i as f32)),
123 _ => None,
124 }
125 }
126
127 fn t_to_param(value: T) -> ParamValue {
128 ParamValue::Float(value.to_f32())
129 }
130}
131
132impl<T: Transcendental, const BUF_SIZE: usize> Default for SamplePlayerNode<T, BUF_SIZE> {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl<T: Transcendental, const BUF_SIZE: usize> Node<T, BUF_SIZE> for SamplePlayerNode<T, BUF_SIZE> {
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: self.outputs.len(),
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 #[cfg(feature = "wav")]
294 "file" => {
295 if let ParamValue::String(path) = &value {
296 match load_wav(path) {
297 Ok(sample) => {
298 let converted = SampleBuffer {
299 data: sample.data.into_iter().map(|s| T::from_f32(s)).collect(),
300 right: sample
301 .right
302 .map(|r| r.into_iter().map(|s| T::from_f32(s)).collect()),
303 sample_rate: sample.sample_rate,
304 channels: sample.channels,
305 name: sample.name,
306 };
307 self.load(converted);
308 self.gate = true;
309 self.left.set_gate(true);
310 if let Some(ref mut r) = self.right {
311 r.set_gate(true);
312 }
313 eprintln!("SamplePlayer: loaded {path}");
314 Ok(())
315 }
316 Err(e) => {
317 eprintln!("SamplePlayer: could not load {path}: {e}");
318 Err(ProcessError::Parameter(format!(
319 "Cannot load {}: {}",
320 path, e
321 )))
322 }
323 }
324 } else {
325 Err(ProcessError::Parameter("Expected string path".into()))
326 }
327 }
328 _ => Err(ProcessError::Parameter(format!(
329 "Unknown parameter: {}",
330 id
331 ))),
332 }
333 }
334
335 fn id(&self) -> NodeId {
336 NodeId(0)
337 }
338
339 fn set_id(&mut self, _id: NodeId) {}
340
341 fn input_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> {
342 None
343 }
344
345 fn input_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
346 None
347 }
348
349 fn output_port(&self, index: usize) -> Option<&Port<T, BUF_SIZE>> {
350 self.outputs.get(index)
351 }
352
353 fn output_port_mut(&mut self, index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
354 self.outputs.get_mut(index)
355 }
356
357 fn control_port(&self, _index: usize) -> Option<&Port<T, BUF_SIZE>> {
358 None
359 }
360
361 fn control_port_mut(&mut self, _index: usize) -> Option<&mut Port<T, BUF_SIZE>> {
362 None
363 }
364
365 fn state(&self) -> &NodeState<T, BUF_SIZE> {
366 self.state.as_ref().unwrap()
367 }
368
369 fn state_mut(&mut self) -> &mut NodeState<T, BUF_SIZE> {
370 self.state.as_mut().unwrap()
371 }
372
373 fn num_signal_inputs(&self) -> usize {
374 0
375 }
376
377 fn num_signal_outputs(&self) -> usize {
378 self.outputs.len()
379 }
380}
381
382impl<T: Transcendental, const BUF_SIZE: usize> Source<T, BUF_SIZE>
383 for SamplePlayerNode<T, BUF_SIZE>
384{
385 fn generate(
386 &mut self,
387 clock: &ClockTick,
388 _control_inputs: &[T],
389 _clock_inputs: &[ClockTick],
390 ) -> ProcessResult<()> {
391 let amp = self.amplitude;
392
393 let mut temp = [T::ZERO; BUF_SIZE];
394 self.left.process(
395 None,
396 &mut temp[..],
397 &rill_core::traits::ActionContext::new(clock),
398 )?;
399 if amp != T::from_f32(1.0) {
400 for s in temp.iter_mut() {
401 *s *= amp;
402 }
403 }
404 *self.outputs[0].buffer.as_mut_array() = temp;
405
406 if let Some(ref mut right_player) = self.right {
407 let mut right_temp = [T::ZERO; BUF_SIZE];
408 right_player.process(
409 None,
410 &mut right_temp[..],
411 &rill_core::traits::ActionContext::new(clock),
412 )?;
413 if amp != T::from_f32(1.0) {
414 for s in right_temp.iter_mut() {
415 *s *= amp;
416 }
417 }
418 if self.outputs.len() > 1 {
419 *self.outputs[1].buffer.as_mut_array() = right_temp;
420 }
421 }
422
423 Ok(())
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use rill_core::traits::Node;
431
432 #[test]
433 fn test_set_and_get_parameter() {
434 const B: usize = 64;
435 let mut player = SamplePlayerNode::<f32, B>::new();
436
437 let pid = ParameterId::new("rate").unwrap();
439 let _ = player.set_parameter(&pid, ParamValue::Float(2.0));
440 let val = player.get_parameter(&pid);
441 assert_eq!(val, Some(ParamValue::Float(2.0)));
442
443 let pid = ParameterId::new("amplitude").unwrap();
445 let _ = player.set_parameter(&pid, ParamValue::Float(0.75));
446 let val = player.get_parameter(&pid);
447 assert_eq!(val, Some(ParamValue::Float(0.75)));
448
449 let pid = ParameterId::new("gate").unwrap();
451 let _ = player.set_parameter(&pid, ParamValue::Bool(true));
452 let val = player.get_parameter(&pid);
453 assert_eq!(val, Some(ParamValue::Bool(true)));
454
455 let unknown = ParameterId::new("nonexistent").unwrap();
457 let result = player.set_parameter(&unknown, ParamValue::Float(0.0));
458 assert!(result.is_err());
459 assert!(player.get_parameter(&unknown).is_none());
460 }
461}