Skip to content

Commit ceb09a3

Browse files
rppicomidiatoktoto
authored andcommitted
Add MIDI host support to tinyusb
1 parent b624664 commit ceb09a3

File tree

8 files changed

+1226
-3
lines changed

8 files changed

+1226
-3
lines changed

hw/bsp/rp2040/family.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ if (NOT TARGET _rp2040_family_inclusion_marker)
9393
${TOP}/src/class/cdc/cdc_host.c
9494
${TOP}/src/class/hid/hid_host.c
9595
${TOP}/src/class/msc/msc_host.c
96+
${TOP}/src/class/midi/midi_host.c
9697
${TOP}/src/class/vendor/vendor_host.c
9798
)
9899

src/class/midi/README_midi_host.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# MIDI HOST DRIVER
2+
This README file contains the design notes and limitations of the
3+
MIDI host driver.
4+
5+
# MAXIMUM NUMBER OF MIDI DEVICES ATTACHED TO HOST
6+
In this version of the driver, only one MIDI device is supported. This
7+
constraint may change in the future.
8+
9+
# MAXIMUM NUMBER OF ENDPOINTS
10+
Although the USB MIDI 1.0 Class specification allows an arbitrary number
11+
of endpoints, this driver supports at most one USB BULK DATA IN endpoint
12+
and one USB BULK DATA OUT endpoint. Each endpoint can support up to 16
13+
virtual cables. If a device has multiple IN endpoints or multiple OUT
14+
endpoints, it will fail to enumerate.
15+
16+
Most USB MIDI devices contain both an IN endpoint and an OUT endpoint,
17+
but not all do. For example, some USB pedals only support an OUT endpoint.
18+
This driver allows that.
19+
20+
# PUBLIC API
21+
Applications interact with this driver via 8-bit buffers of MIDI messages
22+
formed using the rules for sending bytes on a 5-pin DIN cable per the
23+
original MIDI 1.0 specification.
24+
25+
To send a message to a device, the Host application composes a sequence
26+
of status and data bytes in a byte array and calls the API function.
27+
The arguments of the function are a pointer to the byte array, the number
28+
of bytes in the array, and the target virtual cable number 0-15.
29+
30+
When the host driver receives a message from the device, the host driver
31+
will call a callback function that the host application registers. This
32+
callback function contains a pointer to a message buffer, a message length,
33+
and the virtual cable number of the message buffer. One complete bulk IN
34+
endpoint transfer might contain multiple messages targeted to different
35+
virtual cables.
36+
37+
# SUBCLASS AUDIO CONTROL
38+
A MIDI device does not absolutely need to have an Audio Control Interface,
39+
unless it adheres to the USB Audio Class 2 spec, but many devices
40+
have them even if the devices do not have an audio streaming interface.
41+
Because this driver does not support audio streaming, the descriptor parser
42+
will skip past any audio control interface and audio streaming interface
43+
and open only the MIDI interface.
44+
45+
An audio streaming host driver can use this driver by passing a pointer
46+
to the MIDI interface descriptor that is found after the audio streaming
47+
interface to the midih_open() function. That is, an audio streaming host
48+
driver would parse the audio control interface descriptor and then the
49+
audio streaming interface and endpoint descriptors. When the next descriptor
50+
pointer points to a MIDI interface descriptor, call midih_open() with that
51+
descriptor pointer.
52+
53+
# CLASS SPECIFIC INTERFACE AND REQUESTS
54+
The host driver does not make use of the informaton in the class specific
55+
interface descriptors. In the future, a public API could be created to
56+
retrieve the string descriptors for the names of each ELEMENT,
57+
IN JACK and OUT JACK, and how the device describes the connections.
58+
59+
This driver also does not support class specific requests to control
60+
ELEMENT items, nor does it support non-MIDI Streaming bulk endpoints.
61+
62+
# MIDI CLASS SPECIFIC DESCRIPTOR TOTAL LENGTH FIELD IGNORED
63+
I have observed at least one keyboard by a leading manufacturer that
64+
sets the wTotalLength field of the Class-Specific MS Interface Header
65+
Descriptor to include the length of the MIDIStreaming Endpoint
66+
Descriptors. This is wrong per my reading of the specification.
67+
68+
# MESSAGE BUFFER DETAILS
69+
Messages buffers composed from USB data received on the IN endpoint will never contain
70+
running status because USB MIDI 1.0 class does not support that. Messages
71+
buffers to be sent to the device on the OUT endpont may contain running status
72+
(the message might come from a UART data stream from a 5-pin DIN MIDI IN
73+
cable on the host, for example). The driver may in the future correctly compose
74+
4-byte USB MIDI Class packets using the running status if need be. However,
75+
it does not currently do that. Also, use of running status is not a good idea
76+
overall because a single byte error can really mess up the data stream with no
77+
way to recover until the next non-real time status byte is in the message buffer.
78+
79+
Message buffers to be sent to the device may contain Real time messages
80+
such as MIDI clock. Real time messages may be inserted in the message
81+
byte stream between status and data bytes of another message without disrupting
82+
the running status. However, because MIDI 1.0 class messages are sent
83+
as four byte packets, a real-time message so inserted will be re-ordered
84+
to be sent to the device in a new 4-byte packet immediately before the
85+
interrupted data stream.
86+
87+
Real time messages the device sends to the host can only appear between
88+
the status byte and data bytes of the message in System Exclusive messages
89+
that are longer than 3 bytes.
90+
91+
# POORLY FORMED USB MIDI DATA PACKETS FROM THE DEVICE
92+
Some devices do not properly encode the code index number (CIN) for the
93+
MIDI message status byte even though the 3-byte data payload correctly encodes
94+
the MIDI message. This driver looks to the byte after the CIN byte to decide
95+
how many bytes to place in the message buffer.
96+
97+
Some devices do not properly encode the virtual cable number. If the virtual
98+
cable number in the CIN data byte of the packet is not less than bNumEmbMIDIJack
99+
for that endpoint, then the host driver assumes virtual cable 0 and does not
100+
report an error.
101+
102+
Some MIDI devices will always send back exactly wMaxPacketSize bytes on
103+
every endpoint even if only one 4-byte packet is required (e.g., NOTE ON).
104+
These devices send packets with 4 packet bytes 0. This driver ignores all
105+
zero packets without reporting an error.
106+
107+
# ENUMERATION FAILURES
108+
The host may fail to enumerate a device if it has too many endpoints, if it has
109+
if it has a Standard MS Transfer Bulk Data Endpoint Descriptor (not supported),
110+
if it has a poorly formed descriptor, or if the descriptor is too long for
111+
the host to read the whole thing.

