1use std::borrow::Borrow;
24use std::io::Write;
25
26use cookie_factory::bytes::be_u8;
27use cookie_factory::multi::all;
28use cookie_factory::{gen, GenError};
29
30use crate::{
31 MIDIMessage, MIDIMessageNote, ACTIVE_SENSING_MASK, CHANNEL_PRESSURE_MASK, CONTINUE_MASK,
32 CONTROL_CHANGE_MASK, NOTE_OFF_MASK, NOTE_ON_MASK, PITCH_WHEEL_CHANGE_MASK,
33 POLYPHONIC_KEY_PRESSURE_MASK, PROGRAM_CHANGE_MASK, RESET_MASK, SONG_POSITION_POINTER_MASK,
34 SONG_SELECT_MASK, START_MASK, STOP_MASK, SYSEX_MESSAGE_END_MASK, SYSEX_MESSAGE_MASK,
35 TIMING_CLOCK_MASK, TUNE_REQUEST_MASK,
36};
37
38pub fn serialize_message<W: Write, Buffer: Borrow<[u8]>>(
39 message: MIDIMessage<Buffer>,
40 output: W,
41) -> Result<(W, u64), GenError> {
42 let result = match message {
43 MIDIMessage::NoteOff(MIDIMessageNote {
44 channel,
45 note,
46 velocity,
47 }) => {
48 let status = NOTE_OFF_MASK | channel;
49 gen(
50 all([be_u8(status), be_u8(note), be_u8(velocity)].iter()),
51 output,
52 )?
53 }
54 MIDIMessage::NoteOn(MIDIMessageNote {
55 channel,
56 note,
57 velocity,
58 }) => {
59 let status = NOTE_ON_MASK | channel;
60 gen(
61 all([be_u8(status), be_u8(note), be_u8(velocity)].iter()),
62 output,
63 )?
64 }
65 MIDIMessage::PolyphonicKeyPressure {
66 channel,
67 note,
68 pressure,
69 } => {
70 let status = POLYPHONIC_KEY_PRESSURE_MASK | channel;
71 gen(
72 all([be_u8(status), be_u8(note), be_u8(pressure)].iter()),
73 output,
74 )?
75 }
76 MIDIMessage::ProgramChange {
77 channel,
78 program_number,
79 } => {
80 let status = PROGRAM_CHANGE_MASK | channel;
81 gen(all([be_u8(status), be_u8(program_number)].iter()), output)?
82 }
83 MIDIMessage::ChannelPressure { channel, pressure } => {
84 let status = CHANNEL_PRESSURE_MASK | channel;
85 gen(all([be_u8(status), be_u8(pressure)].iter()), output)?
86 }
87 MIDIMessage::PitchWheelChange { channel, value } => {
88 let status = PITCH_WHEEL_CHANGE_MASK | channel;
89 let (lsb, msb) = serialize_14_bit_midi_number(value);
90 gen(all([be_u8(status), be_u8(lsb), be_u8(msb)].iter()), output)?
91 }
92 MIDIMessage::ControlChange {
93 channel,
94 controller_number,
95 value,
96 } => {
97 let status = CONTROL_CHANGE_MASK | channel;
98 gen(
99 all([be_u8(status), be_u8(controller_number), be_u8(value)].iter()),
100 output,
101 )?
102 }
103 MIDIMessage::SysExMessage(message) => {
104 let status = SYSEX_MESSAGE_MASK;
105 let end = SYSEX_MESSAGE_END_MASK;
106 let message_bytes = message.message.borrow().iter().cloned().map(|b| be_u8(b));
107 let (output, _pos) = gen(be_u8(status), output)?;
108 let (output, _pos) = gen(all(message_bytes), output)?;
109 let (output, pos) = gen(be_u8(end), output)?;
110 (output, pos)
111 }
112 MIDIMessage::SongPositionPointer { beats } => {
113 let status = SONG_POSITION_POINTER_MASK;
114 let (lsb, msb) = serialize_14_bit_midi_number(beats);
115 gen(all([be_u8(status), be_u8(lsb), be_u8(msb)].iter()), output)?
116 }
117 MIDIMessage::SongSelect { song } => {
118 let status = SONG_SELECT_MASK;
119 gen(all([be_u8(status), be_u8(song)].iter()), output)?
120 }
121 MIDIMessage::TimingClock => {
122 let status = TIMING_CLOCK_MASK;
123 gen(all([be_u8(status)].iter()), output)?
124 }
125 MIDIMessage::Start => {
126 let status = START_MASK;
127 gen(all([be_u8(status)].iter()), output)?
128 }
129 MIDIMessage::Continue => {
130 let status = CONTINUE_MASK;
131 gen(all([be_u8(status)].iter()), output)?
132 }
133 MIDIMessage::Stop => {
134 let status = STOP_MASK;
135 gen(all([be_u8(status)].iter()), output)?
136 }
137 MIDIMessage::ActiveSensing => {
138 let status = ACTIVE_SENSING_MASK;
139 gen(all([be_u8(status)].iter()), output)?
140 }
141 MIDIMessage::Reset => {
142 let status = RESET_MASK;
143 gen(all([be_u8(status)].iter()), output)?
144 }
145 MIDIMessage::TuneRequest => {
146 let status = TUNE_REQUEST_MASK;
147 gen(all([be_u8(status)].iter()), output)?
148 }
149 MIDIMessage::Other { status } => gen(be_u8(status), output)?,
150 };
151 Ok(result)
152}
153
154fn serialize_14_bit_midi_number(input: u16) -> (u8, u8) {
160 let value1 = input & 0b00_0000_0111_1111;
161 let value2 = (input & 0b11_1111_1000_0000) >> 7;
162 (value1 as u8, value2 as u8)
163}
164
165#[cfg(test)]
166mod test {
167 use crate::{parse_midi_event, MIDIMessage, MIDISysExEvent};
168
169 use super::*;
170
171 #[test]
172 fn test_serialize_14_bit_midi_number() {
173 let (_, result) = crate::parse_14bit_midi_number(&[0x54, 0x39]).unwrap();
174 assert_eq!(result, 7380);
175 let (v1, v2) = serialize_14_bit_midi_number(result);
176 assert_eq!(v1, 0x54);
177 assert_eq!(v2, 0x39);
178 }
179
180 fn test_roundtrip(input: MIDIMessage<Vec<u8>>) {
181 let (bytes, _) = serialize_message(input.clone(), vec![]).unwrap();
182 let mut state = Default::default();
183 assert_eq!(bytes.len(), input.size_hint());
184 let (_, output) = parse_midi_event::<Vec<u8>>(&bytes, &mut state).unwrap();
185 assert_eq!(input, output);
186 }
187
188 #[test]
189 fn test_roundtrip_control_change() {
190 let message = MIDIMessage::ControlChange {
191 channel: 1,
192 controller_number: 22,
193 value: 10,
194 };
195 test_roundtrip(message);
196 }
197
198 #[test]
199 fn test_roundtrip_note_on() {
200 let note_on = MIDIMessage::<Vec<u8>>::note_on(8, 14, 20);
201 test_roundtrip(note_on);
202 }
203
204 #[test]
205 fn test_roundtrip_note_off() {
206 let note_off = MIDIMessage::<Vec<u8>>::note_off(8, 14, 20);
207 test_roundtrip(note_off);
208 }
209
210 #[test]
211 fn test_roundtrip_polyphonic_key_pressure() {
212 let input = MIDIMessage::<Vec<u8>>::PolyphonicKeyPressure {
213 channel: 8,
214 note: 10,
215 pressure: 15,
216 };
217 test_roundtrip(input);
218 }
219
220 #[test]
221 fn test_roundtrip_program_change() {
222 let input = MIDIMessage::<Vec<u8>>::ProgramChange {
223 channel: 0,
224 program_number: 15,
225 };
226 test_roundtrip(input);
227 }
228
229 #[test]
230 fn test_roundtrip_channel_pressure() {
231 let input = MIDIMessage::<Vec<u8>>::ChannelPressure {
232 channel: 10,
233 pressure: 5,
234 };
235 test_roundtrip(input);
236 }
237
238 #[test]
239 fn test_roundtrip_pitch_wheel_change() {
240 let input = MIDIMessage::<Vec<u8>>::PitchWheelChange {
241 channel: 3,
242 value: 55,
243 };
244 test_roundtrip(input);
245 }
246
247 #[test]
248 fn test_roundtrip_sysex() {
249 let input = MIDIMessage::<Vec<u8>>::SysExMessage(MIDISysExEvent {
250 message: vec![0, 1, 2, 3, 4],
251 });
252 test_roundtrip(input);
253 }
254
255 #[test]
256 fn test_roundtrip_song_position_pointer() {
257 let input = MIDIMessage::<Vec<u8>>::SongPositionPointer { beats: 4 };
258 test_roundtrip(input);
259 }
260
261 #[test]
262 fn test_roundtrip_song_select() {
263 let input = MIDIMessage::<Vec<u8>>::SongSelect { song: 3 };
264 test_roundtrip(input);
265 }
266
267 #[test]
268 fn test_roundtrip_timing_clock() {
269 let input = MIDIMessage::<Vec<u8>>::TimingClock;
270 test_roundtrip(input);
271 }
272
273 #[test]
274 fn test_roundtrip_start() {
275 let input = MIDIMessage::<Vec<u8>>::Start;
276 test_roundtrip(input);
277 }
278
279 #[test]
280 fn test_roundtrip_continue() {
281 let input = MIDIMessage::<Vec<u8>>::Continue;
282 test_roundtrip(input);
283 }
284
285 #[test]
286 fn test_roundtrip_stop() {
287 let input = MIDIMessage::<Vec<u8>>::Stop;
288 test_roundtrip(input);
289 }
290
291 #[test]
292 fn test_roundtrip_active_sensing() {
293 let input = MIDIMessage::<Vec<u8>>::ActiveSensing;
294 test_roundtrip(input);
295 }
296
297 #[test]
298 fn test_roundtrip_reset() {
299 let input = MIDIMessage::<Vec<u8>>::Reset;
300 test_roundtrip(input);
301 }
302
303 #[test]
304 fn test_roundtrip_tune_request() {
305 let input = MIDIMessage::<Vec<u8>>::TuneRequest;
306 test_roundtrip(input);
307 }
308}