Skip to main content

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}