Skip to main content

rill_patchbay/sequencer/
pattern.rs

1use super::step::SequenceStep;
2
3/// Playback mode for a pattern.
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum StepPlayMode {
7    /// Play through once then stop.
8    OneShot,
9    /// Loop the pattern indefinitely.
10    Loop,
11    /// Forward then backward (ping-pong).
12    PingPong,
13    /// Pick steps at random.
14    Random,
15    /// Brownian motion — drift to neighbouring steps.
16    Brownian,
17}
18
19impl StepPlayMode {
20    /// Pick the next step index given the current one and the pattern length.
21    pub fn next_index(&self, current: usize, len: usize) -> usize {
22        if len == 0 {
23            return 0;
24        }
25        match self {
26            StepPlayMode::OneShot => current.min(len.saturating_sub(1)),
27            StepPlayMode::Loop => (current + 1) % len,
28            StepPlayMode::PingPong => {
29                current
30            }
31            StepPlayMode::Random => {
32                use rand::Rng;
33                let mut rng = rand::thread_rng();
34                rng.gen_range(0..len)
35            }
36            StepPlayMode::Brownian => {
37                use rand::Rng;
38                let mut rng = rand::thread_rng();
39                let offset: isize = rng.gen_range(-1..=1);
40                let next = current as isize + offset;
41                next.clamp(0, len.saturating_sub(1) as isize) as usize
42            }
43        }
44    }
45}
46
47/// A sequence of steps forming a pattern.
48#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
49#[derive(Debug, Clone)]
50pub struct Pattern {
51    /// Unique pattern identifier.
52    pub id: String,
53    /// Ordered list of steps in this pattern.
54    pub steps: Vec<SequenceStep>,
55    /// Playback mode for the pattern.
56    pub play_mode: StepPlayMode,
57}
58
59impl Pattern {
60    /// Create a new pattern with the given ID and steps (defaults to `Loop` mode).
61    pub fn new(id: impl Into<String>, steps: Vec<SequenceStep>) -> Self {
62        Self {
63            id: id.into(),
64            steps,
65            play_mode: StepPlayMode::Loop,
66        }
67    }
68
69    /// Set the playback mode.
70    pub fn with_mode(mut self, mode: StepPlayMode) -> Self {
71        self.play_mode = mode;
72        self
73    }
74
75    /// Whether the pattern has zero steps.
76    pub fn is_empty(&self) -> bool {
77        self.steps.is_empty()
78    }
79
80    /// Number of steps in this pattern.
81    pub fn len(&self) -> usize {
82        self.steps.len()
83    }
84}