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