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