midi_msg/
channel_mode.rs

1use super::parse_error::*;
2use crate::util::*;
3use alloc::vec::Vec;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6/// Channel-level messages that should alter the mode of the receiver. Used in [`MidiMsg`](crate::MidiMsg).
7pub enum ChannelModeMsg {
8    /// Sound playing on the channel should be stopped as soon as possible, per GM2.
9    AllSoundOff,
10    /// Stop sounding all notes on the channel.
11    AllNotesOff,
12    /// All controllers should be reset to their default values. GM specifies some of these defaults.
13    ResetAllControllers,
14    /// An instrument set to `OmniMode(true)` should respond to MIDI messages sent over all channels.
15    OmniMode(bool),
16    /// Request that the receiver set itself to be monophonic/polyphonic.
17    PolyMode(PolyMode),
18    /// Used to turn on or off "local control" of a MIDI synthesizer instrument. When the instrument
19    /// does not have local control, its controller should only send out MIDI signals while the synthesizer should only respond to remote MIDI messages.
20    LocalControl(bool),
21}
22
23impl ChannelModeMsg {
24    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
25        v.push(0xB0);
26        self.extend_midi_running(v);
27    }
28
29    pub(crate) fn extend_midi_running(&self, v: &mut Vec<u8>) {
30        match self {
31            ChannelModeMsg::AllSoundOff => {
32                v.push(120);
33                v.push(0);
34            }
35            ChannelModeMsg::ResetAllControllers => {
36                v.push(121);
37                v.push(0);
38            }
39            ChannelModeMsg::LocalControl(on) => {
40                v.push(122);
41                v.push(if *on { 127 } else { 0 });
42            }
43            ChannelModeMsg::AllNotesOff => {
44                v.push(123);
45                v.push(0);
46            }
47            ChannelModeMsg::OmniMode(on) => {
48                v.push(if *on { 125 } else { 124 });
49                v.push(0);
50            }
51            ChannelModeMsg::PolyMode(m) => {
52                v.push(if *m == PolyMode::Poly { 127 } else { 126 });
53                v.push(match *m {
54                    PolyMode::Poly => 0,
55                    PolyMode::Mono(n) => n.min(16),
56                })
57            }
58        }
59    }
60
61    pub(crate) fn from_midi(m: &[u8]) -> Result<(Self, usize), ParseError> {
62        // Skip the status byte since it's already been parsed
63        let (msg, len) = ChannelModeMsg::from_midi_running(&m[1..])?;
64        Ok((msg, len + 1))
65    }
66
67    pub(crate) fn from_midi_running(m: &[u8]) -> Result<(Self, usize), ParseError> {
68        if let (Some(b1), Some(b2)) = (m.first(), m.get(1)) {
69            if *b2 > 127 {
70                return Err(ParseError::ByteOverflow);
71            }
72            match (b1, b2) {
73                (120, _) => Ok((Self::AllSoundOff, 2)),
74                (121, _) => Ok((Self::ResetAllControllers, 2)),
75                (122, b2) => Ok((Self::LocalControl(bool_from_u7(*b2)?), 2)),
76                (123, _) => Ok((Self::AllNotesOff, 2)),
77                (124, _) => Ok((Self::OmniMode(false), 2)),
78                (125, _) => Ok((Self::OmniMode(true), 2)),
79                (126, b2) => Ok((Self::PolyMode(PolyMode::Mono(u8_from_u7(*b2)?)), 2)),
80                (127, _) => Ok((Self::PolyMode(PolyMode::Poly), 2)),
81                _ => Err(ParseError::Invalid(
82                    "This shouldn't be possible: values below 120 should be control change messages",
83                )),
84            }
85        } else {
86            Err(ParseError::UnexpectedEnd)
87        }
88    }
89}
90
91/// Used by [`ChannelModeMsg::PolyMode`].
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum PolyMode {
94    /// Request that the receiver be monophonic, with the given number M representing the
95    /// number of channels that should be dedicated. Since this is sent with a `ChannelModeMsg`
96    /// there is already a "base" channel associated with it, and the number of requested channels
97    /// should be from this base channel N to N+M. `0` is a special case that directing the receiver
98    /// to assign the voices to as many channels as it can receive.
99    Mono(u8),
100    /// Request the receiver to be polyphonic
101    Poly,
102}
103
104#[cfg(test)]
105mod tests {
106    use crate::*;
107    use alloc::vec;
108
109    #[test]
110    fn serialize_channel_mode_msg() {
111        assert_eq!(
112            MidiMsg::ChannelMode {
113                channel: Channel::Ch3,
114                msg: ChannelModeMsg::AllSoundOff
115            }
116            .to_midi(),
117            vec![0xB2, 120, 0]
118        );
119
120        assert_eq!(
121            MidiMsg::RunningChannelMode {
122                channel: Channel::Ch3,
123                msg: ChannelModeMsg::AllSoundOff
124            }
125            .to_midi(),
126            vec![120, 0]
127        );
128
129        assert_eq!(
130            MidiMsg::ChannelMode {
131                channel: Channel::Ch3,
132                msg: ChannelModeMsg::LocalControl(true)
133            }
134            .to_midi(),
135            vec![0xB2, 122, 127]
136        );
137
138        assert_eq!(
139            MidiMsg::ChannelMode {
140                channel: Channel::Ch3,
141                msg: ChannelModeMsg::OmniMode(true)
142            }
143            .to_midi(),
144            vec![0xB2, 125, 0]
145        );
146
147        assert_eq!(
148            MidiMsg::ChannelMode {
149                channel: Channel::Ch3,
150                msg: ChannelModeMsg::OmniMode(false)
151            }
152            .to_midi(),
153            vec![0xB2, 124, 0]
154        );
155
156        assert_eq!(
157            MidiMsg::ChannelMode {
158                channel: Channel::Ch3,
159                msg: ChannelModeMsg::PolyMode(PolyMode::Poly)
160            }
161            .to_midi(),
162            vec![0xB2, 127, 0]
163        );
164
165        assert_eq!(
166            MidiMsg::ChannelMode {
167                channel: Channel::Ch3,
168                msg: ChannelModeMsg::PolyMode(PolyMode::Mono(4))
169            }
170            .to_midi(),
171            vec![0xB2, 126, 4]
172        );
173    }
174
175    #[test]
176    fn deserialize_channel_mode_msg() {
177        let mut ctx = ReceiverContext::new();
178
179        test_serialization(
180            MidiMsg::ChannelMode {
181                channel: Channel::Ch3,
182                msg: ChannelModeMsg::AllSoundOff,
183            },
184            &mut ctx,
185        );
186
187        test_serialization(
188            MidiMsg::ChannelMode {
189                channel: Channel::Ch3,
190                msg: ChannelModeMsg::LocalControl(true),
191            },
192            &mut ctx,
193        );
194
195        test_serialization(
196            MidiMsg::ChannelMode {
197                channel: Channel::Ch3,
198                msg: ChannelModeMsg::OmniMode(true),
199            },
200            &mut ctx,
201        );
202
203        test_serialization(
204            MidiMsg::ChannelMode {
205                channel: Channel::Ch3,
206                msg: ChannelModeMsg::OmniMode(false),
207            },
208            &mut ctx,
209        );
210
211        test_serialization(
212            MidiMsg::ChannelMode {
213                channel: Channel::Ch3,
214                msg: ChannelModeMsg::PolyMode(PolyMode::Poly),
215            },
216            &mut ctx,
217        );
218
219        test_serialization(
220            MidiMsg::ChannelMode {
221                channel: Channel::Ch3,
222                msg: ChannelModeMsg::PolyMode(PolyMode::Mono(4)),
223            },
224            &mut ctx,
225        );
226    }
227}