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