mirror of
https://github.com/italicsjenga/usbd-midi.git
synced 2025-01-06 18:51:29 +11:00
Implemented basic midi receive
This commit is contained in:
parent
468ce4ed39
commit
dea3594e37
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
Cargo.lock
|
||||
target
|
||||
.idea
|
|
@ -11,4 +11,8 @@ license = "MIT"
|
|||
[dependencies]
|
||||
embedded-hal = "0.2.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
|
||||
|
|
|
@ -2,6 +2,7 @@ use core::convert::TryFrom;
|
|||
use crate::data::byte::from_traits::{FromOverFlow,FromClamped};
|
||||
|
||||
/// A primitive value that can be from 0-0x7F
|
||||
#[derive(Debug)]
|
||||
pub struct U7(u8);
|
||||
|
||||
/// Error representing that this value is not a valid u7
|
||||
|
|
|
@ -3,6 +3,9 @@ use crate::data::midi::channel::Channel;
|
|||
use crate::data::midi::notes::Note;
|
||||
use crate::data::byte::u7::U7;
|
||||
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;
|
||||
|
||||
|
@ -10,13 +13,14 @@ type Velocity = U7;
|
|||
/// Note: not current exhaustive and SysEx messages end up
|
||||
/// being a confusing case. So are currently note implemented
|
||||
/// they are sort-of unbounded
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
NoteOff(Channel,Note,Velocity),
|
||||
NoteOn(Channel,Note,Velocity),
|
||||
PolyphonicAftertouch(Channel,Note,U7),
|
||||
ProgramChange(Channel,U7),
|
||||
ChannelAftertouch(Channel,U7),
|
||||
PitchWheelChange(Channel,U7,U7),
|
||||
PitchWheelChange(Channel,U7,U7)
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
use crate::data::byte::u7::U7;
|
||||
use crate::data::byte::from_traits::FromOverFlow;
|
||||
use num_enum::TryFromPrimitive;
|
||||
/// A simple enum type that represents all the midi 'notes'
|
||||
/// note the flat versions are associated constants
|
||||
/// but can be referenced like Note::Bb3
|
||||
/// C1m is the C-1
|
||||
#[derive(Debug,Copy,Clone)]
|
||||
#[derive(Debug,Copy,Clone,TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Note {
|
||||
C1m, Cs1m, D1m, Ds1m, E1m, F1m, Fs1m, G1m, Gs1m, A1m, As1m, B1m,
|
||||
|
|
|
@ -4,7 +4,7 @@ pub const USB_CLASS_NONE : u8 = 0x00;
|
|||
pub const USB_AUDIO_CLASS: u8 = 0x01;
|
||||
pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
|
||||
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 EMBEDDED : u8 = 0x01;
|
||||
pub const CS_INTERFACE: u8 = 0x24;
|
||||
|
|
36
src/data/usb_midi/midi_packet_reader.rs
Normal file
36
src/data/usb_midi/midi_packet_reader.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
pub mod usb_midi_event_packet;
|
||||
pub mod cable_number;
|
||||
pub mod code_index_number;
|
||||
pub mod code_index_number;
|
||||
pub mod midi_packet_reader;
|
|
@ -3,11 +3,18 @@ use crate::data::usb_midi::code_index_number::CodeIndexNumber;
|
|||
use crate::data::midi::message::Message;
|
||||
use crate::data::byte::u4::U4;
|
||||
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
|
||||
/// Currently supported is sending the specified normal midi
|
||||
/// message over the supplied cable number
|
||||
#[derive(Debug)]
|
||||
pub struct UsbMidiEventPacket {
|
||||
pub cable_number : CableNumber,
|
||||
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{
|
||||
|
||||
pub fn from_midi(cable:CableNumber, midi:Message)
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
use usb_device::class_prelude::*;
|
||||
use usb_device::Result;
|
||||
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;
|
||||
|
||||
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
|
||||
///The Host sees it as a midi in device
|
||||
///This class allows you to send data in
|
||||
pub struct MidiClass<'a,B: UsbBus> {
|
||||
standard_ac: InterfaceNumber,
|
||||
standard_mc: InterfaceNumber,
|
||||
//standard_bulkout: EndpointOut<'a, B>,
|
||||
standard_bulkout: EndpointOut<'a, B>,
|
||||
standard_bulkin: EndpointIn<'a,B>
|
||||
}
|
||||
|
||||
pub enum MidiReadError {
|
||||
ParsingFailed(MidiPacketParsingError),
|
||||
UsbError(UsbError)
|
||||
}
|
||||
|
||||
impl<B: UsbBus> MidiClass<'_, B> {
|
||||
/// Creates a new MidiClass with the provided UsbBus
|
||||
|
@ -23,16 +31,19 @@ impl<B: UsbBus> MidiClass<'_, B> {
|
|||
MidiClass {
|
||||
standard_ac: alloc.interface(),
|
||||
standard_mc: alloc.interface(),
|
||||
//standard_bulkout : alloc.bulk(64),
|
||||
standard_bulkin: alloc.bulk(64)
|
||||
standard_bulkout : alloc.bulk(MAX_PACKET_SIZE as u16),
|
||||
standard_bulkin: alloc.bulk(MAX_PACKET_SIZE as u16)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
|
||||
self.standard_bulkout.read(buffer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
||||
|
@ -82,7 +93,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
|||
|
||||
//JACKS
|
||||
|
||||
/* writer.write(
|
||||
writer.write(
|
||||
CS_INTERFACE,
|
||||
&[
|
||||
MIDI_IN_JACK_SUBTYPE,
|
||||
|
@ -90,7 +101,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
|||
0x01, // id
|
||||
0x00
|
||||
]
|
||||
)?; */
|
||||
)?;
|
||||
|
||||
writer.write (
|
||||
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(
|
||||
CS_ENDPOINT,
|
||||
|
@ -114,7 +125,7 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
|||
0x01,
|
||||
0x01
|
||||
]
|
||||
)?; */
|
||||
)?;
|
||||
|
||||
writer.endpoint(&self.standard_bulkin)?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue