Merge pull request #3 from p4ddy1/receive_midi

Receive midi
This commit is contained in:
btrepp 2021-01-01 11:59:35 +08:00 committed by GitHub
commit f0a03436a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 432 additions and 28 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
Cargo.lock Cargo.lock
target target
.idea

View file

@ -12,3 +12,7 @@ license = "MIT"
embedded-hal = "0.2.2" embedded-hal = "0.2.2"
nb = "0.1.2" nb = "0.1.2"
usb-device = "0.2.3" usb-device = "0.2.3"
[dependencies.num_enum]
version = "0.5.1"
default-features = false

View file

@ -4,8 +4,68 @@ usbd-midi
A simple usb midi device class for [usb-device](https://crates.io/crates/usb-device). A simple usb midi device class for [usb-device](https://crates.io/crates/usb-device).
Currently this aims to be a very simple implementation, that allows the micro Currently this aims to be a very simple implementation, that allows the micro
controller to send midi information to the PC. controller to send MIDI information to the PC and also receive MIDI information.
This crate requires the use of a hardware driver, that implements the This crate requires the use of a hardware driver, that implements the
usb-device traits. usb-device traits.
## Example
### Receive MIDI
Turn on the integrated LED of a STM32 BluePill board as long as C2 is pressed
```rust
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
led.set_high().unwrap();
let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
let usb = Peripheral {
usb: dp.USB,
pin_dm: gpioa.pa11,
pin_dp: usb_dp.into_floating_input(&mut gpioa.crh),
};
let usb_bus = UsbBus::new(usb);
let mut midi = MidiClass::new(&usb_bus);
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x5e4))
.product("MIDI Test")
.device_class(USB_AUDIO_CLASS)
.device_sub_class(USB_MIDISTREAMING_SUBCLASS)
.build();
loop {
if !usb_dev.poll(&mut [&mut midi]) {
continue;
}
let mut buffer = [0; 64];
if let Ok(size) = midi.read(&mut buffer) {
let buffer_reader = MidiPacketBufferReader::new(&buffer, size);
for packet in buffer_reader.into_iter() {
if let Ok(packet) = packet {
match packet.message {
Message::NoteOn(Channel1, Note::C2, ..) => {
led.set_low().unwrap();
},
Message::NoteOff(Channel1, Note::C2, ..) => {
led.set_high().unwrap();
},
_ => {}
}
}
}
}
}
}
```

View file

@ -2,7 +2,8 @@ use core::convert::TryFrom;
use crate::data::byte::from_traits::{FromOverFlow,FromClamped}; use crate::data::byte::from_traits::{FromOverFlow,FromClamped};
/// A primitive value that can be from 0-0x7F /// A primitive value that can be from 0-0x7F
pub struct U7(u8); #[derive(Debug, Eq, PartialEq)]
pub struct U7(pub(crate)u8);
/// Error representing that this value is not a valid u7 /// Error representing that this value is not a valid u7
pub struct InvalidU7(u8); pub struct InvalidU7(u8);

View file

@ -3,7 +3,7 @@ use core::convert::TryFrom;
/// The Channel is a value ranging from 0x0 to 0xF /// The Channel is a value ranging from 0x0 to 0xF
/// This is a standard midi concept /// This is a standard midi concept
/// Note Channel1 = 0 on the wire /// Note Channel1 = 0 on the wire
#[derive(Debug,Copy,Clone)] #[derive(Debug,Copy,Clone, Eq, PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum Channel { pub enum Channel {
Channel1 = 0x0, Channel2 = 0x1, Channel3 = 0x2, Channel4 = 0x3, Channel1 = 0x0, Channel2 = 0x1, Channel3 = 0x2, Channel4 = 0x3,

View file

@ -0,0 +1,137 @@
use crate::data::byte::u7::U7;
#[derive(Debug, Eq, PartialEq)]
pub struct ControlFunction(pub U7);
/// Control Functions as defined in the MIDI 1.0 Specification
/// Source: https://www.midi.org/specifications-old/item/table-3-control-change-messages-data-bytes-2
impl ControlFunction {
pub const BANK_SELECT_0: Self = ControlFunction(U7(0));
pub const MOD_WHEEL_1: Self = ControlFunction(U7(1));
pub const BREATH_CONTROLLER_2: Self = ControlFunction(U7(2));
pub const UNDEFINED_3: Self = ControlFunction(U7(3));
pub const FOOT_CONTROLLER_4: Self = ControlFunction(U7(4));
pub const PORTAMENTO_TIME_5: Self = ControlFunction(U7(5));
pub const DATA_ENTRY_MSB_6: Self = ControlFunction(U7(6));
pub const CHANNEL_VOLUME_7: Self = ControlFunction(U7(7));
pub const BALANCE_8: Self = ControlFunction(U7(8));
pub const UNDEFINED_9: Self = ControlFunction(U7(9));
pub const PAN_10: Self = ControlFunction(U7(10));
pub const EXPRESSION_CONTROLLER_11: Self = ControlFunction(U7(11));
pub const EFFECT_CONTROL_1_12: Self = ControlFunction(U7(12));
pub const EFFECT_CONTROL_2_13: Self = ControlFunction(U7(13));
pub const UNDEFINED_14: Self = ControlFunction(U7(14));
pub const UNDEFINED_15: Self = ControlFunction(U7(15));
pub const GENERAL_PURPOSE_CONTROLLER_1_16: Self = ControlFunction(U7(16));
pub const GENERAL_PURPOSE_CONTROLLER_2_17: Self = ControlFunction(U7(17));
pub const GENERAL_PURPOSE_CONTROLLER_3_18: Self = ControlFunction(U7(18));
pub const GENERAL_PURPOSE_CONTROLLER_4_19: Self = ControlFunction(U7(19));
pub const UNDEFINED_20: Self = ControlFunction(U7(20));
pub const UNDEFINED_21: Self = ControlFunction(U7(21));
pub const UNDEFINED_22: Self = ControlFunction(U7(22));
pub const UNDEFINED_23: Self = ControlFunction(U7(23));
pub const UNDEFINED_24: Self = ControlFunction(U7(24));
pub const UNDEFINED_25: Self = ControlFunction(U7(25));
pub const UNDEFINED_26: Self = ControlFunction(U7(26));
pub const UNDEFINED_27: Self = ControlFunction(U7(27));
pub const UNDEFINED_28: Self = ControlFunction(U7(28));
pub const UNDEFINED_29: Self = ControlFunction(U7(29));
pub const UNDEFINED_30: Self = ControlFunction(U7(30));
pub const UNDEFINED_31: Self = ControlFunction(U7(31));
pub const LSB_FOR_BANK_SELECT_32: Self = ControlFunction(U7(32));
pub const LSB_FOR_MOD_WHEEL_33: Self = ControlFunction(U7(33));
pub const LSB_FOR_BREATH_CONTROLLER_34: Self = ControlFunction(U7(34));
pub const LSB_FOR_UNDEFINED_35: Self = ControlFunction(U7(35));
pub const LSB_FOR_FOOT_CONTROLLER_36: Self = ControlFunction(U7(36));
pub const LSB_FOR_PORTAMENTO_TIME_37: Self = ControlFunction(U7(37));
pub const LSB_FOR_DATA_ENTRY_MSB_38: Self = ControlFunction(U7(38));
pub const LSB_FOR_CHANNEL_VOLUME_39: Self = ControlFunction(U7(39));
pub const LSB_FOR_BALANCE_40: Self = ControlFunction(U7(40));
pub const LSB_FOR_UNDEFINED_41: Self = ControlFunction(U7(41));
pub const LSB_FOR_PAN_42: Self = ControlFunction(U7(42));
pub const LSB_FOR_EXPRESSION_CONTROLLER_43: Self = ControlFunction(U7(43));
pub const LSB_FOR_EFFECT_CONTROL_1_44: Self = ControlFunction(U7(44));
pub const LSB_FOR_EFFECT_CONTROL_2_45: Self = ControlFunction(U7(45));
pub const LSB_FOR_UNDEFINED_14_46: Self = ControlFunction(U7(46));
pub const LSB_FOR_UNDEFINED_15_47: Self = ControlFunction(U7(47));
pub const LSB_FOR_GENERAL_PURPOSE_CONTROLLER_1_48: Self = ControlFunction(U7(48));
pub const LSB_FOR_GENERAL_PURPOSE_CONTROLLER_2_49: Self = ControlFunction(U7(49));
pub const LSB_FOR_GENERAL_PURPOSE_CONTROLLER_3_50: Self = ControlFunction(U7(50));
pub const LSB_FOR_GENERAL_PURPOSE_CONTROLLER_4_51: Self = ControlFunction(U7(51));
pub const LSB_FOR_UNDEFINED_20_52: Self = ControlFunction(U7(52));
pub const LSB_FOR_UNDEFINED_21_53: Self = ControlFunction(U7(53));
pub const LSB_FOR_UNDEFINED_22_54: Self = ControlFunction(U7(54));
pub const LSB_FOR_UNDEFINED_23_55: Self = ControlFunction(U7(55));
pub const LSB_FOR_UNDEFINED_24_56: Self = ControlFunction(U7(56));
pub const LSB_FOR_UNDEFINED_25_57: Self = ControlFunction(U7(57));
pub const LSB_FOR_UNDEFINED_26_58: Self = ControlFunction(U7(58));
pub const LSB_FOR_UNDEFINED_27_59: Self = ControlFunction(U7(59));
pub const LSB_FOR_UNDEFINED_28_60: Self = ControlFunction(U7(60));
pub const LSB_FOR_UNDEFINED_29_61: Self = ControlFunction(U7(61));
pub const LSB_FOR_UNDEFINED_30_62: Self = ControlFunction(U7(62));
pub const LSB_FOR_UNDEFINED_31_63: Self = ControlFunction(U7(63));
pub const DAMPER_PEDAL_ON_OFF_64: Self = ControlFunction(U7(64));
pub const PORTAMENTO_ON_OFF_65: Self = ControlFunction(U7(65));
pub const SOSTENUTO_ON_OFF_66: Self = ControlFunction(U7(66));
pub const SOFT_PEDAL_ON_OFF_67: Self = ControlFunction(U7(67));
pub const LEGATO_FOOTSWITCH_68: Self = ControlFunction(U7(68));
pub const HOLD_2_69: Self = ControlFunction(U7(69));
pub const SOUND_CONTROLLER_1_70: Self = ControlFunction(U7(70));
pub const SOUND_CONTROLLER_2_71: Self = ControlFunction(U7(71));
pub const SOUND_CONTROLLER_3_72: Self = ControlFunction(U7(72));
pub const SOUND_CONTROLLER_4_73: Self = ControlFunction(U7(73));
pub const SOUND_CONTROLLER_5_74: Self = ControlFunction(U7(74));
pub const SOUND_CONTROLLER_6_75: Self = ControlFunction(U7(75));
pub const SOUND_CONTROLLER_7_76: Self = ControlFunction(U7(76));
pub const SOUND_CONTROLLER_8_77: Self = ControlFunction(U7(77));
pub const SOUND_CONTROLLER_9_78: Self = ControlFunction(U7(78));
pub const SOUND_CONTROLLER_10_79: Self = ControlFunction(U7(79));
pub const GENERAL_PURPOSE_CONTROLLER_5_80: Self = ControlFunction(U7(80));
pub const GENERAL_PURPOSE_CONTROLLER_6_81: Self = ControlFunction(U7(81));
pub const GENERAL_PURPOSE_CONTROLLER_7_82: Self = ControlFunction(U7(82));
pub const GENERAL_PURPOSE_CONTROLLER_8_83: Self = ControlFunction(U7(83));
pub const PORTAMENTO_CONTROL_84: Self = ControlFunction(U7(84));
pub const UNDEFINED_85: Self = ControlFunction(U7(85));
pub const UNDEFINED_86: Self = ControlFunction(U7(86));
pub const UNDEFINED_87: Self = ControlFunction(U7(87));
pub const HIGH_RESOLUTION_VELOCITY_PREFIX_88: Self = ControlFunction(U7(88));
pub const UNDEFINED_89: Self = ControlFunction(U7(89));
pub const UNDEFINED_90: Self = ControlFunction(U7(90));
pub const EFFECTS_1_DEPTH_91: Self = ControlFunction(U7(91));
pub const EFFECTS_2_DEPTH_92: Self = ControlFunction(U7(92));
pub const EFFECTS_3_DEPTH_93: Self = ControlFunction(U7(93));
pub const EFFECTS_4_DEPTH_94: Self = ControlFunction(U7(94));
pub const EFFECTS_5_DEPTH_95: Self = ControlFunction(U7(95));
pub const DATA_INCREMENT_96: Self = ControlFunction(U7(96));
pub const DATA_DECREMENT_97: Self = ControlFunction(U7(97));
pub const NPRN_LSB_98: Self = ControlFunction(U7(98));
pub const NPRN_MSB_99: Self = ControlFunction(U7(99));
pub const RPN_LSB_100: Self = ControlFunction(U7(100));
pub const UNDEFINED_101: Self = ControlFunction(U7(101));
pub const UNDEFINED_102: Self = ControlFunction(U7(102));
pub const UNDEFINED_103: Self = ControlFunction(U7(103));
pub const UNDEFINED_104: Self = ControlFunction(U7(104));
pub const UNDEFINED_105: Self = ControlFunction(U7(105));
pub const UNDEFINED_106: Self = ControlFunction(U7(106));
pub const UNDEFINED_107: Self = ControlFunction(U7(107));
pub const UNDEFINED_108: Self = ControlFunction(U7(108));
pub const UNDEFINED_109: Self = ControlFunction(U7(109));
pub const UNDEFINED_110: Self = ControlFunction(U7(110));
pub const UNDEFINED_111: Self = ControlFunction(U7(111));
pub const UNDEFINED_112: Self = ControlFunction(U7(112));
pub const UNDEFINED_113: Self = ControlFunction(U7(113));
pub const UNDEFINED_114: Self = ControlFunction(U7(114));
pub const UNDEFINED_115: Self = ControlFunction(U7(115));
pub const UNDEFINED_116: Self = ControlFunction(U7(116));
pub const UNDEFINED_117: Self = ControlFunction(U7(117));
pub const UNDEFINED_118: Self = ControlFunction(U7(118));
pub const UNDEFINED_119: Self = ControlFunction(U7(119));
pub const ALL_SOUND_OFF_120: Self = ControlFunction(U7(120));
pub const RESET_ALL_CONTROLLERS_121: Self = ControlFunction(U7(121));
pub const LOCAL_CONTROL_OFF_122: Self = ControlFunction(U7(122));
pub const ALL_NOTES_OFF_123: Self = ControlFunction(U7(123));
pub const OMNI_MODE_OFF_124: Self = ControlFunction(U7(124));
pub const OMNI_MODE_ON_125: Self = ControlFunction(U7(125));
pub const MONO_MODE_ON_126: Self = ControlFunction(U7(126));
pub const POLY_MODE_ON_127: Self = ControlFunction(U7(127));
}

View file

@ -3,6 +3,10 @@ use crate::data::midi::channel::Channel;
use crate::data::midi::notes::Note; use crate::data::midi::notes::Note;
use crate::data::byte::u7::U7; use crate::data::byte::u7::U7;
use crate::data::midi::message::raw::{Raw,Payload}; use crate::data::midi::message::raw::{Raw,Payload};
use crate::data::byte::from_traits::FromClamped;
use core::convert::TryFrom;
use crate::data::usb_midi::usb_midi_event_packet::MidiPacketParsingError;
use crate::data::midi::message::control_function::ControlFunction;
type Velocity = U7; type Velocity = U7;
@ -10,6 +14,7 @@ type Velocity = U7;
/// Note: not current exhaustive and SysEx messages end up /// Note: not current exhaustive and SysEx messages end up
/// being a confusing case. So are currently note implemented /// being a confusing case. So are currently note implemented
/// they are sort-of unbounded /// they are sort-of unbounded
#[derive(Debug, Eq, PartialEq)]
pub enum Message { pub enum Message {
NoteOff(Channel,Note,Velocity), NoteOff(Channel,Note,Velocity),
NoteOn(Channel,Note,Velocity), NoteOn(Channel,Note,Velocity),
@ -17,14 +22,16 @@ pub enum Message {
ProgramChange(Channel,U7), ProgramChange(Channel,U7),
ChannelAftertouch(Channel,U7), ChannelAftertouch(Channel,U7),
PitchWheelChange(Channel,U7,U7), PitchWheelChange(Channel,U7,U7),
ControlChange(Channel,ControlFunction,U7)
} }
const NOTE_OFF_MASK :u8 = 0b1000_0000; const NOTE_OFF_MASK :u8 = 0b1000_0000;
const NOTE_ON_MASK :u8 = 0b1001_0000; const NOTE_ON_MASK :u8 = 0b1001_0000;
const POLYPHONIC_MASK :u8 = 0b1010_0000; const POLYPHONIC_MASK :u8 = 0b1010_0000;
const PROGRAM_MASK :u8 = 0b1100_0000; const PROGRAM_MASK :u8 = 0b1100_0000;
const CHANNEL_AFTERTOUCH_MASK :u8 = 0b1101_0000; const CHANNEL_AFTERTOUCH_MASK :u8 = 0b1101_0000;
const PITCH_BEND_MASK :u8 = 0b1110_0000; const PITCH_BEND_MASK :u8 = 0b1110_0000;
const CONTROL_CHANGE_MASK :u8 = 0b1011_0000;
impl From<Message> for Raw { impl From<Message> for Raw {
fn from(value:Message) -> Raw { fn from(value:Message) -> Raw {
@ -58,8 +65,58 @@ impl From<Message> for Raw {
let payload = Payload::DoubleByte(lsb,msb); let payload = Payload::DoubleByte(lsb,msb);
let status = PITCH_BEND_MASK | u8::from(chan); let status = PITCH_BEND_MASK | u8::from(chan);
Raw {status , payload} Raw {status , payload}
},
Message::ControlChange(chan, control_function, value) => {
let payload = Payload::DoubleByte(control_function.0, value);
let status = CONTROL_CHANGE_MASK | u8::from(chan);
Raw {status, payload}
} }
} }
} }
} }
impl<'a> TryFrom<&'a [u8]> for Message {
type Error = MidiPacketParsingError;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let status_byte = match data.get(0) {
Some(byte) => byte,
None => return Err(MidiPacketParsingError::MissingDataPacket)
};
let event_type = status_byte & 0b1111_0000;
let channel_bytes = (status_byte) & 0b0000_1111;
let channel = Channel::try_from(channel_bytes).ok().unwrap();
match event_type {
NOTE_ON_MASK => Ok(Message::NoteOn(channel, get_note(data)?, get_u7_at(data, 2)?)),
NOTE_OFF_MASK => Ok(Message::NoteOff(channel, get_note(data)?, get_u7_at(data, 2)?)),
POLYPHONIC_MASK => Ok(Message::PolyphonicAftertouch(channel, get_note(data)?, get_u7_at(data, 2)?)),
PROGRAM_MASK => Ok(Message::ProgramChange(channel, get_u7_at(data, 1)?)),
CHANNEL_AFTERTOUCH_MASK => Ok(Message::ChannelAftertouch(channel, get_u7_at(data, 1)?)),
PITCH_BEND_MASK => Ok(Message::PitchWheelChange(channel, get_u7_at(data, 1)?, get_u7_at(data, 2)?)),
CONTROL_CHANGE_MASK => Ok(Message::ControlChange(channel, ControlFunction(get_u7_at(data, 1)?), get_u7_at(data, 2)?)),
_ => Err(MidiPacketParsingError::InvalidEventType(event_type))
}
}
}
fn get_note(data: &[u8]) -> Result<Note, MidiPacketParsingError> {
let note_byte = get_byte_at_position(data, 1)?;
match Note::try_from(note_byte) {
Ok(note) => Ok(note),
Err(_) => Err(MidiPacketParsingError::InvalidNote(note_byte))
}
}
fn get_u7_at(data: &[u8], index: usize) -> Result<U7, MidiPacketParsingError> {
let data_byte = get_byte_at_position(data, index)?;
Ok(U7::from_clamped(data_byte))
}
fn get_byte_at_position(data: &[u8], index: usize) -> Result<u8, MidiPacketParsingError> {
match data.get(index) {
Some(byte) => Ok(*byte),
None => Err(MidiPacketParsingError::MissingDataPacket)
}
}

