Skip to main content

audio_automation/
state.rs

1//! Automation State Machine
2//!
3//! Implements DAW-style automation states that control how automation
4//! is recorded and played back for each parameter lane.
5//!
6//! | State | Description |
7//! |-------|-------------|
8//! | Off   | Ignores automation, uses manual value only |
9//! | Play  | Reads automation curve, no recording |
10//! | Write | Records continuously, overwrites all existing automation |
11//! | Touch | Records only while touching the control, returns to existing curve |
12//! | Latch | Records while touching, continues at last value when released |
13//!
14//! These states are standard across professional DAWs (Ardour, Pro Tools, Logic, etc.)
15
16use serde::{Deserialize, Serialize};
17
18/// Automation state for a parameter lane (Ardour/Pro Tools style)
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
20pub enum AutomationState {
21    #[default]
22    Off,
23    Play,
24    Write,
25    Touch,
26    Latch,
27}
28
29impl AutomationState {
30    #[must_use]
31    #[inline]
32    pub fn reads_automation(&self) -> bool {
33        matches!(self, Self::Play | Self::Touch | Self::Latch)
34    }
35
36    #[must_use]
37    #[inline]
38    pub fn can_record(&self) -> bool {
39        matches!(self, Self::Write | Self::Touch | Self::Latch)
40    }
41
42    #[must_use]
43    #[inline]
44    pub fn starts_on_touch(&self) -> bool {
45        matches!(self, Self::Touch | Self::Latch)
46    }
47
48    #[must_use]
49    #[inline]
50    pub fn stops_on_release(&self) -> bool {
51        matches!(self, Self::Touch)
52    }
53
54    #[must_use]
55    pub fn all() -> &'static [AutomationState] {
56        &[Self::Off, Self::Play, Self::Write, Self::Touch, Self::Latch]
57    }
58
59    #[must_use]
60    pub fn display_name(&self) -> &'static str {
61        match self {
62            Self::Off => "Off",
63            Self::Play => "Play",
64            Self::Write => "Write",
65            Self::Touch => "Touch",
66            Self::Latch => "Latch",
67        }
68    }
69
70    #[must_use]
71    pub fn abbreviation(&self) -> &'static str {
72        match self {
73            Self::Off => "OFF",
74            Self::Play => "PLY",
75            Self::Write => "WRT",
76            Self::Touch => "TCH",
77            Self::Latch => "LCH",
78        }
79    }
80}
81
82impl core::fmt::Display for AutomationState {
83    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
84        write!(f, "{}", self.display_name())
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use alloc::format;
92
93    #[test]
94    fn test_automation_state_properties() {
95        // Off: no read, no record
96        assert!(!AutomationState::Off.reads_automation());
97        assert!(!AutomationState::Off.can_record());
98
99        // Play: read only
100        assert!(AutomationState::Play.reads_automation());
101        assert!(!AutomationState::Play.can_record());
102
103        // Write: record only (overwrites)
104        assert!(!AutomationState::Write.reads_automation());
105        assert!(AutomationState::Write.can_record());
106
107        // Touch: read + record on touch, stops on release
108        assert!(AutomationState::Touch.reads_automation());
109        assert!(AutomationState::Touch.can_record());
110        assert!(AutomationState::Touch.starts_on_touch());
111        assert!(AutomationState::Touch.stops_on_release());
112
113        // Latch: read + record on touch, continues after release
114        assert!(AutomationState::Latch.reads_automation());
115        assert!(AutomationState::Latch.can_record());
116        assert!(AutomationState::Latch.starts_on_touch());
117        assert!(!AutomationState::Latch.stops_on_release());
118    }
119
120    #[test]
121    fn test_all_states() {
122        assert_eq!(AutomationState::all().len(), 5);
123    }
124
125    #[test]
126    fn test_display() {
127        assert_eq!(AutomationState::Touch.display_name(), "Touch");
128        assert_eq!(AutomationState::Touch.abbreviation(), "TCH");
129        assert_eq!(format!("{}", AutomationState::Touch), "Touch");
130    }
131
132    #[test]
133    fn test_default() {
134        assert_eq!(AutomationState::default(), AutomationState::Off);
135    }
136}