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