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