midi_msg/
system_common.rs

1use super::parse_error::*;
2use super::time_code::*;
3use super::util::*;
4use super::ReceiverContext;
5use alloc::vec::Vec;
6
7/// A fairly limited set of messages, generally for device synchronization.
8/// Used in [`MidiMsg`](crate::MidiMsg).
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SystemCommonMsg {
11    /// The first of 8 "quarter frame" messages, which are meant to be sent 4 per "frame".
12    /// These messages function similarly to [`SystemRealTimeMsg::TimingClock`](crate::SystemRealTimeMsg::TimingClock)
13    /// but additionally indicate the specific point in the playback that they refer to, as well
14    /// as the frame rate. This means that a full `TimeCode` is send over the course of two frames.
15    ///
16    /// They are sent in reverse order if time is playing in reverse.
17    TimeCodeQuarterFrame1(TimeCode),
18    TimeCodeQuarterFrame2(TimeCode),
19    TimeCodeQuarterFrame3(TimeCode),
20    TimeCodeQuarterFrame4(TimeCode),
21    TimeCodeQuarterFrame5(TimeCode),
22    TimeCodeQuarterFrame6(TimeCode),
23    TimeCodeQuarterFrame7(TimeCode),
24    TimeCodeQuarterFrame8(TimeCode),
25    /// Indicate the song position, in MIDI beats, where 1 MIDI beat = 6 MIDI clocks. 0-16383
26    SongPosition(u16),
27    /// Select a song numbered 0-127.
28    SongSelect(u8),
29    /// Request that the oscillators of an analog synth be tuned.
30    TuneRequest,
31}
32
33impl SystemCommonMsg {
34    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
35        match self {
36            SystemCommonMsg::TimeCodeQuarterFrame1(qf) => {
37                v.push(0xF1);
38                v.push(qf.to_nibbles()[0]);
39            }
40            SystemCommonMsg::TimeCodeQuarterFrame2(qf) => {
41                v.push(0xF1);
42                v.push(qf.to_nibbles()[1]);
43            }
44            SystemCommonMsg::TimeCodeQuarterFrame3(qf) => {
45                v.push(0xF1);
46                v.push(qf.to_nibbles()[2]);
47            }
48            SystemCommonMsg::TimeCodeQuarterFrame4(qf) => {
49                v.push(0xF1);
50                v.push(qf.to_nibbles()[3]);
51            }
52            SystemCommonMsg::TimeCodeQuarterFrame5(qf) => {
53                v.push(0xF1);
54                v.push(qf.to_nibbles()[4]);
55            }
56            SystemCommonMsg::TimeCodeQuarterFrame6(qf) => {
57                v.push(0xF1);
58                v.push(qf.to_nibbles()[5]);
59            }
60            SystemCommonMsg::TimeCodeQuarterFrame7(qf) => {
61                v.push(0xF1);
62                v.push(qf.to_nibbles()[6]);
63            }
64            SystemCommonMsg::TimeCodeQuarterFrame8(qf) => {
65                v.push(0xF1);
66                v.push(qf.to_nibbles()[7]);
67            }
68            SystemCommonMsg::SongPosition(pos) => {
69                v.push(0xF2);
70                push_u14(*pos, v);
71            }
72            SystemCommonMsg::SongSelect(song) => {
73                v.push(0xF3);
74                v.push(to_u7(*song));
75            }
76            SystemCommonMsg::TuneRequest => v.push(0xF6),
77        }
78    }
79
80    pub(crate) fn from_midi(
81        m: &[u8],
82        ctx: &mut ReceiverContext,
83    ) -> Result<(Self, usize), ParseError> {
84        match m.first() {
85            Some(0xF1) => {
86                if let Some(b2) = m.get(1) {
87                    if b2 > &127 {
88                        Err(ParseError::ByteOverflow)
89                    } else {
90                        Ok((
91                            match ctx.time_code.extend(*b2) {
92                                0 => Self::TimeCodeQuarterFrame1(ctx.time_code),
93                                1 => Self::TimeCodeQuarterFrame2(ctx.time_code),
94                                2 => Self::TimeCodeQuarterFrame3(ctx.time_code),
95                                3 => Self::TimeCodeQuarterFrame4(ctx.time_code),
96                                4 => Self::TimeCodeQuarterFrame5(ctx.time_code),
97                                5 => Self::TimeCodeQuarterFrame6(ctx.time_code),
98                                6 => Self::TimeCodeQuarterFrame7(ctx.time_code),
99                                7 => Self::TimeCodeQuarterFrame8(ctx.time_code),
100                                _ => panic!("Should not be reachable"),
101                            },
102                            2,
103                        ))
104                    }
105                } else {
106                    Err(ParseError::UnexpectedEnd)
107                }
108            }
109            Some(0xF2) => Ok((Self::SongPosition(u14_from_midi(&m[1..])?), 3)),
110            Some(0xF3) => Ok((Self::SongSelect(u7_from_midi(&m[1..])?), 2)),
111            Some(0xF6) => Ok((Self::TuneRequest, 1)),
112            Some(0xF7) => Err(ParseError::UnexpectedEndOfSystemExclusiveFlag),
113            Some(x) => Err(ParseError::UndefinedSystemCommonMessage(*x)),
114            _ => panic!("Should not be reachable"),
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::super::*;
122    extern crate std;
123    use std::vec;
124
125    #[test]
126    fn serialize_system_common_msg() {
127        assert_eq!(
128            MidiMsg::SystemCommon {
129                msg: SystemCommonMsg::TuneRequest
130            }
131            .to_midi(),
132            vec![0xF6]
133        );
134
135        assert_eq!(
136            MidiMsg::SystemCommon {
137                msg: SystemCommonMsg::SongSelect(69)
138            }
139            .to_midi(),
140            vec![0xF3, 69]
141        );
142
143        assert_eq!(
144            MidiMsg::SystemCommon {
145                msg: SystemCommonMsg::SongPosition(1000)
146            }
147            .to_midi(),
148            vec![0xF2, 0x68, 0x07]
149        );
150
151        let frame = TimeCode {
152            frames: 40,                    // Should be limited to 29: 0b00011101
153            seconds: 58,                   // 0b00111010
154            minutes: 20,                   // 0b00010100
155            hours: 25,                     // Should be limited to 23: 0b00010111
156            code_type: TimeCodeType::DF30, //      0b01000000
157        };
158
159        assert_eq!(
160            MidiMsg::SystemCommon {
161                msg: SystemCommonMsg::TimeCodeQuarterFrame1(frame)
162            }
163            .to_midi(),
164            vec![0xF1, 0b1101]
165        );
166        assert_eq!(
167            MidiMsg::SystemCommon {
168                msg: SystemCommonMsg::TimeCodeQuarterFrame2(frame)
169            }
170            .to_midi(),
171            vec![0xF1, 0b00010000 + 0b0001]
172        );
173        assert_eq!(
174            MidiMsg::SystemCommon {
175                msg: SystemCommonMsg::TimeCodeQuarterFrame3(frame)
176            }
177            .to_midi(),
178            vec![0xF1, 0b00100000 + 0b1010]
179        );
180        assert_eq!(
181            MidiMsg::SystemCommon {
182                msg: SystemCommonMsg::TimeCodeQuarterFrame4(frame)
183            }
184            .to_midi(),
185            vec![0xF1, 0b00110000 + 0b0011]
186        );
187        assert_eq!(
188            MidiMsg::SystemCommon {
189                msg: SystemCommonMsg::TimeCodeQuarterFrame5(frame)
190            }
191            .to_midi(),
192            vec![0xF1, 0b01000000 + 0b0100]
193        );
194        assert_eq!(
195            MidiMsg::SystemCommon {
196                msg: SystemCommonMsg::TimeCodeQuarterFrame6(frame)
197            }
198            .to_midi(),
199            vec![0xF1, 0b01010000 + 0b0001]
200        );
201        assert_eq!(
202            MidiMsg::SystemCommon {
203                msg: SystemCommonMsg::TimeCodeQuarterFrame7(frame)
204            }
205            .to_midi(),
206            vec![0xF1, 0b01100000 + 0b0111]
207        );
208        assert_eq!(
209            MidiMsg::SystemCommon {
210                msg: SystemCommonMsg::TimeCodeQuarterFrame8(frame)
211            }
212            .to_midi(),
213            vec![0xF1, 0b01110000 + 0b0101]
214        );
215    }
216
217    #[test]
218    fn deserialize_system_common_msg() {
219        let mut ctx = ReceiverContext::new();
220
221        test_serialization(
222            MidiMsg::SystemCommon {
223                msg: SystemCommonMsg::TuneRequest,
224            },
225            &mut ctx,
226        );
227
228        test_serialization(
229            MidiMsg::SystemCommon {
230                msg: SystemCommonMsg::SongPosition(1000),
231            },
232            &mut ctx,
233        );
234
235        MidiMsg::from_midi_with_context(&[0xF1, 0b1101], &mut ctx)
236            .expect("Expected a timecode, got an error");
237        MidiMsg::from_midi_with_context(&[0xF1, 0b00010000 + 0b0001], &mut ctx)
238            .expect("Expected a timecode, got an error");
239        MidiMsg::from_midi_with_context(&[0xF1, 0b00100000 + 0b1010], &mut ctx)
240            .expect("Expected a timecode, got an error");
241        MidiMsg::from_midi_with_context(&[0xF1, 0b00110000 + 0b0011], &mut ctx)
242            .expect("Expected a timecode, got an error");
243        MidiMsg::from_midi_with_context(&[0xF1, 0b01000000 + 0b0100], &mut ctx)
244            .expect("Expected a timecode, got an error");
245        MidiMsg::from_midi_with_context(&[0xF1, 0b01010000 + 0b0001], &mut ctx)
246            .expect("Expected a timecode, got an error");
247        MidiMsg::from_midi_with_context(&[0xF1, 0b01100000 + 0b0111], &mut ctx)
248            .expect("Expected a timecode, got an error");
249        MidiMsg::from_midi_with_context(&[0xF1, 0b01110000 + 0b0101], &mut ctx)
250            .expect("Expected a timecode, got an error");
251
252        assert_eq!(
253            ctx.time_code,
254            TimeCode {
255                frames: 29,                    // 0b00011101
256                seconds: 58,                   // 0b00111010
257                minutes: 20,                   // 0b00010100
258                hours: 23,                     // 0b00010111
259                code_type: TimeCodeType::DF30, // 0b01000000
260            }
261        );
262    }
263}