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].
27pub struct NodeEvent {
28    /// The ID of the node that should receive the event.
29    pub node_id: NodeID,
30    /// Optionally, a time to schedule this event at. If `None`, the event is considered
31    /// to be at the start of the next processing period.
32    #[cfg(feature = "scheduled_events")]
33    pub time: Option<EventInstant>,
34    /// The type of event.
35    pub event: NodeEventType,
36}
37
38impl NodeEvent {
39    /// Construct an event to send to an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
40    ///
41    /// * `node_id` - The ID of the node that should receive the event.
42    /// * `event` - The type of event.
43    pub const fn new(node_id: NodeID, event: NodeEventType) -> Self {
44        Self {
45            node_id,
46            #[cfg(feature = "scheduled_events")]
47            time: None,
48            event,
49        }
50    }
51
52    /// Construct a new scheduled event to send to an
53    /// [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
54    ///
55    /// * `node_id` - The ID of the node that should receive the event.
56    /// * `time` - The time to schedule this event at.
57    /// * `event` - The type of event.
58    #[cfg(feature = "scheduled_events")]
59    pub const fn scheduled(node_id: NodeID, time: EventInstant, event: NodeEventType) -> Self {
60        Self {
61            node_id,
62            time: Some(time),
63            event,
64        }
65    }
66}
67
68/// An event type associated with an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
69#[non_exhaustive]
70pub enum NodeEventType {
71    Param {
72        /// Data for a specific parameter.
73        data: ParamData,
74        /// The path to the parameter.
75        path: ParamPath,
76    },
77    /// Custom event type stored on the heap.
78    Custom(OwnedGc<Box<dyn Any + Send + 'static>>),
79    /// Custom event type stored on the stack as raw bytes.
80    CustomBytes([u8; 36]),
81    #[cfg(feature = "midi_events")]
82    MIDI(MidiMessage<'static>),
83}
84
85impl NodeEventType {
86    pub fn custom<T: Send + 'static>(value: T) -> Self {
87        Self::Custom(OwnedGc::new(Box::new(value)))
88    }
89
90    /// Try to downcast the custom event to an immutable reference to `T`.
91    ///
92    /// If this does not contain [`NodeEventType::Custom`] or if the
93    /// downcast failed, then this returns `None`.
94    pub fn downcast_ref<T: Send + 'static>(&self) -> Option<&T> {
95        if let Self::Custom(owned) = self {
96            owned.downcast_ref()
97        } else {
98            None
99        }
100    }
101
102    /// Try to downcast the custom event to a mutable reference to `T`.
103    ///
104    /// If this does not contain [`NodeEventType::Custom`] or if the
105    /// downcast failed, then this returns `None`.
106    pub fn downcast_mut<T: Send + 'static>(&mut self) -> Option<&mut T> {
107        if let Self::Custom(owned) = self {
108            owned.downcast_mut()
109        } else {
110            None
111        }
112    }
113}
114
115/// Data that can be used to patch an individual parameter.
116#[derive(Clone, Debug)]
117#[non_exhaustive]
118pub enum ParamData {
119    F32(f32),
120    F64(f64),
121    I32(i32),
122    U32(u32),
123    I64(i64),
124    U64(u64),
125    Bool(bool),
126    Volume(Volume),
127    Vector2D(Vec2),
128    Vector3D(Vec3),
129
130    #[cfg(feature = "scheduled_events")]
131    EventInstant(EventInstant),
132    InstantSeconds(InstantSeconds),
133    DurationSeconds(DurationSeconds),
134    InstantSamples(InstantSamples),
135    DurationSamples(DurationSamples),
136    #[cfg(feature = "musical_transport")]
137    InstantMusical(InstantMusical),
138    #[cfg(feature = "musical_transport")]
139    DurationMusical(DurationMusical),
140
141    /// Custom type stored on the heap.
142    Any(ArcGc<dyn Any + Send + Sync>),
143
144    /// Custom type stored on the stack as raw bytes.
145    CustomBytes([u8; 20]),
146
147    /// No data (i.e. the type is `None`).
148    None,
149}
150
151impl ParamData {
152    /// Construct a [`ParamData::Any`] variant.
153    pub fn any<T: Send + Sync + 'static>(value: T) -> Self {
154        Self::Any(ArcGc::new_any(value))
155    }
156
157    /// Construct an optional [`ParamData::Any`] variant.
158    pub fn opt_any<T: Any + Send + Sync + 'static>(value: Option<T>) -> Self {
159        if let Some(value) = value {
160            Self::any(value)
161        } else {
162            Self::None
163        }
164    }
165
166    /// Try to downcast [`ParamData::Any`] into `T`.
167    ///
168    /// If this enum doesn't hold [`ParamData::Any`] or the downcast fails,
169    /// then this returns `None`.
170    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
171        match self {
172            Self::Any(any) => any.downcast_ref(),
173            _ => None,
174        }
175    }
176}
177
178macro_rules! param_data_from {
179    ($ty:ty, $variant:ident) => {
180        impl From<$ty> for ParamData {
181            fn from(value: $ty) -> Self {
182                Self::$variant(value.into())
183            }
184        }
185
186        impl TryInto<$ty> for &ParamData {
187            type Error = crate::diff::PatchError;
188
189            fn try_into(self) -> Result<$ty, crate::diff::PatchError> {
190                match self {
191                    ParamData::$variant(value) => Ok((*value).into()),
192                    _ => Err(crate::diff::PatchError::InvalidData),
193                }
194            }
195        }
196
197        impl From<Option<$ty>> for ParamData {
198            fn from(value: Option<$ty>) -> Self {
199                if let Some(value) = value {
200                    Self::$variant(value.into())
201                } else {
202                    Self::None
203                }
204            }
205        }
206
207        impl TryInto<Option<$ty>> for &ParamData {
208            type Error = crate::diff::PatchError;
209
210            fn try_into(self) -> Result<Option<$ty>, crate::diff::PatchError> {
211                match self {
212                    ParamData::$variant(value) => Ok(Some((*value).into())),
213                    ParamData::None => Ok(None),
214                    _ => Err(crate::diff::PatchError::InvalidData),
215                }
216            }
217        }
218
219        impl From<Notify<$ty>> for ParamData {
220            fn from(value: Notify<$ty>) -> Self {
221                Self::$variant((*value).into())
222            }
223        }
224
225        impl TryInto<Notify<$ty>> for &ParamData {
226            type Error = crate::diff::PatchError;
227
228            fn try_into(self) -> Result<Notify<$ty>, crate::diff::PatchError> {
229                match self {
230                    ParamData::$variant(value) => Ok(Notify::new((*value).into())),
231                    _ => Err(crate::diff::PatchError::InvalidData),
232                }
233            }
234        }
235    };
236}
237
238param_data_from!(Volume, Volume);
239param_data_from!(f32, F32);
240param_data_from!(f64, F64);
241param_data_from!(i32, I32);
242param_data_from!(u32, U32);
243param_data_from!(i64, I64);
244param_data_from!(u64, U64);
245param_data_from!(bool, Bool);
246param_data_from!(Vec2, Vector2D);
247param_data_from!(Vec3, Vector3D);
248#[cfg(feature = "scheduled_events")]
249param_data_from!(EventInstant, EventInstant);
250param_data_from!(InstantSeconds, InstantSeconds);
251param_data_from!(DurationSeconds, DurationSeconds);
252param_data_from!(InstantSamples, InstantSamples);
253param_data_from!(DurationSamples, DurationSamples);
254#[cfg(feature = "musical_transport")]
255param_data_from!(InstantMusical, InstantMusical);
256#[cfg(feature = "musical_transport")]
257param_data_from!(DurationMusical, DurationMusical);
258
259#[cfg(feature = "glam-29")]
260param_data_from!(glam::Vec2, Vector2D);
261#[cfg(feature = "glam-29")]
262param_data_from!(glam::Vec3, Vector3D);
263
264impl From<()> for ParamData {
265    fn from(_value: ()) -> Self {
266        Self::None
267    }
268}
269
270impl TryInto<()> for &ParamData {
271    type Error = crate::diff::PatchError;
272
273    fn try_into(self) -> Result<(), crate::diff::PatchError> {
274        match self {
275            ParamData::None => Ok(()),
276            _ => Err(crate::diff::PatchError::InvalidData),
277        }
278    }
279}
280
281impl From<Notify<()>> for ParamData {
282    fn from(_value: Notify<()>) -> Self {
283        Self::None
284    }
285}
286
287impl TryInto<Notify<()>> for &ParamData {
288    type Error = crate::diff::PatchError;
289
290    fn try_into(self) -> Result<Notify<()>, crate::diff::PatchError> {
291        match self {
292            ParamData::None => Ok(Notify::new(())),
293            _ => Err(crate::diff::PatchError::InvalidData),
294        }
295    }
296}
297
298/// A list of events for an [`AudioNodeProcessor`][crate::node::AudioNodeProcessor].
299pub struct ProcEvents<'a> {
300    immediate_event_buffer: &'a mut [Option<NodeEvent>],
301    #[cfg(feature = "scheduled_events")]
302    scheduled_event_arena: &'a mut [Option<NodeEvent>],
303    indices: &'a mut Vec<ProcEventsIndex>,
304}
305
306impl<'a> ProcEvents<'a> {
307    pub fn new(
308        immediate_event_buffer: &'a mut [Option<NodeEvent>],
309        #[cfg(feature = "scheduled_events")] scheduled_event_arena: &'a mut [Option<NodeEvent>],
310        indices: &'a mut Vec<ProcEventsIndex>,
311    ) -> Self {
312        Self {
313            immediate_event_buffer,
314            #[cfg(feature = "scheduled_events")]
315            scheduled_event_arena,
316            indices,
317        }
318    }
319
320    pub fn num_events(&self) -> usize {
321        self.indices.len()
322    }
323
324    /// Iterate over all events, draining the events from the list.
325    pub fn drain<'b>(&'b mut self) -> impl IntoIterator<Item = NodeEventType> + use<'b> {
326        self.indices.drain(..).map(|index_type| match index_type {
327            ProcEventsIndex::Immediate(i) => {
328                self.immediate_event_buffer[i as usize]
329                    .take()
330                    .unwrap()
331                    .event
332            }
333            #[cfg(feature = "scheduled_events")]
334            ProcEventsIndex::Scheduled(i) => {
335                self.scheduled_event_arena[i as usize].take().unwrap().event
336            }
337        })
338    }
339
340    /// Iterate over all events and their timestamps, draining the
341    /// events from the list.
342    ///
343    /// The iterator returns `(event_type, Option<event_instant>)`
344    /// where `event_type` is the event, `event_instant` is the instant the
345    /// event was schedueld for. If the event was not scheduled, then
346    /// the latter will be `None`.
347    #[cfg(feature = "scheduled_events")]
348    pub fn drain_with_timestamps<'b>(
349        &'b mut self,
350    ) -> impl IntoIterator<Item = (NodeEventType, Option<EventInstant>)> + use<'b> {
351        self.indices.drain(..).map(|index_type| match index_type {
352            ProcEventsIndex::Immediate(i) => {
353                let event = self.immediate_event_buffer[i as usize].take().unwrap();
354
355                (event.event, event.time)
356            }
357            ProcEventsIndex::Scheduled(i) => {
358                let event = self.scheduled_event_arena[i as usize].take().unwrap();
359
360                (event.event, event.time)
361            }
362        })
363    }
364
365    /// Iterate over patches for `T`, draining the events from the list.
366    ///
367    /// ```
368    /// # use firewheel_core::{diff::*, event::ProcEvents};
369    /// # fn for_each_example(mut event_list: ProcEvents) {
370    /// #[derive(Patch, Default)]
371    /// struct FilterNode {
372    ///     frequency: f32,
373    ///     quality: f32,
374    /// }
375    ///
376    /// let mut node = FilterNode::default();
377    ///
378    /// // You can match on individual patch variants.
379    /// for patch in event_list.drain_patches::<FilterNode>() {
380    ///     match patch {
381    ///         FilterNodePatch::Frequency(frequency) => {
382    ///             node.frequency = frequency;
383    ///         }
384    ///         FilterNodePatch::Quality(quality) => {
385    ///             node.quality = quality;
386    ///         }
387    ///     }
388    /// }
389    ///
390    /// // Or simply apply all of them.
391    /// for patch in event_list.drain_patches::<FilterNode>() { node.apply(patch); }
392    /// # }
393    /// ```
394    ///
395    /// Errors produced while constructing patches are simply skipped.
396    pub fn drain_patches<'b, T: crate::diff::Patch>(
397        &'b mut self,
398    ) -> impl IntoIterator<Item = <T as crate::diff::Patch>::Patch> + use<'b, T> {
399        // Ideally this would parameterise the `FnMut` over some `impl From<PatchEvent<T>>`
400        // but it would require a marker trait for the `diff::Patch::Patch` assoc type to
401        // prevent overlapping impls.
402        self.drain().into_iter().filter_map(|e| T::patch_event(&e))
403    }
404
405    /// Iterate over patches for `T`, draining the events from the list, while also
406    /// returning the timestamp the event was scheduled for.
407    ///
408    /// The iterator returns `(patch, Option<event_instant>)`
409    /// where `event_instant` is the instant the event was schedueld for. If the event
410    /// was not scheduled, then the latter will be `None`.
411    ///
412    /// ```
413    /// # use firewheel_core::{diff::*, event::ProcEvents};
414    /// # fn for_each_example(mut event_list: ProcEvents) {
415    /// #[derive(Patch, Default)]
416    /// struct FilterNode {
417    ///     frequency: f32,
418    ///     quality: f32,
419    /// }
420    ///
421    /// let mut node = FilterNode::default();
422    ///
423    /// // You can match on individual patch variants.
424    /// for (patch timestamp) in event_list.drain_patches_with_timestamps::<FilterNode>() {
425    ///     match patch {
426    ///         FilterNodePatch::Frequency(frequency) => {
427    ///             node.frequency = frequency;
428    ///         }
429    ///         FilterNodePatch::Quality(quality) => {
430    ///             node.quality = quality;
431    ///         }
432    ///     }
433    /// }
434    ///
435    /// // Or simply apply all of them.
436    /// for (patch, timestamp) in event_list.drain_patches_with_timestamps::<FilterNode>() { node.apply(patch); }
437    /// # }
438    /// ```
439    ///
440    /// Errors produced while constructing patches are simply skipped.
441    #[cfg(feature = "scheduled_events")]
442    pub fn drain_patches_with_timestamps<'b, T: crate::diff::Patch>(
443        &'b mut self,
444    ) -> impl IntoIterator<Item = (<T as crate::diff::Patch>::Patch, Option<EventInstant>)> + use<'b, T>
445    {
446        // Ideally this would parameterise the `FnMut` over some `impl From<PatchEvent<T>>`
447        // but it would require a marker trait for the `diff::Patch::Patch` assoc type to
448        // prevent overlapping impls.
449        self.drain_with_timestamps()
450            .into_iter()
451            .filter_map(|(e, timestamp)| T::patch_event(&e).map(|patch| (patch, timestamp)))
452    }
453}
454
455/// Used internally by the Firewheel processor.
456#[derive(Debug, Clone, Copy, PartialEq, Eq)]
457pub enum ProcEventsIndex {
458    Immediate(u32),
459    #[cfg(feature = "scheduled_events")]
460    Scheduled(u32),
461}