conformal_component/
events.rs1#[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#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub struct NoteID {
17 #[doc(hidden)]
20 pub internals: NoteIDInternals,
21}
22
23impl NoteID {
24 #[must_use]
26 pub fn from_pitch(pitch: u8) -> Self {
27 Self {
28 internals: NoteIDInternals::NoteIDFromPitch(pitch),
29 }
30 }
31
32 #[must_use]
34 pub fn from_channel_id(channel_id: i16) -> Self {
35 Self {
36 internals: NoteIDInternals::NoteIDFromChannelID(channel_id),
37 }
38 }
39
40 #[must_use]
42 pub fn from_id(id: i32) -> Self {
43 Self {
44 internals: NoteIDInternals::NoteIDWithID(id),
45 }
46 }
47}
48
49#[derive(Copy, Clone, Debug, PartialEq)]
51pub struct NoteData {
52 pub id: NoteID,
54
55 pub pitch: u8,
57
58 pub velocity: f32,
60
61 pub tuning: f32,
63}
64
65#[derive(Clone, Debug, PartialEq)]
67pub enum Data {
68 NoteOn {
72 data: NoteData,
74 },
75
76 NoteOff {
80 data: NoteData,
82 },
83}
84
85#[derive(Clone, Debug, PartialEq)]
87pub struct Event {
88 pub sample_offset: usize,
90
91 pub data: Data,
93}
94
95#[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 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}