mirror of
https://github.com/italicsjenga/usbd-midi.git
synced 2024-12-23 12:21:30 +11:00
Refactoring
Swaps a few things around, and adds some better types
This commit is contained in:
parent
273f897ea5
commit
dd37e5efcc
|
@ -9,4 +9,4 @@ edition = "2018"
|
|||
[dependencies]
|
||||
embedded-hal = "0.2.2"
|
||||
nb = "0.1.2"
|
||||
usb-device = {version = "0.2.3", path = "../usb-device" }
|
||||
usb-device = {version = "0.2.3"}
|
||||
|
|
63
src/data/midi/code_index_number.rs
Normal file
63
src/data/midi/code_index_number.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use crate::util::try_from::{TryFrom};
|
||||
|
||||
/// The Code Index Number(CIN) indicates the classification
|
||||
/// of the bytes in the MIDI_x fields
|
||||
pub struct CodeIndexNumber(u8);
|
||||
|
||||
impl TryFrom<u8> for CodeIndexNumber {
|
||||
|
||||
fn try_from(value:u8) -> Option<Self> {
|
||||
if value > 0xF {
|
||||
None
|
||||
} else {
|
||||
Some(CodeIndexNumber(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for CodeIndexNumber {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl CodeIndexNumber {
|
||||
|
||||
/// Miscellaneous function codes. Reserved for future extensions
|
||||
pub const MISC_FUNCTION : CodeIndexNumber = CodeIndexNumber(0x00);
|
||||
/// Cable events. Reserved for future expansion.
|
||||
pub const CABLE_EVENTS : CodeIndexNumber = CodeIndexNumber(0x1);
|
||||
/// Two-byte System Common messages like MTC, SongSelect, etc.
|
||||
pub const SYSTEM_COMMON_LEN2 :CodeIndexNumber = CodeIndexNumber(0x2);
|
||||
/// Three-byte System Common messages like SPP, etc.
|
||||
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;
|
||||
/// Single-byte System Common Message or SysEx ends with following single byte.
|
||||
pub const SYSTEM_COMMON_LEN1 : CodeIndexNumber= CodeIndexNumber(0x5);
|
||||
/// SysEx ends with the following byte
|
||||
pub const SYSEX_ENDS_NEXT1 :CodeIndexNumber = CodeIndexNumber::SYSTEM_COMMON_LEN1;
|
||||
/// SysEx ends with following two bytes
|
||||
pub const SYSEX_ENDS_NEXT2 : CodeIndexNumber = CodeIndexNumber(0x6);
|
||||
/// SysEx ends with following three bytes
|
||||
pub const SYSEX_ENDS_NEXT3 : CodeIndexNumber = CodeIndexNumber(0x7);
|
||||
/// Note - Off
|
||||
pub const NOTE_OFF : CodeIndexNumber = CodeIndexNumber(0x8);
|
||||
/// Note - On
|
||||
pub const NOTE_ON : CodeIndexNumber = CodeIndexNumber(0x9);
|
||||
/// Poly-KeyPress
|
||||
pub const POLY_KEYPRESS : CodeIndexNumber = CodeIndexNumber(0xA);
|
||||
/// Control Change
|
||||
pub const CONTROL_CHANGE : CodeIndexNumber = CodeIndexNumber(0xB);
|
||||
/// Program Change
|
||||
pub const PROGRAM_CHANGE : CodeIndexNumber = CodeIndexNumber(0xC);
|
||||
/// Channel Pressure
|
||||
pub const CHANNEL_PRESSURE : CodeIndexNumber = CodeIndexNumber(0xD);
|
||||
/// Pitch Bend Change
|
||||
pub const PITCHBEND_CHANGE : CodeIndexNumber = CodeIndexNumber(0xE);
|
||||
/// Single Byte
|
||||
pub const SINGLE_BYTE : CodeIndexNumber= CodeIndexNumber(0xF);
|
||||
|
||||
}
|
25
src/data/midi/midi_channel.rs
Normal file
25
src/data/midi/midi_channel.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
use crate::util::try_from::{TryFrom};
|
||||
|
||||
/// The MidiChannel is a value ranging from 0x0 to 0xF
|
||||
/// This is a standard midi concept
|
||||
pub struct MidiChannel(u8);
|
||||
|
||||
|
||||
impl TryFrom<u8> for MidiChannel {
|
||||
|
||||
fn try_from(value:u8) -> Option<MidiChannel> {
|
||||
if value > 0xF {
|
||||
None
|
||||
} else {
|
||||
Some(MidiChannel(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for MidiChannel {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
0
src/data/midi/midi_message.rs
Normal file
0
src/data/midi/midi_message.rs
Normal file
3
src/data/midi/mod.rs
Normal file
3
src/data/midi/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod notes;
|
||||
pub mod code_index_number;
|
||||
pub mod midi_channel;
|
|
@ -1,121 +1,35 @@
|
|||
|
||||
//A simple enum type that represents all the midi 'notes'
|
||||
//note the flat versions are associated constants
|
||||
//but can be referenced like Note::Bb3
|
||||
/// 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)]
|
||||
#[repr(u8)]
|
||||
pub enum Note {
|
||||
A0 = 21,
|
||||
As0,
|
||||
B0,
|
||||
C1,
|
||||
Cs1,
|
||||
D1,
|
||||
Ds1,
|
||||
E1,
|
||||
F1,
|
||||
Fs1,
|
||||
G1,
|
||||
Gs1,
|
||||
A1,
|
||||
As1,
|
||||
B1,
|
||||
C2,
|
||||
Cs2,
|
||||
D2,
|
||||
Ds2,
|
||||
E2,
|
||||
F2,
|
||||
Fs2,
|
||||
G2,
|
||||
Gs2,
|
||||
A2,
|
||||
As2,
|
||||
B2,
|
||||
C3,
|
||||
Cs3,
|
||||
D3,
|
||||
Ds3,
|
||||
E3,
|
||||
F3,
|
||||
Fs3,
|
||||
G3,
|
||||
Gs3,
|
||||
A3,
|
||||
As3,
|
||||
B3,
|
||||
C4,
|
||||
Cs4,
|
||||
D4,
|
||||
Ds4,
|
||||
E4,
|
||||
F4,
|
||||
Fs4,
|
||||
G4,
|
||||
Gs4,
|
||||
A4,
|
||||
As4,
|
||||
B4,
|
||||
C5,
|
||||
Cs5,
|
||||
D5,
|
||||
Ds5,
|
||||
E5,
|
||||
F5,
|
||||
Fs5,
|
||||
G5,
|
||||
Gs5,
|
||||
A5,
|
||||
As5,
|
||||
B5,
|
||||
C6,
|
||||
Cs6,
|
||||
D6,
|
||||
Ds6,
|
||||
E6,
|
||||
F6,
|
||||
Fs6,
|
||||
G6,
|
||||
Gs6,
|
||||
A6,
|
||||
As6,
|
||||
B6,
|
||||
C7,
|
||||
Cs7,
|
||||
D7,
|
||||
Ds7,
|
||||
E7,
|
||||
F7,
|
||||
Fs7,
|
||||
G7,
|
||||
Gs7,
|
||||
A7,
|
||||
As7,
|
||||
B7,
|
||||
C8,
|
||||
Cs8,
|
||||
D8,
|
||||
Ds8,
|
||||
E8,
|
||||
F8,
|
||||
Fs8,
|
||||
G8,
|
||||
Gs8,
|
||||
A8,
|
||||
As8,
|
||||
B8,
|
||||
C9,
|
||||
Cs9,
|
||||
D9,
|
||||
Ds9,
|
||||
E9,
|
||||
F9,
|
||||
Fs9,
|
||||
G9,
|
||||
Gs9
|
||||
C1m, Cs1m, D1m, Ds1m, E1m, F1m, Fs1m, G1m, Gs1m, A1m, As1m, B1m,
|
||||
C0 , Cs0 , D0 , Ds0 , E0 , F0 , Fs0 , G0 , Gs0 , A0 , As0 , B0 ,
|
||||
C1 , Cs1 , D1 , Ds1 , E1 , F1 , Fs1 , G1 , Gs1 , A1 , As1 , B1 ,
|
||||
C2 , Cs2 , D2 , Ds2 , E2 , F2 , Fs2 , G2 , Gs2 , A2 , As2 , B2 ,
|
||||
C3 , Cs3 , D3 , Ds3 , E3 , F3 , Fs3 , G3 , Gs3 , A3 , As3 , B3 ,
|
||||
C4 , Cs4 , D4 , Ds4 , E4 , F4 , Fs4 , G4 , Gs4 , A4 , As4 , B4 ,
|
||||
C5 , Cs5 , D5 , Ds5 , E5 , F5 , Fs5 , G5 , Gs5 , A5 , As5 , B5 ,
|
||||
C6 , Cs6 , D6 , Ds6 , E6 , F6 , Fs6 , G6 , Gs6 , A6 , As6 , B6 ,
|
||||
C7 , Cs7 , D7 , Ds7 , E7 , F7 , Fs7 , G7 , Gs7 , A7 , As7 , B7 ,
|
||||
C8 , Cs8 , D8 , Ds8 , E8 , F8 , Fs8 , G8 , Gs8 , A8 , As8 , B8 ,
|
||||
C9 , Cs9 , D9 , Ds9 , E9 , F9 , Fs9 , G9 , Gs9
|
||||
}
|
||||
|
||||
impl Note {
|
||||
#[allow(non_upper_case_globals)] pub const Db1m : Note = Note::Cs1m;
|
||||
#[allow(non_upper_case_globals)] pub const Eb1m : Note = Note::Ds1m;
|
||||
#[allow(non_upper_case_globals)] pub const Gb1m : Note = Note::Fs1m;
|
||||
#[allow(non_upper_case_globals)] pub const Ab1m : Note = Note::Gs1m;
|
||||
#[allow(non_upper_case_globals)] pub const Bb1m : Note = Note::As1m;
|
||||
#[allow(non_upper_case_globals)] pub const Db0 : Note = Note::Cs0;
|
||||
#[allow(non_upper_case_globals)] pub const Eb0 : Note = Note::Ds0;
|
||||
#[allow(non_upper_case_globals)] pub const Gb0 : Note = Note::Fs0;
|
||||
#[allow(non_upper_case_globals)] pub const Ab0 : Note = Note::Gs0;
|
||||
#[allow(non_upper_case_globals)] pub const Bb0 : Note = Note::As0;
|
||||
#[allow(non_upper_case_globals)] pub const Db1 : Note = Note::Cs1;
|
||||
#[allow(non_upper_case_globals)] pub const Eb1 : Note = Note::Ds1;
|
||||
#[allow(non_upper_case_globals)] pub const Gb1 : Note = Note::Fs1;
|
||||
|
@ -182,6 +96,40 @@ mod tests {
|
|||
//These test mainly prove we are generating all the sharps/flats
|
||||
//(as the same number) correctly.
|
||||
note_test! {
|
||||
note_c1m: (Note::C1m,0),
|
||||
note_cs1m: (Note::Cs1m,1),
|
||||
note_db1m: (Note::Db1m,1),
|
||||
note_d1m: (Note::D1m,2),
|
||||
note_ds1m: (Note::Ds1m,3),
|
||||
note_eb1m: (Note::Eb1m,3),
|
||||
note_e1m: (Note::E1m,4),
|
||||
note_f1m: (Note::F1m,5),
|
||||
note_fs1m: (Note::Fs1m,6),
|
||||
note_gb1m: (Note::Gb1m,6),
|
||||
note_g1m: (Note::G1m,7),
|
||||
note_gs1m: (Note::Gs1m,8),
|
||||
note_ab1m: (Note::Ab1m,8),
|
||||
note_a1m: (Note::A1m,9),
|
||||
note_as1m: (Note::As1m,10),
|
||||
note_bb1m: (Note::Bb1m,10),
|
||||
note_b1m: (Note::B1m,11),
|
||||
note_c0: (Note::C0,12),
|
||||
note_cs0: (Note::Cs0,13),
|
||||
note_db0: (Note::Db0,13),
|
||||
note_d0: (Note::D0,14),
|
||||
note_ds0: (Note::Ds0,15),
|
||||
note_eb0: (Note::Eb0,15),
|
||||
note_e0: (Note::E0,16),
|
||||
note_f0: (Note::F0,17),
|
||||
note_fs0: (Note::Fs0,18),
|
||||
note_gb0: (Note::Gb0,18),
|
||||
note_g0: (Note::G0,19),
|
||||
note_gs0: (Note::Gs0,20),
|
||||
note_ab0: (Note::Ab0,20),
|
||||
note_a0: (Note::A0,21),
|
||||
note_as0: (Note::As0,22),
|
||||
note_bb0: (Note::Bb0,22),
|
||||
note_b0: (Note::B0,23),
|
||||
note_c1: (Note::C1,24),
|
||||
note_cs1: (Note::Cs1,25),
|
||||
note_db1: (Note::Db1,25),
|
3
src/data/mod.rs
Normal file
3
src/data/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod midi;
|
||||
pub mod usb;
|
||||
pub mod usb_midi;
|
1
src/data/usb/mod.rs
Normal file
1
src/data/usb/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod constants;
|
23
src/data/usb_midi/cable_number.rs
Normal file
23
src/data/usb_midi/cable_number.rs
Normal file
|
@ -0,0 +1,23 @@
|
|||
use crate::util::try_from::{TryFrom};
|
||||
|
||||
/// 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
|
||||
pub struct CableNumber(u8);
|
||||
|
||||
impl TryFrom<u8> for CableNumber {
|
||||
|
||||
fn try_from(value:u8) -> Option<Self> {
|
||||
if value > 0xF {
|
||||
None
|
||||
} else {
|
||||
Some(CableNumber(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for CableNumber {
|
||||
fn into(self) -> u8 {
|
||||
self.0
|
||||
}
|
||||
}
|
2
src/data/usb_midi/mod.rs
Normal file
2
src/data/usb_midi/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod usb_midi_event_packet;
|
||||
pub mod cable_number;
|
57
src/data/usb_midi/usb_midi_event_packet.rs
Normal file
57
src/data/usb_midi/usb_midi_event_packet.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::data::usb_midi::cable_number::CableNumber;
|
||||
use crate::data::midi::code_index_number::CodeIndexNumber;
|
||||
use crate::data::midi::notes::Note;
|
||||
use crate::data::midi::midi_channel::MidiChannel;
|
||||
|
||||
/// A packet that communicates with the host
|
||||
/// Note that the payload seems fairly 'open'
|
||||
/// It's contents can depend on the cable/code index number
|
||||
/// but may not!?
|
||||
pub struct UsbMidiEventPacket {
|
||||
cable_number : CableNumber,
|
||||
code_index_number: CodeIndexNumber,
|
||||
payload: [u8;3]
|
||||
}
|
||||
|
||||
/// Combines two nibbles.
|
||||
/// Note that the upper will overflow if greater than 0xF
|
||||
/// The lower will be clamped to the range 0-0xF
|
||||
fn combine_nibble(upper:u8,lower:u8) -> u8 {
|
||||
let upper = upper.overflowing_shl(8).0;
|
||||
let lower = lower & 0xF;
|
||||
upper | lower
|
||||
}
|
||||
|
||||
/// Constructs a note-on midi message given the cable, note and velocity
|
||||
pub fn note_on( cable:CableNumber,
|
||||
channel: MidiChannel,
|
||||
note:Note,
|
||||
velocity: u8) -> UsbMidiEventPacket {
|
||||
|
||||
let code :u8 = CodeIndexNumber::NOTE_ON.into();
|
||||
let channel : u8 = channel.into();
|
||||
|
||||
UsbMidiEventPacket{
|
||||
cable_number : cable,
|
||||
code_index_number : CodeIndexNumber::NOTE_ON,
|
||||
payload : [ code & channel,
|
||||
note as u8,
|
||||
velocity
|
||||
]
|
||||
}
|
||||
}
|
||||
impl Into<[u8;4]> for UsbMidiEventPacket {
|
||||
/// Converts the midi packet into a byte array
|
||||
/// suitable for transfer via usb
|
||||
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);
|
||||
[ header,
|
||||
self.payload[0],
|
||||
self.payload[1],
|
||||
self.payload[2]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
11
src/lib.rs
11
src/lib.rs
|
@ -1,9 +1,6 @@
|
|||
#![no_std]
|
||||
mod usb_constants;
|
||||
mod midi_device;
|
||||
mod notes;
|
||||
|
||||
pub use usb_device::{Result,UsbError};
|
||||
pub use crate::usb_constants::USB_CLASS_NONE;
|
||||
pub use crate::midi_device::*;
|
||||
pub use crate::notes::Note;
|
||||
mod util;
|
||||
pub mod data;
|
||||
pub mod midi_device;
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use usb_device::class_prelude::*;
|
||||
use usb_device::Result;
|
||||
use crate::notes::Note;
|
||||
use crate::usb_constants::*;
|
||||
use crate::data::midi::notes::Note;
|
||||
use crate::data::usb::constants::*;
|
||||
use crate::data::usb_midi::usb_midi_event_packet::UsbMidiEventPacket;
|
||||
|
||||
//const MIDI_IN_SIZE: u8 = 0x06;
|
||||
const MIDI_OUT_SIZE: u8 = 0x09;
|
||||
|
@ -28,6 +29,11 @@ impl<B: UsbBus> MidiClass<'_, B> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send_message(&mut self, usb_midi:UsbMidiEventPacket) -> Result<usize> {
|
||||
let bytes : [u8;4] = usb_midi.into();
|
||||
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(
|
||||
|
|
1
src/util/mod.rs
Normal file
1
src/util/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod try_from;
|
6
src/util/try_from.rs
Normal file
6
src/util/try_from.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
|
||||
/// A version of try from that works on options
|
||||
/// useful for the bounded ints
|
||||
pub trait TryFrom<T> : Sized{
|
||||
fn try_from(value:T) -> Option<Self>;
|
||||
}
|
Loading…
Reference in a new issue