1use std::error;
2use std::fmt;
3use std::convert::From;
4use std::io::{Error,Read};
5
6use num::FromPrimitive;
7
8use util::read_byte;
9
10#[derive(Debug)]
12pub enum MidiError {
13 InvalidStatus(u8),
14 OtherErr(&'static str),
15 Error(Error),
16}
17
18impl From<Error> for MidiError {
19 fn from(err: Error) -> MidiError {
20 MidiError::Error(err)
21 }
22}
23
24impl error::Error for MidiError {
25 fn description(&self) -> &str {
26 match *self {
27 MidiError::InvalidStatus(_) => "Midi data has invalid status byte",
28 MidiError::OtherErr(_) => "A general midi error has occured",
29 MidiError::Error(ref e) => e.description(),
30 }
31 }
32
33 fn cause(&self) -> Option<&error::Error> {
34 match *self {
35 MidiError::Error(ref err) => Some(err as &error::Error),
36 _ => None,
37 }
38 }
39}
40
41impl fmt::Display for MidiError {
42 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43 match *self {
44 MidiError::InvalidStatus(ref s) => write!(f,"Invalid Midi status: {}",s),
45 MidiError::OtherErr(ref s) => write!(f,"Midi Error: {}",s),
46 MidiError::Error(ref e) => write!(f,"{}",e),
47 }
48 }
49}
50
51enum_from_primitive! {
54#[derive(Debug,PartialEq,Clone,Copy)]
55pub enum Status {
56 NoteOff = 0x80,
58 NoteOn = 0x90,
59 PolyphonicAftertouch = 0xA0,
60 ControlChange = 0xB0,
61 ProgramChange = 0xC0,
62 ChannelAftertouch = 0xD0,
63 PitchBend = 0xE0,
64
65 SysExStart = 0xF0,
67 MIDITimeCodeQtrFrame = 0xF1,
68 SongPositionPointer = 0xF2,
69 SongSelect = 0xF3,
70 TuneRequest = 0xF6, SysExEnd = 0xF7,
72 TimingClock = 0xF8,
73 Start = 0xFA,
74 Continue = 0xFB,
75 Stop = 0xFC,
76 ActiveSensing = 0xFE, SystemReset = 0xFF,
78}
79}
80
81#[derive(Debug)]
85pub struct MidiMessage {
86 pub data: Vec<u8>,
87}
88
89impl Clone for MidiMessage {
90 fn clone(&self) -> MidiMessage {
91 MidiMessage {
92 data: self.data.clone()
93 }
94 }
95}
96
97static STATUS_MASK: u8 = 0xF0;
98static CHANNEL_MASK: u8 = 0x0F;
99
100impl MidiMessage {
101 pub fn status(&self) -> Status {
103 Status::from_u8(self.data[0] & STATUS_MASK).unwrap()
104 }
105
106 pub fn channel(&self) -> u8 {
108 (self.data[0] & CHANNEL_MASK) + 1
109 }
110
111 pub fn data(&self, index: usize) -> u8 {
114 self.data[index]
115 }
116
117 fn make_status(status: Status, channel: u8) -> u8 {
119 status as u8 | channel
120 }
121
122 pub fn from_bytes(bytes: Vec<u8>) -> MidiMessage{
124 MidiMessage {
126 data: bytes,
127 }
128 }
129
130 fn data_bytes(status: u8) -> isize {
135 match Status::from_u8(status & STATUS_MASK) {
136 Some(stat) => {
137 match stat {
138 Status::NoteOff |
139 Status::NoteOn |
140 Status::PolyphonicAftertouch |
141 Status::ControlChange |
142 Status::PitchBend |
143 Status::SongPositionPointer => { 2 }
144
145 Status::SysExStart => { -2 }
146
147 Status::ProgramChange |
148 Status::ChannelAftertouch |
149 Status::MIDITimeCodeQtrFrame |
150 Status::SongSelect => { 1 }
151
152 Status::TuneRequest |
153 Status::SysExEnd |
154 Status::TimingClock |
155 Status::Start |
156 Status::Continue |
157 Status::Stop |
158 Status::ActiveSensing |
159 Status::SystemReset => { 0 }
160 }
161 }
162 None => -3
163 }
164 }
165
166 pub fn next_message_given_status(stat: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
169 let mut ret:Vec<u8> = Vec::with_capacity(3);
170 ret.push(stat);
171 match MidiMessage::data_bytes(stat) {
172 0 => {}
173 1 => { ret.push(try!(read_byte(reader))); }
174 2 => { ret.push(try!(read_byte(reader)));
175 ret.push(try!(read_byte(reader))); }
176 -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
177 -2 => { return Err(MidiError::OtherErr("Don't handle sysex yet")); }
178 _ => { return Err(MidiError::InvalidStatus(stat)); }
179 }
180 Ok(MidiMessage{data: ret})
181 }
182
183 pub fn next_message_running_status(stat: u8, databyte: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
186 let mut ret:Vec<u8> = Vec::with_capacity(3);
187 ret.push(stat);
188 ret.push(databyte);
189 match MidiMessage::data_bytes(stat) {
190 0 => { panic!("Can't have zero length message with running status"); }
191 1 => { } 2 => { ret.push(try!(read_byte(reader))); } -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
194 -2 => { return Err(MidiError::OtherErr("Don't handle sysex yet")); }
195 _ => { return Err(MidiError::InvalidStatus(stat)); }
196 }
197 Ok(MidiMessage{data: ret})
198 }
199
200 pub fn next_message(reader: &mut Read) -> Result<MidiMessage,MidiError> {
202 let stat = try!(read_byte(reader));
203 MidiMessage::next_message_given_status(stat,reader)
204 }
205
206
207 pub fn note_on(note: u8, velocity: u8, channel: u8) -> MidiMessage {
211 MidiMessage {
212 data: vec![MidiMessage::make_status(Status::NoteOn,channel), note, velocity],
213 }
214 }
215
216 pub fn note_off(note: u8, velocity: u8, channel: u8) -> MidiMessage {
218 MidiMessage {
219 data: vec![MidiMessage::make_status(Status::NoteOff,channel), note, velocity],
220 }
221 }
222
223 pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8) -> MidiMessage {
226 MidiMessage {
227 data: vec![MidiMessage::make_status(Status::PolyphonicAftertouch,channel), note, pressure],
228 }
229 }
230
231 pub fn control_change(controler: u8, data: u8, channel: u8) -> MidiMessage {
235 MidiMessage {
236 data: vec![MidiMessage::make_status(Status::ControlChange,channel), controler, data],
237 }
238 }
239
240 pub fn program_change(program: u8, channel: u8) -> MidiMessage {
243 MidiMessage {
244 data: vec![MidiMessage::make_status(Status::ProgramChange,channel), program],
245 }
246 }
247
248 pub fn channel_aftertouch(pressure: u8, channel: u8) -> MidiMessage {
253 MidiMessage {
254 data: vec![MidiMessage::make_status(Status::ChannelAftertouch,channel), pressure],
255 }
256 }
257
258 pub fn pitch_bend(lsb: u8, msb: u8, channel: u8) -> MidiMessage {
264 MidiMessage {
265 data: vec![MidiMessage::make_status(Status::PitchBend,channel), lsb, msb],
266 }
267 }
268
269}
270
271impl fmt::Display for Status {
272 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
273 write!(f, "{}",
274 match *self {
275 Status::NoteOff => "Note Off",
276 Status::NoteOn => "Note On",
277 Status::PolyphonicAftertouch => "Polyphonic Aftertouch",
278 Status::ControlChange => "Control Change",
279 Status::ProgramChange => "Program Change",
280 Status::ChannelAftertouch => "Channel Aftertouch",
281 Status::PitchBend => "Pitch Bend",
282 Status::SysExStart => "SysEx Start",
283 Status::MIDITimeCodeQtrFrame => "MIDI Time Code Qtr Frame",
284 Status::SongPositionPointer => "Song Position Pointer",
285 Status::SongSelect => "Song Select",
286 Status::TuneRequest => "Tune Request",
287 Status::SysExEnd => "SysEx End",
288 Status::TimingClock => "Timing Clock",
289 Status::Start => "Start",
290 Status::Continue => "Continue",
291 Status::Stop => "Stop",
292 Status::ActiveSensing => "Active Sensing",
293 Status::SystemReset => "System Reset",
294 })
295 }
296}
297
298impl fmt::Display for MidiMessage {
299 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300 if self.data.len() == 2 {
301 write!(f, "{}: [{}]\tchannel: {}", self.status(),self.data[1],self.channel())
302 }
303 else if self.data.len() == 3 {
304 write!(f, "{}: [{},{}]\tchannel: {}", self.status(),self.data[1],self.data[2],self.channel())
305 }
306 else {
307 write!(f, "{}: [no data]\tchannel: {}", self.status(),self.channel())
308 }
309 }
310}