Implemented basic midi receive

This commit is contained in:
Patric Kanngießer 2020-12-25 13:03:40 +01:00
parent 468ce4ed39
commit dea3594e37
10 changed files with 138 additions and 15 deletions

1
.gitignore vendored
View file

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

View file

@ -11,4 +11,8 @@ license = "MIT"
[dependencies] [dependencies]
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

@ -2,6 +2,7 @@ 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
#[derive(Debug)]
pub struct U7(u8); pub struct U7(u8);
/// Error representing that this value is not a valid u7 /// Error representing that this value is not a valid u7

View file

@ -3,6 +3,9 @@ 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;
type Velocity = U7; type Velocity = U7;
@ -10,13 +13,14 @@ 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)]
pub enum Message { pub enum Message {
NoteOff(Channel,Note,Velocity), NoteOff(Channel,Note,Velocity),
NoteOn(Channel,Note,Velocity), NoteOn(Channel,Note,Velocity),
PolyphonicAftertouch(Channel,Note,U7), PolyphonicAftertouch(Channel,Note,U7),
ProgramChange(Channel,U7), ProgramChange(Channel,U7),
ChannelAftertouch(Channel,U7), ChannelAftertouch(Channel,U7),
PitchWheelChange(Channel,U7,U7), PitchWheelChange(Channel,U7,U7)
} }
const NOTE_OFF_MASK :u8 = 0b1000_0000; const NOTE_OFF_MASK :u8 = 0b1000_0000;
@ -62,4 +66,33 @@ impl From<Message> for Raw {
} }
} }
}
impl<'a> TryFrom<&'a [u8]> for Message {
type Error = MidiPacketParsingError;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
let status_byte = data[0];
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_OFF_MASK => Ok(Message::NoteOff(channel, get_note(data)?, get_velocity(data))),
NOTE_ON_MASK => Ok(Message::NoteOn(channel, get_note(data)?, get_velocity(data))),
_ => Err(MidiPacketParsingError::InvalidEventType(event_type))
}
}
}
fn get_note(data: &[u8]) -> Result<Note, MidiPacketParsingError> {
let note_byte = data[1];
match Note::try_from(note_byte) {
Ok(note) => Ok(note),
Err(_) => Err(MidiPacketParsingError::InvalidNote(note_byte))
}
}
fn get_velocity(data: &[u8]) -> U7 {
U7::from_clamped(data[2])
} }

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)]
#[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

@ -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 + 4)) {
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,18 @@ 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 crate::data::byte::u4;
use core::convert::TryFrom;
use crate::data::midi::channel::Channel::Channel1;
use crate::data::midi::notes::Note::C7;
use crate::data::byte::u7::U7;
use crate::data::byte::from_traits::FromClamped;
/// 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)]
pub struct UsbMidiEventPacket { pub struct UsbMidiEventPacket {
pub cable_number : CableNumber, pub cable_number : CableNumber,
pub message: Message pub message: Message
@ -37,6 +44,34 @@ 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= value[0] >> 4;
let cable_number = match CableNumber::try_from(u8::from(raw_cable_number)) {
Ok(val) => val,
_ => return Err(MidiPacketParsingError::InvalidCableNumber(raw_cable_number))
};
let message = Message::try_from(&value[1..])?;
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)

View file

@ -1,21 +1,29 @@
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};
use core::convert::TryFrom;
//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 +31,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 +93,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 +101,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 +116,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 +125,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
0x01, 0x01,
0x01 0x01
] ]
)?; */ )?;
writer.endpoint(&self.standard_bulkin)?; writer.endpoint(&self.standard_bulkin)?;