View file

@ -1,3 +1,4 @@
pub mod message; pub mod message;
pub mod raw; pub mod raw;
pub use crate::data::midi::message::message::{Message}; pub use crate::data::midi::message::message::{Message};
pub mod control_function;

View file

@ -1,10 +1,11 @@
use crate::data::byte::u7::U7; use crate::data::byte::u7::U7;
use crate::data::byte::from_traits::FromOverFlow; use crate::data::byte::from_traits::FromOverFlow;
use num_enum::TryFromPrimitive;
/// A simple enum type that represents all the midi 'notes' /// A simple enum type that represents all the midi 'notes'
/// note the flat versions are associated constants /// note the flat versions are associated constants
/// but can be referenced like Note::Bb3 /// but can be referenced like Note::Bb3
/// C1m is the C-1 /// C1m is the C-1
#[derive(Debug,Copy,Clone)] #[derive(Debug,Copy,Clone,TryFromPrimitive,Eq,PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum Note { pub enum Note {
C1m, Cs1m, D1m, Ds1m, E1m, F1m, Fs1m, G1m, Gs1m, A1m, As1m, B1m, C1m, Cs1m, D1m, Ds1m, E1m, F1m, Fs1m, G1m, Gs1m, A1m, As1m, B1m,

View file

@ -4,7 +4,7 @@ pub const USB_CLASS_NONE : u8 = 0x00;
pub const USB_AUDIO_CLASS: u8 = 0x01; pub const USB_AUDIO_CLASS: u8 = 0x01;
pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01; pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
pub const USB_MIDISTREAMING_SUBCLASS: u8 =0x03; pub const USB_MIDISTREAMING_SUBCLASS: u8 =0x03;
//pub const MIDI_IN_JACK_SUBTYPE : u8 = 0x02; pub const MIDI_IN_JACK_SUBTYPE : u8 = 0x02;
pub const MIDI_OUT_JACK_SUBTYPE : u8 = 0x03; pub const MIDI_OUT_JACK_SUBTYPE : u8 = 0x03;
pub const EMBEDDED : u8 = 0x01; pub const EMBEDDED : u8 = 0x01;
pub const CS_INTERFACE: u8 = 0x24; pub const CS_INTERFACE: u8 = 0x24;

