1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use crate::prelude::*;
use std::io::ErrorKind;
#[doc = r#"
A System Common Message, used to relay data for ALL receivers, regardless of channel.
"#]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum SystemCommonMessage<'a> {
/// A system-exclusive message.
///
/// System Exclusive events start with a `0xF0` byte and finish with a `0xF7` byte.
///
/// Note that `SystemExclusiveMessage` is found in both [`LiveEvent`]s and [`FileEvent`]s.
SystemExclusive(SystemExclusiveMessage<'a>),
/// An undefined System Common message
Undefined(StatusByte),
/// The number of MIDI beats (6 x MIDI clocks) that have elapsed since the start of the
/// sequence.
SongPositionPointer(SongPositionPointer),
/// Select a given song index.
SongSelect(u8),
/// Request the device to tune itself.
TuneRequest,
}
impl SystemCommonMessage<'_> {
fn status(&self) -> u8 {
use SystemCommonMessage::*;
match self {
SystemExclusive(_) => 0xF0,
SongPositionPointer { .. } => 0xF2,
SongSelect(_) => 0xF3,
TuneRequest => 0xF6,
Undefined(v) => v.byte(),
}
}
/// Represents the message as an array of bytes for some live MIDI stream
pub fn to_bytes(&self) -> Vec<u8> {
use SystemCommonMessage::*;
match self {
SystemExclusive(b) => b.to_live_bytes(),
SongPositionPointer(spp) => {
vec![self.status(), spp.lsb().value(), spp.msb().value()]
}
SongSelect(v) => vec![self.status(), *v],
TuneRequest | Undefined(_) => vec![self.status()],
}
}
}
impl FromLiveEventBytes for SystemCommonMessage<'_> {
const MIN_STATUS_BYTE: u8 = 0xF0;
const MAX_STATUS_BYTE: u8 = 0xF7;
fn from_status_and_data(status: u8, data: &[u8]) -> Result<Self, std::io::Error> {
let ev = match status {
0xF0 => {
//SystemExclusiveMessage
let data = data
.iter()
.copied()
.take_while(|byte| byte != &0xF7)
.collect::<Vec<_>>();
SystemCommonMessage::SystemExclusive(SystemExclusiveMessage::new(data))
}
/*0xF1 if data.len() >= 1 => {
//MTC Quarter Frame
SystemCommonMessage::MidiTimeCodeQuarterFrame {
message: MtcQuarterFrameMessage::from_bits(data[0] >> 4).unwrap(),
tag: data[0] & 0b0000_1111,
}
}*/
0xF2 if data.len() == 2 => {
//Song Position
SystemCommonMessage::SongPositionPointer(SongPositionPointer::new(
data[0], data[1],
)?)
}
0xF3 if data.len() == 1 => {
//Song Select
SystemCommonMessage::SongSelect(check_u7(data[0])?)
}
0xF6 => {
//Tune Request
SystemCommonMessage::TuneRequest
}
0xF1..=0xF5 if data.is_empty() => {
//Unknown system common event
SystemCommonMessage::Undefined(StatusByte::new(status)?)
}
_ => {
//Invalid/Unknown/Unreachable event
//(Including F7 SystemExclusiveMessage End Marker)
return Err(io_error!(
ErrorKind::InvalidInput,
"Could not read System Common Message"
));
}
};
Ok(ev)
}
}
/// The different kinds of info a Midi Time Code Quarter Frame message can carry.
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum MtcQuarterFrameMessage {
/// The low nibble of the frame count.
FramesLow,
/// The high nibble of the frame count.
FramesHigh,
/// The low nibble of the second count.
SecondsLow,
/// The high nibble of the second count.
SecondsHigh,
/// The low nibble of the minute count.
MinutesLow,
/// The high nibble of the minute count.
MinutesHigh,
/// The low nibble of the hour count.
HoursLow,
/// The high nibble of the hour count.
HoursHigh,
}
impl MtcQuarterFrameMessage {
/// Represents the message as a byte
pub fn as_byte(&self) -> u8 {
use MtcQuarterFrameMessage::*;
match self {
FramesLow => 0,
FramesHigh => 1,
SecondsLow => 2,
SecondsHigh => 3,
MinutesLow => 4,
MinutesHigh => 5,
HoursLow => 6,
HoursHigh => 7,
}
}
/// Creates a new message from a byte. This type always checks for correctness.
pub fn new(code: u8) -> Result<MtcQuarterFrameMessage, std::io::Error> {
use MtcQuarterFrameMessage::*;
Ok(match code {
0 => FramesLow,
1 => FramesHigh,
2 => SecondsLow,
3 => SecondsHigh,
4 => MinutesLow,
5 => MinutesHigh,
6 => HoursLow,
7 => HoursHigh,
_ => {
return Err(io_error!(
ErrorKind::InvalidData,
"Invalid MtcQuarterFrameMessage"
));
}
})
}
}