1use std::collections::HashMap;
2
3use super::pattern::{Pattern, StepPlayMode};
4use super::snapshot::Snapshot;
5use rill_core::queues::{SetParameter, SignalOrigin};
6use rill_core::traits::ParamValue;
7use rill_core::traits::{ParameterId, PortId};
8
9#[derive(Debug, Clone)]
24pub struct SnapshotSequencer {
25 snapshots: HashMap<String, Snapshot>,
27 patterns: HashMap<String, Pattern>,
29 active_pattern: String,
31 current_step: usize,
33 step_start_sample: u64,
35 direction: i8,
37 running: bool,
39 latest_beat_position: f32,
41 latest_new_beat: bool,
43 latest_new_bar: bool,
45}
46
47impl SnapshotSequencer {
48 pub fn new() -> Self {
50 Self {
51 snapshots: HashMap::new(),
52 patterns: HashMap::new(),
53 active_pattern: String::new(),
54 current_step: 0,
55 step_start_sample: 0,
56 direction: 1,
57 running: false,
58 latest_beat_position: 0.0,
59 latest_new_beat: false,
60 latest_new_bar: false,
61 }
62 }
63
64 pub fn with_lib(snapshots: Vec<Snapshot>, patterns: Vec<Pattern>) -> Self {
68 let mut s = Self::new();
69 for snap in snapshots {
70 s.add_snapshot(snap);
71 }
72 for pat in patterns {
73 s.add_pattern(pat);
74 }
75 if let Some(first) = s.patterns.keys().next() {
76 s.active_pattern = first.clone();
77 }
78 s
79 }
80
81 pub fn add_snapshot(&mut self, snapshot: Snapshot) {
85 self.snapshots.insert(snapshot.id.clone(), snapshot);
86 }
87
88 pub fn get_snapshot(&self, id: &str) -> Option<&Snapshot> {
90 self.snapshots.get(id)
91 }
92
93 pub fn remove_snapshot(&mut self, id: &str) -> bool {
95 self.snapshots.remove(id).is_some()
96 }
97
98 pub fn add_pattern(&mut self, pattern: Pattern) {
102 if self.active_pattern.is_empty() {
103 self.active_pattern = pattern.id.clone();
104 }
105 self.patterns.insert(pattern.id.clone(), pattern);
106 }
107
108 pub fn get_pattern(&self, id: &str) -> Option<&Pattern> {
110 self.patterns.get(id)
111 }
112
113 pub fn remove_pattern(&mut self, id: &str) -> bool {
116 if self.patterns.remove(id).is_some() {
117 if self.active_pattern == id {
118 self.active_pattern.clear();
119 self.running = false;
120 }
121 true
122 } else {
123 false
124 }
125 }
126
127 pub fn set_active_pattern(&mut self, id: &str) {
132 if self.patterns.contains_key(id) || id.is_empty() {
133 self.active_pattern = id.to_string();
134 self.current_step = 0;
135 self.step_start_sample = 0;
136 self.direction = 1;
137 }
138 }
139
140 pub fn active_pattern(&self) -> &str {
142 &self.active_pattern
143 }
144
145 pub fn start(&mut self) {
149 self.running = true;
150 }
151
152 pub fn stop(&mut self) {
154 self.running = false;
155 }
156
157 pub fn reset(&mut self, sample_pos: u64) {
159 self.current_step = 0;
160 self.step_start_sample = sample_pos;
161 self.direction = 1;
162 }
163
164 pub fn is_running(&self) -> bool {
166 self.running
167 }
168
169 pub fn current_step(&self) -> usize {
171 self.current_step
172 }
173
174 pub fn latest_beat_position(&self) -> f32 {
179 self.latest_beat_position
180 }
181
182 pub fn is_new_beat(&self) -> bool {
184 self.latest_new_beat
185 }
186
187 pub fn is_new_bar(&self) -> bool {
189 self.latest_new_bar
190 }
191
192 pub fn tick(&mut self, sample_pos: u64, sample_rate: f32, tempo: f32) -> Vec<SetParameter> {
200 self.tick_ext(sample_pos, sample_rate, tempo, 0.0, false, false)
201 }
202
203 pub fn tick_ext(
216 &mut self,
217 sample_pos: u64,
218 sample_rate: f32,
219 tempo: f32,
220 beat_position: f32,
221 is_new_beat: bool,
222 is_new_bar: bool,
223 ) -> Vec<SetParameter> {
224 self.latest_beat_position = beat_position;
225 self.latest_new_beat = is_new_beat;
226 self.latest_new_bar = is_new_bar;
227 if !self.running {
228 return Vec::new();
229 }
230
231 let (len, play_mode, step_dur) = {
232 let pat = match self.patterns.get(&self.active_pattern) {
233 Some(p) if !p.steps.is_empty() => p,
234 _ => return Vec::new(),
235 };
236 let step = &pat.steps[self.current_step];
237 (
238 pat.steps.len(),
239 pat.play_mode,
240 step.duration_samples(tempo, sample_rate),
241 )
242 };
243
244 let elapsed = sample_pos.saturating_sub(self.step_start_sample);
245
246 if elapsed >= step_dur {
247 self.current_step = self.advance_step(len, play_mode);
248 self.step_start_sample = sample_pos;
249
250 if let Some(pat) = self.patterns.get(&self.active_pattern) {
251 if self.current_step < pat.steps.len() {
252 let new_step = &pat.steps[self.current_step];
253 return new_step
254 .parameters
255 .iter()
256 .map(|p| {
257 SetParameter::new(
258 PortId::param(p.node_id, 0),
259 ParameterId::new(&p.param_name).unwrap(),
260 ParamValue::Float(p.value),
261 SignalOrigin::Manual,
262 )
263 })
264 .collect();
265 }
266 }
267 }
268
269 Vec::new()
270 }
271
272 fn advance_step(&mut self, len: usize, play_mode: StepPlayMode) -> usize {
274 if len == 0 {
275 return 0;
276 }
277 match play_mode {
278 StepPlayMode::OneShot => (self.current_step + 1).min(len.saturating_sub(1)),
279 StepPlayMode::Loop => (self.current_step + 1) % len,
280 StepPlayMode::PingPong => {
281 let next = self.current_step as isize + self.direction as isize;
282 if next < 0 {
283 self.direction = 1;
284 1
285 } else if next >= len as isize {
286 self.direction = -1;
287 len.saturating_sub(2)
288 } else {
289 next as usize
290 }
291 }
292 StepPlayMode::Random => {
293 use rand::Rng;
294 let mut rng = rand::thread_rng();
295 rng.gen_range(0..len)
296 }
297 StepPlayMode::Brownian => {
298 use rand::Rng;
299 let mut rng = rand::thread_rng();
300 let offset: isize = rng.gen_range(-1..=1);
301 (self.current_step as isize + offset).clamp(0, len.saturating_sub(1) as isize)
302 as usize
303 }
304 }
305 }
306}
307
308impl Default for SnapshotSequencer {
309 fn default() -> Self {
310 Self::new()
311 }
312}
313
314#[derive(Debug, Clone, PartialEq)]
320pub enum SequencerCommand {
321 Start,
323 Stop,
325 Reset {
327 sample_pos: u64,
329 },
330 SetPattern(String),
332}
333
334#[derive(Debug, Clone)]
340pub struct SequencerHandle {
341 cmd_tx: std::sync::Arc<crossbeam_channel::Sender<SequencerCommand>>,
342}
343
344impl SequencerHandle {
345 pub(crate) fn new(cmd_tx: crossbeam_channel::Sender<SequencerCommand>) -> Self {
346 Self {
347 cmd_tx: std::sync::Arc::new(cmd_tx),
348 }
349 }
350
351 pub fn start(&self) {
353 let _ = self.cmd_tx.try_send(SequencerCommand::Start);
354 }
355
356 pub fn stop(&self) {
358 let _ = self.cmd_tx.try_send(SequencerCommand::Stop);
359 }
360
361 pub fn reset(&self, sample_pos: u64) {
363 let _ = self.cmd_tx.try_send(SequencerCommand::Reset { sample_pos });
364 }
365
366 pub fn set_pattern(&self, id: &str) {
368 let _ = self
369 .cmd_tx
370 .try_send(SequencerCommand::SetPattern(id.to_string()));
371 }
372}
373
374#[cfg(test)]
379mod tests {
380 use super::*;
381 use crate::sequencer::{ParameterTarget, SequenceStep};
382 use rill_core::NodeId;
383
384 fn make_step(value: f32, dur: f64) -> SequenceStep {
385 SequenceStep::single(NodeId(1), "param", value, dur)
386 }
387
388 fn simple_pattern() -> Pattern {
389 Pattern::new(
390 "p1",
391 vec![
392 make_step(0.0, 1.0),
393 make_step(0.5, 1.0),
394 make_step(1.0, 1.0),
395 make_step(0.5, 1.0),
396 ],
397 )
398 }
399
400 #[test]
401 fn test_sequencer_loop() {
402 let mut seq = SnapshotSequencer::with_lib(vec![], vec![simple_pattern()]);
403 seq.start();
404
405 let sr = 48000.0;
406 let tempo = 120.0;
407
408 let cmds = seq.tick(24000, sr, tempo);
409 assert!(!cmds.is_empty(), "should advance to step 1");
410 assert_eq!(seq.current_step, 1);
411
412 let cmds = seq.tick(48000, sr, tempo);
413 assert!(!cmds.is_empty());
414 assert_eq!(seq.current_step, 2);
415
416 let cmds = seq.tick(72000, sr, tempo);
417 assert!(!cmds.is_empty());
418 assert_eq!(seq.current_step, 3);
419
420 let cmds = seq.tick(96000, sr, tempo);
421 assert!(!cmds.is_empty());
422 assert_eq!(seq.current_step, 0);
423 }
424
425 #[test]
426 fn test_sequencer_not_running() {
427 let mut seq = SnapshotSequencer::with_lib(vec![], vec![simple_pattern()]);
428 let cmds = seq.tick(24000, 48000.0, 120.0);
429 assert!(cmds.is_empty());
430 assert_eq!(seq.current_step, 0);
431 }
432
433 #[test]
434 fn test_sequencer_stop() {
435 let mut seq = SnapshotSequencer::with_lib(vec![], vec![simple_pattern()]);
436 seq.start();
437 seq.tick(24000, 48000.0, 120.0);
438 assert_eq!(seq.current_step, 1);
439
440 seq.stop();
441 seq.tick(48000, 48000.0, 120.0);
442 assert_eq!(seq.current_step, 1, "should not advance after stop");
443 }
444
445 #[test]
446 fn test_sequencer_pingpong() {
447 let mut seq = SnapshotSequencer::with_lib(
448 vec![],
449 vec![Pattern::new(
450 "p1",
451 vec![
452 make_step(0.0, 1.0),
453 make_step(0.5, 1.0),
454 make_step(1.0, 1.0),
455 ],
456 )
457 .with_mode(StepPlayMode::PingPong)],
458 );
459 seq.start();
460
461 seq.tick(24000, 48000.0, 120.0);
462 assert_eq!(seq.current_step, 1);
463 seq.tick(48000, 48000.0, 120.0);
464 assert_eq!(seq.current_step, 2);
465 seq.tick(72000, 48000.0, 120.0);
466 assert_eq!(seq.current_step, 1);
467 seq.tick(96000, 48000.0, 120.0);
468 assert_eq!(seq.current_step, 0);
469 }
470
471 #[test]
472 fn test_sequencer_oneshot() {
473 let mut seq = SnapshotSequencer::with_lib(
474 vec![],
475 vec![
476 Pattern::new("p1", vec![make_step(0.0, 1.0), make_step(0.5, 1.0)])
477 .with_mode(StepPlayMode::OneShot),
478 ],
479 );
480 seq.start();
481
482 seq.tick(24000, 48000.0, 120.0);
483 assert_eq!(seq.current_step, 1);
484 seq.tick(48000, 48000.0, 120.0);
485 assert_eq!(seq.current_step, 1);
486 }
487
488 #[test]
489 fn test_sequencer_set_pattern() {
490 let mut seq = SnapshotSequencer::with_lib(
491 vec![],
492 vec![
493 Pattern::new("a", vec![make_step(1.0, 1.0)]),
494 Pattern::new("b", vec![make_step(0.0, 1.0), make_step(0.5, 1.0)]),
495 ],
496 );
497 seq.set_active_pattern("a");
498 seq.start();
499
500 assert_eq!(seq.active_pattern(), "a");
501
502 seq.set_active_pattern("b");
503 assert_eq!(seq.active_pattern(), "b");
504 assert_eq!(seq.current_step, 0);
505
506 let cmds = seq.tick(24000, 48000.0, 120.0);
507 assert!(!cmds.is_empty());
508 assert_eq!(seq.current_step, 1);
509
510 let cmds = seq.tick(48000, 48000.0, 120.0);
511 assert!(!cmds.is_empty());
512 assert_eq!(seq.current_step, 0);
513 }
514
515 #[test]
516 fn test_step_duration_samples() {
517 let step = make_step(0.5, 1.0);
518 assert_eq!(step.duration_samples(120.0, 48000.0), 24000);
519 assert_eq!(step.duration_samples(120.0, 44100.0), 22050);
520 assert_eq!(step.duration_samples(60.0, 48000.0), 48000);
521
522 let eighth = make_step(0.5, 0.5);
523 assert_eq!(eighth.duration_samples(120.0, 48000.0), 12000);
524
525 let sixteenth = make_step(0.5, 0.25);
526 assert_eq!(sixteenth.duration_samples(120.0, 48000.0), 6000);
527 }
528
529 #[test]
530 fn test_parameter_target_creation() {
531 let pt = ParameterTarget::new(NodeId(1), "gain", 0.5);
532 assert_eq!(pt.node_id, NodeId(1));
533 assert_eq!(pt.param_name, "gain");
534 assert_eq!(pt.value, 0.5);
535 }
536
537 #[test]
538 fn test_sequencer_handle_send() {
539 let (tx, rx) = crossbeam_channel::unbounded::<SequencerCommand>();
540 let handle = SequencerHandle::new(tx);
541
542 handle.start();
543 assert_eq!(rx.try_recv(), Ok(SequencerCommand::Start));
544
545 handle.stop();
546 assert_eq!(rx.try_recv(), Ok(SequencerCommand::Stop));
547
548 handle.set_pattern("foo");
549 assert_eq!(
550 rx.try_recv(),
551 Ok(SequencerCommand::SetPattern("foo".into()))
552 );
553
554 handle.reset(12345);
555 assert_eq!(
556 rx.try_recv(),
557 Ok(SequencerCommand::Reset { sample_pos: 12345 })
558 );
559 }
560
561 #[test]
562 fn test_tick_ext_stores_beat_info() {
563 let mut seq = SnapshotSequencer::new();
564 let pat = Pattern::new("p1", vec![SequenceStep::single(NodeId(1), "p", 0.5, 1.0)]);
565 seq.add_pattern(pat);
566 seq.set_active_pattern("p1");
567 seq.start();
568
569 let _ = seq.tick_ext(0, 48000.0, 120.0, 0.0, true, true);
570 assert!((seq.latest_beat_position() - 0.0).abs() < 1e-6);
571 assert!(seq.is_new_beat());
572 assert!(seq.is_new_bar());
573
574 let _ = seq.tick(24000, 48000.0, 120.0);
575 assert!((seq.latest_beat_position() - 0.0).abs() < 1e-6);
576 assert!(!seq.is_new_beat());
577 assert!(!seq.is_new_bar());
578
579 let _ = seq.tick_ext(48000, 48000.0, 120.0, 2.5, false, false);
580 assert!((seq.latest_beat_position() - 2.5).abs() < 1e-6);
581 assert!(!seq.is_new_beat());
582 assert!(!seq.is_new_bar());
583 }
584}