Skip to main content

midi_msg/
system_common.rs

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