View file

@ -4,7 +4,7 @@ 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
/// with the endpoint that is transferring the data /// with the endpoint that is transferring the data
#[derive(Debug,Clone,Copy)] #[derive(Debug,Clone,Copy,Eq, PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum CableNumber { pub enum CableNumber {
Cable0 = 0x0, Cable1 = 0x1, Cable2 = 0x2, Cable3 = 0x3, Cable0 = 0x0, Cable1 = 0x1, Cable2 = 0x2, Cable3 = 0x3,

View file

@ -71,7 +71,8 @@ impl CodeIndexNumber {
Message::ChannelAftertouch(_,_) => CodeIndexNumber::CHANNEL_PRESSURE, Message::ChannelAftertouch(_,_) => CodeIndexNumber::CHANNEL_PRESSURE,
Message::PitchWheelChange(_,_,_) => CodeIndexNumber::PITCHBEND_CHANGE, Message::PitchWheelChange(_,_,_) => CodeIndexNumber::PITCHBEND_CHANGE,
Message::PolyphonicAftertouch(_,_,_) => CodeIndexNumber::POLY_KEYPRESS, Message::PolyphonicAftertouch(_,_,_) => CodeIndexNumber::POLY_KEYPRESS,
Message::ProgramChange(_,_) => CodeIndexNumber::PROGRAM_CHANGE Message::ProgramChange(_,_) => CodeIndexNumber::PROGRAM_CHANGE,
Message::ControlChange(_,_,_) => CodeIndexNumber::CONTROL_CHANGE
} }
} }
} }

