Simplifies more midi domain types

The usb interface now only allows you to send midi messages,
created via fairly restrictive structs

It should be very difficult to create an invalid midi message now.
This commit is contained in:
beau trepp 2019-12-17 19:19:27 +08:00
parent 6b7f71f7bd
commit 55d94f7024
10 changed files with 101 additions and 82 deletions

View file

@ -1 +1,2 @@
pub mod u7; pub mod u7;
pub mod u4;

46
src/data/byte/u4.rs Normal file
View file

@ -0,0 +1,46 @@
use core::convert::TryFrom;
/// A primitive value that can be from 0-0x7F
pub struct U4(u8);
/// Error representing that this value is not a valid u4
pub struct InvalidU4(u8);
impl TryFrom<u8> for U4{
type Error = InvalidU4;
fn try_from(value:u8) -> Result<Self,Self::Error> {
if value > U4::MAX.0 {
Err(InvalidU4(value))
} else {
Ok(U4(value))
}
}
}
impl From<U4> for u8 {
fn from(value:U4) -> u8 {
value.0
}
}
impl U4 {
pub const MAX: U4= U4(0x0F);
pub const MIN: U4 = U4(0);
/// Combines two nibbles (u4) eg half byte
/// result will be a full byte
pub fn combine(upper:U4,lower:U4) -> u8 {
let upper = upper.0.overflowing_shl(8).0;
let lower = lower.0 & U4::MAX.0;
upper | lower
}
/// Constructs a U4 from a u8.
/// Note this clamps off the upper portions
pub fn from_overflowing_u8(value:u8) -> U4 {
const MASK :u8 = 0b0000_1111;
let number = MASK & value;
U4(number)
}
}

View file

@ -1,5 +1,4 @@
pub mod notes; pub mod notes;
pub mod code_index_number;
pub mod channel; pub mod channel;
pub mod message; pub mod message;
pub mod velocity; pub mod velocity;

View file

@ -1,4 +1,5 @@
use core::convert::TryFrom; use core::convert::TryFrom;
use crate::data::byte::u4::U4;
/// The Cable Number (CN) is a value ranging from 0x0 to 0xF /// The Cable Number (CN) is a value ranging from 0x0 to 0xF
/// indicating the number assignment of the Embedded MIDI Jack associated /// indicating the number assignment of the Embedded MIDI Jack associated
@ -39,9 +40,15 @@ impl TryFrom<u8> for CableNumber {
} }
impl Into<u8> for CableNumber { impl From<CableNumber> for u8{
fn into(self) -> u8 { fn from(value:CableNumber) -> u8 {
self as u8 value as u8
}
}
impl From<CableNumber> for U4{
fn from(value:CableNumber) -> U4 {
U4::from_overflowing_u8(u8::from(value))
} }
} }
@ -55,7 +62,7 @@ mod tests {
#[test] #[test]
fn $id() { fn $id() {
let (input,expected) = $value; let (input,expected) = $value;
assert_eq!(input as u8, expected); assert_eq!(u8::from(input), expected);
} }
)* )*
} }

View file

