use crate::Note;
#[derive(Debug, PartialEq, Clone, Copy, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum MidiMessage {
NoteOff(Channel, Note, Value7),
NoteOn(Channel, Note, Value7),
KeyPressure(Channel, Note, Value7),
ControlChange(Channel, Control, Value7),
ProgramChange(Channel, Program),
ChannelPressure(Channel, Value7),
PitchBendChange(Channel, Value14),
QuarterFrame(QuarterFrame),
SongPositionPointer(Value14),
SongSelect(Value7),
TuneRequest,
TimingClock,
Start,
Continue,
Stop,
ActiveSensing,
Reset,
}
impl MidiMessage {
#[allow(clippy::len_without_is_empty)]
#[must_use]
pub const fn len(&self) -> usize {
match self {
Self::NoteOff(..)
| Self::NoteOn(..)
| Self::KeyPressure(..)
| Self::ControlChange(..)
| Self::PitchBendChange(..)
| Self::SongPositionPointer(..) => 3,
Self::ProgramChange(..)
| Self::ChannelPressure(..)
| Self::QuarterFrame(..)
| Self::SongSelect(..) => 2,
Self::TuneRequest
| Self::TimingClock
| Self::Start
| Self::Continue
| Self::Stop
| Self::ActiveSensing
| Self::Reset => 1,
}
}
}
#[allow(missing_docs)]
pub mod status {
pub const NOTE_OFF: u8 = 0x80;
pub const NOTE_ON: u8 = 0x90;
pub const KEY_PRESSURE: u8 = 0xA0;
pub const CONTROL_CHANGE: u8 = 0xB0;
pub const PITCH_BEND_CHANGE: u8 = 0xE0;
pub const SONG_POSITION_POINTER: u8 = 0xF2;
pub const PROGRAM_CHANGE: u8 = 0xC0;
pub const CHANNEL_PRESSURE: u8 = 0xD0;
pub const QUARTER_FRAME: u8 = 0xF1;
pub const SONG_SELECT: u8 = 0xF3;
pub const TUNE_REQUEST: u8 = 0xF6;
pub const TIMING_CLOCK: u8 = 0xF8;
pub const START: u8 = 0xFA;
pub const CONTINUE: u8 = 0xFB;
pub const STOP: u8 = 0xFC;
pub const ACTIVE_SENSING: u8 = 0xFE;
pub const RESET: u8 = 0xFF;
pub const SYSEX_START: u8 = 0xF0;
pub const SYSEX_END: u8 = 0xF7;
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Channel(u8);
impl Channel {
#[must_use]
pub const fn new(channel: u8) -> Self {
debug_assert!(channel <= 15, "Channel exceeds valid range");
Self(if channel > 15 { 15 } else { channel })
}
pub const C1: Self = Self::new(0);
pub const C2: Self = Self::new(1);
pub const C3: Self = Self::new(2);
pub const C4: Self = Self::new(3);
pub const C5: Self = Self::new(4);
pub const C6: Self = Self::new(5);
pub const C7: Self = Self::new(6);
pub const C8: Self = Self::new(7);
pub const C9: Self = Self::new(8);
pub const C10: Self = Self::new(9);
pub const C11: Self = Self::new(10);
pub const C12: Self = Self::new(11);
pub const C13: Self = Self::new(12);
pub const C14: Self = Self::new(13);
pub const C15: Self = Self::new(14);
pub const C16: Self = Self::new(15);
pub const MIN: Self = Self::C1;
pub const MAX: Self = Self::C16;
}
impl From<u8> for Channel {
fn from(channel: u8) -> Self {
Self::new(channel)
}
}
impl From<Channel> for u8 {
fn from(channel: Channel) -> Self {
channel.0
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Control(u8);
impl Control {
#[must_use]
pub const fn new(control: u8) -> Self {
debug_assert!(control < 127, "Control exceeds valid range");
Self(if control > 127 { 127 } else { control })
}
}
impl From<u8> for Control {
fn from(control: u8) -> Self {
Self::new(control)
}
}
impl From<Control> for u8 {
fn from(control: Control) -> Self {
control.0
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Program(u8);
impl Program {
#[must_use]
pub const fn new(program: u8) -> Self {
debug_assert!(program < 127, "Program exceeds valid range");
Self(if program > 127 { 127 } else { program })
}
}
impl From<u8> for Program {
fn from(program: u8) -> Self {
Self::new(program)
}
}
impl From<Program> for u8 {
fn from(program: Program) -> Self {
program.0
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Value7(u8);
impl Value7 {
#[must_use]
pub const fn new(value: u8) -> Self {
debug_assert!(value <= 127, "Value7 exceeds valid range");
Self(if value > 127 { 127 } else { value })
}
}
impl From<u8> for Value7 {
fn from(value: u8) -> Self {
Self::new(value)
}
}
impl From<Value7> for u8 {
fn from(value: Value7) -> Self {
value.0
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Value14(u8, u8);
impl Value14 {
#[must_use]
pub const fn new(msb: u8, lsb: u8) -> Self {
debug_assert!(msb <= 127, "Value14 msb exceeds valid range");
debug_assert!(lsb <= 127, "Value14 lsb exceeds valid range");
Self(
if msb >= 127 { 127 } else { msb },
if lsb >= 127 { 127 } else { lsb },
)
}
}
impl From<(u8, u8)> for Value14 {
fn from(value: (u8, u8)) -> Self {
Self::new(value.0, value.1)
}
}
impl From<Value14> for (u8, u8) {
fn from(value: Value14) -> (u8, u8) {
(value.0, value.1)
}
}
impl From<u16> for Value14 {
fn from(value: u16) -> Self {
debug_assert!(value <= 16383, "Value14 exceeds valid range");
let value = if value > 16383 { 16383 } else { value };
Self(((value & 0x3f80) >> 7) as u8, (value & 0x007f) as u8)
}
}
impl From<Value14> for u16 {
fn from(value: Value14) -> Self {
(Self::from(value.0) << 7) + Self::from(value.1)
}
}
impl From<i16> for Value14 {
#[allow(clippy::cast_sign_loss)]
fn from(value: i16) -> Self {
debug_assert!(value >= -8192, "Value14 exceeds valid range");
debug_assert!(value <= 8191, "Value14 exceeds valid range");
let value = value.clamp(-8192, 8191) + 8192;
Self::new(((value & 0x3f80) >> 7) as u8, (value & 0x007f) as u8)
}
}
impl From<Value14> for i16 {
#[allow(clippy::cast_possible_wrap)]
fn from(value: Value14) -> Self {
let v: u16 = value.into();
(v as Self) - 8192
}
}
impl From<f32> for Value14 {
#[allow(clippy::cast_possible_truncation)]
fn from(value: f32) -> Self {
Self::from((value * if value > 0.0 { 8191.0 } else { 8192.0 }) as i16)
}
}
impl From<Value14> for f32 {
fn from(value: Value14) -> Self {
let v: i16 = value.into();
let v = Self::from(v) / if v > 0 { 8191.0 } else { 8192.0 };
v.clamp(-1.0, 1.0)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct QuarterFrame(u8);
impl QuarterFrame {
#[must_use]
pub const fn new(frame: u8) -> Self {
debug_assert!(frame <= 127, "QuarterFrame exceeds valid range");
Self(if frame > 127 { 127 } else { frame })
}
}
impl From<u8> for QuarterFrame {
fn from(frame: u8) -> Self {
Self::new(frame)
}
}
impl From<QuarterFrame> for u8 {
fn from(value: QuarterFrame) -> Self {
value.0
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn should_combine_7_bit_vals_into_14() {
let val = Value14::new(0b01010101u8, 0b01010111u8);
assert_eq!(0b0010101011010111u16, val.into());
assert_eq!((0b01010101u8, 0b01010111u8), val.into())
}
#[test]
fn conversion_u16_14() {
let val: Value14 = Value14::from(16383u16);
assert_eq!((127, 127), val.into());
assert_eq!(16383u16, val.into());
let val: Value14 = Value14::from(16256u16);
assert_eq!((127, 0), val.into());
assert_eq!(16256u16, val.into());
let val: Value14 = Value14::from(127u16);
assert_eq!((0, 127), val.into());
assert_eq!(127u16, val.into());
let val: Value14 = Value14::from(0u16);
assert_eq!((0, 0), val.into());
assert_eq!(0u16, val.into());
}
#[test]
fn conversion_i16_14() {
let val: Value14 = Value14::from(8191i16);
assert_eq!((127, 127), val.into());
assert_eq!(8191i16, val.into());
assert_eq!(val, Value14::new(127, 127));
let val: Value14 = Value14::from(8190i16);
assert_eq!((127, 126), val.into());
assert_eq!(8190i16, val.into());
let val: Value14 = Value14::from(-8192i16);
assert_eq!((0, 0), val.into());
assert_eq!(-8192i16, val.into());
let val: Value14 = Value14::from(0i16);
assert_eq!((64, 0), val.into());
assert_eq!(0i16, val.into());
let val: Value14 = Value14::from(1i16);
assert_eq!((64, 1), val.into());
assert_eq!(1i16, val.into());
}
#[test]
fn conversion_f32_14() {
let val: Value14 = Value14::from(0.0f32);
assert_eq!((64, 0), val.into());
assert_eq!(0.0f32, val.into());
let val: Value14 = Value14::from(1.0f32);
assert_eq!((127, 127), val.into());
assert_eq!(1.0f32, val.into());
let val: Value14 = Value14::from(-1.0f32);
assert_eq!((0, 0), val.into());
assert_eq!(-1.0f32, val.into());
}
}