Merge pull request #6 from Windfisch/arbitrary_number_of_ports

Allow using an arbitrary number of ports
This commit is contained in:
btrepp 2021-06-26 07:24:36 +08:00 committed by GitHub
commit 2649f116ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 148 additions and 53 deletions

View file

@ -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"

View file

@ -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.

View file

@ -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;

View file

@ -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(())
} }