use crate::pitch::Pitch;
use serde::{Deserialize, Serialize};
#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CellNote {
#[default]
Empty,
Play(Pitch),
KeyOff,
NoteCut,
NoteFade,
}
impl CellNote {
#[inline]
pub fn from_byte(b: u8) -> Self {
match b {
0..=119 => match Pitch::try_from(b) {
Ok(p) => Self::Play(p),
Err(_) => Self::Empty,
},
120..=252 => Self::NoteFade,
253 => Self::Empty,
254 => Self::NoteCut,
255 => Self::KeyOff,
}
}
#[inline]
pub fn pitch(&self) -> Option<Pitch> {
match self {
Self::Play(p) => Some(*p),
_ => None,
}
}
}
impl core::fmt::Debug for CellNote {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Empty => write!(f, "---"),
Self::Play(p) => write!(f, "{:?}", p),
Self::KeyOff => write!(f, "==="),
Self::NoteCut => write!(f, "^^^"),
Self::NoteFade => write!(f, "~~~"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_byte_round_trip_on_pitch_values() {
for b in 0u8..=119 {
match CellNote::from_byte(b) {
CellNote::Play(p) => assert_eq!(p.value(), b),
_ => panic!("byte {} should decode as Play", b),
}
}
}
#[test]
fn from_byte_handles_event_bytes() {
assert_eq!(CellNote::from_byte(120), CellNote::NoteFade);
assert_eq!(CellNote::from_byte(200), CellNote::NoteFade);
assert_eq!(CellNote::from_byte(252), CellNote::NoteFade);
assert_eq!(CellNote::from_byte(253), CellNote::Empty);
assert_eq!(CellNote::from_byte(254), CellNote::NoteCut);
assert_eq!(CellNote::from_byte(255), CellNote::KeyOff);
}
#[test]
fn pitch_extraction() {
assert_eq!(CellNote::Empty.pitch(), None);
assert_eq!(CellNote::Play(Pitch::C5).pitch(), Some(Pitch::C5));
assert_eq!(CellNote::KeyOff.pitch(), None);
assert_eq!(CellNote::NoteCut.pitch(), None);
assert_eq!(CellNote::NoteFade.pitch(), None);
}
#[test]
fn default_is_empty() {
let n: CellNote = Default::default();
assert_eq!(n, CellNote::Empty);
}
}