clack_common/events.rs
1//! Audio-processing events and related utilities.
2//!
3//! Events notify a plugin's Audio Processor of anything that may change its audio output, such as
4//! note on/off events, parameter changes, MIDI events, and more.
5//!
6//! All events in CLAP are sample-accurate time-stamped events ([`Event`]).
7//! They are provided to the plugin's audio processor alongside the audio buffers through
8//! [`InputEventBuffer`](io::InputEventBuffer)s and read from
9//! [`OutputEventBuffer`](io::OutputEventBuffer)s
10//! (see the plugin's `process` method).
11
12use crate::events::spaces::*;
13use clap_sys::events::clap_event_header;
14use std::fmt::{Debug, Formatter};
15
16pub mod event_types;
17pub mod io;
18pub mod spaces;
19
20mod header;
21mod helpers;
22mod pckn;
23
24use crate::utils::usize_to_clap_size;
25pub use header::*;
26pub use pckn::*;
27
28/// A specific event type.
29///
30/// # Safety
31///
32/// This trait allows casting to and from pointers to raw event headers. This means implementers of
33/// this trait must enforce the following:
34///
35/// * The [`EventSpace`](Event::EventSpace) type *must* be the [`EventSpace`] implementation that
36/// it belongs to;
37/// * [`TYPE_ID`](Event::TYPE_ID) *must* match the event ID from its type.
38/// * The type *must* be ABI-compatible with the matching raw, C-FFI compatible event type.
39/// * All instances of this type *must* be initialized and valid.
40pub unsafe trait Event: AsRef<UnknownEvent> + Sized + 'static {
41 /// The raw event type identifier for this event type.
42 ///
43 /// This *MUST* match the identifier defined in the CLAP specification for this event.
44 const TYPE_ID: u16;
45 /// The event space this event type is a part of.
46 type EventSpace<'a>: EventSpace<'a>;
47
48 /// Returns the [`EventFlags`] of this event.
49 #[inline]
50 fn flags(&self) -> EventFlags {
51 self.header().flags()
52 }
53
54 /// Sets the [`EventFlags`] of this event.
55 #[inline]
56 fn set_flags(&mut self, flags: EventFlags) {
57 self.header_mut().set_flags(flags)
58 }
59
60 /// Builds an event with the given [`EventFlags`], and returns it.
61 ///
62 /// This is useful to use in a builder-style pattern.
63 #[inline]
64 fn with_flags(mut self, flags: EventFlags) -> Self {
65 self.header_mut().set_flags(flags);
66 self
67 }
68
69 /// Returns the time stamp of this event, in samples.
70 #[inline]
71 fn time(&self) -> u32 {
72 self.header().time()
73 }
74
75 /// Sets the time stamp of this event, in samples.
76 #[inline]
77 fn set_time(&mut self, time: u32) {
78 self.header_mut().set_time(time)
79 }
80
81 /// Builds an event with the given time stamp, and returns it.
82 ///
83 /// This is useful to use in a builder-style pattern.
84 #[inline]
85 fn with_time(mut self, time: u32) -> Self {
86 self.header_mut().set_time(time);
87 self
88 }
89
90 /// Returns a shared, immutable reference to this event's header.
91 #[inline]
92 fn header(&self) -> &EventHeader<Self> {
93 // SAFETY: this trait guarantees the raw_header points to an event
94 // header that matches the current type.
95 unsafe { EventHeader::from_raw_unchecked(self.raw_header()) }
96 }
97
98 /// Returns an exclusive, mutable reference to this event's header.
99 #[inline]
100 fn header_mut(&mut self) -> &mut EventHeader<Self> {
101 // SAFETY: this trait guarantees the raw_header points to an event
102 // header that matches the current type.
103 unsafe { EventHeader::from_raw_unchecked_mut(self.raw_header_mut()) }
104 }
105
106 /// Returns this event as a type-erased [`UnknownEvent`].
107 #[inline]
108 fn as_unknown(&self) -> &UnknownEvent {
109 // SAFETY: this trait guarantees the raw_header points to an initialized and valid event.
110 unsafe { UnknownEvent::from_raw(self.raw_header()) }
111 }
112
113 /// Returns a shared, immutable reference to the raw, C-FFI compatible representation of this header's event.
114 #[inline]
115 fn raw_header(&self) -> &clap_event_header {
116 // SAFETY: This trait guarantees self points to an initialized and valid event.
117 unsafe { &*(self as *const Self as *const clap_event_header) }
118 }
119
120 /// Returns an exclusive, mutable reference to the raw, C-FFI compatible representation of this header's event.
121 ///
122 /// # Safety
123 ///
124 /// This method allows mutating the event header's fields, most notably `type_`, `size` and
125 /// `space_id`.
126 ///
127 /// Modifying those fields may trigger Undefined Behavior, as it can make other readers of this
128 /// event treat it as a type or size that it is actually not.
129 #[inline]
130 unsafe fn raw_header_mut(&mut self) -> &mut clap_event_header {
131 // SAFETY: This trait guarantees self points to an initialized and valid event.
132 unsafe { &mut *(self as *mut Self as *mut clap_event_header) }
133 }
134}
135
136/// An event of an undetermined type.
137///
138/// CLAP event types come in a variety of different sizes. Therefore, unknown events do not have
139/// their sizes known at compile-time, making this type a
140/// [DST](https://doc.rust-lang.org/reference/dynamically-sized-types.html) which can only be
141/// operated on through references (`&`).
142///
143/// Moreover, because CLAP supports defining using custom events, it's possible for any host or
144/// plugin to encounter an event type it cannot possibly figure out the concrete type of.
145///
146/// The [`UnknownEvent`] type allows to safely work with events without knowing their type.
147/// Their headers can be retrieved with the [`header()`](UnknownEvent::header) method, and they can
148/// be directly copied around through [`InputEventBuffer`s](io::InputEventBuffer) and
149/// [`OutputEventBuffer`s](io::OutputEventBuffer).
150///
151/// To try and figure out an event's concrete type, the [`as_event()`](UnknownEvent::as_event)
152/// method can be used to try and downcast it to a specific event type. The
153/// [`as_core_event`](UnknownEvent::as_core_event) can also be used to check if the event is a
154/// standard CLAP event, and then further `match`ed to find its specific type.
155///
156/// Alternatively, one can also use the [`as_event_for_space`](UnknownEvent::as_event_for_space) and
157/// [`as_event_space`](UnknownEvent::as_event_space) methods, which are respectively identical but
158/// for custom [event spaces](EventSpace), by checking against an ID from the `event_registry` extension.
159#[repr(transparent)]
160pub struct UnknownEvent {
161 data: [u8],
162}
163
164impl UnknownEvent {
165 /// Returns the header of this event.
166 #[inline]
167 pub const fn header(&self) -> &EventHeader {
168 // SAFETY: Pointer is guaranteed to be valid from constructors
169 unsafe { EventHeader::from_raw(&*self.as_raw()) }
170 }
171
172 /// Attempts to downcast this event to a specific standard CLAP event type.
173 ///
174 /// This returns a down-casted reference to the event if the event matches the given type, or
175 /// `None` if it doesn't.
176 ///
177 /// # Example
178 ///
179 /// ```
180 /// use clack_common::events::event_types::NoteOnEvent;
181 /// use clack_common::events::UnknownEvent;
182 ///
183 /// fn handle_event(event: &UnknownEvent) {
184 /// if let Some(note_on_event) = event.as_event::<NoteOnEvent>() {
185 /// // Handle the Note On event
186 /// # let _ = note_on_event;
187 /// } else {
188 /// // This is not a Note On event
189 /// }
190 /// }
191 ///
192 /// ```
193 #[inline]
194 pub fn as_event<'s, E: Event<EventSpace<'s> = CoreEventSpace<'s>>>(&self) -> Option<&E> {
195 self.as_event_for_space(EventSpaceId::core())
196 }
197
198 /// Attempts to downcast this event to the [core event space](CoreEventSpace), allowing it to be
199 /// match against all the standard CLAP event types.
200 ///
201 /// This returns `None` instead if the given event doesn't match the known standard events.
202 ///
203 /// # Example
204 ///
205 /// ```
206 /// use clack_common::events::event_types::NoteOnEvent;
207 /// use clack_common::events::UnknownEvent;
208 ///
209 /// fn handle_event(event: &UnknownEvent) {
210 /// use clack_common::events::spaces::CoreEventSpace::*;
211 ///
212 /// let Some(core_event) = event.as_core_event() else {
213 /// // This is not a known standard event.
214 /// return;
215 /// };
216 ///
217 /// match core_event {
218 /// NoteOn(e) => { /* Handle the Note On event */ }
219 /// NoteOff(e) => { /* Handle the Note Off event */ }
220 /// ParamValue(e) => { /* Handle the Parameter Value event */ }
221 /// _ => { /* This is another event type than the ones we chose to handle */ }
222 /// }
223 /// }
224 ///
225 /// ```
226 #[inline]
227 pub fn as_core_event(&self) -> Option<CoreEventSpace<'_>> {
228 self.as_event_space(EventSpaceId::core())
229 }
230
231 /// Attempts to downcast this event to a specific event type from a given [event space](EventSpace).
232 ///
233 /// This returns a down-casted reference to the event if the event matches the given type and
234 /// space, or `None` if it doesn't.
235 #[inline]
236 pub fn as_event_for_space<E: Event>(
237 &self,
238 space_id: EventSpaceId<E::EventSpace<'_>>,
239 ) -> Option<&E> {
240 let raw = self.header().as_raw();
241 if raw.space_id != space_id.id()
242 || raw.type_ != E::TYPE_ID
243 || raw.size != const { usize_to_clap_size(size_of::<E>()) }
244 {
245 return None;
246 }
247
248 // SAFETY: this type guarantees the header is followed by event data, and we just checked the space_id, type and size fields
249 Some(unsafe { self.as_event_unchecked() })
250 }
251
252 /// Attempts to downcast this event to the given [event space](EventSpace), allowing it to be
253 /// match against all the event types of that space.
254 ///
255 /// This returns `None` instead if the given event doesn't match the known events from that
256 /// event space.
257 #[inline]
258 pub fn as_event_space<'s, S: EventSpace<'s>>(&'s self, space_id: EventSpaceId<S>) -> Option<S> {
259 if space_id.id() != self.header().space_id()?.id() {
260 return None;
261 }
262
263 // SAFETY: we just checked the event space ID is valid for the current event.
264 unsafe { S::from_unknown(self) }
265 }
266
267 /// Casts this event as an event of a given type, without performing any checks.
268 ///
269 /// # Safety
270 /// The caller *must* ensure the event is of the given type, otherwise this will perform an
271 /// incorrect cast, leading to Undefined Behavior.
272 #[inline]
273 pub unsafe fn as_event_unchecked<E: Event>(&self) -> &E {
274 &*(self as *const _ as *const E)
275 }
276
277 /// Gets an unknown event from a raw event header.
278 ///
279 /// # Safety
280 /// The caller *must* ensure that not only the contents of the header are valid, but also that
281 /// they are immediately preceding the rest of the event struct matching the event and space IDs
282 /// in the header.
283 #[inline]
284 pub const unsafe fn from_raw<'e>(header: *const clap_event_header) -> &'e Self {
285 // SAFETY: no need to check for len > 0, since the data slice includes the header itself.
286 let data = core::slice::from_raw_parts(header as *const _, (*header).size as usize);
287 // SAFETY: The caller guarantees the right number of bytes is available after the given pointer in the size field.
288 Self::from_bytes_unchecked(data)
289 }
290
291 /// Returns a raw, C-FFI compatible event header pointer to this event.
292 #[inline]
293 pub const fn as_raw(&self) -> *const clap_event_header {
294 self.data.as_ptr().cast()
295 }
296
297 /// Returns the event as a raw byte buffer. This includes the event's (header)[EventHeader].
298 #[inline]
299 pub fn as_bytes(&self) -> &[u8] {
300 &self.data
301 }
302
303 /// Retrieves an event from a byte buffer, without performing any checks.
304 ///
305 /// The given buffer must include the event's (header)[EventHeader].
306 ///
307 /// # Safety
308 ///
309 /// The caller must ensure the byte buffer is properly aligned, and that is also contains a
310 /// valid event header as well as the remaining of the event struct.
311 #[inline]
312 pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &UnknownEvent {
313 &*(bytes as *const [u8] as *const _)
314 }
315}
316
317impl<E: Event> PartialEq<E> for UnknownEvent
318where
319 E: PartialEq,
320{
321 fn eq(&self, other: &E) -> bool {
322 match self.as_event_for_space::<E>(other.header().space_id()) {
323 None => false,
324 Some(s) => s.eq(other),
325 }
326 }
327}
328
329impl Debug for UnknownEvent {
330 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
331 match self.as_core_event() {
332 Some(e) => Debug::fmt(&e, f),
333 None => f
334 .debug_struct("UnknownEvent")
335 .field("header", self.header())
336 .finish(),
337 }
338 }
339}
340
341impl AsRef<UnknownEvent> for UnknownEvent {
342 #[inline]
343 fn as_ref(&self) -> &UnknownEvent {
344 self
345 }
346}
347
348#[inline]
349pub(crate) const fn ensure_event_matches<'s, E: Event<EventSpace<'s> = CoreEventSpace<'s>>>(
350 header: &clap_event_header,
351) {
352 if header.space_id != 0 {
353 panic_event_space_mismatch()
354 }
355
356 if header.type_ != E::TYPE_ID {
357 panic_event_type_mismatch()
358 }
359}
360
361#[inline(never)]
362#[cold]
363const fn panic_event_space_mismatch() {
364 panic!(
365 "CLAP Event mismatch: expected core event space (ID 0), got a non-core event space instead.",
366 )
367}
368
369#[inline(never)]
370#[cold]
371const fn panic_event_type_mismatch() {
372 panic!("CLAP Event mismatch: got a different event ID from expected")
373}