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 {
|
impl From<Channel> for u8 {
|
||||||
fn into(self) -> u8 {
|
fn from(src:Channel) -> u8 {
|
||||||
self as u8
|
src as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
impl MidiMessage {
|
match value {
|
||||||
pub fn note_on(channel:Channel, note:Note, velocity:Velocity)
|
Message::NoteOn(chan,note,vel) => {
|
||||||
-> MidiMessage{
|
let payload = Payload::DoubleByte(note.into(),vel.into());
|
||||||
let channel : u8 = channel.into();
|
let status = NOTE_ON_MASK | u8::from(chan);
|
||||||
let note : u8 = note.into();
|
Raw { status, payload }
|
||||||
let velocity : u8 = velocity.into();
|
},
|
||||||
MidiMessage {
|
Message::NoteOff(chan,note,vel) => {
|
||||||
payload: [channel,note,velocity]
|
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 mod message;
|
||||||
|
pub mod raw;
|
||||||
pub use crate::data::midi::message::message::{MidiMessage};
|
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 {
|
pub enum Voice {
|
||||||
NoteOff(Channel,Note,Velocity),
|
NoteOff(Channel,Note,Velocity),
|
||||||
NoteOn(Channel,Note,Velocity),
|
NoteOn(Channel,Note,Velocity),
|
||||||
PolyPressure
|
PolyPressure(Channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Voice {
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
|
@ -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!()
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue