midi_msg/system_exclusive/
file_dump.rs1use super::DeviceID;
2use crate::parse_error::*;
3use crate::util::*;
4use alloc::vec::Vec;
5use bstr::BString;
6
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum FileDumpMsg {
12 Request {
14 requester_device: DeviceID,
15 file_type: FileType,
16 name: BString,
17 },
18 Header {
20 sender_device: DeviceID,
21 file_type: FileType,
22 length: u32,
24 name: BString,
25 },
26 Packet {
30 running_count: u8,
32 data: Vec<u8>,
34 },
35}
36
37impl FileDumpMsg {
38 pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
39 match self {
40 Self::Header {
41 sender_device,
42 file_type,
43 length,
44 name,
45 } => {
46 v.push(0x1);
47 v.push(sender_device.to_u8());
48 file_type.extend_midi(v);
49 push_u28(*length, v);
50 v.extend_from_slice(name);
51 }
52 Self::Packet {
53 running_count,
54 data,
55 ..
56 } => {
57 v.push(0x2);
58 v.push(to_u7(*running_count));
59 let mut len = data.len().min(112);
60 len += len / 7;
63 assert!(len < 128);
64 v.push(len as u8);
65 v.extend(Self::encode_data(data));
66 v.push(0); }
68 Self::Request {
69 requester_device,
70 file_type,
71 name,
72 } => {
73 v.push(0x3);
74 v.push(requester_device.to_u8());
75 file_type.extend_midi(v);
76 v.extend_from_slice(name);
77 }
78 }
79 }
80
81 pub fn packet(num: u32, data: Vec<u8>) -> Self {
84 Self::Packet {
85 running_count: (num % 128) as u8,
86 data,
87 }
88 }
89
90 fn encode_data(data: &[u8]) -> Vec<u8> {
91 let mut r = Vec::with_capacity(128);
92 let mut d = 0; let mut e = 0; loop {
95 if e >= 128 || d >= data.len() {
96 break;
97 }
98 r.push(0); let mut j = 0;
100 loop {
101 if j >= 7 || d + j >= data.len() {
102 break;
103 }
104 r[e] += (data[d + j] >> 7) << (6 - j);
105 r.push(data[d + j] & 0b01111111);
106 j += 1;
107 }
108
109 e += 8;
110 d += j;
111 }
112 r
113 }
114
115 #[allow(dead_code)]
116 pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
117 Err(ParseError::NotImplemented("FileDumpMsg"))
118 }
119}
120
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
123#[derive(Debug, Copy, Clone, PartialEq, Eq)]
124pub enum FileType {
125 MIDI,
126 MIEX,
127 ESEQ,
128 TEXT,
129 BIN,
130 MAC,
131 Custom([u8; 4]),
132}
133
134impl FileType {
135 fn extend_midi(&self, v: &mut Vec<u8>) {
136 match self {
137 Self::MIDI => b"MIDI".iter().for_each(|c| v.push(*c)),
138 Self::MIEX => b"MIEX".iter().for_each(|c| v.push(*c)),
139 Self::ESEQ => b"ESEQ".iter().for_each(|c| v.push(*c)),
140 Self::TEXT => b"TEXT".iter().for_each(|c| v.push(*c)),
141 Self::BIN => b"BIN ".iter().for_each(|c| v.push(*c)),
142 Self::MAC => b"MAC ".iter().for_each(|c| v.push(*c)),
143 Self::Custom(chars) => chars[0..4].iter().for_each(|c| v.push(*c)),
144 }
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use crate::*;
152 use alloc::vec;
153
154 #[test]
155 fn encode_data() {
156 assert_eq!(
157 FileDumpMsg::encode_data(&[
158 0b11111111, 0b10101010, 0b00000000, 0b01010101, 0b11111111, 0b10101010, 0b00000000,
159 0b11010101
160 ]),
161 vec![
162 0b01100110, 0b01111111, 0b00101010, 0b00000000, 0b01010101, 0b01111111, 0b00101010,
163 0b00000000, 0b01000000, 0b01010101
164 ]
165 );
166 }
167
168 #[test]
169 fn serialize_file_dump_packet() {
170 let packet_msg = MidiMsg::SystemExclusive {
171 msg: SystemExclusiveMsg::UniversalNonRealTime {
172 device: DeviceID::AllCall,
173 msg: UniversalNonRealTimeMsg::FileDump(FileDumpMsg::packet(
174 129,
175 vec![
176 0b11111111, 0b10101010, 0b00000000, 0b01010101, 0b11111111, 0b10101010,
177 0b00000000, 0b11010101,
178 ],
179 )),
180 },
181 }
182 .to_midi();
183
184 assert_eq!(packet_msg.len(), 19);
185 assert_eq!(&packet_msg[0..7], &[0xF0, 0x7E, 0x7F, 0x07, 0x02, 0x01, 9]);
186 assert_eq!(
187 &packet_msg[7..17],
188 &[
189 0b01100110, 0b01111111, 0b00101010, 0b00000000, 0b01010101, 0b01111111, 0b00101010,
190 0b00000000, 0b01000000, 0b01010101
191 ]
192 );
193 assert_eq!(
194 packet_msg[17], checksum(&[
196 0x7E, 0x7F, 0x07, 0x02, 0x01, 9, 0b01100110, 0b01111111, 0b00101010, 0b00000000,
197 0b01010101, 0b01111111, 0b00101010, 0b00000000, 0b01000000, 0b01010101
198 ])
199 );
200 }
201
202 #[test]
203 fn serialize_file_dump_header() {
204 assert_eq!(
205 MidiMsg::SystemExclusive {
206 msg: SystemExclusiveMsg::UniversalNonRealTime {
207 device: DeviceID::AllCall,
208 msg: UniversalNonRealTimeMsg::FileDump(FileDumpMsg::Header {
209 sender_device: DeviceID::Device(9),
210 file_type: FileType::MIDI,
211 length: 66,
212 name: BString::from("Hello"),
213 }),
214 },
215 }
216 .to_midi(),
217 vec![
218 0xF0, 0x7E, 0x7F, 0x7, 0x1, 0x9, b"M"[0], b"I"[0], b"D"[0], b"I"[0], 66, 0x0, 0x0, 0x0, b"H"[0], b"e"[0], b"l"[0], b"l"[0], b"o"[0], 0xF7
222 ]
223 );
224 }
225}