mirror of
https://github.com/italicsjenga/usbd-midi.git
synced 2024-12-23 20:31: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]
|
[dependencies]
|
||||||
embedded-hal = "0.2.2"
|
embedded-hal = "0.2.2"
|
||||||
nb = "0.1.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'
|
/// 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
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Note {
|
pub enum Note {
|
||||||
A0 = 21,
|
C1m, Cs1m, D1m, Ds1m, E1m, F1m, Fs1m, G1m, Gs1m, A1m, As1m, B1m,
|
||||||
As0,
|
C0 , Cs0 , D0 , Ds0 , E0 , F0 , Fs0 , G0 , Gs0 , A0 , As0 , B0 ,
|
||||||
B0,
|
C1 , Cs1 , D1 , Ds1 , E1 , F1 , Fs1 , G1 , Gs1 , A1 , As1 , B1 ,
|
||||||
C1,
|
C2 , Cs2 , D2 , Ds2 , E2 , F2 , Fs2 , G2 , Gs2 , A2 , As2 , B2 ,
|
||||||
Cs1,
|
C3 , Cs3 , D3 , Ds3 , E3 , F3 , Fs3 , G3 , Gs3 , A3 , As3 , B3 ,
|
||||||
D1,
|
C4 , Cs4 , D4 , Ds4 , E4 , F4 , Fs4 , G4 , Gs4 , A4 , As4 , B4 ,
|
||||||
Ds1,
|
C5 , Cs5 , D5 , Ds5 , E5 , F5 , Fs5 , G5 , Gs5 , A5 , As5 , B5 ,
|
||||||
E1,
|
C6 , Cs6 , D6 , Ds6 , E6 , F6 , Fs6 , G6 , Gs6 , A6 , As6 , B6 ,
|
||||||
F1,
|
C7 , Cs7 , D7 , Ds7 , E7 , F7 , Fs7 , G7 , Gs7 , A7 , As7 , B7 ,
|
||||||
Fs1,
|
C8 , Cs8 , D8 , Ds8 , E8 , F8 , Fs8 , G8 , Gs8 , A8 , As8 , B8 ,
|
||||||
G1,
|
C9 , Cs9 , D9 , Ds9 , E9 , F9 , Fs9 , G9 , Gs9
|
||||||
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 {
|
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 Db1 : Note = Note::Cs1;
|
||||||
#[allow(non_upper_case_globals)] pub const Eb1 : Note = Note::Ds1;
|
#[allow(non_upper_case_globals)] pub const Eb1 : Note = Note::Ds1;
|
||||||
#[allow(non_upper_case_globals)] pub const Gb1 : Note = Note::Fs1;
|
#[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
|
//These test mainly prove we are generating all the sharps/flats
|
||||||
//(as the same number) correctly.
|
//(as the same number) correctly.
|
||||||
note_test! {
|
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_c1: (Note::C1,24),
|
||||||
note_cs1: (Note::Cs1,25),
|
note_cs1: (Note::Cs1,25),
|
||||||
note_db1: (Note::Db1,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]
|
#![no_std]
|
||||||
mod usb_constants;
|
|
||||||
mod midi_device;
|
|
||||||
mod notes;
|
|
||||||
|
|
||||||
pub use usb_device::{Result,UsbError};
|
mod util;
|
||||||
pub use crate::usb_constants::USB_CLASS_NONE;
|
pub mod data;
|
||||||
pub use crate::midi_device::*;
|
pub mod midi_device;
|
||||||
pub use crate::notes::Note;
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use usb_device::class_prelude::*;
|
use usb_device::class_prelude::*;
|
||||||
use usb_device::Result;
|
use usb_device::Result;
|
||||||
use crate::notes::Note;
|
use crate::data::midi::notes::Note;
|
||||||
use crate::usb_constants::*;
|
use crate::data::usb::constants::*;
|
||||||
|
use crate::data::usb_midi::usb_midi_event_packet::UsbMidiEventPacket;
|
||||||
|
|
||||||
//const MIDI_IN_SIZE: u8 = 0x06;
|
//const MIDI_IN_SIZE: u8 = 0x06;
|
||||||
const MIDI_OUT_SIZE: u8 = 0x09;
|
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> {
|
pub fn note_on(&mut self, chan: u8, note: Note, vel : u8) -> Result<usize> {
|
||||||
let note = note as u8;
|
let note = note as u8;
|
||||||
self.standard_bulkin.write(
|
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