Skip to main content

midi_msg/system_exclusive/
mod.rs

1mod controller_destination;
2pub use controller_destination::*;
3mod file_dump;
4pub use file_dump::*;
5mod file_reference;
6pub use file_reference::*;
7mod global_parameter;
8pub use global_parameter::*;
9mod key_based_instrument_control;
10pub use key_based_instrument_control::*;
11mod machine_control;
12pub use machine_control::*;
13mod notation;
14pub use notation::*;
15mod sample_dump;
16pub use sample_dump::*;
17mod show_control;
18pub use show_control::*;
19mod tuning;
20pub use tuning::*;
21
22use alloc::vec::Vec;
23
24use super::ReceiverContext;
25use super::general_midi::GeneralMidi;
26use super::parse_error::*;
27use super::time_code::*;
28use super::util::*;
29
30/// The bulk of the MIDI spec lives here, in "Universal System Exclusive" messages.
31/// Also used for manufacturer-specific messages.
32/// Used in [`MidiMsg`](crate::MidiMsg).
33#[derive(Debug, Clone, PartialEq)]
34pub enum SystemExclusiveMsg {
35    /// An arbitrary set of 7-bit "bytes", the meaning of which must be derived from the
36    /// message, the definition of which is determined by the given manufacturer.
37    Commercial { id: ManufacturerID, data: Vec<u8> },
38    /// Similar to `Commercial` but for use in non-commercial situations.
39    NonCommercial { data: Vec<u8> },
40    /// A diverse range of messages, for real-time applications.
41    /// A message is targeted to the given `device`.
42    UniversalRealTime {
43        device: DeviceID,
44        msg: UniversalRealTimeMsg,
45    },
46    /// A diverse range of messages, for non-real-time applications.
47    /// A message is targeted to the given `device`.
48    UniversalNonRealTime {
49        device: DeviceID,
50        msg: UniversalNonRealTimeMsg,
51    },
52}
53
54impl SystemExclusiveMsg {
55    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>, first_byte_is_f0: bool) {
56        if first_byte_is_f0 {
57            v.push(0xF0);
58        }
59        match self {
60            SystemExclusiveMsg::Commercial { id, data } => {
61                id.extend_midi(v);
62                data.iter().for_each(|d| v.push(to_u7(*d)));
63            }
64            SystemExclusiveMsg::NonCommercial { data } => {
65                v.push(0x7D);
66                data.iter().for_each(|d| v.push(to_u7(*d)));
67            }
68            SystemExclusiveMsg::UniversalRealTime { device, msg } => {
69                v.push(0x7F);
70                v.push(device.to_u8());
71                msg.extend_midi(v);
72            }
73            SystemExclusiveMsg::UniversalNonRealTime { device, msg } => {
74                let p = v.len();
75                v.push(0x7E);
76                v.push(device.to_u8());
77                msg.extend_midi(v);
78                if let UniversalNonRealTimeMsg::SampleDump(SampleDumpMsg::Packet { .. }) = msg {
79                    let q = v.len();
80                    v[q - 1] = checksum(&v[p..q - 1]);
81                }
82                if let UniversalNonRealTimeMsg::KeyBasedTuningDump(_) = msg {
83                    let q = v.len();
84                    v[q - 1] = checksum(&v[p..q - 1]);
85                }
86                if let UniversalNonRealTimeMsg::ScaleTuning1Byte(_) = msg {
87                    let q = v.len();
88                    v[q - 1] = checksum(&v[p..q - 1]);
89                }
90                if let UniversalNonRealTimeMsg::ScaleTuning2Byte(_) = msg {
91                    let q = v.len();
92                    v[q - 1] = checksum(&v[p..q - 1]);
93                }
94                if let UniversalNonRealTimeMsg::FileDump(FileDumpMsg::Packet { .. }) = msg {
95                    let q = v.len();
96                    v[q - 1] = checksum(&v[p..q - 1]);
97                }
98            }
99        }
100        v.push(0xF7);
101    }
102
103    fn sysex_bytes_from_midi(m: &[u8], first_byte_is_f0: bool) -> Result<&[u8], ParseError> {
104        if first_byte_is_f0 && m.first() != Some(&0xF0) {
105            return Err(ParseError::UndefinedSystemExclusiveMessage(
106                m.first().copied(),
107            ));
108        }
109        let offset = if first_byte_is_f0 { 1 } else { 0 };
110        for (i, b) in m[offset..].iter().enumerate() {
111            if b == &0xF7 {
112                return Ok(&m[offset..i + offset]);
113            }
114            if b > &127 {
115                return Err(ParseError::ByteOverflow);
116            }
117        }
118        Err(ParseError::NoEndOfSystemExclusiveFlag)
119    }
120
121    pub(crate) fn from_midi(
122        m: &[u8],
123        ctx: &mut ReceiverContext,
124    ) -> Result<(Self, usize), ParseError> {
125        let m = Self::sysex_bytes_from_midi(m, !ctx.is_smf_sysex)?;
126        match m.first() {
127            Some(0x7D) => Ok((
128                Self::NonCommercial {
129                    data: m[1..].to_vec(),
130                },
131                m.len() + 2,
132            )),
133            Some(0x7E) => Ok((
134                Self::UniversalNonRealTime {
135                    device: DeviceID::from_midi(&m[1..])?,
136                    msg: UniversalNonRealTimeMsg::from_midi(&m[2..])?,
137                },
138                m.len() + 2,
139            )),
140            Some(0x7F) => Ok((
141                Self::UniversalRealTime {
142                    device: DeviceID::from_midi(&m[1..])?,
143                    msg: UniversalRealTimeMsg::from_midi(&m[2..], ctx)?,
144                },
145                m.len() + 2,
146            )),
147            Some(_) => {
148                let (id, len) = ManufacturerID::from_midi(m)?;
149                Ok((
150                    Self::Commercial {
151                        id,
152                        data: m[len..].to_vec(),
153                    },
154                    m.len() + 2,
155                ))
156            }
157            None => Err(crate::ParseError::UnexpectedEnd),
158        }
159    }
160}
161
162/// Two 7-bit "bytes", used to identify the manufacturer for [`SystemExclusiveMsg::Commercial`] messages.
163/// See [the published list of IDs](https://www.midi.org/specifications-old/item/manufacturer-id-numbers).
164///
165/// If second byte is None, it is a one-byte ID.
166/// The first byte in a one-byte ID may not be greater than 0x7C.
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168pub struct ManufacturerID(pub u8, pub Option<u8>);
169
170impl ManufacturerID {
171    fn extend_midi(&self, v: &mut Vec<u8>) {
172        if let Some(second) = self.1 {
173            v.push(0x00);
174            v.push(to_u7(self.0));
175            v.push(to_u7(second));
176        } else {
177            v.push(self.0.min(0x7C))
178        }
179    }
180
181    fn from_midi(m: &[u8]) -> Result<(Self, usize), ParseError> {
182        let b1 = u7_from_midi(m)?;
183        if b1 == 0x00 {
184            if m.len() < 3 {
185                return Err(crate::ParseError::UnexpectedEnd);
186            }
187            let b2 = u7_from_midi(&m[1..])?;
188            let b3 = u7_from_midi(&m[2..])?;
189            Ok((Self(b2, Some(b3)), 3))
190        } else {
191            Ok((Self(b1, None), 1))
192        }
193    }
194}
195
196impl From<u8> for ManufacturerID {
197    fn from(a: u8) -> Self {
198        Self(a, None)
199    }
200}
201
202impl From<(u8, u8)> for ManufacturerID {
203    fn from((a, b): (u8, u8)) -> Self {
204        Self(a, Some(b))
205    }
206}
207
208/// The device ID being addressed, either a number between 0-126 or `AllCall` (all devices).
209/// Used by [`SystemExclusiveMsg::UniversalNonRealTime`] and [`SystemExclusiveMsg::UniversalRealTime`].
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum DeviceID {
212    Device(u8),
213    AllCall,
214}
215
216impl DeviceID {
217    fn to_u8(self) -> u8 {
218        match self {
219            Self::AllCall => 0x7F,
220            Self::Device(x) => to_u7(x),
221        }
222    }
223
224    fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
225        let b = u7_from_midi(m)?;
226        if b == 0x7F {
227            Ok(Self::AllCall)
228        } else {
229            Ok(Self::Device(b))
230        }
231    }
232}
233
234/// A diverse range of messages for real-time applications. Used by [`SystemExclusiveMsg::UniversalRealTime`].
235#[derive(Debug, Clone, PartialEq)]
236pub enum UniversalRealTimeMsg {
237    /// For use when a [`SystemCommonMsg::TimeCodeQuarterFrame`](crate::SystemCommonMsg::TimeCodeQuarterFrame1) is not appropriate:
238    /// When rewinding, fast-forwarding, or otherwise locating and cueing, where sending quarter frame
239    /// messages continuously would be excessive.
240    TimeCodeFull(TimeCode),
241    /// Provided for sending SMTPE "user bits", which are application specific.
242    TimeCodeUserBits(UserBits),
243    /// Used to control equipment for liver performances and installations.
244    ShowControl(ShowControlMsg),
245    /// Indicates that the next MIDI clock message is the first clock of a new measure.
246    BarMarker(BarMarker),
247    /// Indicates a change in time signature, effective immediately (or on the next MIDI clock).
248    TimeSignature(TimeSignature),
249    /// Indicates a change in time signature, effective upon receipt of the next `BarMarker` message.
250    TimeSignatureDelayed(TimeSignature),
251    /// Change the volume of all sound, from 0 (volume off) to 16383.
252    MasterVolume(u16),
253    /// Change the balance of all sound, from 0 (hard left) to 8192 (center) to 16383 (hard right).
254    MasterBalance(u16),
255    /// A value from -8192-8191, used like [`Parameter::FineTuning`](crate::Parameter::FineTuning), but affecting all channels.
256    ///
257    /// Defined in CA-025.
258    MasterFineTuning(i16),
259    /// A value from -64-63, used like [`Parameter::CoarseTuning`](crate::Parameter::CoarseTuning), but affecting all channels.
260    ///
261    /// Defined in CA-025.
262    MasterCoarseTuning(i8),
263    /// Used to control parameters on a device that affect all sound, e.g. a global reverb.
264    GlobalParameterControl(GlobalParameterControl),
265    /// Used to define a range of time points.
266    TimeCodeCueing(TimeCodeCueingMsg),
267    /// Used to control audio recording and production systems.
268    MachineControlCommand(MachineControlCommandMsg),
269    /// Responses to `MachineControlCommand`.
270    MachineControlResponse(MachineControlResponseMsg),
271    /// Immediately change the tuning of 1 or more notes.
272    TuningNoteChange(TuningNoteChange),
273    /// A set of 12 tunings across all octaves targeting a set of channels, to take effect immediately.
274    ScaleTuning1Byte(ScaleTuning1Byte),
275    /// A set of 12 high-res tunings across all octaves targeting a set of channels, to take effect immediately.
276    ScaleTuning2Byte(ScaleTuning2Byte),
277    /// Select the destination of a [`ChannelPressure`](crate::ChannelVoiceMsg::ChannelPressure) message.
278    ChannelPressureControllerDestination(ControllerDestination),
279    /// Select the destination of a [`PolyPressure`](crate::ChannelVoiceMsg::PolyPressure) message.
280    PolyphonicKeyPressureControllerDestination(ControllerDestination),
281    /// Select the destination of a [`ControlChange`](crate::ChannelVoiceMsg::ControlChange) message.
282    ControlChangeControllerDestination(ControlChangeControllerDestination),
283    /// Intended to act like Control Change messages, but targeted at an individual key for e.g. changing the release time for individual drum sounds.
284    KeyBasedInstrumentControl(KeyBasedInstrumentControl),
285}
286
287impl UniversalRealTimeMsg {
288    fn extend_midi(&self, v: &mut Vec<u8>) {
289        match self {
290            UniversalRealTimeMsg::TimeCodeFull(code) => {
291                v.push(0x1);
292                v.push(0x1);
293                code.extend_midi(v);
294            }
295            UniversalRealTimeMsg::TimeCodeUserBits(user_bits) => {
296                v.push(0x1);
297                v.push(0x2);
298                let [ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9] = user_bits.to_nibbles();
299                v.extend_from_slice(&[ub1, ub2, ub3, ub4, ub5, ub6, ub7, ub8, ub9]);
300            }
301            UniversalRealTimeMsg::ShowControl(msg) => {
302                v.push(0x2);
303                msg.extend_midi(v);
304            }
305            UniversalRealTimeMsg::BarMarker(marker) => {
306                v.push(0x3);
307                v.push(0x1);
308                marker.extend_midi(v);
309            }
310            UniversalRealTimeMsg::TimeSignature(signature) => {
311                v.push(0x3);
312                v.push(0x2);
313                signature.extend_midi(v);
314            }
315            UniversalRealTimeMsg::TimeSignatureDelayed(signature) => {
316                v.push(0x3);
317                v.push(0x42);
318                signature.extend_midi(v);
319            }
320            UniversalRealTimeMsg::MasterVolume(vol) => {
321                v.push(0x4);
322                v.push(0x1);
323                push_u14(*vol, v);
324            }
325            UniversalRealTimeMsg::MasterBalance(bal) => {
326                v.push(0x4);
327                v.push(0x2);
328                push_u14(*bal, v);
329            }
330            UniversalRealTimeMsg::MasterFineTuning(t) => {
331                v.push(0x4);
332                v.push(0x3);
333                let [msb, lsb] = i_to_u14(*t);
334                v.push(lsb);
335                v.push(msb);
336            }
337            UniversalRealTimeMsg::MasterCoarseTuning(t) => {
338                v.push(0x4);
339                v.push(0x4);
340                v.push(i_to_u7(*t));
341            }
342            UniversalRealTimeMsg::GlobalParameterControl(gp) => {
343                v.push(0x4);
344                v.push(0x5);
345                gp.extend_midi(v);
346            }
347            UniversalRealTimeMsg::TimeCodeCueing(msg) => {
348                v.push(0x5);
349                msg.extend_midi(v);
350            }
351            UniversalRealTimeMsg::MachineControlCommand(msg) => {
352                v.push(0x6);
353                msg.extend_midi(v);
354            }
355            UniversalRealTimeMsg::MachineControlResponse(msg) => {
356                v.push(0x7);
357                msg.extend_midi(v);
358            }
359            UniversalRealTimeMsg::TuningNoteChange(note_change) => {
360                v.push(0x8);
361                v.push(if note_change.tuning_bank_num.is_some() {
362                    0x7
363                } else {
364                    0x2
365                });
366                if let Some(bank_num) = note_change.tuning_bank_num {
367                    v.push(to_u7(bank_num))
368                }
369                note_change.extend_midi(v);
370            }
371            UniversalRealTimeMsg::ScaleTuning1Byte(tuning) => {
372                v.push(0x8);
373                v.push(0x8);
374                tuning.extend_midi(v);
375            }
376            UniversalRealTimeMsg::ScaleTuning2Byte(tuning) => {
377                v.push(0x8);
378                v.push(0x9);
379                tuning.extend_midi(v);
380            }
381            UniversalRealTimeMsg::ChannelPressureControllerDestination(d) => {
382                v.push(0x9);
383                v.push(0x1);
384                d.extend_midi(v);
385            }
386            UniversalRealTimeMsg::PolyphonicKeyPressureControllerDestination(d) => {
387                v.push(0x9);
388                v.push(0x2);
389                d.extend_midi(v);
390            }
391            UniversalRealTimeMsg::ControlChangeControllerDestination(d) => {
392                v.push(0x9);
393                v.push(0x3);
394                d.extend_midi(v);
395            }
396            UniversalRealTimeMsg::KeyBasedInstrumentControl(control) => {
397                v.push(0xA);
398                v.push(0x1);
399                control.extend_midi(v);
400            }
401        }
402    }
403
404    fn from_midi(m: &[u8], ctx: &mut ReceiverContext) -> Result<Self, ParseError> {
405        if m.len() < 2 {
406            return Err(crate::ParseError::UnexpectedEnd);
407        }
408
409        match (m[0], m[1]) {
410            (0x1, 0x1) => {
411                if m.len() > 6 {
412                    Err(ParseError::Invalid(
413                        "Extra bytes after a UniversalRealTimeMsg::TimeCodeFull",
414                    ))
415                } else {
416                    let time_code = TimeCode::from_midi(&m[2..])?;
417                    ctx.time_code = time_code;
418                    Ok(Self::TimeCodeFull(time_code))
419                }
420            }
421            _ => Err(ParseError::NotImplemented("UniversalRealTimeMsg")),
422        }
423    }
424}
425
426/// A diverse range of messages for non-real-time applications. Used by [`SystemExclusiveMsg::UniversalNonRealTime`].
427#[derive(Debug, Clone, PartialEq)]
428pub enum UniversalNonRealTimeMsg {
429    /// Used to transmit sampler data.
430    SampleDump(SampleDumpMsg),
431    /// Additional ways/features for transmitting sampler data per CA-019.
432    ExtendedSampleDump(ExtendedSampleDumpMsg),
433    /// Used to define a range of time points per MMA0001.
434    TimeCodeCueingSetup(TimeCodeCueingSetupMsg),
435    /// Request that the targeted device identify itself.
436    IdentityRequest,
437    /// The response to an `IdentityRequest`.
438    IdentityReply(IdentityReply),
439    /// Used to transmit general file data.
440    FileDump(FileDumpMsg),
441    /// Request a tuning bulk dump for the provided
442    /// tuning program number, 0-127, and optional tuning bank number, 0-127
443    TuningBulkDumpRequest(u8, Option<u8>),
444    /// A "key based" tuning dump, with one tuning for every key.
445    KeyBasedTuningDump(KeyBasedTuningDump),
446    /// A "1 byte scale" tuning dump, with 12 tunings applied across all octaves.
447    ScaleTuningDump1Byte(ScaleTuningDump1Byte),
448    /// A "2 byte scale" tuning dump, with 12 tunings applied across all octaves.
449    /// Like `ScaleTuningDump1Byte` but higher resolution.
450    ScaleTuningDump2Byte(ScaleTuningDump2Byte),
451    /// Change the tuning of 1 or more notes for the next sounding of those notes.
452    TuningNoteChange(TuningNoteChange),
453    /// Similar to `ScaleTuningDump1Byte`, but targets a channel, to take effect the next time a note is sounded.
454    ScaleTuning1Byte(ScaleTuning1Byte),
455    /// Similar to `ScaleTuningDump2Byte`, but targets a channel, to take effect the next time a note is sounded.
456    ScaleTuning2Byte(ScaleTuning2Byte),
457    /// Turn on or off General MIDI 1 or 2.
458    GeneralMidi(GeneralMidi),
459    /// Messages for accessing files on a shared network or filesystem.
460    FileReference(FileReferenceMsg),
461    /// Used by both `SampleDump` and `FileDump` to indicate all packets have been sent.
462    EOF,
463    /// Used by both `SampleDump` and `FileDump` from the receiver to request that the sender
464    /// does not send any more packets until an `ACK` or `NAK` is sent.
465    Wait,
466    /// Used to abort an ongoing `SampleDump` or `FileDump`.
467    Cancel,
468    /// Used by both `SampleDump` and `FileDump` from the receiver to indicate that it did not
469    /// receive the last packet correctly.
470    NAK(u8),
471    /// Used by both `SampleDump` and `FileDump` from the receiver to indicate that it
472    /// received the last packet correctly.
473    ACK(u8),
474}
475
476impl UniversalNonRealTimeMsg {
477    fn extend_midi(&self, v: &mut Vec<u8>) {
478        match self {
479            UniversalNonRealTimeMsg::SampleDump(msg) => {
480                match msg {
481                    SampleDumpMsg::Header { .. } => v.push(0x1),
482                    SampleDumpMsg::Packet { .. } => v.push(0x2),
483                    SampleDumpMsg::Request { .. } => v.push(0x3),
484                    SampleDumpMsg::LoopPointTransmission { .. } => {
485                        v.push(0x5);
486                        v.push(0x1);
487                    }
488                    SampleDumpMsg::LoopPointsRequest { .. } => {
489                        v.push(0x5);
490                        v.push(0x2);
491                    }
492                }
493                msg.extend_midi(v);
494            }
495            UniversalNonRealTimeMsg::ExtendedSampleDump(msg) => {
496                v.push(0x5);
497                match msg {
498                    ExtendedSampleDumpMsg::SampleName { .. } => v.push(0x3),
499                    ExtendedSampleDumpMsg::SampleNameRequest { .. } => v.push(0x4),
500                    ExtendedSampleDumpMsg::Header { .. } => v.push(0x5),
501                    ExtendedSampleDumpMsg::LoopPointTransmission { .. } => v.push(0x6),
502                    ExtendedSampleDumpMsg::LoopPointsRequest { .. } => v.push(0x7),
503                }
504                msg.extend_midi(v);
505            }
506            UniversalNonRealTimeMsg::TimeCodeCueingSetup(msg) => {
507                v.push(0x4);
508                msg.extend_midi(v);
509            }
510            UniversalNonRealTimeMsg::IdentityRequest => {
511                v.push(0x6);
512                v.push(0x1);
513            }
514            UniversalNonRealTimeMsg::IdentityReply(identity) => {
515                v.push(0x6);
516                v.push(0x2);
517                identity.extend_midi(v);
518            }
519            UniversalNonRealTimeMsg::FileDump(msg) => {
520                v.push(0x7);
521                msg.extend_midi(v);
522            }
523            UniversalNonRealTimeMsg::TuningBulkDumpRequest(program_num, bank_num) => {
524                v.push(0x8);
525                v.push(if bank_num.is_some() { 0x3 } else { 0x0 });
526                if let Some(bank_num) = bank_num {
527                    v.push(to_u7(*bank_num))
528                }
529                v.push(to_u7(*program_num));
530            }
531            UniversalNonRealTimeMsg::KeyBasedTuningDump(tuning) => {
532                v.push(0x8);
533                v.push(if tuning.tuning_bank_num.is_some() {
534                    0x4
535                } else {
536                    0x1
537                });
538                tuning.extend_midi(v);
539            }
540            UniversalNonRealTimeMsg::ScaleTuningDump1Byte(tuning) => {
541                v.push(0x8);
542                v.push(0x5);
543                tuning.extend_midi(v);
544            }
545            UniversalNonRealTimeMsg::ScaleTuningDump2Byte(tuning) => {
546                v.push(0x8);
547                v.push(0x6);
548                tuning.extend_midi(v);
549            }
550            UniversalNonRealTimeMsg::TuningNoteChange(tuning) => {
551                v.push(0x8);
552                v.push(0x7);
553                if let Some(bank_num) = tuning.tuning_bank_num {
554                    v.push(to_u7(bank_num))
555                } else {
556                    v.push(0x0); // Fallback to Bank 0
557                }
558                tuning.extend_midi(v);
559            }
560            UniversalNonRealTimeMsg::ScaleTuning1Byte(tuning) => {
561                v.push(0x8);
562                v.push(0x8);
563                tuning.extend_midi(v);
564            }
565            UniversalNonRealTimeMsg::ScaleTuning2Byte(tuning) => {
566                v.push(0x8);
567                v.push(0x9);
568                tuning.extend_midi(v);
569            }
570            UniversalNonRealTimeMsg::GeneralMidi(gm) => {
571                v.push(0x9);
572                v.push(*gm as u8);
573            }
574            UniversalNonRealTimeMsg::FileReference(msg) => {
575                v.push(0xB);
576                match msg {
577                    FileReferenceMsg::Open { .. } => v.push(0x1),
578                    FileReferenceMsg::SelectContents { .. } => v.push(0x2),
579                    FileReferenceMsg::OpenSelectContents { .. } => v.push(0x3),
580                    FileReferenceMsg::Close { .. } => v.push(0x4),
581                }
582                msg.extend_midi(v);
583            }
584
585            UniversalNonRealTimeMsg::EOF => {
586                v.push(0x7B);
587                v.push(0x0);
588            }
589            UniversalNonRealTimeMsg::Wait => {
590                v.push(0x7C);
591                v.push(0x0);
592            }
593            UniversalNonRealTimeMsg::Cancel => {
594                v.push(0x7D);
595                v.push(0x0);
596            }
597            UniversalNonRealTimeMsg::NAK(packet_num) => {
598                v.push(0x7E);
599                v.push(to_u7(*packet_num));
600            }
601            UniversalNonRealTimeMsg::ACK(packet_num) => {
602                v.push(0x7F);
603                v.push(to_u7(*packet_num));
604            }
605        }
606    }
607
608    fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
609        if m.len() < 2 {
610            return Err(crate::ParseError::UnexpectedEnd);
611        }
612
613        match (m[0], m[1]) {
614            (0x6, 0x2) => {
615                if m.len() < 3 {
616                    return Err(crate::ParseError::UnexpectedEnd);
617                }
618                Ok(Self::IdentityReply(IdentityReply::from_midi(&m[2..])?))
619            }
620            _ => Err(ParseError::NotImplemented("UniversalNonRealTimeMsg")),
621        }
622    }
623}
624
625/// A response to [`UniversalNonRealTimeMsg::IdentityRequest`], meant to indicate the type of device
626/// that this message is sent from.
627/// Used by [`UniversalNonRealTimeMsg::IdentityReply`].
628#[derive(Debug, Copy, Clone, PartialEq, Eq)]
629pub struct IdentityReply {
630    pub id: ManufacturerID,
631    pub family: u16,
632    pub family_member: u16,
633    /// Four values, 0-127, sent in order provided
634    pub software_revision: (u8, u8, u8, u8),
635}
636
637impl IdentityReply {
638    fn extend_midi(&self, v: &mut Vec<u8>) {
639        self.id.extend_midi(v);
640        push_u14(self.family, v);
641        push_u14(self.family_member, v);
642        v.push(to_u7(self.software_revision.0));
643        v.push(to_u7(self.software_revision.1));
644        v.push(to_u7(self.software_revision.2));
645        v.push(to_u7(self.software_revision.3));
646    }
647
648    fn from_midi(m: &[u8]) -> Result<Self, ParseError> {
649        let (manufacturer_id, shift) = ManufacturerID::from_midi(m)?;
650        if m.len() < shift + 8 {
651            return Err(crate::ParseError::UnexpectedEnd);
652        }
653        Ok(IdentityReply {
654            id: manufacturer_id,
655            family: u14_from_midi(&m[shift..])?,
656            family_member: u14_from_midi(&m[(shift + 2)..])?,
657            software_revision: (m[shift + 4], m[shift + 5], m[shift + 6], m[shift + 7]),
658        })
659    }
660
661    /// Return the family + family member as bytes, as per many MIDI implementations docs
662    pub fn family_as_bytes(&self) -> [u8; 4] {
663        let [family_msb, family_lsb] = to_u14(self.family);
664        let [family_member_msb, family_member_lsb] = to_u14(self.family_member);
665        [family_lsb, family_msb, family_member_lsb, family_member_msb]
666    }
667}
668
669#[cfg(test)]
670mod tests {
671    use super::super::*;
672    use alloc::vec;
673
674    #[test]
675    fn serialize_system_exclusive_msg() {
676        assert_eq!(
677            MidiMsg::SystemExclusive {
678                msg: SystemExclusiveMsg::Commercial {
679                    id: 1.into(),
680                    data: vec![0xff, 0x77, 0x00]
681                }
682            }
683            .to_midi(),
684            vec![0xF0, 0x01, 0x7F, 0x77, 0x00, 0xF7]
685        );
686
687        assert_eq!(
688            MidiMsg::SystemExclusive {
689                msg: SystemExclusiveMsg::Commercial {
690                    id: (1, 3).into(),
691                    data: vec![0xff, 0x77, 0x00]
692                }
693            }
694            .to_midi(),
695            vec![0xF0, 0x00, 0x01, 0x03, 0x7F, 0x77, 0x00, 0xF7]
696        );
697
698        assert_eq!(
699            MidiMsg::SystemExclusive {
700                msg: SystemExclusiveMsg::NonCommercial {
701                    data: vec![0xff, 0x77, 0x00]
702                }
703            }
704            .to_midi(),
705            vec![0xF0, 0x7D, 0x7F, 0x77, 0x00, 0xF7]
706        );
707
708        assert_eq!(
709            MidiMsg::SystemExclusive {
710                msg: SystemExclusiveMsg::UniversalNonRealTime {
711                    device: DeviceID::AllCall,
712                    msg: UniversalNonRealTimeMsg::EOF
713                }
714            }
715            .to_midi(),
716            vec![0xF0, 0x7E, 0x7F, 0x7B, 0x00, 0xF7]
717        );
718
719        assert_eq!(
720            MidiMsg::SystemExclusive {
721                msg: SystemExclusiveMsg::UniversalRealTime {
722                    device: DeviceID::Device(3),
723                    msg: UniversalRealTimeMsg::MasterVolume(1000)
724                }
725            }
726            .to_midi(),
727            vec![0xF0, 0x7F, 0x03, 0x04, 0x01, 0x68, 0x07, 0xF7]
728        );
729    }
730
731    #[test]
732    fn deserialize_system_exclusive_msg() {
733        let mut ctx = ReceiverContext::new();
734
735        test_serialization(
736            MidiMsg::SystemExclusive {
737                msg: SystemExclusiveMsg::Commercial {
738                    id: 1.into(),
739                    data: vec![0x7f, 0x77, 0x00],
740                },
741            },
742            &mut ctx,
743        );
744
745        test_serialization(
746            MidiMsg::SystemExclusive {
747                msg: SystemExclusiveMsg::Commercial {
748                    id: (1, 3).into(),
749                    data: vec![0x7f, 0x77, 0x00],
750                },
751            },
752            &mut ctx,
753        );
754
755        test_serialization(
756            MidiMsg::SystemExclusive {
757                msg: SystemExclusiveMsg::NonCommercial {
758                    data: vec![0x7f, 0x77, 0x00],
759                },
760            },
761            &mut ctx,
762        );
763
764        test_serialization(
765            MidiMsg::SystemExclusive {
766                msg: SystemExclusiveMsg::UniversalRealTime {
767                    device: DeviceID::AllCall,
768                    msg: UniversalRealTimeMsg::TimeCodeFull(TimeCode {
769                        frames: 29,
770                        seconds: 58,
771                        minutes: 20,
772                        hours: 23,
773                        code_type: TimeCodeType::DF30,
774                    }),
775                },
776            },
777            &mut ctx,
778        );
779
780        assert_eq!(
781            ctx.time_code,
782            TimeCode {
783                frames: 29,
784                seconds: 58,
785                minutes: 20,
786                hours: 23,
787                code_type: TimeCodeType::DF30,
788            }
789        );
790    }
791}