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