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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
use std::error;
use std::fmt;
use std::convert::From;
use std::io::{Error,Read};

use num::FromPrimitive;

use util::read_byte;

/// An error that can occur trying to parse a midi message
#[derive(Debug)]
pub enum MidiError {
    InvalidStatus(u8),
    OtherErr(&'static str),
    Error(Error),
}

impl From<Error> for MidiError {
    fn from(err: Error) -> MidiError {
        MidiError::Error(err)
    }
}

impl error::Error for MidiError {
    fn description(&self) -> &str {
        match *self {
            MidiError::InvalidStatus(_) => "Midi data has invalid status byte",
            MidiError::OtherErr(_) => "A general midi error has occured",
            MidiError::Error(ref e) => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            MidiError::Error(ref err) => Some(err as &error::Error),
            _ => None,
        }
    }
}

impl fmt::Display for MidiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MidiError::InvalidStatus(ref s) => write!(f,"Invalid Midi status: {}",s),
            MidiError::OtherErr(ref s) => write!(f,"Midi Error: {}",s),
            MidiError::Error(ref e) => write!(f,"{}",e),
        }
    }
}

/// The status field of a midi message indicates what midi command it
/// represents and what channel it is on
enum_from_primitive! {
#[derive(Debug,PartialEq,Clone,Copy)]
pub enum Status {
    // voice
    NoteOff = 0x80,
    NoteOn = 0x90,
    PolyphonicAftertouch = 0xA0,
    ControlChange = 0xB0,
    ProgramChange = 0xC0,
    ChannelAftertouch = 0xD0,
    PitchBend = 0xE0,

    // sysex
    SysExStart = 0xF0,
    MIDITimeCodeQtrFrame = 0xF1,
    SongPositionPointer = 0xF2,
    SongSelect = 0xF3,
    TuneRequest = 0xF6, // F4 anf 5 are reserved and unused
    SysExEnd = 0xF7,
    TimingClock = 0xF8,
    Start = 0xFA,
    Continue = 0xFB,
    Stop = 0xFC,
    ActiveSensing = 0xFE, // FD also res/unused
    SystemReset = 0xFF,
}
}

/// Midi message building and parsing.  See
/// http://www.midi.org/techspecs/midimessages.php for a description
/// of the various Midi messages that exist.
#[derive(Debug)]
pub struct MidiMessage {
    pub data: Vec<u8>,
}

impl Clone for MidiMessage {
    fn clone(&self) -> MidiMessage {
        MidiMessage {
            data: self.data.clone()
        }
    }
}

static STATUS_MASK: u8 = 0xF0;
static CHANNEL_MASK: u8 = 0x0F;

impl MidiMessage {
    /// Return the status (type) of this message
    pub fn status(&self) -> Status {
        Status::from_u8(self.data[0] & STATUS_MASK).unwrap()
    }

    /// Return the channel this message is on (TODO: return 0 for messages with no channel)
    pub fn channel(&self) -> u8 {
        (self.data[0] & CHANNEL_MASK) + 1
    }

    /// Get te data at index `index` from this message.  Status is at
    /// index 0
    pub fn data(&self, index: usize) -> u8 {
        self.data[index]
    }

    // Or in the channel bits to a status
    fn make_status(status: Status, channel: u8) -> u8 {
        status as u8 | channel
    }

    /// Create a midi message from a vector of bytes
    pub fn from_bytes(bytes: Vec<u8>) -> MidiMessage{
        // TODO: Validate bytes
        MidiMessage {
            data: bytes,
        }
    }

    // return the number of data bytes for a message with the given status
    // -1 -> variable sized message, call get_variable_size
    // -2 -> sysex, read until SysExEnd
    // -3 -> invalid status
    fn data_bytes(status: u8) -> isize {
        match Status::from_u8(status & STATUS_MASK) {
            Some(stat) => {
                match stat {
                    Status::NoteOff |
                    Status::NoteOn |
                    Status::PolyphonicAftertouch |
                    Status::ControlChange |
                    Status::PitchBend |
                    Status::SongPositionPointer => { 2 }

                    Status::SysExStart => { -2 }

                    Status::ProgramChange |
                    Status::ChannelAftertouch |
                    Status::MIDITimeCodeQtrFrame |
                    Status::SongSelect => { 1 }

                    Status::TuneRequest |
                    Status::SysExEnd |
                    Status::TimingClock |
                    Status::Start |
                    Status::Continue |
                    Status::Stop |
                    Status::ActiveSensing |
                    Status::SystemReset => { 0 }
                }
            }
            None => -3
        }
    }

    /// Get the next midi message from the reader given that the
    /// status `stat` has just been read
    pub fn next_message_given_status(stat: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
        let mut ret:Vec<u8> = Vec::with_capacity(3);
        ret.push(stat);
        match MidiMessage::data_bytes(stat) {
            0 => {}
            1 => { ret.push(try!(read_byte(reader))); }
            2 => { ret.push(try!(read_byte(reader)));
                   ret.push(try!(read_byte(reader))); }
            -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
            -2 => { return Err(MidiError::OtherErr("Don't handle sysex yet")); }
            _ =>  { return Err(MidiError::InvalidStatus(stat)); }
        }
        Ok(MidiMessage{data: ret})
    }

