Skip to main content

midi_io/midi/
channel.rs

1use crate::ValueError;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
4#[repr(u8)]
5pub enum Channel {
6    Ch1 = 0,
7    Ch2,
8    Ch3,
9    Ch4,
10    Ch5,
11    Ch6,
12    Ch7,
13    Ch8,
14    Ch9,
15    Ch10,
16    Ch11,
17    Ch12,
18    Ch13,
19    Ch14,
20    Ch15,
21    Ch16,
22}
23
24impl Channel {
25    pub const ALL: [Channel; 16] = [
26        Channel::Ch1,
27        Channel::Ch2,
28        Channel::Ch3,
29        Channel::Ch4,
30        Channel::Ch5,
31        Channel::Ch6,
32        Channel::Ch7,
33        Channel::Ch8,
34        Channel::Ch9,
35        Channel::Ch10,
36        Channel::Ch11,
37        Channel::Ch12,
38        Channel::Ch13,
39        Channel::Ch14,
40        Channel::Ch15,
41        Channel::Ch16,
42    ];
43
44    pub const fn index(self) -> u8 {
45        self as u8
46    }
47
48    pub const fn number(self) -> u8 {
49        self as u8 + 1
50    }
51
52    pub fn from_index(index: u8) -> Result<Self, ValueError> {
53        Self::ALL
54            .get(index as usize)
55            .copied()
56            .ok_or(ValueError::ChannelIndex(index))
57    }
58
59    pub fn from_number(number: u8) -> Result<Self, ValueError> {
60        number
61            .checked_sub(1)
62            .and_then(|index| Self::from_index(index).ok())
63            .ok_or(ValueError::ChannelNumber(number))
64    }
65}
66
67pub(crate) fn channel_from_nibble(status: u8) -> Channel {
68    Channel::from_index(status & 0x0F).expect("status nibble is always 0..=15")
69}
70
71impl std::fmt::Display for Channel {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        write!(f, "{}", self.number())
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn channel_from_number_roundtrips() {
83        for ch in Channel::ALL {
84            assert_eq!(Channel::from_number(ch.number()), Ok(ch));
85        }
86        assert_eq!(Channel::from_number(1), Ok(Channel::Ch1));
87        assert_eq!(Channel::from_number(16), Ok(Channel::Ch16));
88    }
89
90    #[test]
91    fn channel_from_number_rejects_out_of_range() {
92        assert_eq!(Channel::from_number(0), Err(ValueError::ChannelNumber(0)));
93        assert_eq!(Channel::from_number(17), Err(ValueError::ChannelNumber(17)));
94        assert_eq!(Channel::from_index(16), Err(ValueError::ChannelIndex(16)));
95    }
96}