View file

@ -0,0 +1,36 @@
use core::convert::TryFrom;
use crate::data::usb_midi::usb_midi_event_packet::{UsbMidiEventPacket, MidiPacketParsingError};
use crate::midi_device::{MAX_PACKET_SIZE, MIDI_PACKET_SIZE};
pub struct MidiPacketBufferReader<'a> {
buffer: &'a [u8; MAX_PACKET_SIZE],
position: usize,
raw_bytes_received: usize
}
impl<'a> MidiPacketBufferReader<'a> {
pub fn new(buffer: &'a [u8; MAX_PACKET_SIZE], raw_bytes_received: usize) -> Self {
MidiPacketBufferReader {
buffer,
position: 0,
raw_bytes_received
}
}
}
impl<'a> Iterator for MidiPacketBufferReader<'a> {
type Item = Result<UsbMidiEventPacket, MidiPacketParsingError>;
fn next(&mut self) -> Option<Self::Item> {
if self.position <= MAX_PACKET_SIZE && self.position < self.raw_bytes_received {
let packet = match self.buffer.get(self.position .. (self.position + MIDI_PACKET_SIZE)) {
Some(packet) => Some(UsbMidiEventPacket::try_from(packet)),
None => None
};
self.position += MIDI_PACKET_SIZE;
return packet;
}
None
}
}

