diff --git a/Cargo.toml b/Cargo.toml index 242be37..2ed3e99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,6 @@ license = "MIT" [dependencies] embedded-hal = "0.2.2" -nb = "1.0.0" usb-device = "0.2.3" midi-convert = "0.1.1" midi-types = "0.1.4" - -[dependencies.num_enum] -version = "0.5.1" -default-features = false diff --git a/README.md b/README.md index a721756..c647b2f 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ fn main() -> ! { let mut buffer = [0; 64]; if let Ok(size) = midi.read(&mut buffer) { - let buffer_reader = MidiPacketBufferReader::new(&buffer, size); + let buffer_reader = MidiPacketBufferReader::new(&buffer[0..size]); for packet in buffer_reader.into_iter() { if let Ok(packet) = packet { match packet.message { diff --git a/src/data/usb_midi/cable_number.rs b/src/cable_number.rs similarity index 56% rename from src/data/usb_midi/cable_number.rs rename to src/cable_number.rs index 089a8da..cd08f42 100644 --- a/src/data/usb_midi/cable_number.rs +++ b/src/cable_number.rs @@ -1,62 +1,66 @@ use core::convert::TryFrom; -use crate::data::byte::u4::U4; -/// The Cable Number (CN) is a value ranging from 0x0 to 0xF -/// indicating the number assignment of the Embedded MIDI Jack associated +/// The Cable Number (CN) is a value ranging from 0x0 to 0xF +/// indicating the number assignment of the Embedded MIDI Jack associated /// with the endpoint that is transferring the data -#[derive(Debug,Clone,Copy,Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[repr(u8)] pub enum CableNumber { - Cable0 = 0x0, Cable1 = 0x1, Cable2 = 0x2, Cable3 = 0x3, - Cable4 = 0x4, Cable5 = 0x5, Cable6 = 0x6, Cable7 = 0x7, - Cable8 = 0x8, Cable9 = 0x9, Cable10 = 0xA, Cable11 = 0xB, - Cable12 = 0xC, Cable13 = 0xD, Cable14 = 0xE, Cable15 = 0xF + Cable0 = 0x0, + Cable1 = 0x1, + Cable2 = 0x2, + Cable3 = 0x3, + Cable4 = 0x4, + Cable5 = 0x5, + Cable6 = 0x6, + Cable7 = 0x7, + Cable8 = 0x8, + Cable9 = 0x9, + Cable10 = 0xA, + Cable11 = 0xB, + Cable12 = 0xC, + Cable13 = 0xD, + Cable14 = 0xE, + Cable15 = 0xF, } -#[derive(Debug,Clone,Copy,Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct InvalidCableNumber(u8); impl TryFrom for CableNumber { type Error = InvalidCableNumber; - fn try_from(value:u8) -> Result { + fn try_from(value: u8) -> Result { match value { - x if x == CableNumber::Cable0 as u8 => Ok(CableNumber::Cable0), - x if x == CableNumber::Cable1 as u8 => Ok(CableNumber::Cable1), - x if x == CableNumber::Cable2 as u8 => Ok(CableNumber::Cable2), - x if x == CableNumber::Cable3 as u8 => Ok(CableNumber::Cable3), - x if x == CableNumber::Cable4 as u8 => Ok(CableNumber::Cable4), - x if x == CableNumber::Cable5 as u8 => Ok(CableNumber::Cable5), - x if x == CableNumber::Cable6 as u8 => Ok(CableNumber::Cable6), - x if x == CableNumber::Cable7 as u8 => Ok(CableNumber::Cable7), - x if x == CableNumber::Cable8 as u8 => Ok(CableNumber::Cable8), - x if x == CableNumber::Cable9 as u8 => Ok(CableNumber::Cable9), + x if x == CableNumber::Cable0 as u8 => Ok(CableNumber::Cable0), + x if x == CableNumber::Cable1 as u8 => Ok(CableNumber::Cable1), + x if x == CableNumber::Cable2 as u8 => Ok(CableNumber::Cable2), + x if x == CableNumber::Cable3 as u8 => Ok(CableNumber::Cable3), + x if x == CableNumber::Cable4 as u8 => Ok(CableNumber::Cable4), + x if x == CableNumber::Cable5 as u8 => Ok(CableNumber::Cable5), + x if x == CableNumber::Cable6 as u8 => Ok(CableNumber::Cable6), + x if x == CableNumber::Cable7 as u8 => Ok(CableNumber::Cable7), + x if x == CableNumber::Cable8 as u8 => Ok(CableNumber::Cable8), + x if x == CableNumber::Cable9 as u8 => Ok(CableNumber::Cable9), x if x == CableNumber::Cable10 as u8 => Ok(CableNumber::Cable10), x if x == CableNumber::Cable11 as u8 => Ok(CableNumber::Cable11), x if x == CableNumber::Cable12 as u8 => Ok(CableNumber::Cable12), x if x == CableNumber::Cable13 as u8 => Ok(CableNumber::Cable13), x if x == CableNumber::Cable14 as u8 => Ok(CableNumber::Cable14), x if x == CableNumber::Cable15 as u8 => Ok(CableNumber::Cable15), - _ => Err(InvalidCableNumber(value)) + _ => Err(InvalidCableNumber(value)), } } - } -impl From for u8{ - fn from(value:CableNumber) -> u8 { +impl From for u8 { + fn from(value: CableNumber) -> u8 { value as u8 } } -impl From for U4{ - fn from(value:CableNumber) -> U4 { - U4::from_overflowing_u8(u8::from(value)) - } -} - #[cfg(test)] mod tests { - + use super::*; macro_rules! cable_test { ($($id:ident:$value:expr,)*) => { diff --git a/src/data/usb_midi/code_index_number.rs b/src/code_index_number.rs similarity index 60% rename from src/data/usb_midi/code_index_number.rs rename to src/code_index_number.rs index 5858282..3362215 100644 --- a/src/data/usb_midi/code_index_number.rs +++ b/src/code_index_number.rs @@ -1,15 +1,14 @@ -use core::convert::TryFrom; -use crate::data::byte::u4::U4; use crate::midi_types::MidiMessage; +use core::convert::TryFrom; -/// The Code Index Number(CIN) indicates the classification +/// The Code Index Number(CIN) indicates the classification /// of the bytes in the MIDI_x fields pub struct CodeIndexNumber(u8); pub struct InvalidCodeIndexNumber(u8); impl TryFrom for CodeIndexNumber { type Error = InvalidCodeIndexNumber; - fn try_from(value:u8) -> Result { + fn try_from(value: u8) -> Result { if value > 0xF { Err(InvalidCodeIndexNumber(value)) } else { @@ -18,52 +17,49 @@ impl TryFrom for CodeIndexNumber { } } -impl From for U4{ - fn from(value:CodeIndexNumber) -> U4{ - U4::from_overflowing_u8(value.0) +impl From for u8 { + fn from(value: CodeIndexNumber) -> u8 { + value.0 } } - - impl CodeIndexNumber { - /// Miscellaneous function codes. Reserved for future extensions - pub const MISC_FUNCTION : CodeIndexNumber = CodeIndexNumber(0x00); + pub const MISC_FUNCTION: CodeIndexNumber = CodeIndexNumber(0x00); /// Cable events. Reserved for future expansion. - pub const CABLE_EVENTS : CodeIndexNumber = CodeIndexNumber(0x1); + pub const CABLE_EVENTS: CodeIndexNumber = CodeIndexNumber(0x1); /// Two-byte System Common messages like MTC, SongSelect, etc. - pub const SYSTEM_COMMON_LEN2 :CodeIndexNumber = CodeIndexNumber(0x2); + pub const SYSTEM_COMMON_LEN2: CodeIndexNumber = CodeIndexNumber(0x2); /// Three-byte System Common messages like SPP, etc. - pub const SYSTEM_COMMON_LEN3 :CodeIndexNumber = CodeIndexNumber(0x3); + pub const SYSTEM_COMMON_LEN3: CodeIndexNumber = CodeIndexNumber(0x3); /// SysEx starts or continues - pub const SYSEX_STARTS : CodeIndexNumber = CodeIndexNumber(0x4); - pub const SYSEX_CONTINUES : CodeIndexNumber = CodeIndexNumber::SYSEX_STARTS; + pub const SYSEX_STARTS: CodeIndexNumber = CodeIndexNumber(0x4); + pub const SYSEX_CONTINUES: CodeIndexNumber = CodeIndexNumber::SYSEX_STARTS; /// Single-byte System Common Message or SysEx ends with following single byte. - pub const SYSTEM_COMMON_LEN1 : CodeIndexNumber= CodeIndexNumber(0x5); + pub const SYSTEM_COMMON_LEN1: CodeIndexNumber = CodeIndexNumber(0x5); /// SysEx ends with the following byte - pub const SYSEX_ENDS_NEXT1 :CodeIndexNumber = CodeIndexNumber::SYSTEM_COMMON_LEN1; + pub const SYSEX_ENDS_NEXT1: CodeIndexNumber = CodeIndexNumber::SYSTEM_COMMON_LEN1; /// SysEx ends with following two bytes - pub const SYSEX_ENDS_NEXT2 : CodeIndexNumber = CodeIndexNumber(0x6); + pub const SYSEX_ENDS_NEXT2: CodeIndexNumber = CodeIndexNumber(0x6); /// SysEx ends with following three bytes - pub const SYSEX_ENDS_NEXT3 : CodeIndexNumber = CodeIndexNumber(0x7); + pub const SYSEX_ENDS_NEXT3: CodeIndexNumber = CodeIndexNumber(0x7); /// Note - Off - pub const NOTE_OFF : CodeIndexNumber = CodeIndexNumber(0x8); + pub const NOTE_OFF: CodeIndexNumber = CodeIndexNumber(0x8); /// Note - On - pub const NOTE_ON : CodeIndexNumber = CodeIndexNumber(0x9); + pub const NOTE_ON: CodeIndexNumber = CodeIndexNumber(0x9); /// Poly-KeyPress - pub const POLY_KEYPRESS : CodeIndexNumber = CodeIndexNumber(0xA); + pub const POLY_KEYPRESS: CodeIndexNumber = CodeIndexNumber(0xA); /// Control Change - pub const CONTROL_CHANGE : CodeIndexNumber = CodeIndexNumber(0xB); + pub const CONTROL_CHANGE: CodeIndexNumber = CodeIndexNumber(0xB); /// Program Change - pub const PROGRAM_CHANGE : CodeIndexNumber = CodeIndexNumber(0xC); + pub const PROGRAM_CHANGE: CodeIndexNumber = CodeIndexNumber(0xC); /// Channel Pressure - pub const CHANNEL_PRESSURE : CodeIndexNumber = CodeIndexNumber(0xD); + pub const CHANNEL_PRESSURE: CodeIndexNumber = CodeIndexNumber(0xD); /// Pitch Bend Change - pub const PITCHBEND_CHANGE : CodeIndexNumber = CodeIndexNumber(0xE); + pub const PITCHBEND_CHANGE: CodeIndexNumber = CodeIndexNumber(0xE); /// Single Byte - pub const SINGLE_BYTE : CodeIndexNumber= CodeIndexNumber(0xF); - + pub const SINGLE_BYTE: CodeIndexNumber = CodeIndexNumber(0xF); + pub fn find_from_message(value: &MidiMessage) -> CodeIndexNumber { match value { MidiMessage::NoteOn(..) => CodeIndexNumber::NOTE_ON, diff --git a/src/data/byte/mod.rs b/src/data/byte/mod.rs deleted file mode 100644 index ae800e1..0000000 --- a/src/data/byte/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod u4; diff --git a/src/data/byte/u4.rs b/src/data/byte/u4.rs deleted file mode 100644 index eb60eae..0000000 --- a/src/data/byte/u4.rs +++ /dev/null @@ -1,46 +0,0 @@ -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 for U4{ - type Error = InvalidU4; - - fn try_from(value:u8) -> Result { - if value > U4::MAX.0 { - Err(InvalidU4(value)) - } else { - Ok(U4(value)) - } - } -} - -impl From 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) - } -} diff --git a/src/data/mod.rs b/src/data/mod.rs deleted file mode 100644 index b2c2b0a..0000000 --- a/src/data/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod usb; -pub mod usb_midi; -pub mod byte; diff --git a/src/data/usb/constants.rs b/src/data/usb/constants.rs deleted file mode 100644 index 293bbe6..0000000 --- a/src/data/usb/constants.rs +++ /dev/null @@ -1,16 +0,0 @@ -//This should be better organized in future - -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_OUT_JACK_SUBTYPE : u8 = 0x03; -pub const EMBEDDED : u8 = 0x01; -pub const EXTERNAL : u8 = 0x02; -pub const CS_INTERFACE: u8 = 0x24; -pub const CS_ENDPOINT: u8 = 0x25; -pub const HEADER_SUBTYPE: u8 = 0x01; -pub const MS_HEADER_SUBTYPE: u8 = 0x01; -pub const MS_GENERAL: u8 = 0x01; - diff --git a/src/data/usb/mod.rs b/src/data/usb/mod.rs deleted file mode 100644 index 987b09f..0000000 --- a/src/data/usb/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod constants; \ No newline at end of file diff --git a/src/data/usb_midi/midi_packet_reader.rs b/src/data/usb_midi/midi_packet_reader.rs deleted file mode 100644 index b9149b1..0000000 --- a/src/data/usb_midi/midi_packet_reader.rs +++ /dev/null @@ -1,36 +0,0 @@ -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; - - fn next(&mut self) -> Option { - 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 - } -} \ No newline at end of file diff --git a/src/data/usb_midi/mod.rs b/src/data/usb_midi/mod.rs deleted file mode 100644 index f3ba9b0..0000000 --- a/src/data/usb_midi/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod usb_midi_event_packet; -pub mod cable_number; -pub mod code_index_number; -pub mod midi_packet_reader; \ No newline at end of file diff --git a/src/data/usb_midi/usb_midi_event_packet.rs b/src/event_packet.rs similarity index 63% rename from src/data/usb_midi/usb_midi_event_packet.rs rename to src/event_packet.rs index 653a8d6..a39b838 100644 --- a/src/data/usb_midi/usb_midi_event_packet.rs +++ b/src/event_packet.rs @@ -1,34 +1,26 @@ -use crate::data::usb_midi::cable_number::CableNumber; -use crate::data::usb_midi::code_index_number::CodeIndexNumber; -use crate::data::byte::u4::U4; -use crate:: midi_types::MidiMessage; -use core::convert::TryFrom; -use midi_convert::{ - MidiParseError, - MidiRenderSlice, - MidiTryParseSlice, +use crate::{ + cable_number::CableNumber, + code_index_number::CodeIndexNumber, + midi_convert::{MidiParseError, MidiRenderSlice, MidiTryParseSlice}, + midi_types::MidiMessage, }; - +use core::convert::TryFrom; /// A packet that communicates with the host /// Currently supported is sending the specified normal midi /// message over the supplied cable number #[derive(Debug, PartialEq)] pub struct UsbMidiEventPacket { - pub cable_number : CableNumber, - pub message: MidiMessage + pub cable_number: CableNumber, + pub message: MidiMessage, } -impl From for [u8;4] { - fn from(value:UsbMidiEventPacket) -> [u8;4] { - let message= value.message; - let cable_number = U4::from(value.cable_number); - let index_number = { - let code_index = - CodeIndexNumber::find_from_message(&message); - U4::from(code_index) - }; - let header = U4::combine(cable_number,index_number); +impl From for [u8; 4] { + fn from(value: UsbMidiEventPacket) -> [u8; 4] { + let message = value.message; + let cable_number: u8 = value.cable_number.into(); + let index_number: u8 = CodeIndexNumber::find_from_message(&message).into(); + let header = cable_number << 4 | index_number; let mut data: [u8; 4] = [header, 0, 0, 0]; message.render_slice(&mut data[1..]); @@ -36,9 +28,19 @@ impl From for [u8;4] { } } -#[derive(Debug)] +impl From<(CableNumber, MidiMessage)> for UsbMidiEventPacket { + fn from(value: (CableNumber, MidiMessage)) -> Self { + let (cable_number, message) = value; + Self { + cable_number, + message, + } + } +} + +#[derive(Debug, PartialEq, Clone)] pub enum MidiPacketParsingError { - MissingCableNumber, + MissingHeader, MissingDataPacket, ParseError(MidiParseError), } @@ -47,50 +49,43 @@ impl TryFrom<&[u8]> for UsbMidiEventPacket { type Error = MidiPacketParsingError; fn try_from(value: &[u8]) -> Result { - let raw_cable_number = match value.get(0) { + let raw_cable_number = match value.first() { Some(byte) => *byte >> 4, - None => return Err(MidiPacketParsingError::MissingCableNumber) + None => return Err(MidiPacketParsingError::MissingHeader), }; - let cable_number = CableNumber::try_from(u8::from(raw_cable_number)).expect("(u8 >> 4) < 16"); + let cable_number = + CableNumber::try_from(raw_cable_number).expect("(u8 >> 4) < 16"); let message_body = match value.get(1..) { Some(bytes) => bytes, - None => return Err(MidiPacketParsingError::MissingDataPacket) + None => return Err(MidiPacketParsingError::MissingDataPacket), }; - let message = MidiMessage::try_parse_slice(message_body).map_err(|e| MidiPacketParsingError::ParseError(e))?; + let message = MidiMessage::try_parse_slice(message_body) + .map_err(MidiPacketParsingError::ParseError)?; Ok(UsbMidiEventPacket { cable_number, - message + message, }) } } -impl UsbMidiEventPacket{ - - pub fn from_midi(cable:CableNumber, midi:MidiMessage) - -> UsbMidiEventPacket{ - UsbMidiEventPacket{ - cable_number : cable, - message : midi - } - } -} - #[cfg(test)] mod tests { + use crate::{ + cable_number::CableNumber::{Cable0, Cable1}, + event_packet::UsbMidiEventPacket, + midi_types::{Channel, Control, MidiMessage, Note, Program, Value14, Value7}, + }; use core::convert::TryFrom; - use crate::data::usb_midi::usb_midi_event_packet::UsbMidiEventPacket; - use crate::midi_types::{Channel, MidiMessage, Value7, Value14, Note, Program, Control}; - use crate::data::usb_midi::cable_number::CableNumber::{Cable0,Cable1}; macro_rules! decode_message_test { ($($id:ident:$value:expr,)*) => { $( #[test] fn $id() { - let (usb_midi_data_packet,expected) = $value; + let (usb_midi_data_packet, expected) = $value; let message = UsbMidiEventPacket::try_from(&usb_midi_data_packet[..]).unwrap(); assert_eq!(expected, message); } diff --git a/src/lib.rs b/src/lib.rs index 83c564a..23f1a1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,29 @@ #![no_std] -mod util; -pub mod data; -pub mod midi_device; - +/// re-exports +pub use midi_convert; pub use midi_types; + +pub use { + cable_number::{CableNumber, InvalidCableNumber}, + event_packet::{MidiPacketParsingError, UsbMidiEventPacket}, + midi_device::{MidiClass, MidiClassInvalidArgs}, + packet_reader::MidiPacketBufferReader, +}; + +pub mod constants { + 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_PACKET_SIZE: usize = 4; + pub const MAX_PACKET_SIZE: usize = 64; +} + +mod cable_number; +mod code_index_number; +mod event_packet; +mod midi_device; +mod packet_reader; + diff --git a/src/midi_device.rs b/src/midi_device.rs index 60a9761..2e68910 100644 --- a/src/midi_device.rs +++ b/src/midi_device.rs @@ -1,49 +1,52 @@ -use usb_device::class_prelude::*; -use usb_device::Result; -use crate::data::usb::constants::*; -use crate::data::usb_midi::usb_midi_event_packet::{UsbMidiEventPacket, MidiPacketParsingError}; +use crate::{constants::*, event_packet::UsbMidiEventPacket}; +use usb_device::{class_prelude::*, Result}; 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> { +const MIDI_IN_JACK_SUBTYPE: u8 = 0x02; +const MIDI_OUT_JACK_SUBTYPE: u8 = 0x03; +const EMBEDDED: u8 = 0x01; +const EXTERNAL: u8 = 0x02; +const CS_INTERFACE: u8 = 0x24; +const CS_ENDPOINT: u8 = 0x25; +const HEADER_SUBTYPE: u8 = 0x01; +const MS_HEADER_SUBTYPE: u8 = 0x01; +const MS_GENERAL: u8 = 0x01; + +/// USB Midi Device +pub struct MidiClass<'a, B: UsbBus> { standard_ac: InterfaceNumber, standard_mc: InterfaceNumber, standard_bulkout: EndpointOut<'a, B>, - standard_bulkin: EndpointIn<'a,B>, + standard_bulkin: EndpointIn<'a, B>, n_in_jacks: u8, n_out_jacks: u8, } -pub enum MidiReadError { - ParsingFailed(MidiPacketParsingError), - UsbError(UsbError) -} - +/// Invalid construction error struct #[derive(Debug)] -pub struct InvalidArguments; +pub struct MidiClassInvalidArgs; impl MidiClass<'_, B> { /// Creates a new MidiClass with the provided UsbBus and `n_in/out_jacks` embedded input/output jacks (or "cables", /// depending on the terminology). /// Note that a maximum of 16 in and 16 out jacks are supported. - pub fn new(alloc: &UsbBusAllocator, n_in_jacks: u8, n_out_jacks: u8) -> core::result::Result, InvalidArguments> { + pub fn new( + alloc: &UsbBusAllocator, + n_in_jacks: u8, + n_out_jacks: u8, + ) -> core::result::Result, MidiClassInvalidArgs> { if n_in_jacks > 16 || n_out_jacks > 16 { - return Err(InvalidArguments); + return Err(MidiClassInvalidArgs); } Ok(MidiClass { standard_ac: alloc.interface(), standard_mc: alloc.interface(), - standard_bulkout : alloc.bulk(MAX_PACKET_SIZE as u16), + standard_bulkout: alloc.bulk(MAX_PACKET_SIZE as u16), standard_bulkin: alloc.bulk(MAX_PACKET_SIZE as u16), n_in_jacks, - n_out_jacks + n_out_jacks, }) } @@ -51,7 +54,7 @@ impl MidiClass<'_, B> { self.standard_bulkin.write(&buffer) } pub fn send_message(&mut self, usb_midi: UsbMidiEventPacket) -> Result { - let bytes : [u8;MIDI_PACKET_SIZE] = usb_midi.into(); + let bytes: [u8; MIDI_PACKET_SIZE] = usb_midi.into(); self.standard_bulkin.write(&bytes) } @@ -62,35 +65,34 @@ impl MidiClass<'_, B> { /// calculates the index'th external midi in jack id fn in_jack_id_ext(&self, index: u8) -> u8 { debug_assert!(index < self.n_in_jacks); - return 2 * index + 1; + 2 * index + 1 } /// calculates the index'th embedded midi out jack id fn out_jack_id_emb(&self, index: u8) -> u8 { debug_assert!(index < self.n_in_jacks); - return 2 * index + 2; + 2 * index + 2 } /// calculates the index'th external midi out jack id fn out_jack_id_ext(&self, index: u8) -> u8 { debug_assert!(index < self.n_out_jacks); - return 2 * self.n_in_jacks + 2 * index + 1; + 2 * self.n_in_jacks + 2 * index + 1 } /// calculates the index'th embedded midi in jack id fn in_jack_id_emb(&self, index: u8) -> u8 { debug_assert!(index < self.n_out_jacks); - return 2 * self.n_in_jacks + 2 * index + 2; + 2 * self.n_in_jacks + 2 * index + 2 } } impl UsbClass for MidiClass<'_, B> { - - fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { + fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { //AUDIO CONTROL STANDARD writer.interface( self.standard_ac, USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, - 0 //no protocol, + 0, //no protocol, )?; // AUDIO CONTROL EXTRA INFO @@ -98,11 +100,13 @@ impl UsbClass for MidiClass<'_, B> { CS_INTERFACE, &[ HEADER_SUBTYPE, - 0x00,0x01, // REVISION - 0x09,0x00, //SIZE of class specific descriptions + 0x00, + 0x01, // REVISION + 0x09, + 0x00, //SIZE of class specific descriptions 0x01, //Number of streaming interfaces - 0x01 // MIDIStreaming interface 1 belongs to this AC interface - ] + 0x01, // MIDIStreaming interface 1 belongs to this AC interface + ], )?; //Streaming Standard @@ -115,79 +119,90 @@ impl UsbClass for MidiClass<'_, B> { )?; //Num endpoints? let midi_streaming_start_byte = writer.position(); - let midi_streaming_total_length = - 7 + (self.n_in_jacks + self.n_out_jacks) as usize * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize - + 7 + (4+self.n_out_jacks as usize) + 7 + (4+self.n_in_jacks as usize); + let midi_streaming_total_length = 7 + + (self.n_in_jacks + self.n_out_jacks) as usize + * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize + + 7 + + (4 + self.n_out_jacks as usize) + + 7 + + (4 + self.n_in_jacks as usize); //Streaming extra info - writer.write( // len = 7 + writer.write( + // len = 7 CS_INTERFACE, &[ MS_HEADER_SUBTYPE, - 0x00,0x01, //REVISION - (midi_streaming_total_length & 0xFF) as u8, ((midi_streaming_total_length >> 8) & 0xFF) as u8 - ] + 0x00, + 0x01, //REVISION + (midi_streaming_total_length & 0xFF) as u8, + ((midi_streaming_total_length >> 8) & 0xFF) as u8, + ], )?; //JACKS for i in 0..self.n_in_jacks { - writer.write( // len = 6 = MIDI_IN_SIZE + writer.write( + // len = 6 = MIDI_IN_SIZE CS_INTERFACE, &[ MIDI_IN_JACK_SUBTYPE, EXTERNAL, self.in_jack_id_ext(i), // id - 0x00 - ] + 0x00, + ], )?; } for i in 0..self.n_out_jacks { - writer.write( // len = 6 = MIDI_IN_SIZE + writer.write( + // len = 6 = MIDI_IN_SIZE CS_INTERFACE, &[ MIDI_IN_JACK_SUBTYPE, EMBEDDED, self.in_jack_id_emb(i), // id - 0x00 - ] + 0x00, + ], )?; } for i in 0..self.n_out_jacks { - writer.write ( // len = 9 = MIDI_OUT_SIZE + writer.write( + // len = 9 = MIDI_OUT_SIZE CS_INTERFACE, &[ MIDI_OUT_JACK_SUBTYPE, EXTERNAL, self.out_jack_id_ext(i), //id - 0x01, // 1 pin - self.in_jack_id_emb(i), // pin is connected to this entity... - 0x01, // ...to the first pin - 0x00 - ] + 0x01, // 1 pin + self.in_jack_id_emb(i), // pin is connected to this entity... + 0x01, // ...to the first pin + 0x00, + ], )?; } for i in 0..self.n_in_jacks { - writer.write ( // len = 9 = MIDI_OUT_SIZE + writer.write( + // len = 9 = MIDI_OUT_SIZE CS_INTERFACE, &[ MIDI_OUT_JACK_SUBTYPE, EMBEDDED, self.out_jack_id_emb(i), //id - 0x01, // 1 pin - self.in_jack_id_ext(i), // pin is connected to this entity... - 0x01, // ...to the first pin - 0x00 - ] + 0x01, // 1 pin + self.in_jack_id_ext(i), // pin is connected to this entity... + 0x01, // ...to the first pin + 0x00, + ], )?; } let mut endpoint_data = [ - MS_GENERAL, - 0, // number of jacks. must be filled in! - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // jack mappings. must be filled in and cropped. + MS_GENERAL, 0, // number of jacks. must be filled in! + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // jack mappings. must be filled in and cropped. ]; writer.endpoint(&self.standard_bulkout)?; // len = 7 @@ -196,9 +211,10 @@ impl UsbClass for MidiClass<'_, B> { for i in 0..self.n_out_jacks { endpoint_data[2 + i as usize] = self.in_jack_id_emb(i); } - writer.write( // len = 4 + self.n_out_jacks + writer.write( + // len = 4 + self.n_out_jacks CS_ENDPOINT, - &endpoint_data[0..2+self.n_out_jacks as usize] + &endpoint_data[0..2 + self.n_out_jacks as usize], )?; writer.endpoint(&self.standard_bulkin)?; // len = 7 @@ -206,9 +222,10 @@ impl UsbClass for MidiClass<'_, B> { for i in 0..self.n_in_jacks { endpoint_data[2 + i as usize] = self.out_jack_id_emb(i); } - writer.write( // len = 4 + self.n_in_jacks + writer.write( + // len = 4 + self.n_in_jacks CS_ENDPOINT, - &endpoint_data[0..2+self.n_in_jacks as usize] + &endpoint_data[0..2 + self.n_in_jacks as usize], )?; let midi_streaming_end_byte = writer.position(); @@ -216,5 +233,4 @@ impl UsbClass for MidiClass<'_, B> { Ok(()) } - } diff --git a/src/packet_reader.rs b/src/packet_reader.rs new file mode 100644 index 0000000..099b4af --- /dev/null +++ b/src/packet_reader.rs @@ -0,0 +1,150 @@ +use crate::{ + constants::MIDI_PACKET_SIZE, + event_packet::{MidiPacketParsingError, UsbMidiEventPacket}, +}; +use core::convert::TryFrom; + +/// Wrapper for parsing/iterating `UsbMidiEventPacket` messages from a slice +pub struct MidiPacketBufferReader<'a>(core::slice::Chunks<'a, u8>); + +impl<'a> MidiPacketBufferReader<'a> { + pub fn new(buffer: &'a [u8]) -> Self { + let inner = buffer.chunks(MIDI_PACKET_SIZE); + MidiPacketBufferReader(inner) + } +} + +impl<'a> Iterator for MidiPacketBufferReader<'a> { + type Item = Result; + + fn next(&mut self) -> Option { + self.0 + .next() + .map(UsbMidiEventPacket::try_from) + } +} + +impl<'a> ExactSizeIterator for MidiPacketBufferReader<'a> { + fn len(&self) -> usize { + self.0.len() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::midi_convert::MidiParseError; + use crate::midi_types::{Channel, Control, MidiMessage, Note, Program, Value14, Value7}; + const VALID_BUF: [u8; 64] = [ + 9, 144, 36, 127, //note on + 8, 128, 36, 0, //note off + 10, 160, 36, 64, //poly after touch (key pressure) + 28, 192, 127, 0, //prog change + 13, 208, 127, 0, //channel pressure + 14, 224, 64, 32, //pitch bend + 11, 177, 1, 32, //control change + 11, 177, 1, 123, //control change + //repeat + 9, 144, 36, 127, //note on + 8, 128, 36, 0, //note off + 10, 160, 36, 64, //poly after touch (key pressure) + 28, 192, 127, 0, //prog change + 13, 208, 127, 0, //channel pressure + 14, 224, 64, 32, //pitch bend + 11, 177, 1, 32, //control change + 11, 177, 1, 123, //control change + ]; + + #[test] + fn read() { + let reader = MidiPacketBufferReader::new(&VALID_BUF); + let mut iter = reader.into_iter(); + + assert_eq!(iter.len(), 16); + + //repeats 2x + for _ in 0..2 { + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::NoteOn(Channel::from(0), Note::from(36), Value7::from(127)) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::NoteOff(Channel::from(0), Note::from(36), Value7::from(0)) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::KeyPressure(Channel::from(0), Note::from(36), Value7::from(64)) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::ProgramChange(Channel::from(0), Program::from(127)) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::ChannelPressure(Channel::from(0), Value7::from(127)) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::PitchBendChange(Channel::from(0), Value14::from((64, 32))) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::ControlChange(Channel::from(1), Control::from(1), Value7::from(32)) + ); + + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::ControlChange(Channel::from(1), Control::from(1), Value7::from(123)) + ); + } + assert_eq!(None, iter.next()); + } + + #[test] + fn read_partial() { + let reader = MidiPacketBufferReader::new(&VALID_BUF[0..4]); + let mut iter = reader.into_iter(); + + assert_eq!(iter.len(), 1); + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::NoteOn(Channel::from(0), Note::from(36), Value7::from(127)) + ); + assert_eq!(None, iter.next()); + + for len in 5..8 { + let reader = MidiPacketBufferReader::new(&VALID_BUF[0..len]); + let mut iter = reader.into_iter(); + + assert_eq!(iter.len(), 2); + let v = iter.next().unwrap().unwrap(); + assert_eq!( + v.message, + MidiMessage::NoteOn(Channel::from(0), Note::from(36), Value7::from(127)) + ); + assert_eq!( + Some(Err(MidiPacketParsingError::ParseError( + MidiParseError::BufferTooShort + ))), + iter.next() + ); + assert_eq!(None, iter.next()); + } + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs deleted file mode 100644 index e69de29..0000000