@ -1,4 +1,6 @@
use core::convert::TryFrom; use core::convert::TryFrom;
use crate::data::midi::message::Message;
use crate::data::byte::u4::U4;
/// The Code Index Number(CIN) indicates the classification /// The Code Index Number(CIN) indicates the classification
/// of the bytes in the MIDI_x fields /// of the bytes in the MIDI_x fields
@ -16,12 +18,14 @@ impl TryFrom<u8> for CodeIndexNumber {
} }
} }
impl Into<u8> for CodeIndexNumber { impl From<CodeIndexNumber> for U4{
fn into(self) -> u8 { fn from(value:CodeIndexNumber) -> U4{
self.0 U4::from_overflowing_u8(value.0)
} }
} }
impl CodeIndexNumber { impl CodeIndexNumber {
/// Miscellaneous function codes. Reserved for future extensions /// Miscellaneous function codes. Reserved for future extensions
@ -60,4 +64,14 @@ impl CodeIndexNumber {
/// Single Byte /// Single Byte
pub const SINGLE_BYTE : CodeIndexNumber= CodeIndexNumber(0xF); pub const SINGLE_BYTE : CodeIndexNumber= CodeIndexNumber(0xF);
pub fn find_from_message(value:&Message) -> CodeIndexNumber{
match value {
Message::NoteOn(_,_,_) => CodeIndexNumber::NOTE_ON,
Message::NoteOff(_,_,_) => CodeIndexNumber::NOTE_OFF,
Message::ChannelAftertouch(_,_) => CodeIndexNumber::CHANNEL_PRESSURE,
Message::PitchWheelChange(_,_,_) => CodeIndexNumber::PITCHBEND_CHANGE,
Message::PolyphonicAftertouch(_,_,_) => CodeIndexNumber::POLY_KEYPRESS,
Message::ProgramChange(_,_) => CodeIndexNumber::PROGRAM_CHANGE
}
}
} }

View file

@ -1,2 +1,3 @@
pub mod usb_midi_event_packet; pub mod usb_midi_event_packet;
pub mod cable_number; pub mod cable_number;
pub mod code_index_number;

View file

@ -1,50 +1,35 @@
use crate::data::usb_midi::cable_number::CableNumber; use crate::data::usb_midi::cable_number::CableNumber;
use crate::data::midi::code_index_number::CodeIndexNumber; use crate::data::usb_midi::code_index_number::CodeIndexNumber;
use crate::data::midi::notes::Note;
use crate::data::midi::channel::Channel;
use crate::data::midi::message::Message; use crate::data::midi::message::Message;
use crate::data::midi::velocity::Velocity; use crate::data::byte::u4::U4;
use crate::util::nibble::{combine_nibble}; use crate::data::midi::message::raw::{Payload,Raw};
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' /// Currently supported is sending the specified normal midi
/// It's contents can depend on the cable/code index number /// message over the supplied cable number
/// but may not!?
pub struct UsbMidiEventPacket { pub struct UsbMidiEventPacket {
cable_number : CableNumber, cable_number : CableNumber,
code_index_number: CodeIndexNumber, //Is this strictly necessary
//if can be built from the midi message?
message: Message message: Message
} }
/// Constructs a note-on midi message given the cable, note and velocity impl From<UsbMidiEventPacket> for [u8;4] {
pub fn note_on( cable:CableNumber, fn from(value:UsbMidiEventPacket) -> [u8;4] {
channel: Channel, let message= value.message;
note:Note, let cable_number = U4::from(value.cable_number);
velocity: Velocity) -> UsbMidiEventPacket { let index_number = {
let code_index =
CodeIndexNumber::find_from_message(&message);
U4::from(code_index)
};
let header = U4::combine(cable_number,index_number);
let raw_midi = Raw::from(message);
let status = raw_midi.status;
let message = Message::NoteOn(channel,note,velocity); match raw_midi.payload {
Payload::Empty => [header,status,0,0],
UsbMidiEventPacket{ Payload::SingleByte(byte) => [header,status,byte,0],
cable_number : cable, Payload::DoubleByte(byte1,byte2) => [header,status,byte1,byte2]
code_index_number : CodeIndexNumber::NOTE_ON, }
message : message
} }
} }
impl Into<[u8;4]> for UsbMidiEventPacket {
/// Converts the midi packet into a byte array
/// 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 raw : Raw = self.message.into();
let status : u8 = raw.status.into();
panic!()
}
}

View file

@ -1,6 +1,5 @@
use usb_device::class_prelude::*; use usb_device::class_prelude::*;
use usb_device::Result; use usb_device::Result;
use crate::data::midi::notes::Note;
use crate::data::usb::constants::*; use crate::data::usb::constants::*;
use crate::data::usb_midi::usb_midi_event_packet::UsbMidiEventPacket; use crate::data::usb_midi::usb_midi_event_packet::UsbMidiEventPacket;
@ -34,28 +33,6 @@ impl<B: UsbBus> MidiClass<'_, B> {
self.standard_bulkin.write(&bytes) self.standard_bulkin.write(&bytes)
} }
pub fn note_on(&mut self, chan: u8, note: Note, vel : u8) -> Result<usize> {
let note = note as u8;
self.standard_bulkin.write(
&[
0x09,//Note-on message (usb-midi)
0x90 | (chan & 0x0f),// (note-on, normal midi)
note & 0x7f, //note
vel & 0x7f //vel
])
}
pub fn note_off(&mut self, chan: u8, note: Note, vel : u8) -> Result<usize> {
let note = note as u8;
self.standard_bulkin.write(
&[
0x08,//Note-on message (usb-midi)
0x80 | (chan & 0x0f),// (note-on, normal midi)
note & 0x7f, //note
vel & 0x7f //vel
])
}
} }
impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> { impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {

View file

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

View file

@ -1,10 +0,0 @@
/// Combines two nibbles.
/// Note that the upper will overflow if greater than 0xF
/// The lower will be clamped to the range 0-0xF
pub fn combine_nibble(upper:u8,lower:u8) -> u8 {
let upper = upper.overflowing_shl(8).0;
let lower = lower & 0xF;
upper | lower
}