#[doc(hidden)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NoteIDInternals {
NoteIDWithID(i32),
NoteIDFromPitch(u8),
NoteIDFromChannelID(i16),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NoteID {
#[doc(hidden)]
pub internals: NoteIDInternals,
}
impl NoteID {
#[must_use]
pub fn from_pitch(pitch: u8) -> Self {
Self {
internals: NoteIDInternals::NoteIDFromPitch(pitch),
}
}
#[must_use]
pub fn from_channel_id(channel_id: i16) -> Self {
Self {
internals: NoteIDInternals::NoteIDFromChannelID(channel_id),
}
}
#[must_use]
pub fn from_id(id: i32) -> Self {
Self {
internals: NoteIDInternals::NoteIDWithID(id),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct NoteData {
pub id: NoteID,
pub pitch: u8,
pub velocity: f32,
pub tuning: f32,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Data {
NoteOn {
data: NoteData,
},
NoteOff {
data: NoteData,
},
}
#[derive(Clone, Debug, PartialEq)]
pub struct Event {
pub sample_offset: usize,
pub data: Data,
}
#[derive(Clone, Debug)]
pub struct Events<I> {
events: I,
}
fn check_events_invariants<I: Iterator<Item = Event>>(iter: I, buffer_size: usize) -> bool {
let mut last = None;
for event in iter {
if event.sample_offset >= buffer_size {
return false;
}
if let Some(last) = last
&& event.sample_offset < last
{
return false;
}
last = Some(event.sample_offset);
}
true
}
impl<I: Iterator<Item = Event> + Clone> Events<I> {
pub fn new(events: I, buffer_size: usize) -> Option<Self> {
if check_events_invariants(events.clone(), buffer_size) {
Some(Self { events })
} else {
None
}
}
}
impl<I: Iterator<Item = Event>> IntoIterator for Events<I> {
type Item = Event;
type IntoIter = I;
fn into_iter(self) -> Self::IntoIter {
self.events
}
}
#[cfg(test)]
mod tests {
use super::{Data, Event, Events, NoteData, NoteID};
static EXAMPLE_NOTE: NoteData = NoteData {
id: NoteID {
internals: super::NoteIDInternals::NoteIDFromPitch(60),
},
pitch: 60,
velocity: 1.0,
tuning: 0.0,
};
#[test]
fn out_of_order_events_rejected() {
assert!(
Events::new(
(&[
Event {
sample_offset: 5,
data: Data::NoteOn {
data: EXAMPLE_NOTE.clone()
}
},
Event {
sample_offset: 4,
data: Data::NoteOff {
data: EXAMPLE_NOTE.clone()
}
}
])
.iter()
.cloned(),
10
)
.is_none()
)
}
#[test]
fn out_of_bounds_events_rejected() {
assert!(
Events::new(
(&[Event {
sample_offset: 50,
data: Data::NoteOn {
data: EXAMPLE_NOTE.clone()
}
},])
.iter()
.cloned(),
10
)
.is_none()
)
}
#[test]
fn empty_events_accepted() {
assert!(Events::new((&[]).iter().cloned(), 10).is_some())
}
}