conformal_component/
events.rs

1//! Contains data structures representing _events_ sent to [`crate::synth::Synth`]s
2
3#[doc(hidden)]
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub enum NoteIDInternals {
6    NoteIDWithID(i32),
7    NoteIDFromPitch(u8),
8    NoteIDFromChannelID(i16),
9}
10
11/// Represents an identifier for a note
12///
13/// This is an opaque identifier that can be used to refer to a specific note
14/// that is playing.
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub struct NoteID {
17    /// This is only for use by implementors of format adaptors, do not look into this
18    /// as a plug-in writer.
19    #[doc(hidden)]
20    pub internals: NoteIDInternals,
21}
22
23impl NoteID {
24    /// Create a note ID from a pitch.
25    #[must_use]
26    pub fn from_pitch(pitch: u8) -> Self {
27        Self {
28            internals: NoteIDInternals::NoteIDFromPitch(pitch),
29        }
30    }
31
32    /// Create a note ID from a channel ID.
33    #[must_use]
34    pub fn from_channel_id(channel_id: i16) -> Self {
35        Self {
36            internals: NoteIDInternals::NoteIDFromChannelID(channel_id),
37        }
38    }
39
40    /// Create a note ID from a VST Node ID.
41    #[must_use]
42    pub fn from_id(id: i32) -> Self {
43        Self {
44            internals: NoteIDInternals::NoteIDWithID(id),
45        }
46    }
47}
48
49/// Contains data common to both `NoteOn` and `NoteOff` events.
50#[derive(Copy, Clone, Debug, PartialEq)]
51pub struct NoteData {
52    /// Opaque ID of the note.
53    pub id: NoteID,
54
55    /// Pitch of the note in terms of semitones higher than C-2
56    pub pitch: u8,
57
58    /// 0->1 velocity of the note on or off
59    pub velocity: f32,
60
61    /// Microtuning of the note in cents.
62    pub tuning: f32,
63}
64
65/// The data associated with an event, independent of the time it occurred.
66#[derive(Clone, Debug, PartialEq)]
67pub enum Data {
68    /// A note began.
69    ///
70    /// This will never be sent while a note with the same ID is still playing.
71    NoteOn {
72        /// Data associated with the note.
73        data: NoteData,
74    },
75
76    /// A note ended.
77    ///
78    /// This will never be sent while a note with the same ID is not playing.
79    NoteOff {
80        /// Data associated with the note.
81        data: NoteData,
82    },
83}
84
85/// An event that occurred at a specific time within a buffer.
86#[derive(Clone, Debug, PartialEq)]
87pub struct Event {
88    /// Number of sample frames after the beginning of the buffer that this event occurred
89    pub sample_offset: usize,
90
91    /// Data about the event!
92    pub data: Data,
93}
94
95/// Contains an iterator that yields events in order of increasing sample offset.
96///
97/// Invariants:
98///  - All events will have a sample offset in the range of the buffer
99///  - Events are sorted by `sample_offset`
100#[derive(Clone, Debug)]
101pub struct Events<I> {
102    events: I,
103}
104
105fn check_events_invariants<I: Iterator<Item = Event>>(iter: I, buffer_size: usize) -> bool {
106    let mut last = None;
107    for event in iter {
108        if event.sample_offset >= buffer_size {
109            return false;
110        }
111        if let Some(last) = last
112            && event.sample_offset < last
113        {
114            return false;
115        }
116        last = Some(event.sample_offset);
117    }
118    true
119}
120
121impl<I: Iterator<Item = Event> + Clone> Events<I> {
122    /// Create an `Events` object from the given iterator of events.
123    ///
124    /// Note that if any of the invariants are missed, this will return `None`.
125    pub fn new(events: I, buffer_size: usize) -> Option<Self> {
126        if check_events_invariants(events.clone(), buffer_size) {
127            Some(Self { events })
128        } else {
129            None
130        }
131    }
132}
133
134impl<I: Iterator<Item = Event>> IntoIterator for Events<I> {
135    type Item = Event;
136    type IntoIter = I;
137
138    fn into_iter(self) -> Self::IntoIter {
139        self.events
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::{Data, Event, Events, NoteData, NoteID};
146
147    static EXAMPLE_NOTE: NoteData = NoteData {
148        id: NoteID {
149            internals: super::NoteIDInternals::NoteIDFromPitch(60),
150        },
151        pitch: 60,
152        velocity: 1.0,
153        tuning: 0.0,
154    };
155
156    #[test]
157    fn out_of_order_events_rejected() {
158        assert!(
159            Events::new(
160                (&[
161                    Event {
162                        sample_offset: 5,
163                        data: Data::NoteOn {
164                            data: EXAMPLE_NOTE.clone()
165                        }
166                    },
167                    Event {
168                        sample_offset: 4,
169                        data: Data::NoteOff {
170                            data: EXAMPLE_NOTE.clone()
171                        }
172                    }
173                ])
174                    .iter()
175                    .cloned(),
176                10
177            )
178            .is_none()
179        )
180    }
181
182    #[test]
183    fn out_of_bounds_events_rejected() {
184        assert!(
185            Events::new(
186                (&[Event {
187                    sample_offset: 50,
188                    data: Data::NoteOn {
189                        data: EXAMPLE_NOTE.clone()
190                    }
191                },])
192                    .iter()
193                    .cloned(),
194                10
195            )
196            .is_none()
197        )
198    }
199
200    #[test]
201    fn empty_events_accepted() {
202        assert!(Events::new((&[]).iter().cloned(), 10).is_some())
203    }
204}