1use super::ReceiverContext;
2use super::parse_error::*;
3use super::time_code::*;
4use super::util::*;
5use alloc::vec::Vec;
6
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum SystemCommonMsg {
12 TimeCodeQuarterFrame1(TimeCode),
19 TimeCodeQuarterFrame2(TimeCode),
20 TimeCodeQuarterFrame3(TimeCode),
21 TimeCodeQuarterFrame4(TimeCode),
22 TimeCodeQuarterFrame5(TimeCode),
23 TimeCodeQuarterFrame6(TimeCode),
24 TimeCodeQuarterFrame7(TimeCode),
25 TimeCodeQuarterFrame8(TimeCode),
26 SongPosition(u16),
28 SongSelect(u8),
30 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, seconds: 58, minutes: 20, hours: 25, code_type: TimeCodeType::DF30, };
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, seconds: 58, minutes: 20, hours: 23, code_type: TimeCodeType::DF30, }
262 );
263 }
264}