    /// Get the next midi message from the reader given that there's a running
    /// status of `stat` and that in place of a status was read `databyte`
    pub fn next_message_running_status(stat: u8, databyte: u8, reader: &mut Read) -> Result<MidiMessage, MidiError> {
        let mut ret:Vec<u8> = Vec::with_capacity(3);
        ret.push(stat);
        ret.push(databyte);
        match MidiMessage::data_bytes(stat) {
            0 => { panic!("Can't have zero length message with running status"); }
            1 => { } // already read it
            2 => { ret.push(try!(read_byte(reader))); } // only need one more byte
            -1 => { return Err(MidiError::OtherErr("Don't handle variable sized yet")); }
            -2 => { return Err(MidiError::OtherErr("Don't handle sysex yet")); }
            _ =>  { return Err(MidiError::InvalidStatus(stat)); }
        }
        Ok(MidiMessage{data: ret})
    }

    /// Extract next midi message from a reader
    pub fn next_message(reader: &mut Read) -> Result<MidiMessage,MidiError> {
        let stat = try!(read_byte(reader));
        MidiMessage::next_message_given_status(stat,reader)
    }


    // Functions to build midi messages

    /// Create a note on message
    pub fn note_on(note: u8, velocity: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::NoteOn,channel), note, velocity],
        }
    }

    /// Create a note off message
    pub fn note_off(note: u8, velocity: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::NoteOff,channel), note, velocity],
        }
    }

    /// Create a polyphonic aftertouch message
    /// This message is most often sent by pressing down on the key after it "bottoms out".
    pub fn polyphonic_aftertouch(note: u8, pressure: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::PolyphonicAftertouch,channel), note, pressure],
        }
    }

    /// Create a control change message
    /// This message is sent when a controller value changes. Controllers include devices such as
    /// pedals and levers. Controller numbers 120-127 are reserved as "Channel Mode Messages".
    pub fn control_change(controler: u8, data: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ControlChange,channel), controler, data],
        }
    }

    /// Create a program change message
    /// This message sent when the patch number changes. `program` is the new program number.
    pub fn program_change(program: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ProgramChange,channel), program],
        }
    }

    /// Create a channel aftertouch
    /// This message is most often sent by pressing down on the key after it "bottoms out". This message
    /// is different from polyphonic after-touch. Use this message to send the single greatest pressure
    /// value (of all the current depressed keys). `pressure` is the pressure value.
    pub fn channel_aftertouch(pressure: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::ChannelAftertouch,channel), pressure],
        }
    }

    /// Create a pitch bench message
    /// This message is sent to indicate a change in the pitch bender (wheel or lever, typically).
    /// The pitch bender is measured by a fourteen bit value. Center (no pitch change) is 2000H.
    /// Sensitivity is a function of the transmitter. `lsb` are the least significant 7 bits.
    /// `msb` are the most significant 7 bits.
    pub fn pitch_bend(lsb: u8, msb: u8, channel: u8) -> MidiMessage {
        MidiMessage {
            data: vec![MidiMessage::make_status(Status::PitchBend,channel), lsb, msb],
        }
    }

}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}",
               match *self {
                   Status::NoteOff => "Note Off",
                   Status::NoteOn => "Note On",
                   Status::PolyphonicAftertouch => "Polyphonic Aftertouch",
                   Status::ControlChange => "Control Change",
                   Status::ProgramChange => "Program Change",
                   Status::ChannelAftertouch => "Channel Aftertouch",
                   Status::PitchBend => "Pitch Bend",
                   Status::SysExStart => "SysEx Start",
                   Status::MIDITimeCodeQtrFrame => "MIDI Time Code Qtr Frame",
                   Status::SongPositionPointer => "Song Position Pointer",
                   Status::SongSelect => "Song Select",
                   Status::TuneRequest => "Tune Request",
                   Status::SysExEnd => "SysEx End",
                   Status::TimingClock => "Timing Clock",
                   Status::Start => "Start",
                   Status::Continue => "Continue",
                   Status::Stop => "Stop",
                   Status::ActiveSensing => "Active Sensing",
                   Status::SystemReset => "System Reset",
               })
    }
}

impl fmt::Display for MidiMessage {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.data.len() == 2 {
            write!(f, "{}: [{}]\tchannel: {}", self.status(),self.data[1],self.channel())
        }
        else if self.data.len() == 3 {
            write!(f, "{}: [{},{}]\tchannel: {}", self.status(),self.data[1],self.data[2],self.channel())
        }
        else {
            write!(f, "{}: [no data]\tchannel: {}", self.status(),self.channel())
        }
    }
}