src/class/midi/midi.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ typedef enum
7171
MIDI_CIN_SYSEX_END_1BYTE = 5, // SysEx ends with 1 data, or 1 byte system common message
7272
MIDI_CIN_SYSEX_END_2BYTE = 6, // SysEx ends with 2 data
7373
MIDI_CIN_SYSEX_END_3BYTE = 7, // SysEx ends with 3 data
74-
MIDI_CIN_NOTE_ON = 8,
75-
MIDI_CIN_NOTE_OFF = 9,
74+
MIDI_CIN_NOTE_ON = 9,
75+
MIDI_CIN_NOTE_OFF = 8,
7676
MIDI_CIN_POLY_KEYPRESS = 10,
7777
MIDI_CIN_CONTROL_CHANGE = 11,
7878
MIDI_CIN_PROGRAM_CHANGE = 12,
@@ -106,6 +106,11 @@ enum
106106
MIDI_STATUS_SYSREAL_SYSTEM_RESET = 0xFF,
107107
};
108108

109+
enum
110+
{
111+
MIDI_MAX_DATA_VAL = 0x7F,
112+
};
113+
109114
/// MIDI Interface Header Descriptor
110115
typedef struct TU_ATTR_PACKED
111116
{
@@ -201,6 +206,16 @@ typedef struct TU_ATTR_PACKED
201206
uint8_t iElement; \
202207
}
203208

209+
// This descriptor follows the standard bulk data endpoint descriptor
210+
typedef struct
211+
{
212+
uint8_t bLength ; ///< Size of this descriptor in bytes (4+bNumEmbMIDIJack)
213+
uint8_t bDescriptorType ; ///< Descriptor Type, must be CS_ENDPOINT
214+
uint8_t bDescriptorSubType ; ///< Descriptor SubType, must be MS_GENERAL
215+
uint8_t bNumEmbMIDIJack; ; ///< Number of embedded MIDI jacks associated with this endpoint
216+
uint8_t baAssocJackID[]; ; ///< A list of associated jacks
217+
} midi_cs_desc_endpoint_t;
218+
204219
/** @} */
205220

206221
#ifdef __cplusplus

0 commit comments

Comments
 (0)