Skip to main content

midi_msg/system_exclusive/
notation.rs

1use crate::parse_error::*;
2use crate::util::*;
3use alloc::vec;
4use alloc::vec::Vec;
5
6/// Indicates that the next MIDI clock message is the first clock of a new measure. Which bar
7/// is optionally indicated by this message.
8/// Used by [`UniversalRealTimeMsg::BarMarker`](crate::UniversalRealTimeMsg::BarMarker).
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
11pub enum BarMarker {
12    /// "Actually, we're not running right now, so there is no bar." Don't know why this is used.
13    NotRunning,
14    /// The bar is a count-in and are thus negative numbers from 8191-0.
15    CountIn(u16), // ?
16    /// A regular bar numbered 1-8191.
17    Number(u16),
18    /// Next clock message will be a new bar, but it's not known what its number is.
19    RunningUnknown,
20}
21
22impl BarMarker {
23    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
24        match *self {
25            Self::NotRunning => {
26                // Most negative number
27                v.push(0x00);
28                v.push(0x40);
29            }
30            Self::CountIn(x) => {
31                push_i14(-(x.min(8191) as i16), v);
32            }
33            Self::Number(x) => {
34                push_i14(x.min(8191) as i16, v);
35            }
36            Self::RunningUnknown => {
37                // Most positive number
38                v.push(0x7F);
39                v.push(0x3F);
40            }
41        }
42    }
43
44    #[allow(dead_code)]
45    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
46        Err(ParseError::NotImplemented("BarMarker"))
47    }
48}
49
50/// Used to communicate a new time signature to the receiver.
51/// Used by [`UniversalRealTimeMsg`](crate::UniversalRealTimeMsg).
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct TimeSignature {
55    /// The base time signature.
56    pub signature: Signature,
57    /// How many MIDI clock events per metronome click.
58    /// 24 indicates one click per quarter note (unless specified otherwise by `thirty_second_notes_in_midi_quarter_note`)
59    pub midi_clocks_in_metronome_click: u8,
60    /// Number of notated 32nd notes in a MIDI quarter note.
61    /// 8 is the normal value (e.g. a midi quarter note is a quarter note)
62    pub thirty_second_notes_in_midi_quarter_note: u8,
63    /// At most 61 (!) additional times signatures for compound time definitions
64    pub compound: Vec<Signature>,
65}
66
67impl Default for TimeSignature {
68    fn default() -> Self {
69        Self {
70            signature: Default::default(),
71            midi_clocks_in_metronome_click: 24, // one click per quarter note
72            thirty_second_notes_in_midi_quarter_note: 8, // Midi quarter note is a quarter note
73            compound: vec![],
74        }
75    }
76}
77
78impl TimeSignature {
79    pub(crate) fn extend_midi(&self, v: &mut Vec<u8>) {
80        v.push((4 + (self.compound.len() * 2)).min(126) as u8); // Bytes to follow
81        self.signature.extend_midi(v);
82        v.push(to_u7(self.midi_clocks_in_metronome_click));
83        v.push(to_u7(self.thirty_second_notes_in_midi_quarter_note));
84        for (i, s) in self.compound.iter().enumerate() {
85            if i >= 61 {
86                break;
87            }
88            s.extend_midi(v);
89        }
90    }
91
92    #[allow(dead_code)]
93    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
94        Err(ParseError::NotImplemented("TimeSignature"))
95    }
96}
97
98/// A [time signature](https://en.wikipedia.org/wiki/Time_signature). Used by [`TimeSignature`].
99#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
100#[derive(Debug, Copy, Clone, PartialEq, Eq)]
101pub struct Signature {
102    /// Number of beats in a bar.
103    pub beats: u8,
104    /// The note value for each beat.
105    pub beat_value: BeatValue,
106}
107
108impl Signature {
109    fn extend_midi(&self, v: &mut Vec<u8>) {
110        v.push(to_u7(self.beats));
111        v.push(self.beat_value.to_u8());
112    }
113
114    #[allow(dead_code)]
115    pub(crate) fn from_midi(_m: &[u8]) -> Result<(Self, usize), ParseError> {
116        Err(ParseError::NotImplemented("Signature"))
117    }
118}
119
120impl Default for Signature {
121    fn default() -> Self {
122        Self {
123            beats: 4,
124            beat_value: BeatValue::Quarter,
125        }
126    }
127}
128
129/// The note value of a beat, used by [`Signature`].
130#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
131#[derive(Debug, Copy, Clone, PartialEq, Eq)]
132pub enum BeatValue {
133    Whole,
134    Half,
135    Quarter,
136    Eighth,
137    Sixteenth,
138    ThirtySecond,
139    SixtyFourth,
140    /// Any other value interpreted as 2^x.
141    /// The spec does not limit this value, so the maximum is a crazy 2^127
142    Other(u8),
143}
144
145impl BeatValue {
146    fn to_u8(self) -> u8 {
147        match self {
148            Self::Whole => 0,
149            Self::Half => 1,
150            Self::Quarter => 2,
151            Self::Eighth => 3,
152            Self::Sixteenth => 4,
153            Self::ThirtySecond => 5,
154            Self::SixtyFourth => 6,
155            Self::Other(x) => to_u7(x),
156        }
157    }
158
159    #[allow(dead_code)]
160    fn from_byte(_m: u8) -> Self {
161        // TODO
162        Self::Quarter
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use crate::*;
169    use alloc::vec;
170
171    #[test]
172    fn serialize_bar_marker() {
173        assert_eq!(
174            MidiMsg::SystemExclusive {
175                msg: SystemExclusiveMsg::UniversalRealTime {
176                    device: DeviceID::AllCall,
177                    msg: UniversalRealTimeMsg::BarMarker(BarMarker::NotRunning),
178                },
179            }
180            .to_midi(),
181            vec![0xF0, 0x7F, 0x7f, 0x3, 0x1, 0x00, 0x40, 0xF7]
182        );
183
184        assert_eq!(
185            MidiMsg::SystemExclusive {
186                msg: SystemExclusiveMsg::UniversalRealTime {
187                    device: DeviceID::AllCall,
188                    msg: UniversalRealTimeMsg::BarMarker(BarMarker::CountIn(1)),
189                },
190            }
191            .to_midi(),
192            vec![0xF0, 0x7F, 0x7f, 0x3, 0x1, 0x7f, 0x7f, 0xF7]
193        );
194
195        assert_eq!(
196            MidiMsg::SystemExclusive {
197                msg: SystemExclusiveMsg::UniversalRealTime {
198                    device: DeviceID::AllCall,
199                    msg: UniversalRealTimeMsg::BarMarker(BarMarker::Number(1)),
200                },
201            }
202            .to_midi(),
203            vec![0xF0, 0x7F, 0x7f, 0x3, 0x1, 0x01, 0x00, 0xF7]
204        );
205
206        assert_eq!(
207            MidiMsg::SystemExclusive {
208                msg: SystemExclusiveMsg::UniversalRealTime {
209                    device: DeviceID::AllCall,
210                    msg: UniversalRealTimeMsg::BarMarker(BarMarker::RunningUnknown),
211                },
212            }
213            .to_midi(),
214            vec![0xF0, 0x7F, 0x7f, 0x3, 0x1, 0x7f, 0x3f, 0xF7]
215        );
216    }
217
218    #[test]
219    fn serialize_time_signature() {
220        assert_eq!(
221            MidiMsg::SystemExclusive {
222                msg: SystemExclusiveMsg::UniversalRealTime {
223                    device: DeviceID::AllCall,
224                    msg: UniversalRealTimeMsg::TimeSignatureDelayed(TimeSignature::default()),
225                },
226            }
227            .to_midi(),
228            vec![0xF0, 0x7F, 0x7f, 0x3, 0x42, 4, 4, 2, 24, 8, 0xF7]
229        );
230
231        assert_eq!(
232            MidiMsg::SystemExclusive {
233                msg: SystemExclusiveMsg::UniversalRealTime {
234                    device: DeviceID::AllCall,
235                    msg: UniversalRealTimeMsg::TimeSignature(TimeSignature {
236                        compound: vec! {Signature {
237                            beats: 3,
238                            beat_value: BeatValue::Eighth,
239                        }},
240                        ..Default::default()
241                    }),
242                },
243            }
244            .to_midi(),
245            vec![0xF0, 0x7F, 0x7f, 0x3, 0x02, 6, 4, 2, 24, 8, 3, 3, 0xF7]
246        );
247    }
248}