rill_patchbay/automaton/
function.rs1use crate::control::{Automaton, NoAction, Range, Time};
7use std::fmt;
8use std::sync::Arc;
9
10#[derive(Debug, Clone)]
12pub struct FunctionState {
13 pub value: f64,
15 pub last_time: Time,
17 pub user_state: Arc<dyn std::any::Any + Send + Sync>,
19}
20
21#[derive(Clone)]
23pub struct FunctionAutomaton {
24 name: String,
26 generator: Arc<dyn Fn(Time) -> f64 + Send + Sync>,
28 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 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 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#[derive(Clone)]
92#[allow(clippy::type_complexity)]
93pub struct StatefulFunctionAutomaton<S> {
94 name: String,
96 generator: Arc<dyn Fn(Time, &mut S) -> f64 + Send + Sync>,
98 initial_state: S,
100 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 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 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
167pub 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}