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}