midi_msg/
util.rs

1use super::ParseError;
2use alloc::vec::Vec;
3use micromath::F32Ext;
4
5#[inline]
6pub fn to_u7(x: u8) -> u8 {
7    x.min(127)
8}
9
10#[inline]
11pub fn i_to_u7(x: i8) -> u8 {
12    to_u7((x.max(-64) + 64) as u8)
13}
14
15#[inline]
16pub fn u7_to_i(x: u8) -> i8 {
17    x as i8 - 64
18}
19
20#[inline]
21pub fn bool_from_u7(x: u8) -> Result<bool, ParseError> {
22    if x > 127 {
23        Err(ParseError::ByteOverflow)
24    } else {
25        Ok(x >= 0x40)
26    }
27}
28
29#[inline]
30pub fn u8_from_u7(x: u8) -> Result<u8, ParseError> {
31    if x > 127 {
32        Err(ParseError::ByteOverflow)
33    } else {
34        Ok(x)
35    }
36}
37
38#[inline]
39pub fn u7_from_midi(m: &[u8]) -> Result<u8, ParseError> {
40    if m.is_empty() {
41        Err(ParseError::UnexpectedEnd)
42    } else {
43        u8_from_u7(m[0])
44    }
45}
46
47// #[inline]
48// pub fn to_i7(x: i8) -> u8 {
49//     if x > 63 {
50//         0x7f
51//     } else if x < -64 {
52//         0x40
53//     } else {
54//         x as u8 & 0b01111111
55//     }
56// }
57
58#[inline]
59pub fn to_u14(x: u16) -> [u8; 2] {
60    if x > 16383 {
61        [0x7f, 0x7f]
62    } else {
63        [(x >> 7) as u8, x as u8 & 0b01111111]
64    }
65}
66
67#[inline]
68pub fn i_to_u14(x: i16) -> [u8; 2] {
69    to_u14((x.max(-8192) + 8192) as u16)
70}
71
72#[inline]
73pub fn u14_from_midi(m: &[u8]) -> Result<u16, ParseError> {
74    if m.len() < 2 {
75        Err(crate::ParseError::UnexpectedEnd)
76    } else {
77        let (lsb, msb) = (m[0], m[1]);
78        if lsb > 127 || msb > 127 {
79            Err(ParseError::ByteOverflow)
80        } else {
81            let mut x = lsb as u16;
82            x += (msb as u16) << 7;
83            Ok(x)
84        }
85    }
86}
87
88#[inline]
89pub fn replace_u14_lsb(msb: u16, lsb: u8) -> u16 {
90    (msb & 0b11111110000000) + (lsb as u16)
91}
92
93#[inline]
94pub fn u14_from_u7s(msb: u8, lsb: u8) -> u16 {
95    ((msb as u16) << 7) + (lsb as u16)
96}
97
98#[inline]
99pub fn i14_from_u7s(msb: u8, lsb: u8) -> i16 {
100    u14_from_u7s(msb, lsb) as i16 - 8192
101}
102
103#[inline]
104pub fn to_nibble(x: u8) -> [u8; 2] {
105    [x >> 4, x & 0b00001111]
106}
107
108#[inline]
109pub fn push_u7(x: u8, v: &mut Vec<u8>) {
110    v.push(to_u7(x));
111}
112
113// #[inline]
114// pub fn push_i7(x: i8, v: &mut Vec<u8>) {
115//     v.push(to_i7(x));
116// }
117
118#[inline]
119pub fn push_u14(x: u16, v: &mut Vec<u8>) {
120    let [msb, lsb] = to_u14(x);
121    v.push(lsb);
122    v.push(msb);
123}
124
125/// Given a frequency in Hertz, returns a floating point midi note number with 1.0 = 100 cents
126pub fn freq_to_midi_note_float(freq: f32) -> f32 {
127    12.0 * F32Ext::log2(freq / 440.0) + 69.0
128}
129
130/// Given a floating point midi note number, return the frequency in Hertz
131pub fn midi_note_float_to_freq(note: f32) -> f32 {
132    F32Ext::powf(2.0, (note - 69.0) / 12.0) * 440.0
133}
134
135/// Given a midi note number and additional cents, return the frequency
136pub fn midi_note_cents_to_freq(note: u8, cents: f32) -> f32 {
137    midi_note_float_to_freq(note as f32 + cents / 100.0)
138}
139
140/// Given a frequency in Hertz, returns (midi_note_number, additional cents from semitone)
141pub fn freq_to_midi_note_cents(freq: f32) -> (u8, f32) {
142    let semitone = freq_to_midi_note_float(freq);
143    (semitone as u8, F32Ext::fract(semitone) * 100.0)
144}
145
146#[cfg(feature = "sysex")]
147mod sysex_util {
148    use alloc::vec::Vec;
149
150    #[inline]
151    pub fn push_i14(x: i16, v: &mut Vec<u8>) {
152        let [msb, lsb] = to_i14(x);
153        v.push(lsb);
154        v.push(msb);
155    }
156
157    #[inline]
158    pub fn push_u21(x: u32, v: &mut Vec<u8>) {
159        let [msb, b, lsb] = to_u21(x);
160        v.push(lsb);
161        v.push(b);
162        v.push(msb);
163    }
164
165    #[inline]
166    pub fn push_u28(x: u32, v: &mut Vec<u8>) {
167        let [mmsb, msb, lsb, llsb] = to_u28(x);
168        v.push(llsb);
169        v.push(lsb);
170        v.push(msb);
171        v.push(mmsb);
172    }
173
174    #[inline]
175    pub fn push_u35(x: u64, v: &mut Vec<u8>) {
176        let [msb, b2, b3, b4, lsb] = to_u35(x);
177        v.push(lsb);
178        v.push(b4);
179        v.push(b3);
180        v.push(b2);
181        v.push(msb);
182    }
183
184    pub fn checksum(bytes: &[u8]) -> u8 {
185        let mut sum: u8 = 0;
186        for b in bytes.iter() {
187            sum ^= b;
188        }
189        sum
190    }
191
192    /// Takes a positive value between 0.0 and 100.0 and fits it into the u14 range
193    /// 1 = 0.0061 cents
194    pub fn cents_to_u14(cents: f32) -> u16 {
195        let cents = cents.clamp(0.0, 100.0);
196        super::F32Ext::round(cents / 100.0 * (0b11111111111111 as f32)) as u16
197    }
198
199    #[inline]
200    pub fn to_i14(x: i16) -> [u8; 2] {
201        if x > 8191 {
202            [0x3f, 0x7f]
203        } else if x < -8191 {
204            [0x40, 0x00]
205        } else {
206            [(x >> 7) as u8 & 0b01111111, x as u8 & 0b01111111]
207        }
208    }
209
210    #[inline]
211    pub fn to_u21(x: u32) -> [u8; 3] {
212        if x > 2097151 {
213            [0x7f, 0x7f, 0x7f]
214        } else {
215            [
216                (x >> 14) as u8,
217                (x >> 7) as u8 & 0b01111111,
218                x as u8 & 0b01111111,
219            ]
220        }
221    }
222
223    #[inline]
224    pub fn to_u28(x: u32) -> [u8; 4] {
225        if x > 2684354561 {
226            [0x7f, 0x7f, 0x7f, 0x7f]
227        } else {
228            [
229                (x >> 21) as u8,
230                (x >> 14) as u8 & 0b01111111,
231                (x >> 7) as u8 & 0b01111111,
232                x as u8 & 0b01111111,
233            ]
234        }
235    }
236
237    #[inline]
238    pub fn to_u35(x: u64) -> [u8; 5] {
239        if x > 34359738367 {
240            [0x7f, 0x7f, 0x7f, 0x7f, 0x7f]
241        } else {
242            [
243                (x >> 28) as u8,
244                (x >> 21) as u8 & 0b01111111,
245                (x >> 14) as u8 & 0b01111111,
246                (x >> 7) as u8 & 0b01111111,
247                x as u8 & 0b01111111,
248            ]
249        }
250    }
251}
252
253#[cfg(feature = "sysex")]
254pub use sysex_util::*;
255
256#[cfg(feature = "file")]
257mod file_util {
258    use super::ParseError;
259    use alloc::vec::Vec;
260    use core::convert::TryInto;
261
262    #[inline]
263    pub fn push_u16(x: u16, v: &mut Vec<u8>) {
264        let [b1, b2] = x.to_be_bytes();
265        v.push(b1);
266        v.push(b2);
267    }
268
269    #[inline]
270    pub fn push_u32(x: u32, v: &mut Vec<u8>) {
271        let [b1, b2, b3, b4] = x.to_be_bytes();
272        v.push(b1);
273        v.push(b2);
274        v.push(b3);
275        v.push(b4);
276    }
277
278    // Variable length quanity
279    pub fn push_vlq(x: u32, v: &mut Vec<u8>) {
280        if x < 0x00000080 {
281            v.push(x as u8 & 0b01111111);
282        } else if x < 0x00004000 {
283            v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000);
284            v.push(x as u8 & 0b01111111);
285        } else if x < 0x00200000 {
286            v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000);
287            v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000);
288            v.push(x as u8 & 0b01111111);
289        } else if x <= 0x0FFFFFFF {
290            v.push(((x >> 21) as u8 & 0b01111111) + 0b10000000);
291            v.push(((x >> 14) as u8 & 0b01111111) + 0b10000000);
292            v.push(((x >> 7) as u8 & 0b01111111) + 0b10000000);
293            v.push(x as u8 & 0b01111111);
294        } else {
295            panic!("Cannot use such a large number as a variable quantity")
296        }
297    }
298
299    /*
300    #[inline]
301        pub fn u16_from_midi(m: &[u8]) -> Result<u16, ParseError> {
302            if m.len() < 2 {
303                Err(crate::ParseError::UnexpectedEnd)
304            } else {
305                Ok(u16::from_be_bytes(m[0..2].try_into().unwrap()))
306            }
307        }
308     */
309
310    #[inline]
311    pub fn u32_from_midi(m: &[u8]) -> Result<u32, ParseError> {
312        if m.len() < 4 {
313            Err(crate::ParseError::UnexpectedEnd)
314        } else {
315            Ok(u32::from_be_bytes(m[0..4].try_into().unwrap()))
316        }
317    }
318
319    pub fn read_vlq(data: &[u8]) -> Result<(u32, usize), ParseError> {
320        let mut result: u32 = 0;
321        let mut bytes_read = 0;
322
323        for &byte in data {
324            result = (result << 7) | (byte & 0b01111111) as u32;
325            bytes_read += 1;
326
327            if byte & 0b10000000 == 0 {
328                // If the MSB is not set, this is the last byte of the VLQ
329                return Ok((result, bytes_read));
330            }
331
332            // Check if we've read too many bytes for a VLQ
333            if bytes_read >= 4 {
334                return Err(ParseError::VlqOverflow);
335            }
336        }
337
338        // If we've exhausted the slice without finding the end of VLQ
339        Err(ParseError::UnexpectedEnd)
340    }
341}
342
343#[cfg(feature = "file")]
344pub use file_util::*;
345
346#[cfg(test)]
347mod tests {
348    use super::*;
349
350    #[test]
351    fn test_to_u7() {
352        assert_eq!(to_u7(0xff), 127); // Overflow is treated as max value
353        assert_eq!(to_u7(0x77), 0x77);
354        assert_eq!(to_u7(0x00), 0x00);
355        assert_eq!(to_u7(0x7f), 127);
356    }
357
358    #[test]
359    fn test_to_u14() {
360        assert_eq!(to_u14(0xff), [1, 127]);
361        assert_eq!(to_u14(0xff00), [127, 127]); // Overflow is treated as max value
362        assert_eq!(to_u14(0x00), [0, 0]);
363        assert_eq!(to_u14(0xfff), [0x1f, 127]);
364        assert_eq!(to_u14(1000), [0x07, 0x68]);
365    }
366
367    #[test]
368    fn test_i_to_u() {
369        assert_eq!(i_to_u7(63), 127);
370        assert_eq!(i_to_u7(0), 64);
371        assert_eq!(i_to_u7(-64), 0);
372
373        assert_eq!(i_to_u14(0), to_u14(8192));
374        assert_eq!(i_to_u14(-8192), to_u14(0));
375        assert_eq!(i_to_u14(8191), to_u14(16383));
376    }
377
378    #[test]
379    fn test_u_to_i() {
380        assert_eq!(u7_to_i(127), 63);
381        assert_eq!(u7_to_i(64), 0);
382        assert_eq!(u7_to_i(0), -64);
383
384        assert_eq!(i14_from_u7s(to_u14(8192)[0], to_u14(8192)[1]), 0);
385        assert_eq!(i14_from_u7s(0, 0), -8192);
386        assert_eq!(i14_from_u7s(to_u14(16383)[0], to_u14(16383)[1]), 8191);
387    }
388
389    #[test]
390    fn test_midi_note_float_to_freq() {
391        assert!((midi_note_float_to_freq(67.0) - 392.0).abs() <= 0.01);
392    }
393
394    #[test]
395    #[cfg(feature = "sysex")]
396    fn test_to_i14() {
397        assert_eq!(to_i14(0xff), [0x01, 0x7f]);
398        assert_eq!(to_i14(0x6f00), [0x3f, 0x7f]); // Overflow is treated as max value
399        assert_eq!(to_i14(0x00), [0, 0]);
400        assert_eq!(to_i14(0xfff), [0x1f, 0x7f]);
401        assert_eq!(to_i14(1000), [0x07, 0x68]);
402        assert_eq!(to_i14(-10000), [0x40, 0x00]); // Min overflow is treated as min value
403        assert_eq!(to_i14(-8192), [0x40, 0x00]);
404        assert_eq!(to_i14(-8191), [0x40, 0x01]);
405        assert_eq!(to_i14(-1), [0x7f, 0x7f]);
406    }
407
408    #[test]
409    #[cfg(feature = "sysex")]
410    fn test_to_u21() {
411        assert_eq!(to_u21(0xff), [0, 1, 127]);
412        assert_eq!(to_u21(0xff00), [3, 126, 0]);
413        assert_eq!(to_u21(0xffff00), [127, 127, 127]); // Overflow is treated as max value
414        assert_eq!(to_u21(0x00), [0, 0, 0]);
415        assert_eq!(to_u21(0xfff), [0, 0x1f, 127]);
416        assert_eq!(
417            to_u21(0b1000011010100000),
418            [0b00000010, 0b00001101, 0b00100000]
419        );
420    }
421
422    #[test]
423    #[cfg(feature = "sysex")]
424    fn text_checksum() {
425        assert_eq!(checksum(&[0b11110000, 0b00001111, 0b10101010]), 0b01010101);
426        assert_eq!(
427            checksum(&[0x41, 0x4D, 0x02, 0x41, 0x21, 0x04, 0x02, 0x02]),
428            0x6A
429        )
430    }
431
432    #[cfg(feature = "sysex")]
433    fn freq_to_midi_note_u14(freq: f32) -> (u8, u16) {
434        let (n, c) = crate::freq_to_midi_note_cents(freq);
435        (n, cents_to_u14(c))
436    }
437
438    #[test]
439    #[cfg(feature = "sysex")]
440    fn test_freq_to_midi_note() {
441        // The test data below is taken from the "Frequency data format" section (page 48)
442        // of The MIDI 1.0 Detailed Specification 4.2.1.
443
444        // This crate uses micromath for fast, no_std friendly approximations
445        // for math functions like powf and ln2. The tests below verify
446        // that these approximations are reasonable.
447
448        // Frequency      : 8.1758 Hz
449        // Expected bytes : 00 00 00
450        // Actual bytes   : 00 00 0f
451        // Error          : 0.0061 * 15 = 0.0915 cents
452        assert_eq!(freq_to_midi_note_u14(8.1758), (0x00, 0x0f));
453
454        // Frequency      : 8.662 Hz
455        // Expected bytes : 01 00 00
456        // Actual bytes   : 01 00 11
457        // Error          : 0.0061 * 17 = 0.1037 cents
458        assert_eq!(freq_to_midi_note_u14(8.662), (0x01, 0x11));
459
460        // Frequency      : 261.6256 Hz
461        // Expected bytes : 3c 00 00
462        // Actual bytes   : 3c 00 0f
463        // Error          : 0.0061 * 15 = 0.0915 cents
464        assert_eq!(freq_to_midi_note_u14(261.6256), (0x3C, 0x0f));
465
466        // This happens to be an exact match since the refrence frequency
467        // for the approximation is 440.000 (the spec example data says
468        // note number 0x43, which seems to be wrong).
469        assert_eq!(freq_to_midi_note_u14(440.0000), (0x45, 0x00));
470
471        // Frequency      : 8372.0190 Hz
472        // Expected bytes : 78 00 00
473        // Actual bytes   : 78 00 01
474        // Error          : 0.0061 cents
475        assert_eq!(freq_to_midi_note_u14(8_372.019), (0x78, 0x01));
476
477        // Frequency      : 12543.8800 Hz
478        // Expected bytes : 7f 00 00
479        // Actual bytes   : 7f 00 02
480        // Error          : 0.0061 * 2 = 0.0122 cents
481        assert_eq!(freq_to_midi_note_u14(12_543.88), (0x7F, 0x02));
482    }
483
484    #[test]
485    #[cfg(feature = "file")]
486    fn test_vlq() {
487        fn test(x: u32, expected_len: usize) {
488            let mut v = Vec::new();
489            push_vlq(x, &mut v);
490            let (y, len) = read_vlq(&v).unwrap();
491            assert_eq!(
492                x, y,
493                "Got {} after converting {} to and from a variable-length quantity",
494                y, x
495            );
496            assert_eq!(
497                len, expected_len,
498                "Expected a variable-length quantity of length {} but got {}",
499                expected_len, len
500            );
501        }
502        test(0, 1);
503        test(0x40, 1);
504        test(0x7F, 1);
505        test(0x80, 2);
506        test(0x2000, 2);
507        test(0x3FFF, 2);
508        test(0x4000, 3);
509        test(0x100000, 3);
510        test(0x1FFFFF, 3);
511        test(0x200000, 4);
512        test(0x8000000, 4);
513        test(0xFFFFFFF, 4);
514
515        assert_eq!(
516            read_vlq(&[0x80, 0x80, 0x80, 0x80]),
517            Err(ParseError::VlqOverflow)
518        );
519    }
520}