Skip to main content

rill_patchbay/automaton/
sequencer.rs

1//! # Секвенсоры
2//!
3//! Автоматы для генерации ритмических паттернов и последовательностей
4//! значений во времени.
5
6use crate::control::{Automaton, NoAction, Range, Time};
7use std::collections::VecDeque;
8
9/// Шаг секвенсора
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[derive(Debug, Clone)]
12pub struct Step {
13    /// Значение (0.0 - 1.0)
14    pub value: f64,
15    /// Длительность в долях такта
16    pub duration: f64,
17    /// Кривая перехода к следующему шагу
18    #[cfg_attr(feature = "serde", serde(default))]
19    pub curve: Option<f64>,
20}
21
22/// Режим воспроизведения секвенсора
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum PlayMode {
26    /// Один раз
27    OneShot,
28    /// Зациклено
29    Loop,
30    /// Вперёд-назад
31    PingPong,
32    /// Случайный выбор
33    Random,
34    /// Броуновское движение
35    Brownian,
36}
37
38/// Состояние секвенсора
39#[derive(Debug, Clone)]
40pub struct SequencerState {
41    /// Текущий индекс шага
42    pub current_step: usize,
43    /// Время начала текущего шага
44    pub step_start_time: Time,
45    /// Значение на текущем шаге
46    pub current_value: f64,
47    /// Целевое значение (для интерполяции)
48    pub target_value: f64,
49    /// Направление (для PingPong)
50    pub direction: i8,
51    /// История последних шагов (для Brownian)
52    pub history: VecDeque<usize>,
53}
54
55/// Секвенсор автомат
56#[derive(Debug, Clone)]
57pub struct SequencerAutomaton {
58    /// Имя автомата
59    name: String,
60    /// Шаги секвенсора
61    steps: Vec<Step>,
62    /// Режим воспроизведения
63    mode: PlayMode,
64    /// Темп (BPM)
65    tempo: f64,
66    /// Масштаб длительности (1.0 = четверть)
67    duration_scale: f64,
68    /// Интерполировать ли между шагами
69    interpolate: bool,
70    /// Диапазон выходных значений
71    range: Range,
72    /// Случайное зерно
73    rng_state: u64,
74}
75
76impl SequencerAutomaton {
77    /// Создать новый секвенсор
78    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    /// Установить режим воспроизведения
92    pub fn with_mode(mut self, mode: PlayMode) -> Self {
93        self.mode = mode;
94        self
95    }
96
97    /// Установить темп
98    pub fn with_tempo(mut self, bpm: f64) -> Self {
99        self.tempo = bpm.max(1.0);
100        self
101    }
102
103    /// Включить/выключить интерполяцию
104    pub fn with_interpolation(mut self, interpolate: bool) -> Self {
105        self.interpolate = interpolate;
106        self
107    }
108
109    /// Установить диапазон
110    pub fn with_range(mut self, range: Range) -> Self {
111        self.range = range;
112        self
113    }
114
115    /// Получить длительность шага в секундах
116    fn step_duration(&self, step: &Step) -> f64 {
117        step.duration * 60.0 / self.tempo * 4.0 * self.duration_scale
118    }
119
120    /// Выбрать следующий шаг
121    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    /// Случайный индекс
151    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    /// Следующий шаг для броуновского движения
162    fn brownian_next(&self, state: &SequencerState, rng: &mut u64) -> usize {
163        let mut candidates = Vec::new();
164
165        // Может остаться на месте
166        candidates.push(state.current_step);
167
168        // Или перейти к соседним
169        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        // Проверяем, не пора ли перейти к следующему шагу
194        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            // Переходим к следующему шагу
200            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
256/// Создать простую последовательность из равных шагов
257pub 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}