From 6b7f71f7bd7455cacc3e8c6a0084f75d2c5ebdfe Mon Sep 17 00:00:00 2001 From: beau trepp Date: Mon, 16 Dec 2019 19:25:17 +0800 Subject: [PATCH] Adds midi message data structures This implements a partial implementation of the midi messages, with the view we can expand later on. Note that we introduce a datatype to represent the different kinds of midi messages that may be sent, of various lengths. --- src/data/byte/mod.rs | 1 + src/data/byte/u7.rs | 30 +++++++ src/data/midi/channel.rs | 6 +- src/data/midi/message/message.rs | 89 +++++++++++-------- src/data/midi/message/mod.rs | 6 +- src/data/midi/message/raw.rs | 16 ++++ src/data/midi/message/system.rs | 0 .../midi/message/{channel.rs => voice.rs} | 7 +- src/data/mod.rs | 1 + src/data/usb_midi/usb_midi_event_packet.rs | 25 +++--- 10 files changed, 127 insertions(+), 54 deletions(-) create mode 100644 src/data/byte/mod.rs create mode 100644 src/data/byte/u7.rs create mode 100644 src/data/midi/message/raw.rs delete mode 100644 src/data/midi/message/system.rs rename src/data/midi/message/{channel.rs => voice.rs} (82%) diff --git a/src/data/byte/mod.rs b/src/data/byte/mod.rs new file mode 100644 index 0000000..ff07688 --- /dev/null +++ b/src/data/byte/mod.rs @@ -0,0 +1 @@ +pub mod u7; \ No newline at end of file diff --git a/src/data/byte/u7.rs b/src/data/byte/u7.rs new file mode 100644 index 0000000..bd6d244 --- /dev/null +++ b/src/data/byte/u7.rs @@ -0,0 +1,30 @@ +use core::convert::TryFrom; + +/// A primitive value that can be from 0-0x7F +pub struct U7(u8); + +/// Error representing that this value is not a valid u7 +pub struct InvalidU7(u8); + +impl TryFrom for U7{ + type Error = InvalidU7; + + fn try_from(value:u8) -> Result { + if value > 0x7F { + Err(InvalidU7(value)) + } else { + Ok(U7(value)) + } + } +} + +impl From for u8 { + fn from(value:U7) -> u8 { + value.0 + } +} + +impl U7 { + pub const MAX: U7= U7(0x7F); + pub const MIN: U7 = U7(0); +} \ No newline at end of file diff --git a/src/data/midi/channel.rs b/src/data/midi/channel.rs index ffabae3..6b6fc97 100644 --- a/src/data/midi/channel.rs +++ b/src/data/midi/channel.rs @@ -40,9 +40,9 @@ impl TryFrom for Channel { } -impl Into for Channel { - fn into(self) -> u8 { - self as u8 +impl From for u8 { + fn from(src:Channel) -> u8 { + src as u8 } } diff --git a/src/data/midi/message/message.rs b/src/data/midi/message/message.rs index 3c25af9..bae35d5 100644 --- a/src/data/midi/message/message.rs +++ b/src/data/midi/message/message.rs @@ -2,46 +2,63 @@ use crate::data::midi::channel::Channel; use crate::data::midi::velocity::Velocity; use crate::data::midi::notes::Note; +use crate::data::byte::u7::U7; +use crate::data::midi::message::raw::{Raw,Payload}; -pub enum Mode {Mode} - -pub enum RealTime {} -pub enum Common {} -pub enum SysEx {} - -pub enum System { - RealTime(RealTime), - Common(Common), - SysEx(SysEx) -} - +/// Represents midi messages +/// Note: not current exhaustive and SysEx messages end up +/// being a confusing case. So are currently note implemented +/// they are sort-of unbounded pub enum Message { - Channel(Channel), - System(System) + NoteOff(Channel,Note,Velocity), + NoteOn(Channel,Note,Velocity), + PolyphonicAftertouch(Channel,Note,U7), + ProgramChange(Channel,U7), + ChannelAftertouch(Channel,U7), + PitchWheelChange(Channel,U7,U7), } -pub struct MidiMessage { - payload: [u8;3] -} +const NOTE_OFF_MASK :u8 = 0b1000_0000; +const NOTE_ON_MASK :u8 = 0b1001_0000; +const POLYPHONIC_MASK :u8 = 0b1010_0000; +const PROGRAM_MASK :u8 = 0b1100_0000; +const CHANNEL_AFTERTOUCH_MASK :u8 = 0b1101_0000; +const PITCH_BEND_MASK :u8 = 0b1110_0000; - - -impl MidiMessage { - pub fn note_on(channel:Channel, note:Note, velocity:Velocity) - -> MidiMessage{ - let channel : u8 = channel.into(); - let note : u8 = note.into(); - let velocity : u8 = velocity.into(); - MidiMessage { - payload: [channel,note,velocity] +impl From for Raw { + fn from(value:Message) -> Raw { + match value { + Message::NoteOn(chan,note,vel) => { + let payload = Payload::DoubleByte(note.into(),vel.into()); + let status = NOTE_ON_MASK | u8::from(chan); + Raw { status, payload } + }, + Message::NoteOff(chan,note,vel) => { + let payload = Payload::DoubleByte(note.into(),vel.into()); + let status = NOTE_OFF_MASK | u8::from(chan); + Raw {status, payload} + }, + Message::PolyphonicAftertouch(chan,note,pressure) => { + let payload = Payload::DoubleByte(note.into(),pressure.into()); + let status = POLYPHONIC_MASK | u8::from(chan); + Raw {status, payload} + }, + Message::ProgramChange(chan,program) => { + let payload = Payload::SingleByte(u8::from(program)); + let status = PROGRAM_MASK | u8::from(chan); + Raw {status, payload} + }, + Message::ChannelAftertouch(chan,pressure) => { + let payload = Payload::SingleByte(u8::from(pressure)); + let status = CHANNEL_AFTERTOUCH_MASK | u8::from(chan); + Raw {status, payload} + }, + Message::PitchWheelChange(chan,lsb,msb) => { + let payload = Payload::DoubleByte(u8::from(lsb),u8::from(msb)); + let status = PITCH_BEND_MASK | u8::from(chan); + Raw {status , payload} + } + } } -} - -impl Into<[u8;3]> for MidiMessage { - /// Converts the midi packet into a byte array - /// suitable for transfer via usb - fn into(self) -> [u8;3] { - self.payload - } -} +} \ No newline at end of file diff --git a/src/data/midi/message/mod.rs b/src/data/midi/message/mod.rs index 7043249..af3f77a 100644 --- a/src/data/midi/message/mod.rs +++ b/src/data/midi/message/mod.rs @@ -1,4 +1,4 @@ -pub mod channel; +pub mod voice; pub mod message; - -pub use crate::data::midi::message::message::{MidiMessage}; \ No newline at end of file +pub mod raw; +pub use crate::data::midi::message::message::{Message}; \ No newline at end of file diff --git a/src/data/midi/message/raw.rs b/src/data/midi/message/raw.rs new file mode 100644 index 0000000..3384794 --- /dev/null +++ b/src/data/midi/message/raw.rs @@ -0,0 +1,16 @@ +/// Represents the payloads that the midi message may contain +pub enum Payload { + Empty, + SingleByte(u8), + DoubleByte(u8,u8) +} + +/// A struct that captures the valid states +/// a midi message may be in, but without domain logic +/// mainly useful for serializing. +/// This represents the possible 'shapes', doesn't verify if +/// the data makes sense though! +pub struct Raw { + pub status: u8, + pub payload: Payload +} diff --git a/src/data/midi/message/system.rs b/src/data/midi/message/system.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/data/midi/message/channel.rs b/src/data/midi/message/voice.rs similarity index 82% rename from src/data/midi/message/channel.rs rename to src/data/midi/message/voice.rs index 21255ae..234d3bb 100644 --- a/src/data/midi/message/channel.rs +++ b/src/data/midi/message/voice.rs @@ -6,5 +6,10 @@ pub use crate::data::midi::velocity::Velocity; pub enum Voice { NoteOff(Channel,Note,Velocity), NoteOn(Channel,Note,Velocity), - PolyPressure + PolyPressure(Channel) } + + +impl Voice { + +} \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index b55381c..554cf39 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1,3 +1,4 @@ pub mod midi; pub mod usb; pub mod usb_midi; +pub mod byte; \ No newline at end of file diff --git a/src/data/usb_midi/usb_midi_event_packet.rs b/src/data/usb_midi/usb_midi_event_packet.rs index 66e3d06..6487f0f 100644 --- a/src/data/usb_midi/usb_midi_event_packet.rs +++ b/src/data/usb_midi/usb_midi_event_packet.rs @@ -2,9 +2,10 @@ use crate::data::usb_midi::cable_number::CableNumber; use crate::data::midi::code_index_number::CodeIndexNumber; use crate::data::midi::notes::Note; use crate::data::midi::channel::Channel; -use crate::data::midi::message::MidiMessage; +use crate::data::midi::message::Message; use crate::data::midi::velocity::Velocity; use crate::util::nibble::{combine_nibble}; +use crate::data::midi::message::raw::Raw; /// A packet that communicates with the host /// Note that the payload seems fairly 'open' @@ -12,8 +13,9 @@ use crate::util::nibble::{combine_nibble}; /// but may not!? pub struct UsbMidiEventPacket { cable_number : CableNumber, - code_index_number: CodeIndexNumber, - message: MidiMessage + code_index_number: CodeIndexNumber, //Is this strictly necessary + //if can be built from the midi message? + message: Message } /// Constructs a note-on midi message given the cable, note and velocity @@ -21,7 +23,9 @@ pub fn note_on( cable:CableNumber, channel: Channel, note:Note, velocity: Velocity) -> UsbMidiEventPacket { - let message = MidiMessage::note_on(channel,note,velocity); + + + let message = Message::NoteOn(channel,note,velocity); UsbMidiEventPacket{ cable_number : cable, @@ -31,17 +35,16 @@ pub fn note_on( cable:CableNumber, } impl Into<[u8;4]> for UsbMidiEventPacket { /// Converts the midi packet into a byte array - /// suitable for transfer via usb + /// suitable for transfer via usbgit fn into(self) -> [u8;4] { let cable_number : u8 = self.cable_number.into(); let index_number : u8 = self.code_index_number.into(); let header = combine_nibble(cable_number,index_number); - let payload : [u8;3]= self.message.into(); - [ header, - payload[0], - payload[1], - payload[2] - ] + + let raw : Raw = self.message.into(); + let status : u8 = raw.status.into(); + + panic!() } }