View file

@ -1,3 +1,4 @@
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; pub mod code_index_number;
pub mod midi_packet_reader;

View file

@ -3,11 +3,13 @@ use crate::data::usb_midi::code_index_number::CodeIndexNumber;
use crate::data::midi::message::Message; use crate::data::midi::message::Message;
use crate::data::byte::u4::U4; use crate::data::byte::u4::U4;
use crate::data::midi::message::raw::{Payload,Raw}; use crate::data::midi::message::raw::{Payload,Raw};
use core::convert::TryFrom;
/// A packet that communicates with the host /// A packet that communicates with the host
/// Currently supported is sending the specified normal midi /// Currently supported is sending the specified normal midi
/// message over the supplied cable number /// message over the supplied cable number
#[derive(Debug,Eq, PartialEq)]
pub struct UsbMidiEventPacket { pub struct UsbMidiEventPacket {
pub cable_number : CableNumber, pub cable_number : CableNumber,
pub message: Message pub message: Message
@ -37,6 +39,42 @@ impl From<UsbMidiEventPacket> for [u8;4] {
} }
} }
#[derive(Debug)]
pub enum MidiPacketParsingError {
InvalidNote(u8),
InvalidCableNumber(u8),
InvalidEventType(u8),
MissingDataPacket
}
impl TryFrom<&[u8]> for UsbMidiEventPacket {
type Error = MidiPacketParsingError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let raw_cable_number= match value.get(0) {
Some(byte) => *byte >> 4,
None => return Err(MidiPacketParsingError::MissingDataPacket)
};
let cable_number = match CableNumber::try_from(u8::from(raw_cable_number)) {
Ok(val) => val,
_ => return Err(MidiPacketParsingError::InvalidCableNumber(raw_cable_number))
};
let message_body = match value.get(1..) {
Some(bytes) => bytes,
None => return Err(MidiPacketParsingError::MissingDataPacket)
};
let message = Message::try_from(message_body)?;
Ok(UsbMidiEventPacket {
cable_number,
message
})
}
}
impl UsbMidiEventPacket{ impl UsbMidiEventPacket{
pub fn from_midi(cable:CableNumber, midi:Message) pub fn from_midi(cable:CableNumber, midi:Message)
@ -47,3 +85,59 @@ impl UsbMidiEventPacket{
} }
} }
} }
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use crate::data::usb_midi::usb_midi_event_packet::UsbMidiEventPacket;
use crate::data::midi::channel::Channel::{Channel1, Channel2};
use crate::data::midi::notes::Note;
use crate::data::byte::u7::U7;
use crate::data::midi::message::Message;
use crate::data::usb_midi::cable_number::CableNumber::{Cable0,Cable1};
use crate::data::midi::message::control_function::ControlFunction;
macro_rules! decode_message_test {
($($id:ident:$value:expr,)*) => {
$(
#[test]
fn $id() {
let (usb_midi_data_packet,expected) = $value;
let message = UsbMidiEventPacket::try_from(&usb_midi_data_packet[..]).unwrap();
assert_eq!(expected, message);
}
)*
}
}
decode_message_test! {
note_on: ([9, 144, 36, 127], UsbMidiEventPacket {
cable_number: Cable0,
message: Message::NoteOn(Channel1, Note::C2, U7(127))
}),
note_off: ([8, 128, 36, 0], UsbMidiEventPacket {
cable_number: Cable0,
message: Message::NoteOff(Channel1, Note::C2, U7(0))
}),
polyphonic_aftertouch: ([10, 160, 36, 64], UsbMidiEventPacket {
cable_number: Cable0,
message: Message::PolyphonicAftertouch(Channel1, Note::C2, U7(64))
}),
program_change: ([28, 192, 127, 0], UsbMidiEventPacket {
cable_number: Cable1,
message: Message::ProgramChange(Channel1, U7(127))
}),
channel_aftertouch: ([13, 208, 127, 0], UsbMidiEventPacket {
cable_number: Cable0,
message: Message::ChannelAftertouch(Channel1, U7(127))
}),
pitch_wheel: ([14, 224, 64, 32], UsbMidiEventPacket {
cable_number: Cable0,
message: Message::PitchWheelChange(Channel1, U7(64), U7(32))
}),
control_change: ([11, 177, 1, 32], UsbMidiEventPacket {
cable_number: Cable0,
message: Message::ControlChange(Channel2, ControlFunction::MOD_WHEEL_1, U7(32))
}),
}
}

