augmented_midi/
serializer.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23use 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
154/// Input is a 14-bit number
155/// 0b0lllllll - 1st 7 bits are the least significant bits
156/// 0b0mmmmmmm - 2nd 7 bits are the most significant bits
157///
158/// Returns both bytes split
159fn 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}