rill_core_dsp/generators/
sample_player.rs1use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
2use crate::generators::{Generator, InterpolatedReader};
3use rill_core::traits::{ActionContext, ProcessResult};
4use rill_core::Transcendental;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum LoopMode {
9 OneShot,
11 Forward,
13 PingPong,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq)]
19pub enum PlayState {
20 Stopped,
21 Playing,
22}
23
24pub struct SamplePlayer<T: Transcendental> {
39 reader: InterpolatedReader<T>,
40 loop_mode: LoopMode,
41 loop_start: f64,
42 loop_end: f64,
43 state: PlayState,
44 gate: bool,
45 amplitude: T,
46 sample_rate: f32,
47}
48
49impl<T: Transcendental> SamplePlayer<T> {
50 pub fn new(buffer: Vec<T>) -> Self {
51 let len = buffer.len() as f64;
52 Self {
53 reader: InterpolatedReader::new(buffer),
54 loop_mode: LoopMode::OneShot,
55 loop_start: 0.0,
56 loop_end: len,
57 state: PlayState::Stopped,
58 gate: false,
59 amplitude: T::from_f32(1.0),
60 sample_rate: 44100.0,
61 }
62 }
63
64 pub fn from_boxed(buffer: Box<[T]>) -> Self {
65 let len = buffer.len() as f64;
66 Self {
67 reader: InterpolatedReader::from_boxed(buffer),
68 loop_mode: LoopMode::OneShot,
69 loop_start: 0.0,
70 loop_end: len,
71 state: PlayState::Stopped,
72 gate: false,
73 amplitude: T::from_f32(1.0),
74 sample_rate: 44100.0,
75 }
76 }
77
78 pub fn len(&self) -> usize {
79 self.reader.len()
80 }
81
82 pub fn is_empty(&self) -> bool {
83 self.reader.is_empty()
84 }
85
86 pub fn loop_mode(&self) -> LoopMode {
87 self.loop_mode
88 }
89
90 pub fn set_loop_mode(&mut self, mode: LoopMode) {
91 self.loop_mode = mode;
92 }
93
94 pub fn loop_start(&self) -> f64 {
95 self.loop_start
96 }
97
98 pub fn set_loop_start(&mut self, start: f64) {
99 self.loop_start = start.clamp(0.0, self.reader.len() as f64);
100 }
101
102 pub fn loop_end(&self) -> f64 {
103 self.loop_end
104 }
105
106 pub fn set_loop_end(&mut self, end: f64) {
107 let max = self.reader.len() as f64;
108 self.loop_end = end.clamp(0.0, max);
109 }
110
111 pub fn gate(&self) -> bool {
112 self.gate
113 }
114
115 pub fn set_gate(&mut self, gate: bool) {
119 if gate && !self.gate {
120 self.reader.set_position(self.loop_start);
121 self.state = PlayState::Playing;
122 } else if !gate {
123 self.state = PlayState::Stopped;
124 }
125 self.gate = gate;
126 }
127
128 pub fn play_state(&self) -> PlayState {
129 self.state
130 }
131
132 pub fn set_buffer(&mut self, buffer: Vec<T>) {
134 self.reader.set_buffer(buffer);
135 self.loop_end = self.reader.len() as f64;
136 self.loop_start = 0.0;
137 }
138
139 pub fn set_cubic(&mut self, cubic: bool) {
140 self.reader.set_cubic(cubic);
141 }
142
143 pub fn is_cubic(&self) -> bool {
144 self.reader.is_cubic()
145 }
146
147 pub fn set_playback_rate(&mut self, rate: f64) {
148 self.reader.set_rate(rate);
149 }
150
151 pub fn playback_rate(&self) -> f64 {
152 self.reader.rate()
153 }
154
155 fn playable_len(&self) -> f64 {
156 (self.loop_end - self.loop_start).max(1.0)
157 }
158}
159
160impl<T: Transcendental> Algorithm<T> for SamplePlayer<T> {
161 fn init(&mut self, sample_rate: f32) {
162 self.sample_rate = sample_rate;
163 self.reader.set_position(self.loop_start);
164 self.state = PlayState::Stopped;
165 }
166
167 fn reset(&mut self) {
168 self.gate = false;
169 self.state = PlayState::Stopped;
170 self.reader.set_position(self.loop_start);
171 }
172
173 fn process(
174 &mut self,
175 _input: Option<&[T]>,
176 output: &mut [T],
177 _ctx: &ActionContext,
178 ) -> ProcessResult<()> {
179 if !self.gate || self.state == PlayState::Stopped || self.is_empty() {
180 for s in output.iter_mut() {
181 *s = T::ZERO;
182 }
183 return Ok(());
184 }
185
186 let amp = self.amplitude;
187 let start = self.loop_start;
188 let end = self.loop_end;
189
190 for s in output.iter_mut() {
191 if !self.gate || self.state == PlayState::Stopped {
192 *s = T::ZERO;
193 continue;
194 }
195
196 *s = self.reader.read_one() * amp;
197 self.reader.advance();
198
199 let pos = self.reader.position();
200 let going_forward = self.reader.rate() >= 0.0;
201
202 if self.loop_mode == LoopMode::OneShot {
203 if pos >= end || pos < 0.0 {
204 self.state = PlayState::Stopped;
205 self.gate = false;
206 }
207 } else if self.loop_mode == LoopMode::Forward {
208 if going_forward && pos >= end {
209 self.reader.set_position(start + (pos - end));
210 } else if !going_forward && pos < start {
211 self.reader.set_position(end - (start - pos));
212 }
213 } else if going_forward && pos >= end {
214 self.reader.set_rate(-self.reader.rate());
216 self.reader.set_position(end - 1.0);
217 } else if !going_forward && pos <= start {
218 self.reader.set_rate(-self.reader.rate());
220 self.reader.set_position(start);
221 }
222 }
223
224 Ok(())
225 }
226
227 fn metadata(&self) -> AlgorithmMetadata {
228 AlgorithmMetadata {
229 name: "SamplePlayer",
230 category: AlgorithmCategory::Generator,
231 description: "Sample playback with loop modes".into(),
232 author: "Rill",
233 version: env!("CARGO_PKG_VERSION"),
234 }
235 }
236}
237
238impl<T: Transcendental + Copy> Generator<T> for SamplePlayer<T> {
239 fn phase(&self) -> T {
240 let len = self.reader.len() as f64;
241 if len == 0.0 {
242 return T::ZERO;
243 }
244 T::from_f64((self.reader.position() / len).clamp(0.0, 1.0))
245 }
246
247 fn set_phase(&mut self, phase: T) {
248 let p = phase.to_f64().clamp(0.0, 1.0);
249 let len = self.reader.len() as f64;
250 self.reader.set_position(p * len);
251 }
252
253 fn reset_phase(&mut self) {
254 self.reader.set_position(0.0);
255 }
256
257 fn frequency(&self) -> f32 {
258 if self.is_empty() {
259 return 0.0;
260 }
261 let rate = self.reader.rate();
262 let len = self.reader.len() as f64;
263 (rate * self.sample_rate as f64 / len) as f32
264 }
265
266 fn set_frequency(&mut self, freq: f32) {
267 if self.is_empty() {
268 return;
269 }
270 let len = self.reader.len() as f64;
271 let rate = freq as f64 * len / self.sample_rate as f64;
272 self.reader.set_rate(rate);
273 }
274
275 fn amplitude(&self) -> T {
276 self.amplitude
277 }
278
279 fn set_amplitude(&mut self, amp: T) {
280 self.amplitude = amp.clamp(T::ZERO, T::from_f32(1.0));
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use rill_core::time::ClockTick;
288 use rill_core::traits::ActionContext;
289
290 fn process(player: &mut SamplePlayer<f64>, out: &mut [f64]) {
291 let tick = ClockTick::new(0, 0, 44100.0);
292 player.process(None, out, &ActionContext::new(&tick)).unwrap();
293 }
294
295 #[test]
296 fn test_one_shot() {
297 let buf = vec![1.0, 2.0, 3.0, 4.0];
298 let mut player = SamplePlayer::new(buf);
299 player.set_gate(true);
300 let mut out = [0.0f64; 6];
301 process(&mut player, &mut out[..3]);
302 assert!(player.play_state() == PlayState::Playing, "still playing after 3/4");
303 process(&mut player, &mut out[3..]);
304 assert_eq!(out[0..4], [1.0, 2.0, 3.0, 4.0], "all samples read");
305 assert_eq!(out[4..6], [0.0, 0.0], "silence after end");
306 assert_eq!(player.play_state(), PlayState::Stopped, "stopped after end");
307 }
308
309 #[test]
310 fn test_loop_forward() {
311 let buf = vec![1.0, 2.0, 3.0];
312 let mut player = SamplePlayer::new(buf);
313 player.set_loop_mode(LoopMode::Forward);
314 player.set_gate(true);
315 let mut out = [0.0f64; 6];
316 process(&mut player, &mut out);
317 assert_eq!(out, [1.0, 2.0, 3.0, 1.0, 2.0, 3.0]);
318 }
319
320 #[test]
321 fn test_ping_pong() {
322 let buf = vec![1.0, 2.0, 3.0, 4.0];
323 let mut player = SamplePlayer::new(buf);
324 player.set_loop_mode(LoopMode::PingPong);
325 player.set_gate(true);
326 let mut out = [0.0f64; 12];
327 process(&mut player, &mut out);
328 assert_eq!(out[0..4], [1.0, 2.0, 3.0, 4.0], "forward pass");
330 assert_eq!(out[5..8], [3.0, 2.0, 1.0], "reverse pass (minus endpoint)");
331 assert_eq!(out[8..11], [2.0, 3.0, 4.0], "second forward pass");
332 }
333
334 #[test]
335 fn test_gate_restart() {
336 let buf = vec![10.0, 20.0, 30.0];
337 let mut player = SamplePlayer::new(buf);
338 player.set_gate(true);
339 let mut out = [0.0f64; 2];
340 process(&mut player, &mut out);
341 assert_eq!(out, [10.0, 20.0]);
342 player.set_gate(false);
343 process(&mut player, &mut out);
344 assert_eq!(out, [0.0, 0.0]);
345 player.set_gate(true);
346 process(&mut player, &mut out);
347 assert_eq!(out, [10.0, 20.0]);
348 }
349
350 #[test]
351 fn test_frequency_mapping() {
352 let buf = vec![1.0, 2.0, 3.0, 4.0];
353 let mut player = SamplePlayer::new(buf);
354 player.init(44100.0);
355 let freq_at_unit_rate = player.frequency();
356 assert!((freq_at_unit_rate - 44100.0 / 4.0).abs() < 1.0,
357 "expected ~11025 Hz at rate=1, got {}", freq_at_unit_rate);
358 player.set_frequency(freq_at_unit_rate * 2.0);
359 assert!((player.playback_rate() - 2.0).abs() < 1e-6,
360 "expected rate=2.0, got {}", player.playback_rate());
361 }
362
363 #[test]
364 fn test_empty_buffer() {
365 let buf: Vec<f64> = vec![];
366 let mut player = SamplePlayer::new(buf);
367 player.set_gate(true);
368 let mut out = [1.0f64; 4];
369 process(&mut player, &mut out);
370 assert_eq!(out, [0.0; 4]);
371 }
372}