Skip to main content

midi_msg/system_exclusive/
machine_control.rs

1use crate::parse_error::*;
2use crate::time_code::*;
3use alloc::vec::Vec;
4
5/// A MIDI Machine Control Command.
6/// Used by [`UniversalRealTimeMsg::MachineControlCommand`](crate::UniversalRealTimeMsg::MachineControlCommand).
7///
8/// Only partially implemented. The `Unimplemented` value can be used to
9/// represent commands not supported here.
10///
11/// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013)
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum MachineControlCommandMsg {
15    Stop,
16    Play,
17    DeferredPlay,
18    FastForward,
19    Rewind,
20    RecordStrobe,
21    RecordExit,
22    RecordPause,
23    Pause,
24    Eject,
25    Chase,
26    CommandErrorReset,
27    MMCReset,
28    // Write(), TODO
29    /// Only `InformationField::GPO-GP7` are valid
30    LocateInformationField(InformationField),
31    LocateTarget(StandardTimeCode),
32    // Move(InformationField, InformationField), TODO
33    // Etc... TODO
34    Wait,
35    Resume,
36    /// Used to represent all unimplemented MCC messages.
37    /// Is inherently not guaranteed to be a valid message.
38    Unimplemented(Vec<u8>),
39}
40
41impl MachineControlCommandMsg {
42    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
43        match self {
44            Self::Stop => v.push(0x01),
45            Self::Play => v.push(0x02),
46            Self::DeferredPlay => v.push(0x03),
47            Self::FastForward => v.push(0x04),
48            Self::Rewind => v.push(0x05),
49            Self::RecordStrobe => v.push(0x06),
50            Self::RecordExit => v.push(0x07),
51            Self::RecordPause => v.push(0x08),
52            Self::Pause => v.push(0x09),
53            Self::Eject => v.push(0x0A),
54            Self::Chase => v.push(0x0B),
55            Self::CommandErrorReset => v.push(0x0C),
56            Self::MMCReset => v.push(0x0D),
57            Self::LocateInformationField(f) => {
58                v.push(0x44);
59                v.push(0x2); // Byte count
60                v.push(0x0); // Sub command
61                v.push(*f as u8);
62            }
63            Self::LocateTarget(stc) => {
64                v.push(0x44);
65                v.push(0x6); // Byte count
66                v.push(0x1); // Sub command
67                stc.extend_midi(v);
68            }
69            Self::Wait => v.push(0x01),
70            Self::Resume => v.push(0x01),
71            Self::Unimplemented(d) => v.extend_from_slice(d),
72        }
73    }
74
75    #[allow(dead_code)]
76    pub(crate) fn from_midi(_: &[u8]) -> Result<(Self, usize), ParseError> {
77        Err(ParseError::NotImplemented("MachineControlCommandMsg"))
78    }
79}
80
81/// A MIDI Machine Control Information Field, which functions something like an address
82///
83/// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013)
84#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum InformationField {
87    SelectedTimeCode = 0x01,
88    SelectedMasterCode = 0x02,
89    RequestedOffset = 0x03,
90    ActualOffset = 0x04,
91    LockDeviation = 0x05,
92    GeneratorTimeCode = 0x06,
93    MidiTimeCodeInput = 0x07,
94    GP0 = 0x08,
95    GP1 = 0x09,
96    GP2 = 0x0A,
97    GP3 = 0x0B,
98    GP4 = 0x0C,
99    GP5 = 0x0D,
100    GP6 = 0x0E,
101    GP7 = 0x0F,
102    // Etc.
103    // TODO
104}
105
106/// A MIDI Machine Control Response>
107/// Used by [`UniversalRealTimeMsg::MachineControlResponse`](crate::UniversalRealTimeMsg::MachineControlResponse).
108///
109/// Not implemented. The `Unimplemented` value can be used to represent generic responses.
110///
111/// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013)
112#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub enum MachineControlResponseMsg {
115    /// Used to represent all unimplemented MCR messages.
116    /// Is inherently not guaranteed to be a valid message.
117    Unimplemented(Vec<u8>),
118}
119
120impl MachineControlResponseMsg {
121    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
122        match self {
123            Self::Unimplemented(d) => v.extend_from_slice(d),
124        }
125    }
126
127    #[allow(dead_code)]
128    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
129        Err(ParseError::NotImplemented("MachineControlResponseMsg"))
130    }
131}
132
133#[doc(hidden)]
134/// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013)
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub struct StandardSpeed(pub f32);
137
138impl StandardSpeed {
139    #[allow(dead_code)]
140    pub(crate) fn extend_midi(&self, _: &mut Vec<u8>) {
141        todo!()
142    }
143}
144
145#[doc(hidden)]
146/// As defined in MIDI Machine Control 1.0 (MMA0016 / RP013)
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148pub struct StandardTrack {
149    pub video_active: bool,
150    pub time_code_active: bool,
151    pub time_code_track_active: bool,
152    pub aux_track_a_active: bool,
153    pub aux_track_b_active: bool,
154    pub track_1_active: bool,
155    pub track_2_active: bool,
156    pub other_tracks: Vec<bool>,
157}
158
159impl StandardTrack {
160    #[allow(dead_code)]
161    pub(crate) fn extend_midi(&self, _: &mut Vec<u8>) {
162        todo!()
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use crate::*;
169    use alloc::vec;
170
171    #[test]
172    fn serialize_machine_control_msg() {
173        assert_eq!(
174            MidiMsg::SystemExclusive {
175                msg: SystemExclusiveMsg::UniversalRealTime {
176                    device: DeviceID::AllCall,
177                    msg: UniversalRealTimeMsg::MachineControlCommand(
178                        MachineControlCommandMsg::Stop
179                    ),
180                },
181            }
182            .to_midi(),
183            vec![0xF0, 0x7F, 0x7f, 0x6, 0x1, 0xF7]
184        );
185
186        assert_eq!(
187            MidiMsg::SystemExclusive {
188                msg: SystemExclusiveMsg::UniversalRealTime {
189                    device: DeviceID::AllCall,
190                    msg: UniversalRealTimeMsg::MachineControlCommand(
191                        MachineControlCommandMsg::LocateTarget(StandardTimeCode {
192                            seconds: 0x20,
193                            code_type: TimeCodeType::FPS24, // Is 0
194                            ..Default::default()
195                        })
196                    ),
197                },
198            }
199            .to_midi(),
200            vec![
201                0xF0, 0x7F, 0x7f, // Call call
202                0x6,  // MCC
203                0x44, // Locate
204                0x06, // Bytes
205                0x1,  // Target
206                0x0, 0x0, 0x20, 0x0, 0x0, // Rest of MTC
207                0xF7
208            ]
209        );
210    }
211}