mirror of
https://github.com/italicsjenga/usbd-midi.git
synced 2024-12-23 12:21:30 +11:00
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:
parent
1b0be0ba49
commit
6b7f71f7bd
1
src/data/byte/mod.rs
Normal file
1
src/data/byte/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod u7;
|
30
src/data/byte/u7.rs
Normal file
30
src/data/byte/u7.rs
Normal 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);
|
||||
}
|
|
@ -40,9 +40,9 @@ impl TryFrom<u8> for Channel {
|
|||
|
||||
}
|
||||
|
||||
impl Into<u8> for Channel {
|
||||
fn into(self) -> u8 {
|
||||
self as u8
|
||||
impl From<Channel> for u8 {
|
||||
fn from(src:Channel) -> u8 {
|
||||
src as u8
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<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 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
pub mod channel;
|
||||
pub mod voice;
|
||||
pub mod message;
|
||||
|
||||
pub use crate::data::midi::message::message::{MidiMessage};
|
||||
pub mod raw;
|
||||
pub use crate::data::midi::message::message::{Message};
|
16
src/data/midi/message/raw.rs
Normal file
16
src/data/midi/message/raw.rs
Normal 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
|
||||
}
|
|
@ -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 {
|
||||
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod midi;
|
||||
pub mod usb;
|
||||
pub mod usb_midi;
|
||||
pub mod byte;
|
|
@ -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!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue