rill_patchbay/automaton/
mod.rs1pub mod cellular;
9pub mod envelope;
10pub mod factory;
11pub mod function;
12pub mod lfo;
13pub mod random;
14pub mod sequencer;
15
16pub use cellular::*;
17pub use envelope::*;
18pub use factory::*;
19pub use function::*;
20pub use lfo::*;
21pub use random::*;
22
23use std::fmt::Debug;
24
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[derive(Debug, Clone, Copy, PartialEq)]
28pub enum SyncMode {
29 Free,
31 Sync,
33 OneShot,
35}
36
37#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[derive(Debug, Clone, Copy)]
40pub struct Range {
41 pub min: f64,
43 pub max: f64,
45}
46
47impl Range {
48 pub const fn new(min: f64, max: f64) -> Self {
50 Self { min, max }
51 }
52
53 pub const fn unipolar() -> Self {
55 Self { min: 0.0, max: 1.0 }
56 }
57
58 pub const fn bipolar() -> Self {
60 Self {
61 min: -1.0,
62 max: 1.0,
63 }
64 }
65
66 pub fn clamp(&self, value: f64) -> f64 {
68 value.clamp(self.min, self.max)
69 }
70
71 pub fn normalize(&self, value: f64) -> f64 {
73 (value - self.min) / (self.max - self.min)
74 }
75
76 pub fn denormalize(&self, norm: f64) -> f64 {
78 self.min + norm * (self.max - self.min)
79 }
80}
81
82#[derive(Debug)]
84pub struct AutomatonComparison;
85
86impl AutomatonComparison {
87 pub fn types() -> &'static str {
89 "Automaton types:\n\
90 ┌─────────────────┬─────────────────────────────┬─────────────────┐\n\
91 │ Automaton │ Characteristics │ Application │\n\
92 ├─────────────────┼─────────────────────────────┼─────────────────┤\n\
93 │ LFO │ Harmonic/relaxation │ Vibrato, tremolo│\n\
94 │ Envelope │ ADSR, AR, ASR │ Amplitude env. │\n\
95 │ Function │ Arbitrary time function │ Complex mod. │\n\
96 │ Sequencer │ Patterns, steps │ Rhythmic │\n\
97 │ RandomWalk │ Random walks │ Generative │\n\
98 │ Chaos │ Deterministic chaos │ Unpredictable │\n\
99 │ Cellular │ Cellular automata │ Organic │\n\
100 └─────────────────┴─────────────────────────────┴─────────────────┘"
101 }
102
103 pub fn selection_guide() -> &'static str {
105 "How to choose an automaton:\n\n\
106 Periodic modulation:\n\
107 → LFO (Sine, Triangle, Saw, Square)\n\n\
108 One-shot events:\n\
109 → Envelope (ADSR, AR, ASR)\n\n\
110 Complex functions:\n\
111 → Function with arbitrary closure\n\n\
112 Rhythmic patterns:\n\
113 → Sequencer with steps and durations\n\n\
114 Generative processes:\n\
115 → RandomWalk, Chaos, Cellular\n\n\
116 Random values:\n\
117 → Sample & Hold (LFO in S&H mode)"
118 }
119
120 pub fn performance_guide() -> &'static str {
122 "Relative performance:\n\
123 **Function** — fastest (simple functions)\n\
124 **LFO** — moderate (trigonometry)\n\
125 **Envelope** — moderate (transition logic)\n\
126 **RandomWalk** — moderate (RNG)\n\
127 **Sequencer** — slower (patterns)\n\
128 **Chaos** — slower (iterations)\n\
129 **Cellular** — slowest (neighbour lookups)"
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 #[test]
136 fn test_automaton_types_are_debug() {
137 fn assert_debug<T: std::fmt::Debug>(_: &T) {}
138 let lfo = super::LfoAutomaton::new("test", 1.0, 1.0, 0.0, super::LfoWaveform::Sine);
139 assert_debug(&lfo);
140 let env = super::EnvelopeAutomaton::adsr("test", 0.1, 0.2, 0.7, 0.3);
141 assert_debug(&env);
142 let func = super::FunctionAutomaton::new("test", |t| t);
143 assert_debug(&func);
144 }
145
146 #[test]
147 fn test_comparison_guides() {
148 assert!(!super::AutomatonComparison::types().is_empty());
149 assert!(!super::AutomatonComparison::selection_guide().is_empty());
150 assert!(!super::AutomatonComparison::performance_guide().is_empty());
151 }
152}