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.
This commit is contained in:
beau trepp 2019-12-16 19:25:17 +08:00
parent 1b0be0ba49
commit 6b7f71f7bd
10 changed files with 127 additions and 54 deletions

1
src/data/byte/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod u7;

30
src/data/byte/u7.rs Normal file
View file

@ -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<u8> for U7{
type Error = InvalidU7;
fn try_from(value:u8) -> Result<Self,Self::Error> {
if value > 0x7F {
Err(InvalidU7(value))
} else {
Ok(U7(value))
}
}
}
impl From<U7> for u8 {
fn from(value:U7) -> u8 {
value.0
}
}
impl U7 {
pub const MAX: U7= U7(0x7F);
pub const MIN: U7 = U7(0);
}

View file

@ -40,9 +40,9 @@ impl TryFrom<u8> for Channel {
} }
impl Into<u8> for Channel { impl From<Channel> for u8 {
fn into(self) -> u8 { fn from(src:Channel) -> u8 {
self as u8 src as u8
} }
} }

View file

@ -2,46 +2,63 @@
use crate::data::midi::channel::Channel; use crate::data::midi::channel::Channel;
use crate::data::midi::velocity::Velocity; use crate::data::midi::velocity::Velocity;
use crate::data::midi::notes::Note; use crate::data::midi::notes::Note;
use crate::data::byte::u7::U7;
use crate::data::midi::message::raw::{Raw,Payload};
pub enum Mode {Mode} /// Represents midi messages
/// Note: not current exhaustive and SysEx messages end up
pub enum RealTime {} /// being a confusing case. So are currently note implemented
pub enum Common {} /// they are sort-of unbounded
pub enum SysEx {}
pub enum System {
RealTime(RealTime),
Common(Common),
SysEx(SysEx)
}
pub enum Message { pub enum Message {
Channel(Channel), NoteOff(Channel,Note,Velocity),
System(System) NoteOn(Channel,Note,Velocity),
PolyphonicAftertouch(Channel,Note,U7),
ProgramChange(Channel,U7),
ChannelAftertouch(Channel,U7),
PitchWheelChange(Channel,U7,U7),
} }
pub struct MidiMessage { const NOTE_OFF_MASK :u8 = 0b1000_0000;
payload: [u8;3] 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 From<Message> 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 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 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
}
}

View file

@ -1,4 +1,4 @@
pub mod channel; pub mod voice;
pub mod message; pub mod message;
pub mod raw;
pub use crate::data::midi::message::message::{MidiMessage}; pub use crate::data::midi::message::message::{Message};

View file

@ -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
}

View file

@ -6,5 +6,10 @@ pub use crate::data::midi::velocity::Velocity;
pub enum Voice { pub enum Voice {
NoteOff(Channel,Note,Velocity), NoteOff(Channel,Note,Velocity),
NoteOn(Channel,Note,Velocity), NoteOn(Channel,Note,Velocity),
PolyPressure PolyPressure(Channel)
}
impl Voice {
} }

View file

@ -1,3 +1,4 @@
pub mod midi; pub mod midi;
pub mod usb; pub mod usb;
pub mod usb_midi; pub mod usb_midi;
pub mod byte;

View file

@ -2,9 +2,10 @@ use crate::data::usb_midi::cable_number::CableNumber;
use crate::data::midi::code_index_number::CodeIndexNumber; use crate::data::midi::code_index_number::CodeIndexNumber;
use crate::data::midi::notes::Note; use crate::data::midi::notes::Note;
use crate::data::midi::channel::Channel; 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::data::midi::velocity::Velocity;
use crate::util::nibble::{combine_nibble}; use crate::util::nibble::{combine_nibble};
use crate::data::midi::message::raw::Raw;
/// A packet that communicates with the host /// A packet that communicates with the host
/// Note that the payload seems fairly 'open' /// Note that the payload seems fairly 'open'
@ -12,8 +13,9 @@ use crate::util::nibble::{combine_nibble};
/// but may not!? /// but may not!?
pub struct UsbMidiEventPacket { pub struct UsbMidiEventPacket {
cable_number : CableNumber, cable_number : CableNumber,
code_index_number: CodeIndexNumber, code_index_number: CodeIndexNumber, //Is this strictly necessary
message: MidiMessage //if can be built from the midi message?
message: Message
} }
/// Constructs a note-on midi message given the cable, note and velocity /// Constructs a note-on midi message given the cable, note and velocity
@ -21,7 +23,9 @@ pub fn note_on( cable:CableNumber,
channel: Channel, channel: Channel,
note:Note, note:Note,
velocity: Velocity) -> UsbMidiEventPacket { velocity: Velocity) -> UsbMidiEventPacket {
let message = MidiMessage::note_on(channel,note,velocity);
let message = Message::NoteOn(channel,note,velocity);
UsbMidiEventPacket{ UsbMidiEventPacket{
cable_number : cable, cable_number : cable,
@ -31,17 +35,16 @@ pub fn note_on( cable:CableNumber,
} }
impl Into<[u8;4]> for UsbMidiEventPacket { impl Into<[u8;4]> for UsbMidiEventPacket {
/// Converts the midi packet into a byte array /// Converts the midi packet into a byte array
/// suitable for transfer via usb /// suitable for transfer via usbgit
fn into(self) -> [u8;4] { fn into(self) -> [u8;4] {
let cable_number : u8 = self.cable_number.into(); let cable_number : u8 = self.cable_number.into();
let index_number : u8 = self.code_index_number.into(); let index_number : u8 = self.code_index_number.into();
let header = combine_nibble(cable_number,index_number); let header = combine_nibble(cable_number,index_number);
let payload : [u8;3]= self.message.into();
[ header, let raw : Raw = self.message.into();
payload[0], let status : u8 = raw.status.into();
payload[1],
payload[2] panic!()
]
} }
} }