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