View file

@ -1,21 +1,28 @@
use usb_device::class_prelude::*; use usb_device::class_prelude::*;
use usb_device::Result; use usb_device::Result;
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, MidiPacketParsingError};
//const MIDI_IN_SIZE: u8 = 0x06; const MIDI_IN_SIZE: u8 = 0x06;
const MIDI_OUT_SIZE: u8 = 0x09; const MIDI_OUT_SIZE: u8 = 0x09;
pub const MIDI_PACKET_SIZE: usize = 4;
pub const MAX_PACKET_SIZE: usize = 64;
///Note we are using MidiIn here to refer to the fact that ///Note we are using MidiIn here to refer to the fact that
///The Host sees it as a midi in device ///The Host sees it as a midi in device
///This class allows you to send data in ///This class allows you to send data in
pub struct MidiClass<'a,B: UsbBus> { pub struct MidiClass<'a,B: UsbBus> {
standard_ac: InterfaceNumber, standard_ac: InterfaceNumber,
standard_mc: InterfaceNumber, standard_mc: InterfaceNumber,
//standard_bulkout: EndpointOut<'a, B>, standard_bulkout: EndpointOut<'a, B>,
standard_bulkin: EndpointIn<'a,B> standard_bulkin: EndpointIn<'a,B>
} }
pub enum MidiReadError {
ParsingFailed(MidiPacketParsingError),
UsbError(UsbError)
}
impl<B: UsbBus> MidiClass<'_, B> { impl<B: UsbBus> MidiClass<'_, B> {
/// Creates a new MidiClass with the provided UsbBus /// Creates a new MidiClass with the provided UsbBus
@ -23,16 +30,19 @@ impl<B: UsbBus> MidiClass<'_, B> {
MidiClass { MidiClass {
standard_ac: alloc.interface(), standard_ac: alloc.interface(),
standard_mc: alloc.interface(), standard_mc: alloc.interface(),
//standard_bulkout : alloc.bulk(64), standard_bulkout : alloc.bulk(MAX_PACKET_SIZE as u16),
standard_bulkin: alloc.bulk(64) standard_bulkin: alloc.bulk(MAX_PACKET_SIZE as u16)
} }
} }
pub fn send_message(&mut self, usb_midi:UsbMidiEventPacket) -> Result<usize> { pub fn send_message(&mut self, usb_midi:UsbMidiEventPacket) -> Result<usize> {
let bytes : [u8;4] = usb_midi.into(); let bytes : [u8;MIDI_PACKET_SIZE] = usb_midi.into();
self.standard_bulkin.write(&bytes) self.standard_bulkin.write(&bytes)
} }
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
self.standard_bulkout.read(buffer)
}
} }
impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> { impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
@ -82,7 +92,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
//JACKS //JACKS
/* writer.write( writer.write(
CS_INTERFACE, CS_INTERFACE,
&[ &[
MIDI_IN_JACK_SUBTYPE, MIDI_IN_JACK_SUBTYPE,
@ -90,7 +100,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
0x01, // id 0x01, // id
0x00 0x00
] ]
)?; */ )?;
writer.write ( writer.write (
CS_INTERFACE, CS_INTERFACE,
@ -105,7 +115,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
] ]
)?; )?;
/* writer.endpoint(&self.standard_bulkout)?; writer.endpoint(&self.standard_bulkout)?;
writer.write( writer.write(
CS_ENDPOINT, CS_ENDPOINT,
@ -114,7 +124,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
0x01, 0x01,
0x01 0x01
] ]
)?; */ )?;
writer.endpoint(&self.standard_bulkin)?; writer.endpoint(&self.standard_bulkin)?;