wmidi/
midi_message.rs

1use crate::{ControlFunction, Error, Note, ToSliceError, U14, U7};
2use core::convert::TryFrom;
3
4#[cfg(feature = "std")]
5use std::{io, vec::Vec};
6
7/// Holds information based on the Midi 1.0 spec.
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum MidiMessage<'a> {
10    /// This message is sent when a note is released (ended).
11    NoteOff(Channel, Note, Velocity),
12
13    /// This message is sent when a note is depressed (start).
14    NoteOn(Channel, Note, Velocity),
15
16    /// This message is most often sent by pressing down on the key after it "bottoms out".
17    PolyphonicKeyPressure(Channel, Note, Velocity),
18
19    /// This message is sent when a controller value changes. Controllers include devices such as pedals and levers.
20    ///
21    /// Controller numbers 120-127 are reserved as "Channel Mode Messages".
22    ControlChange(Channel, ControlFunction, ControlValue),
23
24    /// This message is sent when the patch number changes.
25    ProgramChange(Channel, ProgramNumber),
26
27    /// This message is most often sent by pressing down on the key after it "bottoms out". This message is different
28    /// from polyphonic after-touch. Use this message to send the single greatest pressure value (of all the current
29    /// depressed keys).
30    ChannelPressure(Channel, Velocity),
31
32    /// This message is sent to indicate a change in the pitch bender (wheel or level, typically). The pitch bender is
33    /// measured by a fourteen bit value. Center is 8192.
34    PitchBendChange(Channel, PitchBend),
35
36    /// This message type allows manufacturers to create their own messages (such as bulk dumps, patch parameters, and
37    /// other non-spec data) and provides a mechanism for creating additional MIDI Specification messages.
38    ///
39    /// In the data held by the SysEx message, the Manufacturer's ID code (assigned by MMA or AMEI) is either 1 byte or
40    /// 3 bytes. Two of the 1 Byte IDs are reserved for extensions called Universal Exclusive Messages, which are not
41    /// manufacturer-specific. If a device recognizes the ID code as its own (or as a supported Universal message) it
42    /// will listen to the rest of the message. Otherwise the message will be ignored.
43    SysEx(&'a [U7]),
44
45    /// This message type allows manufacturers to create their own messages (such as bulk dumps, patch parameters, and
46    /// other non-spec data) and provides a mechanism for creating additional MIDI Specification messages.
47    ///
48    /// In the data held by the SysEx message, the Manufacturer's ID code (assigned by MMA or AMEI) is either 1 byte or
49    /// 3 bytes. Two of the 1 Byte IDs are reserved for extensions called Universal Exclusive Messages, which are not
50    /// manufacturer-specific. If a device recognizes the ID code as its own (or as a supported Universal message) it
51    /// will listen to the rest of the message. Otherwise the message will be ignored.
52    #[cfg(feature = "std")]
53    OwnedSysEx(Vec<U7>),
54
55    /// MIDI Time Code Quarter Frame.
56    ///
57    /// The data is in the format 0nnndddd where nnn is the Message Type and dddd is the Value.
58    ///
59    /// TODO: Interpret data instead of providing the raw format.
60    MidiTimeCode(U7),
61
62    /// This is an internal 14 bit value that holds the number of MIDI beats (1 beat = six MIDI clocks) since the start
63    /// of the song.
64    SongPositionPointer(SongPosition),
65
66    /// The Song Select specifies which sequence or song is to be played.
67    SongSelect(Song),
68
69    /// The u8 data holds the status byte.
70    Reserved(u8),
71
72    /// Upon receiving a Tune Request, all analog synthesizers should tune their oscillators.
73    TuneRequest,
74
75    /// Timing Clock. Sent 24 times per quarter note when synchronization is required.
76    TimingClock,
77
78    /// Start the current sequence playing. (This message will be followed with Timing Clocks).
79    Start,
80
81    /// Continue at the point the sequence was Stopped.
82    Continue,
83
84    /// Stop the current sequence.
85    Stop,
86
87    /// This message is intended to be sent repeatedly to tell the receiver that a connection is alive. Use of this
88    /// message is optional. When initially received, the receiver will expect to receive another Active Sensing message
89    /// each 300ms (max), and if it idoes not, then it will assume that the connection has been terminated. At
90    /// termination, the receiver will turn off all voices and return to normal (non-active sensing) operation.
91    ActiveSensing,
92
93    /// Reset all receivers in the system to power-up status. This should be used sparingly, preferably under manual
94    /// control. In particular, it should not be sent on power-up.
95    Reset,
96}
97
98impl<'a> TryFrom<&'a [u8]> for MidiMessage<'a> {
99    type Error = Error;
100    /// Construct a midi message from bytes.
101    fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
102        if bytes.is_empty() {
103            return Err(Error::NoBytes);
104        }
105        if !is_status_byte(bytes[0]) {
106            return Err(Error::UnexpectedDataByte);
107        }
108        let chan = Channel::from_index(bytes[0] & 0x0F)?;
109        let data_a = bytes
110            .get(1)
111            .ok_or(Error::NotEnoughBytes)
112            .and_then(|b| valid_data_byte(*b));
113        let data_b = bytes
114            .get(2)
115            .ok_or(Error::NotEnoughBytes)
116            .and_then(|b| valid_data_byte(*b));
117        match bytes[0] & 0xF0 {
118            0x80 => Ok(MidiMessage::NoteOff(chan, Note::from(data_a?), data_b?)),
119            0x90 => match data_b? {
120                U7::MIN => Ok(MidiMessage::NoteOff(chan, Note::from(data_a?), U7::MIN)),
121                _ => Ok(MidiMessage::NoteOn(chan, Note::from(data_a?), data_b?)),
122            },
123            0xA0 => Ok(MidiMessage::PolyphonicKeyPressure(
124                chan,
125                Note::from(data_a?),
126                data_b?,
127            )),
128            0xB0 => Ok(MidiMessage::ControlChange(chan, data_a?.into(), data_b?)),
129            0xC0 => Ok(MidiMessage::ProgramChange(chan, data_a?)),
130            0xD0 => Ok(MidiMessage::ChannelPressure(chan, data_a?)),
131            0xE0 => Ok(MidiMessage::PitchBendChange(
132                chan,
133                combine_data(data_a?, data_b?),
134            )),
135            0xF0 => match bytes[0] {
136                0xF0 => MidiMessage::new_sysex(bytes),
137                0xF1 => Ok(MidiMessage::MidiTimeCode(data_a?)),
138                0xF2 => Ok(MidiMessage::SongPositionPointer(combine_data(
139                    data_a?, data_b?,
140                ))),
141                0xF3 => Ok(MidiMessage::SongSelect(data_a?)),
142                0xF4 | 0xF5 => Ok(MidiMessage::Reserved(bytes[0])),
143                0xF6 => Ok(MidiMessage::TuneRequest),
144                0xF7 => Err(Error::UnexpectedEndSysExByte),
145                0xF8 => Ok(MidiMessage::TimingClock),
146                0xF9 => Ok(MidiMessage::Reserved(bytes[0])),
147                0xFA => Ok(MidiMessage::Start),
148                0xFB => Ok(MidiMessage::Continue),
149                0xFC => Ok(MidiMessage::Stop),
150                0xFD => Ok(MidiMessage::Reserved(bytes[0])),
151                0xFE => Ok(MidiMessage::ActiveSensing),
152                0xFF => Ok(MidiMessage::Reset),
153                _ => unreachable!(),
154            },
155            _ => unreachable!(),
156        }
157    }
158}
159
160impl<'a> MidiMessage<'a> {
161    /// Construct a midi message from bytes.
162    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
163        MidiMessage::try_from(bytes)
164    }
165
166    /// Copies the message as bytes to slice. If slice does not have enough capacity to fit the
167    /// message, then an error is returned. On success, the number of bytes written will be
168    /// returned. This should be the same number obtained from `self.bytes_size()`.
169    #[allow(clippy::range_plus_one)]
170    pub fn copy_to_slice(&self, slice: &mut [u8]) -> Result<usize, ToSliceError> {
171        if slice.len() < self.bytes_size() {
172            Err(ToSliceError::BufferTooSmall)
173        } else {
174            let slice = &mut slice[..self.bytes_size()];
175            match self {
176                MidiMessage::NoteOff(a, b, c) => {
177                    slice.copy_from_slice(&[0x80 | a.index(), u8::from(*b), u8::from(*c)]);
178                }
179                MidiMessage::NoteOn(a, b, c) => {
180                    slice.copy_from_slice(&[0x90 | a.index(), u8::from(*b), u8::from(*c)]);
181                }
182                MidiMessage::PolyphonicKeyPressure(a, b, c) => {
183                    slice.copy_from_slice(&[0xA0 | a.index(), *b as u8, u8::from(*c)]);
184                }
185                MidiMessage::ControlChange(a, b, c) => {
186                    slice.copy_from_slice(&[0xB0 | a.index(), u8::from(*b), u8::from(*c)]);
187                }
188                MidiMessage::ProgramChange(a, b) => {
189                    slice.copy_from_slice(&[0xC0 | a.index(), u8::from(*b)]);
190                }
191                MidiMessage::ChannelPressure(a, b) => {
192                    slice.copy_from_slice(&[0xD0 | a.index(), u8::from(*b)]);
193                }
194                MidiMessage::PitchBendChange(a, b) => {
195                    let (b1, b2) = split_data(*b);
196                    slice.copy_from_slice(&[0xE0 | a.index(), b1, b2]);
197                }
198                MidiMessage::SysEx(b) => {
199                    slice[0] = 0xF0;
200                    slice[1..1 + b.len()].copy_from_slice(U7::data_to_bytes(b));
201                    slice[1 + b.len()] = 0xF7;
202                }
203                #[cfg(feature = "std")]
204                MidiMessage::OwnedSysEx(ref b) => {
205                    slice[0] = 0xF0;
206                    slice[1..1 + b.len()].copy_from_slice(U7::data_to_bytes(b));
207                    slice[1 + b.len()] = 0xF7;
208                }
209                MidiMessage::MidiTimeCode(a) => slice.copy_from_slice(&[0xF1, u8::from(*a)]),
210                MidiMessage::SongPositionPointer(a) => {
211                    let (a1, a2) = split_data(*a);
212                    slice.copy_from_slice(&[0xF2, a1, a2]);
213                }
214                MidiMessage::SongSelect(a) => slice.copy_from_slice(&[0xF3, u8::from(*a)]),
215                MidiMessage::Reserved(a) => slice.copy_from_slice(&[*a]),
216                MidiMessage::TuneRequest => slice.copy_from_slice(&[0xF6]),
217                MidiMessage::TimingClock => slice.copy_from_slice(&[0xF8]),
218                MidiMessage::Start => slice.copy_from_slice(&[0xFA]),
219                MidiMessage::Continue => slice.copy_from_slice(&[0xFB]),
220                MidiMessage::Stop => slice.copy_from_slice(&[0xFC]),
221                MidiMessage::ActiveSensing => slice.copy_from_slice(&[0xFE]),
222                MidiMessage::Reset => slice.copy_from_slice(&[0xFF]),
223            };
224            Ok(self.bytes_size())
225        }
226    }
227
228    /// Return `Some(midi_message)` if `self` is not a SysEx message, or `None` if it is. This expands the lifetime of
229    /// the `MidiMessage` from `'a` to `'static`.
230    pub fn drop_unowned_sysex(self) -> Option<MidiMessage<'static>> {
231        match self {
232            MidiMessage::NoteOff(a, b, c) => Some(MidiMessage::NoteOff(a, b, c)),
233            MidiMessage::NoteOn(a, b, c) => Some(MidiMessage::NoteOn(a, b, c)),
234            MidiMessage::PolyphonicKeyPressure(a, b, c) => {
235                Some(MidiMessage::PolyphonicKeyPressure(a, b, c))
236            }
237            MidiMessage::ControlChange(a, b, c) => Some(MidiMessage::ControlChange(a, b, c)),
238            MidiMessage::ProgramChange(a, b) => Some(MidiMessage::ProgramChange(a, b)),
239            MidiMessage::ChannelPressure(a, b) => Some(MidiMessage::ChannelPressure(a, b)),
240            MidiMessage::PitchBendChange(a, b) => Some(MidiMessage::PitchBendChange(a, b)),
241            MidiMessage::SysEx(_) => None,
242            #[cfg(feature = "std")]
243            MidiMessage::OwnedSysEx(bytes) => Some(MidiMessage::OwnedSysEx(bytes)),
244            MidiMessage::MidiTimeCode(a) => Some(MidiMessage::MidiTimeCode(a)),
245            MidiMessage::SongPositionPointer(a) => Some(MidiMessage::SongPositionPointer(a)),
246            MidiMessage::SongSelect(a) => Some(MidiMessage::SongSelect(a)),
247            MidiMessage::Reserved(a) => Some(MidiMessage::Reserved(a)),
248            MidiMessage::TuneRequest => Some(MidiMessage::TuneRequest),
249            MidiMessage::TimingClock => Some(MidiMessage::TimingClock),
250            MidiMessage::Start => Some(MidiMessage::Start),
251            MidiMessage::Continue => Some(MidiMessage::Continue),
252            MidiMessage::Stop => Some(MidiMessage::Stop),
253            MidiMessage::ActiveSensing => Some(MidiMessage::ActiveSensing),
254            MidiMessage::Reset => Some(MidiMessage::Reset),
255        }
256    }
257
258    /// Take ownership of the SysEx data. This expands the lifetime of the message to `'static`. If `'static` lifetime
259    /// is needed but SysEx messages can be dropped, consider using `self.drop_unowned_sysex()`.
260    #[inline(always)]
261    pub fn to_owned(&self) -> MidiMessage<'static> {
262        match self.clone() {
263            MidiMessage::NoteOff(a, b, c) => MidiMessage::NoteOff(a, b, c),
264            MidiMessage::NoteOn(a, b, c) => MidiMessage::NoteOn(a, b, c),
265            MidiMessage::PolyphonicKeyPressure(a, b, c) => {
266                MidiMessage::PolyphonicKeyPressure(a, b, c)
267            }
268            MidiMessage::ControlChange(a, b, c) => MidiMessage::ControlChange(a, b, c),
269            MidiMessage::ProgramChange(a, b) => MidiMessage::ProgramChange(a, b),
270            MidiMessage::ChannelPressure(a, b) => MidiMessage::ChannelPressure(a, b),
271            MidiMessage::PitchBendChange(a, b) => MidiMessage::PitchBendChange(a, b),
272            #[cfg(feature = "std")]
273            MidiMessage::SysEx(bytes) => MidiMessage::OwnedSysEx(bytes.to_vec()),
274            #[cfg(not(feature = "std"))]
275            MidiMessage::SysEx(_) => MidiMessage::SysEx(&[]), //to be updated with a better solution.
276            #[cfg(feature = "std")]
277            MidiMessage::OwnedSysEx(bytes) => MidiMessage::OwnedSysEx(bytes),
278            MidiMessage::MidiTimeCode(a) => MidiMessage::MidiTimeCode(a),
279            MidiMessage::SongPositionPointer(a) => MidiMessage::SongPositionPointer(a),
280            MidiMessage::SongSelect(a) => MidiMessage::SongSelect(a),
281            MidiMessage::Reserved(a) => MidiMessage::Reserved(a),
282            MidiMessage::TuneRequest => MidiMessage::TuneRequest,
283            MidiMessage::TimingClock => MidiMessage::TimingClock,
284            MidiMessage::Start => MidiMessage::Start,
285            MidiMessage::Continue => MidiMessage::Continue,
286            MidiMessage::Stop => MidiMessage::Stop,
287            MidiMessage::ActiveSensing => MidiMessage::ActiveSensing,
288            MidiMessage::Reset => MidiMessage::Reset,
289        }
290    }
291
292    /// The number of bytes the MIDI message takes when converted to bytes.
293    pub fn bytes_size(&self) -> usize {
294        match self {
295            MidiMessage::NoteOff(..) => 3,
296            MidiMessage::NoteOn(..) => 3,
297            MidiMessage::PolyphonicKeyPressure(..) => 3,
298            MidiMessage::ControlChange(..) => 3,
299            MidiMessage::ProgramChange(..) => 2,
300            MidiMessage::ChannelPressure(..) => 2,
301            MidiMessage::PitchBendChange(..) => 3,
302            MidiMessage::SysEx(b) => 2 + b.len(),
303            #[cfg(feature = "std")]
304            MidiMessage::OwnedSysEx(b) => 2 + b.len(),
305            MidiMessage::MidiTimeCode(_) => 2,
306            MidiMessage::SongPositionPointer(_) => 3,
307            MidiMessage::SongSelect(_) => 2,
308            MidiMessage::Reserved(_) => 1,
309            MidiMessage::TuneRequest => 1,
310            MidiMessage::TimingClock => 1,
311            MidiMessage::Start => 1,
312            MidiMessage::Continue => 1,
313            MidiMessage::Stop => 1,
314            MidiMessage::ActiveSensing => 1,
315            MidiMessage::Reset => 1,
316        }
317    }
318
319    /// The number of bytes the MIDI message takes when encoded with the `std::io::Read` trait.
320    #[deprecated(
321        since = "3.1.0",
322        note = "Function has been renamed to MidiMessage::bytes_size()."
323    )]
324    pub fn wire_size(&self) -> usize {
325        self.bytes_size()
326    }
327
328    /// The channel associated with the MIDI message, if applicable for the message type.
329    pub fn channel(&self) -> Option<Channel> {
330        match self {
331            MidiMessage::NoteOff(c, ..) => Some(*c),
332            MidiMessage::NoteOn(c, ..) => Some(*c),
333            MidiMessage::PolyphonicKeyPressure(c, ..) => Some(*c),
334            MidiMessage::ControlChange(c, ..) => Some(*c),
335            MidiMessage::ProgramChange(c, ..) => Some(*c),
336            MidiMessage::ChannelPressure(c, ..) => Some(*c),
337            MidiMessage::PitchBendChange(c, ..) => Some(*c),
338            _ => None,
339        }
340    }
341
342    #[inline(always)]
343    fn new_sysex(bytes: &'a [u8]) -> Result<Self, Error> {
344        debug_assert!(bytes[0] == 0xF0);
345        let end_i = 1 + bytes[1..]
346            .iter()
347            .copied()
348            .position(is_status_byte)
349            .ok_or(Error::NoSysExEndByte)?;
350        if bytes[end_i] != 0xF7 {
351            return Err(Error::UnexpectedNonSysExEndByte(bytes[end_i]));
352        }
353        // We've already gone through the bytes to find the first non data byte so we are assured
354        // that values from 1..end_i are valid data bytes.
355        let data_bytes = unsafe { U7::from_bytes_unchecked(&bytes[1..end_i]) };
356        Ok(MidiMessage::SysEx(data_bytes))
357    }
358
359    /// Convert the message to a vector of bytes. Prefer using
360    /// `copy_to_slice` if possible for better performance.
361    #[cfg(feature = "std")]
362    pub fn to_vec(&self) -> Vec<u8> {
363        let mut data = vec![0; self.bytes_size()];
364        // Unwrapping is ok as data has enough capacity for the data.
365        self.copy_to_slice(&mut data).unwrap();
366        data
367    }
368}
369
370#[cfg(feature = "std")]
371impl<'a> io::Read for MidiMessage<'a> {
372    // Use MidiMessage::copy_from_slice instead.
373    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
374        match self.copy_to_slice(buf) {
375            Ok(n) => Ok(n),
376            Err(ToSliceError::BufferTooSmall) => Ok(0),
377        }
378    }
379}
380
381/// Specifies the velocity of an action (often key press, release, or aftertouch).
382pub type Velocity = U7;
383
384/// Specifies the value of a MIDI control.
385pub type ControlValue = U7;
386
387/// Specifies a program. Sometimes known as patch.
388pub type ProgramNumber = U7;
389
390/// A 14bit value specifying the pitch bend. Neutral is 8192.
391pub type PitchBend = U14;
392
393/// 14 bit value that holds the number of MIDI beats (1 beat = six MIDI clocks) since the start of the song.
394pub type SongPosition = U14;
395
396/// A song or sequence.
397pub type Song = U7;
398
399/// The MIDI channel. There are 16 channels. They are numbered between 1 and 16
400/// inclusive, or indexed between 0 and 15 inclusive.
401#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
402pub enum Channel {
403    Ch1,
404    Ch2,
405    Ch3,
406    Ch4,
407    Ch5,
408    Ch6,
409    Ch7,
410    Ch8,
411    Ch9,
412    Ch10,
413    Ch11,
414    Ch12,
415    Ch13,
416    Ch14,
417    Ch15,
418    Ch16,
419}
420
421impl Channel {
422    /// Get a MIDI channel from an index that is between 0 and 15 inclusive.
423    pub fn from_index(i: u8) -> Result<Channel, Error> {
424        match i {
425            0 => Ok(Channel::Ch1),
426            1 => Ok(Channel::Ch2),
427            2 => Ok(Channel::Ch3),
428            3 => Ok(Channel::Ch4),
429            4 => Ok(Channel::Ch5),
430            5 => Ok(Channel::Ch6),
431            6 => Ok(Channel::Ch7),
432            7 => Ok(Channel::Ch8),
433            8 => Ok(Channel::Ch9),
434            9 => Ok(Channel::Ch10),
435            10 => Ok(Channel::Ch11),
436            11 => Ok(Channel::Ch12),
437            12 => Ok(Channel::Ch13),
438            13 => Ok(Channel::Ch14),
439            14 => Ok(Channel::Ch15),
440            15 => Ok(Channel::Ch16),
441            _ => Err(Error::ChannelOutOfRange),
442        }
443    }
444
445    /// The index of this midi channel. The returned value is between 0 and 15
446    /// inclusive.
447    pub fn index(self) -> u8 {
448        match self {
449            Channel::Ch1 => 0,
450            Channel::Ch2 => 1,
451            Channel::Ch3 => 2,
452            Channel::Ch4 => 3,
453            Channel::Ch5 => 4,
454            Channel::Ch6 => 5,
455            Channel::Ch7 => 6,
456            Channel::Ch8 => 7,
457            Channel::Ch9 => 8,
458            Channel::Ch10 => 9,
459            Channel::Ch11 => 10,
460            Channel::Ch12 => 11,
461            Channel::Ch13 => 12,
462            Channel::Ch14 => 13,
463            Channel::Ch15 => 14,
464            Channel::Ch16 => 15,
465        }
466    }
467
468    /// The number of this midi channel. The returned value is between 1 and 16
469    /// inclusive.
470    pub fn number(self) -> u8 {
471        self.index() + 1
472    }
473}
474
475#[inline(always)]
476fn combine_data(lower: U7, higher: U7) -> U14 {
477    let raw = u16::from(u8::from(lower)) + 128 * u16::from(u8::from(higher));
478    unsafe { U14::from_unchecked(raw) }
479}
480
481#[inline(always)]
482fn split_data(data: U14) -> (u8, u8) {
483    ((u16::from(data) % 128) as u8, (u16::from(data) / 128) as u8)
484}
485
486#[inline(always)]
487fn is_status_byte(b: u8) -> bool {
488    b & 0x80 == 0x80
489}
490
491#[inline(always)]
492fn valid_data_byte(b: u8) -> Result<U7, Error> {
493    U7::try_from(b).map_err(|_| Error::UnexpectedStatusByte)
494}
495
496#[cfg(test)]
497mod test {
498    use super::*;
499    use crate::{ControlFunction, Error, Note};
500
501    #[test]
502    fn try_from() {
503        assert_eq!(
504            MidiMessage::try_from([].as_ref()),
505            Err(Error::NoBytes),
506            "no bytes produces an error",
507        );
508        assert_eq!(
509            MidiMessage::try_from([0x00].as_ref()),
510            Err(Error::UnexpectedDataByte),
511            "no status byte produces an error",
512        );
513        assert_eq!(
514            MidiMessage::try_from([0x84].as_ref()),
515            Err(Error::NotEnoughBytes),
516            "NoteOff event produces errors with only 1 byte",
517        );
518        assert_eq!(
519            MidiMessage::try_from([0x84, 64].as_ref()),
520            Err(Error::NotEnoughBytes),
521            "NoteOff event produces errors with only 2 bytes",
522        );
523        assert_eq!(
524            MidiMessage::try_from([0x84, 64, 100].as_ref()),
525            Ok(MidiMessage::NoteOff(
526                Channel::Ch5,
527                Note::E4,
528                U7::try_from(100).unwrap()
529            )),
530            "NoteOff event is decoded.",
531        );
532
533        assert_eq!(
534            MidiMessage::try_from([0x94].as_ref()),
535            Err(Error::NotEnoughBytes),
536            "NoteOn event produces errors with only 1 byte",
537        );
538        assert_eq!(
539            MidiMessage::try_from([0x94, 64].as_ref()),
540            Err(Error::NotEnoughBytes),
541            "NoteOn event produces errors with only 2 bytes",
542        );
543        assert_eq!(
544            MidiMessage::try_from([0x94, 64, 100].as_ref()),
545            Ok(MidiMessage::NoteOn(
546                Channel::Ch5,
547                Note::E4,
548                U7::try_from(100).unwrap()
549            )),
550            "NoteOn event is decoded.",
551        );
552        assert_eq!(
553            MidiMessage::try_from([0x94, 64, 0].as_ref()),
554            Ok(MidiMessage::NoteOff(
555                Channel::Ch5,
556                Note::E4,
557                U7::try_from(0).unwrap()
558            )),
559            "NoteOn message with 0 veloctiy decodes as NoteOff",
560        );
561
562        assert_eq!(
563            MidiMessage::try_from([0xF0, 4, 8, 12, 16, 0xF7].as_ref()),
564            Ok(MidiMessage::SysEx(
565                U7::try_from_bytes(&[4, 8, 12, 16]).unwrap()
566            )),
567            "SysEx message is decoded with borrowed data.",
568        );
569        assert_eq!(
570            MidiMessage::try_from([0xF0, 3, 6, 9, 12, 15, 0xF7, 125].as_ref()),
571            Ok(MidiMessage::SysEx(
572                U7::try_from_bytes(&[3, 6, 9, 12, 15]).unwrap()
573            )),
574            "SysEx message does not include bytes after the end byte.",
575        );
576        assert_eq!(
577            MidiMessage::try_from([0xF0, 1, 2, 3, 4, 5, 6, 7, 8, 9].as_ref()),
578            Err(Error::NoSysExEndByte),
579            "SysEx message without end status produces error.",
580        );
581
582        assert_eq!(
583            MidiMessage::try_from([0xE4].as_ref()),
584            Err(Error::NotEnoughBytes),
585            "PitchBend with single byte produces error.",
586        );
587        assert_eq!(
588            MidiMessage::try_from([0xE4, 64].as_ref()),
589            Err(Error::NotEnoughBytes),
590            "PitchBend with only 2 bytes produces error.",
591        );
592        assert_eq!(
593            MidiMessage::try_from([0xE4, 64, 100].as_ref()),
594            Ok(MidiMessage::PitchBendChange(
595                Channel::Ch5,
596                U14::try_from(12864).unwrap()
597            )),
598            "PitchBendChange is decoded.",
599        );
600    }
601
602    #[test]
603    fn copy_to_slice() {
604        let b = {
605            let mut b = [0u8; 6];
606            let bytes_copied = MidiMessage::PolyphonicKeyPressure(
607                Channel::Ch10,
608                Note::A6,
609                U7::try_from(43).unwrap(),
610            )
611            .copy_to_slice(&mut b)
612            .unwrap();
613            assert_eq!(bytes_copied, 3);
614            b
615        };
616        assert_eq!(b, [0xA9, 93, 43, 0, 0, 0]);
617    }
618
619    #[test]
620    fn copy_to_slice_sysex() {
621        let b = {
622            let mut b = [0u8; 8];
623            let bytes_copied =
624                MidiMessage::SysEx(U7::try_from_bytes(&[10, 20, 30, 40, 50]).unwrap())
625                    .copy_to_slice(&mut b)
626                    .unwrap();
627            assert_eq!(bytes_copied, 7);
628            b
629        };
630        assert_eq!(b, [0xF0, 10, 20, 30, 40, 50, 0xF7, 0]);
631    }
632
633    #[cfg(feature = "std")]
634    #[test]
635    fn drop_unowned_sysex_with_std() {
636        assert_eq!(
637            MidiMessage::SysEx(U7::try_from_bytes(&[1, 2, 3]).unwrap()).drop_unowned_sysex(),
638            None
639        );
640        assert_eq!(
641            MidiMessage::OwnedSysEx(vec![
642                U7::try_from(1).unwrap(),
643                U7::try_from(2).unwrap(),
644                U7::try_from(3).unwrap()
645            ])
646            .drop_unowned_sysex(),
647            Some(MidiMessage::OwnedSysEx(vec![
648                U7::try_from(1).unwrap(),
649                U7::try_from(2).unwrap(),
650                U7::try_from(3).unwrap()
651            ]))
652        );
653        assert_eq!(
654            MidiMessage::TuneRequest.drop_unowned_sysex(),
655            Some(MidiMessage::TuneRequest)
656        );
657    }
658
659    #[test]
660    fn drop_unowned_sysex_with_nostd() {
661        assert_eq!(
662            MidiMessage::SysEx(U7::try_from_bytes(&[1, 2, 3]).unwrap()).drop_unowned_sysex(),
663            None
664        );
665        assert_eq!(
666            MidiMessage::TuneRequest.drop_unowned_sysex(),
667            Some(MidiMessage::TuneRequest)
668        );
669    }
670
671    #[cfg(feature = "std")]
672    #[test]
673    fn to_owned() {
674        assert_eq!(
675            MidiMessage::SysEx(U7::try_from_bytes(&[1, 2, 3]).unwrap()).to_owned(),
676            MidiMessage::OwnedSysEx(vec![
677                U7::try_from(1).unwrap(),
678                U7::try_from(2).unwrap(),
679                U7::try_from(3).unwrap()
680            ])
681        );
682        assert_ne!(
683            MidiMessage::SysEx(U7::try_from_bytes(&[1, 2, 3]).unwrap()).to_owned(),
684            MidiMessage::SysEx(U7::try_from_bytes(&[1, 2, 3]).unwrap())
685        );
686    }
687
688    #[test]
689    fn channel() {
690        assert_eq!(
691            MidiMessage::ControlChange(
692                Channel::Ch8,
693                ControlFunction::DAMPER_PEDAL,
694                U7::try_from(55).unwrap()
695            )
696            .channel(),
697            Some(Channel::Ch8)
698        );
699        assert_eq!(MidiMessage::Start.channel(), None);
700    }
701}