mirror of
https://github.com/italicsjenga/usbd-midi.git
synced 2024-12-23 20:31:30 +11:00
Merge pull request #6 from Windfisch/arbitrary_number_of_ports
Allow using an arbitrary number of ports
This commit is contained in:
commit
2649f116ae
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "usbd-midi"
|
name = "usbd-midi"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
authors = ["beau trepp <beautrepp@gmail.com>"]
|
authors = ["beau trepp <beautrepp@gmail.com>", "Florian Jung <flo@windfis.ch>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
description = "A usb-midi implementation for usb-device"
|
description = "A usb-midi implementation for usb-device"
|
||||||
homepage = "https://github.com/btrepp/usbd-midi"
|
homepage = "https://github.com/btrepp/usbd-midi"
|
||||||
|
|
18
README.md
18
README.md
|
@ -35,7 +35,7 @@ fn main() -> ! {
|
||||||
|
|
||||||
let usb_bus = UsbBus::new(usb);
|
let usb_bus = UsbBus::new(usb);
|
||||||
|
|
||||||
let mut midi = MidiClass::new(&usb_bus);
|
let mut midi = MidiClass::new(&usb_bus, 1, 1);
|
||||||
|
|
||||||
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x5e4))
|
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x5e4))
|
||||||
.product("MIDI Test")
|
.product("MIDI Test")
|
||||||
|
@ -69,3 +69,19 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Using more than one MIDI port
|
||||||
|
|
||||||
|
Calling `MidiClass::new(&usb_bus, N, M);` with `N, M >= 1` to provide more
|
||||||
|
than one input or output port requires the `control-buffer-256` feature of
|
||||||
|
the usb-device crate:
|
||||||
|
|
||||||
|
Cargo.toml:
|
||||||
|
```
|
||||||
|
usb-device = { version = ">=0.2.1", features = ["control-buffer-256"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
Up to 5 in/out pairs can be used this way until we again run out of buffer
|
||||||
|
space. Note that exceeding the available buffer space will silently fail
|
||||||
|
to send the descriptors correctly, no obvious `panic!` will hint the
|
||||||
|
actual problem.
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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 EXTERNAL : u8 = 0x02;
|
||||||
pub const CS_INTERFACE: u8 = 0x24;
|
pub const CS_INTERFACE: u8 = 0x24;
|
||||||
pub const CS_ENDPOINT: u8 = 0x25;
|
pub const CS_ENDPOINT: u8 = 0x25;
|
||||||
pub const HEADER_SUBTYPE: u8 = 0x01;
|
pub const HEADER_SUBTYPE: u8 = 0x01;
|
||||||
|
|
|
@ -16,7 +16,9 @@ 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>,
|
||||||
|
n_in_jacks: u8,
|
||||||
|
n_out_jacks: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MidiReadError {
|
pub enum MidiReadError {
|
||||||
|
@ -24,17 +26,30 @@ pub enum MidiReadError {
|
||||||
UsbError(UsbError)
|
UsbError(UsbError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InvalidArguments;
|
||||||
|
|
||||||
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 and `n_in/out_jacks` embedded input/output jacks (or "cables",
|
||||||
pub fn new(alloc: &UsbBusAllocator<B>) -> MidiClass<'_, B> {
|
/// depending on the terminology).
|
||||||
MidiClass {
|
/// Note that a maximum of 16 in and 16 out jacks are supported.
|
||||||
|
pub fn new(alloc: &UsbBusAllocator<B>, n_in_jacks: u8, n_out_jacks: u8) -> core::result::Result<MidiClass<'_, B>, InvalidArguments> {
|
||||||
|
if n_in_jacks > 16 || n_out_jacks > 16 {
|
||||||
|
return Err(InvalidArguments);
|
||||||
|
}
|
||||||
|
Ok(MidiClass {
|
||||||
standard_ac: alloc.interface(),
|
standard_ac: alloc.interface(),
|
||||||
standard_mc: 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)
|
standard_bulkin: alloc.bulk(MAX_PACKET_SIZE as u16),
|
||||||
}
|
n_in_jacks,
|
||||||
|
n_out_jacks
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn send_bytes(&mut self, buffer: [u8; 4]) -> Result<usize> {
|
||||||
|
self.standard_bulkin.write(&buffer)
|
||||||
|
}
|
||||||
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;MIDI_PACKET_SIZE] = usb_midi.into();
|
let bytes : [u8;MIDI_PACKET_SIZE] = usb_midi.into();
|
||||||
self.standard_bulkin.write(&bytes)
|
self.standard_bulkin.write(&bytes)
|
||||||
|
@ -43,14 +58,34 @@ impl<B: UsbBus> MidiClass<'_, B> {
|
||||||
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
|
pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
|
||||||
self.standard_bulkout.read(buffer)
|
self.standard_bulkout.read(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
|
/// 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
||||||
|
|
||||||
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
|
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
|
||||||
|
|
||||||
//AUDIO CONTROL STANDARD
|
//AUDIO CONTROL STANDARD
|
||||||
|
|
||||||
writer.interface(
|
writer.interface(
|
||||||
self.standard_ac,
|
self.standard_ac,
|
||||||
USB_AUDIO_CLASS,
|
USB_AUDIO_CLASS,
|
||||||
|
@ -79,63 +114,106 @@ impl<B: UsbBus> UsbClass<B> for MidiClass<'_, B> {
|
||||||
0, //no protocol
|
0, //no protocol
|
||||||
)?; //Num endpoints?
|
)?; //Num endpoints?
|
||||||
|
|
||||||
//Streaming extra info
|
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);
|
||||||
|
|
||||||
writer.write(
|
//Streaming extra info
|
||||||
|
writer.write( // len = 7
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
MS_HEADER_SUBTYPE,
|
MS_HEADER_SUBTYPE,
|
||||||
0x00,0x01, //REVISION
|
0x00,0x01, //REVISION
|
||||||
(0x07 + MIDI_OUT_SIZE),0x00 //Total size of class specific descriptors? (little endian?)
|
(midi_streaming_total_length & 0xFF) as u8, ((midi_streaming_total_length >> 8) & 0xFF) as u8
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
//JACKS
|
//JACKS
|
||||||
|
for i in 0..self.n_in_jacks {
|
||||||
|
writer.write( // len = 6 = MIDI_IN_SIZE
|
||||||
|
CS_INTERFACE,
|
||||||
|
&[
|
||||||
|
MIDI_IN_JACK_SUBTYPE,
|
||||||
|
EXTERNAL,
|
||||||
|
self.in_jack_id_ext(i), // id
|
||||||
|
0x00
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
writer.write(
|
for i in 0..self.n_out_jacks {
|
||||||
|
writer.write( // len = 6 = MIDI_IN_SIZE
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
MIDI_IN_JACK_SUBTYPE,
|
MIDI_IN_JACK_SUBTYPE,
|
||||||
EMBEDDED,
|
EMBEDDED,
|
||||||
0x01, // id
|
self.in_jack_id_emb(i), // id
|
||||||
0x00
|
0x00
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
writer.write (
|
for i in 0..self.n_out_jacks {
|
||||||
|
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
|
||||||
|
]
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..self.n_in_jacks {
|
||||||
|
writer.write ( // len = 9 = MIDI_OUT_SIZE
|
||||||
CS_INTERFACE,
|
CS_INTERFACE,
|
||||||
&[
|
&[
|
||||||
MIDI_OUT_JACK_SUBTYPE,
|
MIDI_OUT_JACK_SUBTYPE,
|
||||||
EMBEDDED,
|
EMBEDDED,
|
||||||
0x01,//id
|
self.out_jack_id_emb(i), //id
|
||||||
0x01, // 1 pin
|
0x01, // 1 pin
|
||||||
0x01, // pin 1
|
self.in_jack_id_ext(i), // pin is connected to this entity...
|
||||||
0x01, //sorta vague source pin?
|
0x01, // ...to the first pin
|
||||||
0x00
|
0x00
|
||||||
]
|
]
|
||||||
)?;
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
writer.endpoint(&self.standard_bulkout)?;
|
let mut endpoint_data = [
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_ENDPOINT,
|
|
||||||
&[
|
|
||||||
MS_GENERAL,
|
MS_GENERAL,
|
||||||
0x01,
|
0, // number of jacks. must be filled in!
|
||||||
0x01
|
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
|
||||||
|
|
||||||
|
endpoint_data[1] = self.n_out_jacks;
|
||||||
|
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
|
||||||
|
CS_ENDPOINT,
|
||||||
|
&endpoint_data[0..2+self.n_out_jacks as usize]
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
writer.endpoint(&self.standard_bulkin)?;
|
writer.endpoint(&self.standard_bulkin)?; // len = 7
|
||||||
|
endpoint_data[1] = self.n_in_jacks;
|
||||||
writer.write(
|
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
|
||||||
CS_ENDPOINT,
|
CS_ENDPOINT,
|
||||||
&[
|
&endpoint_data[0..2+self.n_in_jacks as usize]
|
||||||
MS_GENERAL,
|
|
||||||
0x01,
|
|
||||||
0x01
|
|
||||||
]
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let midi_streaming_end_byte = writer.position();
|
||||||
|
assert!(midi_streaming_end_byte - midi_streaming_start_byte == midi_streaming_total_length);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue