rill_patchbay/automaton/
sequencer.rs1use crate::control::{Automaton, NoAction, Range, Time};
7use std::collections::VecDeque;
8
9#[derive(Debug, Clone)]
11pub struct Step {
12 pub value: f64,
14 pub duration: f64,
16 pub curve: Option<f64>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq)]
22pub enum PlayMode {
23 OneShot,
25 Loop,
27 PingPong,
29 Random,
31 Brownian,
33}
34
35#[derive(Debug, Clone)]
37pub struct SequencerState {
38 pub current_step: usize,
40 pub step_start_time: Time,
42 pub current_value: f64,
44 pub target_value: f64,
46 pub direction: i8,
48 pub history: VecDeque<usize>,
50}
51
52#[derive(Debug, Clone)]
54pub struct SequencerAutomaton {
55 name: String,
57 steps: Vec<Step>,
59 mode: PlayMode,
61 tempo: f64,
63 duration_scale: f64,
65 interpolate: bool,
67 range: Range,
69 rng_state: u64,
71}
72
73impl SequencerAutomaton {
74 pub fn new(name: &str, steps: Vec<Step>) -> Self {
76 Self {
77 name: name.to_string(),
78 steps,
79 mode: PlayMode::Loop,
80 tempo: 120.0,
81 duration_scale: 1.0,
82 interpolate: false,
83 range: Range::unipolar(),
84 rng_state: 123456789,
85 }
86 }
87
88 pub fn with_mode(mut self, mode: PlayMode) -> Self {
90 self.mode = mode;
91 self
92 }
93
94 pub fn with_tempo(mut self, bpm: f64) -> Self {
96 self.tempo = bpm.max(1.0);
97 self
98 }
99
100 pub fn with_interpolation(mut self, interpolate: bool) -> Self {
102 self.interpolate = interpolate;
103 self
104 }
105
106 pub fn with_range(mut self, range: Range) -> Self {
108 self.range = range;
109 self
110 }
111
112 fn step_duration(&self, step: &Step) -> f64 {
114 step.duration * 60.0 / self.tempo * 4.0 * self.duration_scale
115 }
116
117 fn next_step(&self, state: &SequencerState) -> usize {
119 match self.mode {
120 PlayMode::OneShot => {
121 if state.current_step < self.steps.len() - 1 {
122 state.current_step + 1
123 } else {
124 state.current_step
125 }
126 }
127
128 PlayMode::Loop => (state.current_step + 1) % self.steps.len(),
129
130 PlayMode::PingPong => {
131 let next = state.current_step as i32 + state.direction as i32;
132 if next < 0 {
133 1
134 } else if next >= self.steps.len() as i32 {
135 self.steps.len() - 2
136 } else {
137 next as usize
138 }
139 }
140
141 PlayMode::Random => self.random_index(&mut self.rng_state.clone()),
142
143 PlayMode::Brownian => self.brownian_next(state, &mut self.rng_state.clone()),
144 }
145 }
146
147 fn random_index(&self, rng: &mut u64) -> usize {
149 let mut x = *rng;
150 x ^= x << 13;
151 x ^= x >> 7;
152 x ^= x << 17;
153 *rng = x;
154
155 (x as usize) % self.steps.len()
156 }
157
158 fn brownian_next(&self, state: &SequencerState, rng: &mut u64) -> usize {
160 let mut candidates = Vec::new();
161
162 candidates.push(state.current_step);
164
165 if state.current_step > 0 {
167 candidates.push(state.current_step - 1);
168 }
169 if state.current_step < self.steps.len() - 1 {
170 candidates.push(state.current_step + 1);
171 }
172
173 let idx = self.random_index(rng) % candidates.len();
174 candidates[idx]
175 }
176}
177
178impl Automaton for SequencerAutomaton {
179 type State = SequencerState;
180 type Action = NoAction;
181
182 fn step(
183 &self,
184 time: Time,
185 _action: &Self::Action,
186 state: &Self::State,
187 ) -> (Self::State, Option<f64>) {
188 let mut new_state = state.clone();
189
190 let current_step = &self.steps[new_state.current_step];
192 let step_dur = self.step_duration(current_step);
193 let elapsed = time - new_state.step_start_time;
194
195 if elapsed >= step_dur {
196 let next = self.next_step(&new_state);
198 new_state.current_step = next;
199 new_state.step_start_time = time;
200 new_state.current_value = self.steps[next].value;
201 new_state.target_value = self.steps[next].value;
202
203 if let PlayMode::PingPong = self.mode {
204 if next == 0 {
205 new_state.direction = 1;
206 } else if next == self.steps.len() - 1 {
207 new_state.direction = -1;
208 }
209 }
210
211 if let PlayMode::Brownian = self.mode {
212 new_state.history.push_back(next);
213 if new_state.history.len() > 10 {
214 new_state.history.pop_front();
215 }
216 }
217 } else if self.interpolate && step_dur > 0.0 {
218 let t = elapsed / step_dur;
219 let next_idx = (new_state.current_step + 1) % self.steps.len();
220 let next_val = self.steps[next_idx].value;
221
222 let curve = current_step.curve.unwrap_or(1.0);
223 let tt = t.powf(curve);
224
225 new_state.current_value = current_step.value * (1.0 - tt) + next_val * tt;
226 }
227
228 let value = self.range.denormalize(new_state.current_value);
229
230 (new_state, Some(value))
231 }
232
233 fn initial_state(&self) -> Self::State {
234 SequencerState {
235 current_step: 0,
236 step_start_time: 0.0,
237 current_value: self.steps[0].value,
238 target_value: self.steps[0].value,
239 direction: 1,
240 history: VecDeque::with_capacity(10),
241 }
242 }
243
244 fn name(&self) -> &str {
245 &self.name
246 }
247
248 fn extract_value(&self, state: &Self::State) -> f64 {
249 self.range.denormalize(state.current_value)
250 }
251}
252
253pub fn simple_sequence(values: Vec<f64>, duration: f64) -> Vec<Step> {
255 values
256 .into_iter()
257 .map(|v| Step {
258 value: v.clamp(0.0, 1.0),
259 duration,
260 curve: None,
261 })
262 .collect()
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn test_sequencer() {
271 let steps = simple_sequence(vec![0.0, 0.5, 1.0, 0.5], 0.25);
272 let seq = SequencerAutomaton::new("Test", steps);
273 let state = seq.initial_state();
274
275 assert_eq!(state.current_value, 0.0);
276
277 let (new_state, _value) = seq.step(0.6, &NoAction, &state);
278 assert_eq!(new_state.current_step, 1);
279 }
280}