228 lines
8.0 KiB
Rust
228 lines
8.0 KiB
Rust
//! MIDI class implementation.
|
|
|
|
use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut};
|
|
use crate::Builder;
|
|
|
|
/// This should be used as `device_class` when building the `UsbDevice`.
|
|
pub const USB_AUDIO_CLASS: u8 = 0x01;
|
|
|
|
const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01;
|
|
const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03;
|
|
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;
|
|
const PROTOCOL_NONE: u8 = 0x00;
|
|
const MIDI_IN_SIZE: u8 = 0x06;
|
|
const MIDI_OUT_SIZE: u8 = 0x09;
|
|
|
|
/// Packet level implementation of a USB MIDI device.
|
|
///
|
|
/// This class can be used directly and it has the least overhead due to directly reading and
|
|
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like port.
|
|
/// The following constraints must be followed if you use this class directly:
|
|
///
|
|
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes.
|
|
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes.
|
|
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
|
|
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
|
|
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
|
|
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
|
|
pub struct MidiClass<'d, D: Driver<'d>> {
|
|
read_ep: D::EndpointOut,
|
|
write_ep: D::EndpointIn,
|
|
}
|
|
|
|
impl<'d, D: Driver<'d>> MidiClass<'d, D> {
|
|
/// Creates a new MidiClass with the provided UsbBus, number of input and output jacks and max_packet_size in bytes.
|
|
/// For full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
|
|
pub fn new(builder: &mut Builder<'d, D>, n_in_jacks: u8, n_out_jacks: u8, max_packet_size: u16) -> Self {
|
|
let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE);
|
|
|
|
// Audio control interface
|
|
let mut iface = func.interface();
|
|
let audio_if = iface.interface_number();
|
|
let midi_if = u8::from(audio_if) + 1;
|
|
let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None);
|
|
alt.descriptor(CS_INTERFACE, &[HEADER_SUBTYPE, 0x00, 0x01, 0x09, 0x00, 0x01, midi_if]);
|
|
|
|
// MIDIStreaming interface
|
|
let mut iface = func.interface();
|
|
let _midi_if = iface.interface_number();
|
|
let mut alt = iface.alt_setting(USB_AUDIO_CLASS, USB_MIDISTREAMING_SUBCLASS, PROTOCOL_NONE, None);
|
|
|
|
let midi_streaming_total_length = 7
|
|
+ (n_in_jacks + n_out_jacks) as usize * (MIDI_IN_SIZE + MIDI_OUT_SIZE) as usize
|
|
+ 7
|
|
+ (4 + n_out_jacks as usize)
|
|
+ 7
|
|
+ (4 + n_in_jacks as usize);
|
|
|
|
alt.descriptor(
|
|
CS_INTERFACE,
|
|
&[
|
|
MS_HEADER_SUBTYPE,
|
|
0x00,
|
|
0x01,
|
|
(midi_streaming_total_length & 0xFF) as u8,
|
|
((midi_streaming_total_length >> 8) & 0xFF) as u8,
|
|
],
|
|
);
|
|
|
|
// Calculates the index'th external midi in jack id
|
|
let in_jack_id_ext = |index| 2 * index + 1;
|
|
// Calculates the index'th embedded midi out jack id
|
|
let out_jack_id_emb = |index| 2 * index + 2;
|
|
// Calculates the index'th external midi out jack id
|
|
let out_jack_id_ext = |index| 2 * n_in_jacks + 2 * index + 1;
|
|
// Calculates the index'th embedded midi in jack id
|
|
let in_jack_id_emb = |index| 2 * n_in_jacks + 2 * index + 2;
|
|
|
|
for i in 0..n_in_jacks {
|
|
alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EXTERNAL, in_jack_id_ext(i), 0x00]);
|
|
}
|
|
|
|
for i in 0..n_out_jacks {
|
|
alt.descriptor(CS_INTERFACE, &[MIDI_IN_JACK_SUBTYPE, EMBEDDED, in_jack_id_emb(i), 0x00]);
|
|
}
|
|
|
|
for i in 0..n_out_jacks {
|
|
alt.descriptor(
|
|
CS_INTERFACE,
|
|
&[
|
|
MIDI_OUT_JACK_SUBTYPE,
|
|
EXTERNAL,
|
|
out_jack_id_ext(i),
|
|
0x01,
|
|
in_jack_id_emb(i),
|
|
0x01,
|
|
0x00,
|
|
],
|
|
);
|
|
}
|
|
|
|
for i in 0..n_in_jacks {
|
|
alt.descriptor(
|
|
CS_INTERFACE,
|
|
&[
|
|
MIDI_OUT_JACK_SUBTYPE,
|
|
EMBEDDED,
|
|
out_jack_id_emb(i),
|
|
0x01,
|
|
in_jack_id_ext(i),
|
|
0x01,
|
|
0x00,
|
|
],
|
|
);
|
|
}
|
|
|
|
let mut endpoint_data = [
|
|
MS_GENERAL, 0, // Number of jacks
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Jack mappings
|
|
];
|
|
endpoint_data[1] = n_out_jacks;
|
|
for i in 0..n_out_jacks {
|
|
endpoint_data[2 + i as usize] = in_jack_id_emb(i);
|
|
}
|
|
let read_ep = alt.endpoint_bulk_out(max_packet_size);
|
|
alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_out_jacks as usize]);
|
|
|
|
endpoint_data[1] = n_in_jacks;
|
|
for i in 0..n_in_jacks {
|
|
endpoint_data[2 + i as usize] = out_jack_id_emb(i);
|
|
}
|
|
let write_ep = alt.endpoint_bulk_in(max_packet_size);
|
|
alt.descriptor(CS_ENDPOINT, &endpoint_data[0..2 + n_in_jacks as usize]);
|
|
|
|
MidiClass { read_ep, write_ep }
|
|
}
|
|
|
|
/// Gets the maximum packet size in bytes.
|
|
pub fn max_packet_size(&self) -> u16 {
|
|
// The size is the same for both endpoints.
|
|
self.read_ep.info().max_packet_size
|
|
}
|
|
|
|
/// Writes a single packet into the IN endpoint.
|
|
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
|
|
self.write_ep.write(data).await
|
|
}
|
|
|
|
/// Reads a single packet from the OUT endpoint.
|
|
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
|
|
self.read_ep.read(data).await
|
|
}
|
|
|
|
/// Waits for the USB host to enable this interface
|
|
pub async fn wait_connection(&mut self) {
|
|
self.read_ep.wait_enabled().await
|
|
}
|
|
|
|
/// Split the class into a sender and receiver.
|
|
///
|
|
/// This allows concurrently sending and receiving packets from separate tasks.
|
|
pub fn split(self) -> (Sender<'d, D>, Receiver<'d, D>) {
|
|
(
|
|
Sender {
|
|
write_ep: self.write_ep,
|
|
},
|
|
Receiver { read_ep: self.read_ep },
|
|
)
|
|
}
|
|
}
|
|
|
|
/// Midi class packet sender.
|
|
///
|
|
/// You can obtain a `Sender` with [`MidiClass::split`]
|
|
pub struct Sender<'d, D: Driver<'d>> {
|
|
write_ep: D::EndpointIn,
|
|
}
|
|
|
|
impl<'d, D: Driver<'d>> Sender<'d, D> {
|
|
/// Gets the maximum packet size in bytes.
|
|
pub fn max_packet_size(&self) -> u16 {
|
|
// The size is the same for both endpoints.
|
|
self.write_ep.info().max_packet_size
|
|
}
|
|
|
|
/// Writes a single packet.
|
|
pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> {
|
|
self.write_ep.write(data).await
|
|
}
|
|
|
|
/// Waits for the USB host to enable this interface
|
|
pub async fn wait_connection(&mut self) {
|
|
self.write_ep.wait_enabled().await
|
|
}
|
|
}
|
|
|
|
/// Midi class packet receiver.
|
|
///
|
|
/// You can obtain a `Receiver` with [`MidiClass::split`]
|
|
pub struct Receiver<'d, D: Driver<'d>> {
|
|
read_ep: D::EndpointOut,
|
|
}
|
|
|
|
impl<'d, D: Driver<'d>> Receiver<'d, D> {
|
|
/// Gets the maximum packet size in bytes.
|
|
pub fn max_packet_size(&self) -> u16 {
|
|
// The size is the same for both endpoints.
|
|
self.read_ep.info().max_packet_size
|
|
}
|
|
|
|
/// Reads a single packet.
|
|
pub async fn read_packet(&mut self, data: &mut [u8]) -> Result<usize, EndpointError> {
|
|
self.read_ep.read(data).await
|
|
}
|
|
|
|
/// Waits for the USB host to enable this interface
|
|
pub async fn wait_connection(&mut self) {
|
|
self.read_ep.wait_enabled().await
|
|
}
|
|
}
|