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)]
92#[allow(clippy::type_complexity)]
93pub struct StatefulFunctionAutomaton<S> {
94    /// Имя автомата
95    name: String,
96    /// Функция-генератор с состоянием
97    generator: Arc<dyn Fn(Time, &mut S) -> f64 + Send + Sync>,
98    /// Начальное состояние
99    initial_state: S,
100    /// Диапазон выходных значений
101    range: Range,
102}
103
104impl<S: fmt::Debug + Send + Sync + Clone + 'static> fmt::Debug for StatefulFunctionAutomaton<S> {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        f.debug_struct("StatefulFunctionAutomaton")
107            .field("name", &self.name)
108            .field("initial_state", &self.initial_state)
109            .field("range", &self.range)
110            .finish()
111    }
112}
113
114impl<S: Send + Sync + Clone + 'static> StatefulFunctionAutomaton<S> {
115    /// Создать новый автомат с состоянием
116    pub fn new<F>(name: &str, generator: F, initial_state: S) -> Self
117    where
118        F: Fn(Time, &mut S) -> f64 + Send + Sync + 'static,
119    {
120        Self {
121            name: name.to_string(),
122            generator: Arc::new(generator),
123            initial_state,
124            range: Range::bipolar(),
125        }
126    }
127
128    /// Установить диапазон
129    pub fn with_range(mut self, range: Range) -> Self {
130        self.range = range;
131        self
132    }
133}
134
135impl<S: fmt::Debug + Send + Sync + Clone + 'static> Automaton for StatefulFunctionAutomaton<S> {
136    type State = (f64, S);
137    type Action = NoAction;
138
139    fn step(
140        &self,
141        time: Time,
142        _action: &Self::Action,
143        state: &Self::State,
144    ) -> (Self::State, Option<f64>) {
145        let mut user_state = state.1.clone();
146        let value = (self.generator)(time, &mut user_state);
147        let clamped = self.range.clamp(value);
148
149        ((clamped, user_state), Some(clamped))
150    }
151
152    fn initial_state(&self) -> Self::State {
153        let mut init = self.initial_state.clone();
154        let value = (self.generator)(0.0, &mut init);
155        (self.range.clamp(value), init)
156    }
157
158    fn name(&self) -> &str {
159        &self.name
160    }
161
162    fn extract_value(&self, state: &Self::State) -> f64 {
163        state.0
164    }
165}
166
167/// Функция-генератор для LFO (удобная обёртка)
168pub fn lfo_function(freq: f64, phase: f64, waveform: &'static str) -> impl Fn(Time) -> f64 {
169    move |t| {
170        let p = (t * freq + phase).fract();
171        match waveform {
172            "sine" => (p * 2.0 * std::f64::consts::PI).sin(),
173            "saw" => 2.0 * p - 1.0,
174            "square" => {
175                if p < 0.5 {
176                    1.0
177                } else {
178                    -1.0
179                }
180            }
181            _ => 0.0,
182        }
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn test_function_automaton() {
192        let automaton = FunctionAutomaton::new("Test", |t| (t * 2.0).sin());
193        let state = automaton.initial_state();
194
195        let (_state, value) = automaton.step(1.0, &NoAction, &state);
196        assert!(value.is_some());
197    }
198
199    #[test]
200    fn test_stateful_automaton() {
201        let automaton = StatefulFunctionAutomaton::new(
202            "Counter",
203            |_t, counter: &mut i32| {
204                *counter += 1;
205                *counter as f64
206            },
207            0,
208        )
209        .with_range(Range::new(0.0, 100.0));
210
211        let state = automaton.initial_state();
212        assert_eq!(state.0, 1.0);
213
214        let (new_state, _) = automaton.step(1.0, &NoAction, &state);
215        assert_eq!(new_state.0, 2.0);
216    }
217}