Skip to main content

firewheel_core/
event.rs

1use core::any::Any;
2
3#[cfg(not(feature = "std"))]
4use bevy_platform::prelude::{Box, Vec};
5
6use crate::{
7    clock::{DurationSamples, DurationSeconds, InstantSamples, InstantSeconds},
8    collector::{ArcGc, OwnedGc},
9    diff::{Notify, ParamPath},
10    dsp::volume::Volume,
11    node::NodeID,
12    vector::{Vec2, Vec3},
13};
14
15#[cfg(feature = "midi_events")]
16pub use wmidi;
17#[cfg(feature = "midi_events")]
18use wmidi::MidiMessage;
19
20#[cfg(feature = "scheduled_events")]
21use crate::clock::EventInstant;
22
23#[cfg(feature = "musical_transport")]
24use crate::clock::{DurationMusical, InstantMusical};
25
26/// An event sent to an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
27#[derive(Debug)]
28pub struct NodeEvent {
29    /// The ID of the node that should receive the event.
30    pub node_id: NodeID,
31    /// Optionally, a time to schedule this event at. If `None`, the event is considered
32    /// to be at the start of the next processing period.
33    #[cfg(feature = "scheduled_events")]
34    pub time: Option<EventInstant>,
35    /// The type of event.
36    pub event: NodeEventType,
37}
38
39impl NodeEvent {
40    /// Construct an event to send to an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
41    ///
42    /// * `node_id` - The ID of the node that should receive the event.
43    /// * `event` - The type of event.
44    pub const fn new(node_id: NodeID, event: NodeEventType) -> Self {
45        Self {
46            node_id,
47            #[cfg(feature = "scheduled_events")]
48            time: None,
49            event,
50        }
51    }
52
53    /// Construct a new scheduled event to send to an
54    /// [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
55    ///
56    /// * `node_id` - The ID of the node that should receive the event.
57    /// * `time` - The time to schedule this event at.
58    /// * `event` - The type of event.
59    #[cfg(feature = "scheduled_events")]
60    pub const fn scheduled(node_id: NodeID, time: EventInstant, event: NodeEventType) -> Self {
61        Self {
62            node_id,
63            time: Some(time),
64            event,
65        }
66    }
67}
68
69/// An event type associated with an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
70#[non_exhaustive]
71pub enum NodeEventType {
72    Param {
73        /// Data for a specific parameter.
74        data: ParamData,
75        /// The path to the parameter.
76        path: ParamPath,
77    },
78    /// Set the bypass state of the node.
79    SetBypassed(bool),
80    /// Custom event type stored on the heap.
81    Custom(OwnedGc<Box<dyn Any + Send + 'static>>),
82    /// Custom event type stored on the stack as raw bytes.
83    CustomBytes([u8; 36]),
84    #[cfg(feature = "midi_events")]
85    MIDI(MidiMessage<'static>),
86}
87
88impl NodeEventType {
89    pub fn custom<T: Send + 'static>(value: T) -> Self {
90        Self::Custom(OwnedGc::new(Box::new(value)))
91    }
92
93    pub fn custom_boxed<T: Send + 'static>(value: Box<T>) -> Self {
94        Self::Custom(OwnedGc::new(value))
95    }
96
97    /// Try to downcast the custom event to an immutable reference to `T`.
98    ///
99    /// If this does not contain [`NodeEventType::Custom`] or if the
100    /// downcast failed, then this returns `None`.
101    pub fn downcast_ref<T: Send + 'static>(&self) -> Option<&T> {
102        if let Self::Custom(owned) = self {
103            owned.as_ref().downcast_ref()
104        } else {
105            None
106        }
107    }
108
109    /// Try to downcast the custom event to a mutable reference to `T`.
110    ///
111    /// If this does not contain [`NodeEventType::Custom`] or if the
112    /// downcast failed, then this returns `None`.
113    pub fn downcast_mut<T: Send + 'static>(&mut self) -> Option<&mut T> {
114        if let Self::Custom(owned) = self {
115            owned.as_mut().downcast_mut()
116        } else {
117            None
118        }
119    }
120
121    /// Try to swap the contents of the custom event with the contents of
122    /// the given value.
123    ///
124    /// If successful, the old contents that were stored in `value` will
125    /// safely be dropped and deallocated on another non-realtime thread.
126    ///
127    /// Returns `true` if the value has been successfully swapped, `false`
128    /// otherwise (i.e. the event did not contain [`NodeEventType::Custom`]
129    /// or the downcast failed).
130    pub fn downcast_swap<T: Send + 'static>(&mut self, value: &mut T) -> bool {
131        if let Some(v) = self.downcast_mut::<T>() {
132            core::mem::swap(v, value);
133            true
134        } else {
135            false
136        }
137    }
138
139    /// Try to swap the contents of the custom event with the contents of
140    /// the given value wrapped in an [`OwnedGc`].
141    ///
142    /// If successful, the old contents that were stored in `value` will
143    /// safely be dropped and deallocated on another non-realtime thread.
144    ///
145    /// Returns `true` if the value has been successfully swapped, `false`
146    /// otherwise (i.e. the event did not contain [`NodeEventType::Custom`]
147    /// or the downcast failed).
148    pub fn downcast_into_owned<T: Send + 'static>(&mut self, value: &mut OwnedGc<T>) -> bool {
149        if let Some(v) = self.downcast_mut::<T>() {
150            value.swap(v);
151            true
152        } else {
153            false
154        }
155    }
156}
157
158impl core::fmt::Debug for NodeEventType {
159    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
160        match self {
161            NodeEventType::Param { data, path } => f
162                .debug_struct("Param")
163                .field("data", &data)
164                .field("path", &path)
165                .finish(),
166            NodeEventType::Custom(_) => f.debug_tuple("Custom").finish_non_exhaustive(),
167            NodeEventType::CustomBytes(f0) => f.debug_tuple("CustomBytes").field(&f0).finish(),
168            NodeEventType::SetBypassed(b) => f.debug_tuple("SetBypassed").field(&b).finish(),
169            #[cfg(feature = "midi_events")]
170            NodeEventType::MIDI(f0) => f.debug_tuple("MIDI").field(&f0).finish(),
171        }
172    }
173}
174
175/// Data that can be used to patch an individual parameter.
176#[derive(Clone, Debug)]
177#[non_exhaustive]
178pub enum ParamData {
179    F32(f32),
180    F64(f64),
181    I32(i32),
182    U32(u32),
183    I64(i64),
184    U64(u64),
185    Bool(bool),
186    Volume(Volume),
187    Vector2D(Vec2),
188    Vector3D(Vec3),
189
190    #[cfg(feature = "scheduled_events")]
191    EventInstant(EventInstant),
192    InstantSeconds(InstantSeconds),
193    DurationSeconds(DurationSeconds),
194    InstantSamples(InstantSamples),
195    DurationSamples(DurationSamples),
196    #[cfg(feature = "musical_transport")]
197    InstantMusical(InstantMusical),
198    #[cfg(feature = "musical_transport")]
199    DurationMusical(DurationMusical),
200
201    /// Custom type stored on the heap.
202    Any(ArcGc<dyn Any + Send + Sync>),
203
204    /// Custom type stored on the stack as raw bytes.
205    CustomBytes([u8; 20]),
206
207    /// No data (i.e. the type is `None`).
208    None,
209}
210
211impl ParamData {
212    /// Construct a [`ParamData::Any`] variant.
213    pub fn any<T: Send + Sync + 'static>(value: T) -> Self {
214        Self::Any(ArcGc::new_any(value))
215    }
216
217    /// Construct an optional [`ParamData::Any`] variant.
218    pub fn opt_any<T: Any + Send + Sync + 'static>(value: Option<T>) -> Self {
219        if let Some(value) = value {
220            Self::any(value)
221        } else {
222            Self::None
223        }
224    }
225
226    /// Try to downcast [`ParamData::Any`] into `T`.
227    ///
228    /// If this enum doesn't hold [`ParamData::Any`] or the downcast fails,
229    /// then this returns `None`.
230    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
231        match self {
232            Self::Any(any) => any.downcast_ref(),
233            _ => None,
234        }
235    }
236}
237
238macro_rules! param_data_from {
239    ($ty:ty, $variant:ident) => {
240        impl From<$ty> for ParamData {
241            fn from(value: $ty) -> Self {
242                Self::$variant(value.into())
243            }
244        }
245
246        impl TryInto<$ty> for &ParamData {
247            type Error = crate::diff::PatchError;
248
249            fn try_into(self) -> Result<$ty, crate::diff::PatchError> {
250                match self {
251                    ParamData::$variant(value) => Ok((*value).into()),
252                    _ => Err(crate::diff::PatchError::InvalidData),
253                }
254            }
255        }
256
257        impl From<Option<$ty>> for ParamData {
258            fn from(value: Option<$ty>) -> Self {
259                if let Some(value) = value {
260                    Self::$variant(value.into())
261                } else {
262                    Self::None
263                }
264            }
265        }
266
267        impl TryInto<Option<$ty>> for &ParamData {
268            type Error = crate::diff::PatchError;
269
270            fn try_into(self) -> Result<Option<$ty>, crate::diff::PatchError> {
271                match self {
272                    ParamData::$variant(value) => Ok(Some((*value).into())),
273                    ParamData::None => Ok(None),
274                    _ => Err(crate::diff::PatchError::InvalidData),
275                }
276            }
277        }
278
279        impl From<Notify<$ty>> for ParamData {
280            fn from(value: Notify<$ty>) -> Self {
281                Self::$variant((*value).into())
282            }
283        }
284
285        impl TryInto<Notify<$ty>> for &ParamData {
286            type Error = crate::diff::PatchError;
287
288            fn try_into(self) -> Result<Notify<$ty>, crate::diff::PatchError> {
289                match self {
290                    ParamData::$variant(value) => Ok(Notify::new((*value).into())),
291                    _ => Err(crate::diff::PatchError::InvalidData),
292                }
293            }
294        }
295    };
296}
297
298param_data_from!(Volume, Volume);
299param_data_from!(f32, F32);
300param_data_from!(f64, F64);
301param_data_from!(i32, I32);
302param_data_from!(u32, U32);
303param_data_from!(i64, I64);
304param_data_from!(u64, U64);
305param_data_from!(bool, Bool);
306param_data_from!(Vec2, Vector2D);
307param_data_from!(Vec3, Vector3D);
308#[cfg(feature = "scheduled_events")]
309param_data_from!(EventInstant, EventInstant);
310param_data_from!(InstantSeconds, InstantSeconds);
311param_data_from!(DurationSeconds, DurationSeconds);
312param_data_from!(InstantSamples, InstantSamples);
313param_data_from!(DurationSamples, DurationSamples);
314#[cfg(feature = "musical_transport")]
315param_data_from!(InstantMusical, InstantMusical);
316#[cfg(feature = "musical_transport")]
317param_data_from!(DurationMusical, DurationMusical);
318
319#[cfg(feature = "glam-29")]
320param_data_from!(glam_29::Vec2, Vector2D);
321#[cfg(feature = "glam-29")]
322param_data_from!(glam_29::Vec3, Vector3D);
323
324#[cfg(feature = "glam-30")]
325param_data_from!(glam_30::Vec2, Vector2D);
326#[cfg(feature = "glam-30")]
327param_data_from!(glam_30::Vec3, Vector3D);
328
329#[cfg(feature = "glam-31")]
330param_data_from!(glam_31::Vec2, Vector2D);
331#[cfg(feature = "glam-31")]
332param_data_from!(glam_31::Vec3, Vector3D);
333
334#[cfg(feature = "glam-32")]
335param_data_from!(glam_32::Vec2, Vector2D);
336#[cfg(feature = "glam-32")]
337param_data_from!(glam_32::Vec3, Vector3D);
338
339impl From<()> for ParamData {
340    fn from(_value: ()) -> Self {
341        Self::None
342    }
343}
344
345impl TryInto<()> for &ParamData {
346    type Error = crate::diff::PatchError;
347
348    fn try_into(self) -> Result<(), crate::diff::PatchError> {
349        match self {
350            ParamData::None => Ok(()),
351            _ => Err(crate::diff::PatchError::InvalidData),
352        }
353    }
354}
355
356impl From<Notify<()>> for ParamData {
357    fn from(_value: Notify<()>) -> Self {
358        Self::None
359    }
360}
361
362impl TryInto<Notify<()>> for &ParamData {
363    type Error = crate::diff::PatchError;
364
365    fn try_into(self) -> Result<Notify<()>, crate::diff::PatchError> {
366        match self {
367            ParamData::None => Ok(Notify::new(())),
368            _ => Err(crate::diff::PatchError::InvalidData),
369        }
370    }
371}
372
373/// Used internally by the Firewheel processor
374#[cfg(feature = "scheduled_events")]
375pub struct ScheduledEventEntry {
376    pub event: NodeEvent,
377    pub is_pre_process: bool,
378}
379
380/// A list of events for an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
381pub struct ProcEvents<'a> {
382    immediate_event_buffer: &'a mut [Option<NodeEvent>],
383    #[cfg(feature = "scheduled_events")]
384    scheduled_event_arena: &'a mut [Option<ScheduledEventEntry>],
385    indices: &'a mut Vec<ProcEventsIndex>,
386}
387
388impl<'a> ProcEvents<'a> {
389    pub fn new(
390        immediate_event_buffer: &'a mut [Option<NodeEvent>],
391        #[cfg(feature = "scheduled_events")] scheduled_event_arena: &'a mut [Option<
392            ScheduledEventEntry,
393        >],
394        indices: &'a mut Vec<ProcEventsIndex>,
395    ) -> Self {
396        Self {
397            immediate_event_buffer,
398            #[cfg(feature = "scheduled_events")]
399            scheduled_event_arena,
400            indices,
401        }
402    }
403
404    pub fn num_events(&self) -> usize {
405        self.indices.len()
406    }
407
408    pub fn is_empty(&self) -> bool {
409        self.indices.is_empty()
410    }
411
412    /// Iterate over all events, draining the events from the list.
413    pub fn drain<'b>(&'b mut self) -> impl IntoIterator<Item = NodeEventType> + use<'b> {
414        self.indices.drain(..).map(|index_type| match index_type {
415            ProcEventsIndex::Immediate(i) => {
416                self.immediate_event_buffer[i as usize]
417                    .take()
418                    .unwrap()
419                    .event
420            }
421            #[cfg(feature = "scheduled_events")]
422            ProcEventsIndex::Scheduled(i) => {
423                self.scheduled_event_arena[i as usize]
424                    .take()
425                    .unwrap()
426                    .event
427                    .event
428            }
429        })
430    }
431
432    /// Iterate over all events and their timestamps, draining the
433    /// events from the list.
434    ///
435    /// The iterator returns `(event_type, Option<event_instant>)`
436    /// where `event_type` is the event, `event_instant` is the instant the
437    /// event was scheduled for. If the event was not scheduled, then
438    /// the latter will be `None`.
439    #[cfg(feature = "scheduled_events")]
440    pub fn drain_with_timestamps<'b>(
441        &'b mut self,
442    ) -> impl IntoIterator<Item = (NodeEventType, Option<EventInstant>)> + use<'b> {
443        self.indices.drain(..).map(|index_type| match index_type {
444            ProcEventsIndex::Immediate(i) => {
445                let event = self.immediate_event_buffer[i as usize].take().unwrap();
446
447                (event.event, event.time)
448            }
449            ProcEventsIndex::Scheduled(i) => {
450                let event = self.scheduled_event_arena[i as usize].take().unwrap();
451
452                (event.event.event, event.event.time)
453            }
454        })
455    }
456
457    /// Iterate over patches for `T`, draining the events from the list.
458    ///
459    /// ```
460    /// # use firewheel_core::{diff::*, event::ProcEvents};
461    /// # fn for_each_example(mut event_list: ProcEvents) {
462    /// #[derive(Patch, Default)]
463    /// struct FilterNode {
464    ///     frequency: f32,
465    ///     quality: f32,
466    /// }
467    ///
468    /// let mut node = FilterNode::default();
469    ///
470    /// // You can match on individual patch variants.
471    /// for patch in event_list.drain_patches::<FilterNode>() {
472    ///     match patch {
473    ///         FilterNodePatch::Frequency(frequency) => {
474    ///             node.frequency = frequency;
475    ///         }
476    ///         FilterNodePatch::Quality(quality) => {
477    ///             node.quality = quality;
478    ///         }
479    ///     }
480    /// }
481    ///
482    /// // Or simply apply all of them.
483    /// for patch in event_list.drain_patches::<FilterNode>() { node.apply(patch); }
484    /// # }
485    /// ```
486    ///
487    /// Errors produced while constructing patches are simply skipped.
488    pub fn drain_patches<'b, T: crate::diff::Patch>(
489        &'b mut self,
490    ) -> impl IntoIterator<Item = <T as crate::diff::Patch>::Patch> + use<'b, T> {
491        // Ideally this would parameterise the `FnMut` over some `impl From<PatchEvent<T>>`
492        // but it would require a marker trait for the `diff::Patch::Patch` assoc type to
493        // prevent overlapping impls.
494        self.drain().into_iter().filter_map(|e| T::patch_event(&e))
495    }
496
497    /// Iterate over patches for `T`, draining the events from the list, while also
498    /// returning the timestamp the event was scheduled for.
499    ///
500    /// The iterator returns `(patch, Option<event_instant>)`
501    /// where `event_instant` is the instant the event was scheduled for. If the event
502    /// was not scheduled, then the latter will be `None`.
503    ///
504    /// ```
505    /// # use firewheel_core::{diff::*, event::ProcEvents};
506    /// # fn for_each_example(mut event_list: ProcEvents) {
507    /// #[derive(Patch, Default)]
508    /// struct FilterNode {
509    ///     frequency: f32,
510    ///     quality: f32,
511    /// }
512    ///
513    /// let mut node = FilterNode::default();
514    ///
515    /// // You can match on individual patch variants.
516    /// for (patch, timestamp) in event_list.drain_patches_with_timestamps::<FilterNode>() {
517    ///     match patch {
518    ///         FilterNodePatch::Frequency(frequency) => {
519    ///             node.frequency = frequency;
520    ///         }
521    ///         FilterNodePatch::Quality(quality) => {
522    ///             node.quality = quality;
523    ///         }
524    ///     }
525    /// }
526    ///
527    /// // Or simply apply all of them.
528    /// for (patch, timestamp) in event_list.drain_patches_with_timestamps::<FilterNode>() { node.apply(patch); }
529    /// # }
530    /// ```
531    ///
532    /// Errors produced while constructing patches are simply skipped.
533    #[cfg(feature = "scheduled_events")]
534    pub fn drain_patches_with_timestamps<'b, T: crate::diff::Patch>(
535        &'b mut self,
536    ) -> impl IntoIterator<Item = (<T as crate::diff::Patch>::Patch, Option<EventInstant>)> + use<'b, T>
537    {
538        // Ideally this would parameterize the `FnMut` over some `impl From<PatchEvent<T>>`
539        // but it would require a marker trait for the `diff::Patch::Patch` assoc type to
540        // prevent overlapping implementations.
541        self.drain_with_timestamps()
542            .into_iter()
543            .filter_map(|(e, timestamp)| T::patch_event(&e).map(|patch| (patch, timestamp)))
544    }
545}
546
547/// Used internally by the Firewheel processor.
548#[derive(Debug, Clone, Copy, PartialEq, Eq)]
549pub enum ProcEventsIndex {
550    Immediate(u32),
551    #[cfg(feature = "scheduled_events")]
552    Scheduled(u32),
553}