Skip to main content

rill_patchbay/automaton/
mod.rs

1//! Automata — generative control-signal sources.
2//!
3//! This module provides various automaton types for generating real-time
4//! control signals. An automaton is an algorithm with internal state:
5//! given time and the current state, it produces a new state and an
6//! optional output value. External state mutation is not required.
7
8pub 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/// Synchronisation mode for an automaton.
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27#[derive(Debug, Clone, Copy, PartialEq)]
28pub enum SyncMode {
29    /// Free-running at the automaton's own rate.
30    Free,
31    /// Synchronised to an external clock.
32    Sync,
33    /// Run once and stop.
34    OneShot,
35}
36
37/// A numeric range with min/max bounds.
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39#[derive(Debug, Clone, Copy)]
40pub struct Range {
41    /// Lower bound of the range.
42    pub min: f64,
43    /// Upper bound of the range.
44    pub max: f64,
45}
46
47impl Range {
48    /// Create a new range with the given bounds.
49    pub const fn new(min: f64, max: f64) -> Self {
50        Self { min, max }
51    }
52
53    /// Return a unipolar range [0.0, 1.0].
54    pub const fn unipolar() -> Self {
55        Self { min: 0.0, max: 1.0 }
56    }
57
58    /// Return a bipolar range [-1.0, 1.0].
59    pub const fn bipolar() -> Self {
60        Self {
61            min: -1.0,
62            max: 1.0,
63        }
64    }
65
66    /// Clamp a value to lie within this range.
67    pub fn clamp(&self, value: f64) -> f64 {
68        value.clamp(self.min, self.max)
69    }
70
71    /// Normalize a value to [0.0, 1.0] based on this range.
72    pub fn normalize(&self, value: f64) -> f64 {
73        (value - self.min) / (self.max - self.min)
74    }
75
76    /// Denormalize a [0.0, 1.0] value back into this range.
77    pub fn denormalize(&self, norm: f64) -> f64 {
78        self.min + norm * (self.max - self.min)
79    }
80}
81
82/// Summary of available automaton types and their characteristics.
83#[derive(Debug)]
84pub struct AutomatonComparison;
85
86impl AutomatonComparison {
87    /// Print a table of automaton types and their applications.
88    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    /// Guide for choosing the right automaton.
104    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    /// Relative performance characteristics of each automaton type.
121    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}