rill_patchbay/automaton/
function.rs1use crate::engine::{Automaton, NoAction, Range, Time};
7use rill_core::traits::ParamValue;
8use std::fmt;
9use std::sync::Arc;
10
11#[derive(Clone)]
13pub struct FunctionAutomaton {
14 name: String,
16 generator: Arc<dyn Fn(Time) -> f64 + Send + Sync>,
18 range: Range,
20}
21
22impl fmt::Debug for FunctionAutomaton {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 f.debug_struct("FunctionAutomaton")
25 .field("name", &self.name)
26 .field("range", &self.range)
27 .finish()
28 }
29}
30
31impl FunctionAutomaton {
32 pub fn new<F>(name: &str, generator: F) -> Self
34 where
35 F: Fn(Time) -> f64 + Send + Sync + 'static,
36 {
37 Self {
38 name: name.to_string(),
39 generator: Arc::new(generator),
40 range: Range::bipolar(),
41 }
42 }
43
44 pub fn with_range(mut self, range: Range) -> Self {
46 self.range = range;
47 self
48 }
49}
50
51impl Automaton for FunctionAutomaton {
52 type Internal = ();
53 type Action = NoAction;
54
55 fn step(
56 &self,
57 _internal: &mut Self::Internal,
58 _current: &ParamValue,
59 time: Time,
60 _action: &Self::Action,
61 ) -> ParamValue {
62 let value = (self.generator)(time);
63 let clamped = self.range.clamp(value);
64 ParamValue::Float(clamped as f32)
65 }
66
67 fn initial_internal(&self) -> Self::Internal {}
68
69 fn name(&self) -> &str {
70 &self.name
71 }
72}
73
74#[derive(Clone)]
76#[allow(clippy::type_complexity)]
77pub struct StatefulFunctionAutomaton<S> {
78 name: String,
80 generator: Arc<dyn Fn(Time, &mut S) -> f64 + Send + Sync>,
82 initial_state: S,
84 range: Range,
86}
87
88impl<S: fmt::Debug + Send + Sync + Clone + 'static> fmt::Debug for StatefulFunctionAutomaton<S> {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 f.debug_struct("StatefulFunctionAutomaton")
91 .field("name", &self.name)
92 .field("initial_state", &self.initial_state)
93 .field("range", &self.range)
94 .finish()
95 }
96}
97
98impl<S: Send + Sync + Clone + 'static> StatefulFunctionAutomaton<S> {
99 pub fn new<F>(name: &str, generator: F, initial_state: S) -> Self
101 where
102 F: Fn(Time, &mut S) -> f64 + Send + Sync + 'static,
103 {
104 Self {
105 name: name.to_string(),
106 generator: Arc::new(generator),
107 initial_state,
108 range: Range::bipolar(),
109 }
110 }
111
112 pub fn with_range(mut self, range: Range) -> Self {
114 self.range = range;
115 self
116 }
117}
118
119impl<S: fmt::Debug + Send + Sync + Clone + 'static> Automaton for StatefulFunctionAutomaton<S> {
120 type Internal = S;
121 type Action = NoAction;
122
123 fn step(
124 &self,
125 internal: &mut Self::Internal,
126 _current: &ParamValue,
127 time: Time,
128 _action: &Self::Action,
129 ) -> ParamValue {
130 let value = (self.generator)(time, internal);
131 let clamped = self.range.clamp(value);
132 ParamValue::Float(clamped as f32)
133 }
134
135 fn initial_internal(&self) -> Self::Internal {
136 self.initial_state.clone()
137 }
138
139 fn name(&self) -> &str {
140 &self.name
141 }
142}
143
144pub fn lfo_function(freq: f64, phase: f64, waveform: &'static str) -> impl Fn(Time) -> f64 {
146 move |t| {
147 let p = (t * freq + phase).fract();
148 match waveform {
149 "sine" => (p * 2.0 * std::f64::consts::PI).sin(),
150 "saw" => 2.0 * p - 1.0,
151 "square" => {
152 if p < 0.5 {
153 1.0
154 } else {
155 -1.0
156 }
157 }
158 _ => 0.0,
159 }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_function_automaton() {
169 let automaton = FunctionAutomaton::new("Test", |t| (t * 2.0).sin());
170 let mut internal = automaton.initial_internal();
171 let current = ParamValue::Float(0.0);
172
173 let value = automaton.step(&mut internal, ¤t, 1.0, &NoAction);
174 assert!(value.as_f32().is_some());
175 }
176
177 #[test]
178 fn test_stateful_automaton() {
179 let automaton = StatefulFunctionAutomaton::new(
180 "Counter",
181 |_t, counter: &mut i32| {
182 *counter += 1;
183 *counter as f64
184 },
185 0,
186 )
187 .with_range(Range::new(0.0, 100.0));
188
189 let mut internal = automaton.initial_internal();
190 let current = ParamValue::Float(0.0);
191
192 let value = automaton.step(&mut internal, ¤t, 1.0, &NoAction);
193 assert_eq!(value.as_f32().unwrap(), 1.0);
194
195 let value = automaton.step(&mut internal, ¤t, 1.0, &NoAction);
196 assert_eq!(value.as_f32().unwrap(), 2.0);
197 }
198}