Skip to main content

rill_patchbay/automaton/
function.rs

1//! # Функциональные автоматы
2//!
3//! Автоматы, построенные на произвольных функциях времени.
4//! Позволяют реализовать любую математическую зависимость.
5
6use crate::control::{Automaton, NoAction, Range, Time};
7use std::fmt;
8use std::sync::Arc;
9
10/// Состояние функционального автомата
11#[derive(Debug, Clone)]
12pub struct FunctionState {
13    /// Последнее вычисленное значение
14    pub value: f64,
15    /// Время последнего обновления
16    pub last_time: Time,
17    /// Пользовательское состояние (для stateful функций)
18    pub user_state: Arc<dyn std::any::Any + Send + Sync>,
19}
20
21/// Функциональный автомат (stateless)
22#[derive(Clone)]
23pub struct FunctionAutomaton {
24    /// Имя автомата
25    name: String,
26    /// Функция-генератор
27    generator: Arc<dyn Fn(Time) -> f64 + Send + Sync>,
28    /// Диапазон выходных значений
29    range: Range,
30}
31
32impl fmt::Debug for FunctionAutomaton {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.debug_struct("FunctionAutomaton")
35            .field("name", &self.name)
36            .field("range", &self.range)
37            .finish()
38    }
39}
40
41impl FunctionAutomaton {
42    /// Создать новый функциональный автомат
43    pub fn new<F>(name: &str, generator: F) -> Self
44    where
45        F: Fn(Time) -> f64 + Send + Sync + 'static,
46    {
47        Self {
48            name: name.to_string(),
49            generator: Arc::new(generator),
50            range: Range::bipolar(),
51        }
52    }
53
54    /// Установить диапазон
55    pub fn with_range(mut self, range: Range) -> Self {
56        self.range = range;
57        self
58    }
59}
60
61impl Automaton for FunctionAutomaton {
62    type State = f64;
63    type Action = NoAction;
64
65    fn step(
66        &self,
67        time: Time,
68        _action: &Self::Action,
69        _state: &Self::State,
70    ) -> (Self::State, Option<f64>) {
71        let value = (self.generator)(time);
72        let clamped = self.range.clamp(value);
73
74        (clamped, Some(clamped))
75    }
76
77    fn initial_state(&self) -> Self::State {
78        0.0
79    }
80
81    fn name(&self) -> &str {
82        &self.name
83    }
84
85    fn extract_value(&self, state: &Self::State) -> f64 {
86        *state
87    }
88}
89
90/// Функциональный автомат с состоянием
91#[derive(Clone)]
92pub struct StatefulFunctionAutomaton<S> {
93    /// Имя автомата
94    name: String,
95    /// Функция-генератор с состоянием
96    generator: Arc<dyn Fn(Time, &mut S) -> f64 + Send + Sync>,
97    /// Начальное состояние
98    initial_state: S,
99    /// Диапазон выходных значений
100    range: Range,
101}
102
103impl<S: fmt::Debug + Send + Sync + Clone + 'static> fmt::Debug for StatefulFunctionAutomaton<S> {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("StatefulFunctionAutomaton")
106            .field("name", &self.name)
107            .field("initial_state", &self.initial_state)
108            .field("range", &self.range)
109            .finish()
110    }
111}
112
113impl<S: Send + Sync + Clone + 'static> StatefulFunctionAutomaton<S> {
114    /// Создать новый автомат с состоянием
115    pub fn new<F>(name: &str, generator: F, initial_state: S) -> Self
116    where
117        F: Fn(Time, &mut S) -> f64 + Send + Sync + 'static,
118    {
119        Self {
120            name: name.to_string(),
121            generator: Arc::new(generator),
122            initial_state,
123            range: Range::bipolar(),
124        }
125    }
126
127    /// Установить диапазон
128    pub fn with_range(mut self, range: Range) -> Self {
129        self.range = range;
130        self
131    }
132}
133
134impl<S: fmt::Debug + Send + Sync + Clone + 'static> Automaton for StatefulFunctionAutomaton<S> {
135    type State = (f64, S);
136    type Action = NoAction;
137
138    fn step(
139        &self,
140        time: Time,
141        _action: &Self::Action,
142        state: &Self::State,
143    ) -> (Self::State, Option<f64>) {
144        let mut user_state = state.1.clone();
145        let value = (self.generator)(time, &mut user_state);
146        let clamped = self.range.clamp(value);
147
148        ((clamped, user_state), Some(clamped))
149    }
150
151    fn initial_state(&self) -> Self::State {
152        let mut init = self.initial_state.clone();
153        let value = (self.generator)(0.0, &mut init);
154        (self.range.clamp(value), init)
155    }
156
157    fn name(&self) -> &str {
158        &self.name
159    }
160
161    fn extract_value(&self, state: &Self::State) -> f64 {
162        state.0
163    }
164}
165
166/// Функция-генератор для LFO (удобная обёртка)
167pub fn lfo_function(freq: f64, phase: f64, waveform: &'static str) -> impl Fn(Time) -> f64 {
168    move |t| {
169        let p = (t * freq + phase).fract();
170        match waveform {
171            "sine" => (p * 2.0 * std::f64::consts::PI).sin(),
172            "saw" => 2.0 * p - 1.0,
173            "square" => {
174                if p < 0.5 {
175                    1.0
176                } else {
177                    -1.0
178                }
179            }
180            _ => 0.0,
181        }
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    #[test]
190    fn test_function_automaton() {
191        let automaton = FunctionAutomaton::new("Test", |t| (t * 2.0).sin());
192        let state = automaton.initial_state();
193
194        let (_state, value) = automaton.step(1.0, &NoAction, &state);
195        assert!(value.is_some());
196    }
197
198    #[test]
199    fn test_stateful_automaton() {
200        let automaton = StatefulFunctionAutomaton::new(
201            "Counter",
202            |_t, counter: &mut i32| {
203                *counter += 1;
204                *counter as f64
205            },
206            0,
207        )
208        .with_range(Range::new(0.0, 100.0));
209
210        let state = automaton.initial_state();
211        assert_eq!(state.0, 1.0);
212
213        let (new_state, _) = automaton.step(1.0, &NoAction, &state);
214        assert_eq!(new_state.0, 2.0);
215    }
216}