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