Skip to main content

rill_core/queues/
control_event.rs

1//! Control events — hardware input events decoded from sensors.
2//!
3//! These types represent raw control input from physical interfaces
4//! (MIDI controllers, OSC surfaces, buttons, knobs, faders) and
5//! MIDI transport/clock events. They are decoded by sensors and
6//! dispatched to servos for mapping to graph parameters.
7
8// =============================================================================
9// MidiNoteKind
10// =============================================================================
11
12/// What aspect of a MIDI note event to extract for mapping.
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
15pub enum MidiNoteKind {
16    /// Extracts frequency: `midi_to_freq(note)`. Note Off produces no value.
17    Frequency,
18    /// Extracts amplitude: `velocity / 127` (On) or `0.0` (Off).
19    #[default]
20    Amplitude,
21    /// Extracts gate: `1.0` (On) or `0.0` (Off).
22    Gate,
23}
24
25// =============================================================================
26// MidiTransportKind
27// =============================================================================
28
29/// MIDI transport state.
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32pub enum MidiTransportKind {
33    /// Transport started.
34    Start,
35    /// Transport stopped.
36    Stop,
37    /// Transport resumed from current position.
38    Continue,
39}
40
41// =============================================================================
42// EventPattern
43// =============================================================================
44
45/// A pattern for matching controller events.
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47#[derive(Debug, Clone, PartialEq, Eq, Hash)]
48pub enum EventPattern {
49    /// Matches any button event regardless of ID.
50    AnyButton,
51    /// Matches a button event with a specific hardware ID.
52    ButtonId(u32),
53    /// Matches any knob event regardless of ID.
54    AnyKnob,
55    /// Matches a knob event with a specific hardware ID.
56    KnobId(u32),
57    /// Matches any fader event regardless of ID.
58    AnyFader,
59    /// Matches a fader event with a specific hardware ID.
60    FaderId(u32),
61    /// Matches any MIDI event (control change, note, clock, or transport).
62    AnyMidi,
63    /// Matches a MIDI control change event by controller number and optional channel.
64    MidiControl {
65        /// Optional MIDI channel filter; `None` matches any channel.
66        channel: Option<u8>,
67        /// MIDI controller number (CC index).
68        controller: u8,
69    },
70    /// Matches a MIDI note-on or note-off event and extracts a mapped value.
71    MidiNote {
72        /// Optional MIDI channel filter; `None` matches any channel.
73        channel: Option<u8>,
74        /// Optional note number filter; `None` matches any note.
75        note: Option<u8>,
76        /// Which aspect of the note event to use as the mapping value.
77        #[cfg_attr(feature = "serde", serde(default))]
78        kind: MidiNoteKind,
79    },
80    /// Matches a MIDI clock tick event.
81    MidiClock,
82    /// Matches a MIDI transport event (start, stop, or continue).
83    MidiTransport {
84        /// Optional transport kind filter; `None` matches any transport event.
85        kind: Option<MidiTransportKind>,
86    },
87    /// Matches an OSC message by exact address string.
88    OscAddress(String),
89    /// Matches an OSC message whose address contains the given substring.
90    OscPattern(String),
91}
92
93// =============================================================================
94// ControlEvent
95// =============================================================================
96
97/// Hardware control event from a physical interface (knob, button, fader, etc.).
98///
99/// Produced by sensors (MIDI, OSC, CV/Gate) and dispatched to servos
100/// for mapping to graph parameters.
101#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
102#[derive(Debug, Clone, PartialEq)]
103pub enum ControlEvent {
104    /// A physical button press or release.
105    Button {
106        /// Hardware control identifier.
107        id: u32,
108        /// `true` if the button is currently held down.
109        pressed: bool,
110    },
111    /// A physical knob (rotary encoder or potentiometer) event.
112    Knob {
113        /// Hardware control identifier.
114        id: u32,
115        /// Raw value in hardware-native units.
116        value: f32,
117        /// Value mapped to the [0.0, 1.0] range.
118        normalized: f32,
119    },
120    /// A physical fader (linear slider) event.
121    Fader {
122        /// Hardware control identifier.
123        id: u32,
124        /// Raw value in hardware-native units.
125        value: f32,
126        /// Value mapped to the [0.0, 1.0] range.
127        normalized: f32,
128    },
129    /// A MIDI control change message.
130    MidiControl {
131        /// MIDI channel (0-indexed).
132        channel: u8,
133        /// MIDI controller number.
134        controller: u8,
135        /// Raw 7-bit MIDI value.
136        value: u8,
137        /// Value normalized to [0.0, 1.0].
138        normalized: f32,
139    },
140    /// A MIDI note-on or note-off message.
141    MidiNote {
142        /// MIDI channel (0-indexed).
143        channel: u8,
144        /// MIDI note number.
145        note: u8,
146        /// MIDI velocity value (0-127).
147        velocity: u8,
148        /// `true` for note-on, `false` for note-off.
149        on: bool,
150    },
151    /// An OSC message event.
152    Osc {
153        /// OSC address path.
154        address: String,
155        /// OSC argument list as float values.
156        args: Vec<f32>,
157    },
158    /// A MIDI clock tick event.
159    MidiClock,
160    /// A MIDI transport state change.
161    MidiTransport {
162        /// The type of transport event (start, stop, or continue).
163        kind: MidiTransportKind,
164    },
165}
166
167impl EventPattern {
168    /// Checks whether this pattern matches a given control event.
169    pub fn matches(&self, event: &ControlEvent) -> bool {
170        match (self, event) {
171            (EventPattern::AnyButton, ControlEvent::Button { .. }) => true,
172            (EventPattern::ButtonId(id), ControlEvent::Button { id: eid, .. }) => *id == *eid,
173            (EventPattern::AnyKnob, ControlEvent::Knob { .. }) => true,
174            (EventPattern::KnobId(id), ControlEvent::Knob { id: eid, .. }) => *id == *eid,
175            (EventPattern::AnyFader, ControlEvent::Fader { .. }) => true,
176            (EventPattern::FaderId(id), ControlEvent::Fader { id: eid, .. }) => *id == *eid,
177            (
178                EventPattern::MidiControl {
179                    channel,
180                    controller,
181                },
182                ControlEvent::MidiControl {
183                    channel: ech,
184                    controller: ectr,
185                    ..
186                },
187            ) => (channel.is_none() || channel.unwrap() == *ech) && *controller == *ectr,
188            (
189                EventPattern::MidiNote { channel, note, .. },
190                ControlEvent::MidiNote {
191                    channel: ech,
192                    note: en,
193                    ..
194                },
195            ) => {
196                (channel.is_none() || channel.unwrap() == *ech)
197                    && (note.is_none() || note.unwrap() == *en)
198            }
199            (EventPattern::AnyMidi, ControlEvent::MidiControl { .. })
200            | (EventPattern::AnyMidi, ControlEvent::MidiNote { .. })
201            | (EventPattern::AnyMidi, ControlEvent::MidiClock)
202            | (EventPattern::AnyMidi, ControlEvent::MidiTransport { .. }) => true,
203            (EventPattern::MidiClock, ControlEvent::MidiClock) => true,
204            (
205                EventPattern::MidiTransport { kind },
206                ControlEvent::MidiTransport { kind: ek, .. },
207            ) => kind.is_none_or(|k| k == *ek),
208            (EventPattern::OscAddress(addr), ControlEvent::Osc { address, .. }) => addr == address,
209            (EventPattern::OscPattern(pat), ControlEvent::Osc { address, .. }) => {
210                address.contains(pat)
211            }
212            _ => false,
213        }
214    }
215}
216
217impl ControlEvent {
218    /// Returns the normalized value (0.0–1.0) of this event, if it carries one.
219    pub fn normalized_value(&self) -> Option<f32> {
220        match self {
221            ControlEvent::Knob { normalized, .. } => Some(*normalized),
222            ControlEvent::Fader { normalized, .. } => Some(*normalized),
223            ControlEvent::MidiControl { normalized, .. } => Some(*normalized),
224            ControlEvent::Button { pressed, .. } => Some(if *pressed { 1.0 } else { 0.0 }),
225            _ => None,
226        }
227    }
228
229    /// Returns the hardware control ID attached to this event, if any.
230    pub fn id(&self) -> Option<u32> {
231        match self {
232            ControlEvent::Button { id, .. } => Some(*id),
233            ControlEvent::Knob { id, .. } => Some(*id),
234            ControlEvent::Fader { id, .. } => Some(*id),
235            _ => None,
236        }
237    }
238}