Skip to content

Commit 615b0d9

Browse files
committed
Added USB Host MIDI support.
I've read, and accepted the Contributor Agreement. http://mbed.org/users/kshoji/
1 parent c3cfff2 commit 615b0d9

File tree

3 files changed

+720
-0
lines changed

3 files changed

+720
-0
lines changed

libraries/USBHost/USBHost/USBHostConf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@
5858
*/
5959
#define USBHOST_3GMODULE 1
6060

61+
/*
62+
* Enable USB MIDI
63+
*/
64+
#define USBHOST_MIDI 1
65+
6166
/*
6267
* Maximum number of interfaces of a usb device
6368
*/
Lines changed: 362 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,362 @@
1+
/* Copyright (c) 2014 mbed.org, MIT License
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
4+
* and associated documentation files (the "Software"), to deal in the Software without
5+
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
6+
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
7+
* Software is furnished to do so, subject to the following conditions:
8+
*
9+
* The above copyright notice and this permission notice shall be included in all copies or
10+
* substantial portions of the Software.
11+
*
12+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
13+
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
15+
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
16+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17+
*/
18+
19+
#include "USBHostMIDI.h"
20+
21+
#if USBHOST_MIDI
22+
23+
#include "dbg.h"
24+
25+
#define SET_LINE_CODING 0x20
26+
27+
USBHostMIDI::USBHostMIDI() {
28+
host = USBHost::getHostInst();
29+
size_bulk_in = 0;
30+
size_bulk_out = 0;
31+
init();
32+
}
33+
34+
void USBHostMIDI::init() {
35+
dev = NULL;
36+
bulk_in = NULL;
37+
bulk_out = NULL;
38+
dev_connected = false;
39+
midi_intf = -1;
40+
midi_device_found = false;
41+
sysExBufferPos = 0;
42+
}
43+
44+
bool USBHostMIDI::connected() {
45+
return dev_connected;
46+
}
47+
48+
bool USBHostMIDI::connect() {
49+
if (dev_connected) {
50+
return true;
51+
}
52+
53+
for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
54+
if ((dev = host->getDevice(i)) != NULL) {
55+
56+
USB_DBG("Trying to connect MIDI device\r\n");
57+
58+
if (host->enumerate(dev, this)) {
59+
break;
60+
}
61+
62+
if (midi_device_found) {
63+
bulk_in = dev->getEndpoint(midi_intf, BULK_ENDPOINT, IN);
64+
bulk_out = dev->getEndpoint(midi_intf, BULK_ENDPOINT, OUT);
65+
66+
if (!bulk_in || !bulk_out) {
67+
break;
68+
}
69+
70+
USB_INFO("New MIDI device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, midi_intf);
71+
dev->setName("MIDI", midi_intf);
72+
host->registerDriver(dev, midi_intf, this, &USBHostMIDI::init);
73+
74+
size_bulk_in = bulk_in->getSize();
75+
size_bulk_out = bulk_out->getSize();
76+
77+
bulk_in->attach(this, &USBHostMIDI::rxHandler);
78+
79+
host->bulkRead(dev, bulk_in, buf, size_bulk_in, false);
80+
dev_connected = true;
81+
return true;
82+
}
83+
}
84+
}
85+
86+
init();
87+
return false;
88+
}
89+
90+
void USBHostMIDI::rxHandler() {
91+
uint8_t *midi;
92+
if (bulk_in) {
93+
int length = bulk_in->getLengthTransferred();
94+
if (bulk_in->getState() == USB_TYPE_IDLE || bulk_in->getState() == USB_TYPE_FREE) {
95+
// MIDI event handling
96+
for (int i = 0; i < length; i += 4) {
97+
if (i + 4 > length) {
98+
// length shortage, ignored.
99+
break;
100+
}
101+
102+
// read each four bytes
103+
midi = &buf[i];
104+
// process MIDI message
105+
// switch by code index number
106+
switch (midi[0] & 0xf) {
107+
case 0: // miscellaneous function codes
108+
miscellaneousFunctionCode(midi[1], midi[2], midi[3]);
109+
break;
110+
case 1: // cable events
111+
cableEvent(midi[1], midi[2], midi[3]);
112+
break;
113+
case 2: // two bytes system common messages
114+
systemCommonTwoBytes(midi[1], midi[2]);
115+
break;
116+
case 3: // three bytes system common messages
117+
systemCommonThreeBytes(midi[1], midi[2], midi[3]);
118+
break;
119+
case 4: // SysEx starts or continues
120+
sysExBuffer[sysExBufferPos++] = midi[1];
121+
if (sysExBufferPos >= 64) {
122+
systemExclusive(sysExBuffer, sysExBufferPos, true);
123+
sysExBufferPos = 0;
124+
}
125+
sysExBuffer[sysExBufferPos++] = midi[2];
126+
if (sysExBufferPos >= 64) {
127+
systemExclusive(sysExBuffer, sysExBufferPos, true);
128+
sysExBufferPos = 0;
129+
}
130+
sysExBuffer[sysExBufferPos++] = midi[3];
131+
// SysEx continues. don't send
132+
break;
133+
case 5: // SysEx ends with single byte
134+
sysExBuffer[sysExBufferPos++] = midi[1];
135+
systemExclusive(sysExBuffer, sysExBufferPos, false);
136+
sysExBufferPos = 0;
137+
break;
138+
case 6: // SysEx ends with two bytes
139+
sysExBuffer[sysExBufferPos++] = midi[1];
140+
if (sysExBufferPos >= 64) {
141+
systemExclusive(sysExBuffer, sysExBufferPos, true);
142+
sysExBufferPos = 0;
143+
}
144+
sysExBuffer[sysExBufferPos++] = midi[2];
145+
systemExclusive(sysExBuffer, sysExBufferPos, false);
146+
sysExBufferPos = 0;
147+
break;
148+
case 7: // SysEx ends with three bytes
149+
sysExBuffer[sysExBufferPos++] = midi[1];
150+
if (sysExBufferPos >= 64) {
151+
systemExclusive(sysExBuffer, sysExBufferPos, true);
152+
sysExBufferPos = 0;
153+
}
154+
sysExBuffer[sysExBufferPos++] = midi[2];
155+
if (sysExBufferPos >= 64) {
156+
systemExclusive(sysExBuffer, sysExBufferPos, true);
157+
sysExBufferPos = 0;
158+
}
159+
sysExBuffer[sysExBufferPos++] = midi[3];
160+
systemExclusive(sysExBuffer, sysExBufferPos, false);
161+
sysExBufferPos = 0;
162+
break;
163+
case 8:
164+
noteOff(midi[1] & 0xf, midi[2], midi[3]);
165+
break;
166+
case 9:
167+
if (midi[3]) {
168+
noteOn(midi[1] & 0xf, midi[2], midi[3]);
169+
} else {
170+
noteOff(midi[1] & 0xf, midi[2], midi[3]);
171+
}
172+
break;
173+
case 10:
174+
polyKeyPress(midi[1] & 0xf, midi[2], midi[3]);
175+
break;
176+
case 11:
177+
controlChange(midi[1] & 0xf, midi[2], midi[3]);
178+
break;
179+
case 12:
180+
programChange(midi[1] & 0xf, midi[2]);
181+
break;
182+
case 13:
183+
channelPressure(midi[1] & 0xf, midi[2]);
184+
break;
185+
case 14:
186+
pitchBend(midi[1] & 0xf, midi[2] | (midi[3] << 7));
187+
break;
188+
case 15:
189+
singleByte(midi[1]);
190+
break;
191+
}
192+
}
193+
194+
// read another message
195+
host->bulkRead(dev, bulk_in, buf, size_bulk_in, false);
196+
}
197+
}
198+
}
199+
200+
bool USBHostMIDI::sendMidiBuffer(uint8_t data0, uint8_t data1, uint8_t data2, uint8_t data3) {
201+
if (bulk_out) {
202+
uint8_t midi[4];
203+
204+
midi[0] = data0;
205+
midi[1] = data1;
206+
midi[2] = data2;
207+
midi[3] = data3;
208+
if (host->bulkWrite(dev, bulk_out, (uint8_t *)midi, 4) == USB_TYPE_OK) {
209+
return true;
210+
}
211+
}
212+
return false;
213+
}
214+
215+
bool USBHostMIDI::sendMiscellaneousFunctionCode(uint8_t data1, uint8_t data2, uint8_t data3) {
216+
return sendMidiBuffer(0, data1, data2, data3);
217+
}
218+
219+
bool USBHostMIDI::sendCableEvent(uint8_t data1, uint8_t data2, uint8_t data3) {
220+
return sendMidiBuffer(1, data1, data2, data3);
221+
}
222+
223+
bool USBHostMIDI::sendSystemCommmonTwoBytes(uint8_t data1, uint8_t data2) {
224+
return sendMidiBuffer(2, data1, data2, 0);
225+
}
226+
227+
bool USBHostMIDI::sendSystemCommmonThreeBytes(uint8_t data1, uint8_t data2, uint8_t data3) {
228+
return sendMidiBuffer(3, data1, data2, 0);
229+
}
230+
231+
bool USBHostMIDI::sendSystemExclusive(uint8_t *buffer, int length) {
232+
uint8_t midi[64];
233+
int midiLength;
234+
int midiPos;
235+
if (bulk_out) {
236+
for (int i = 0; i < length; i += 48) {
237+
if (i + 48 >= length) {
238+
// contains last data
239+
midiLength = (((length - i) + 2) / 3) * 4;
240+
for (int pos = i; pos < length; pos += 3) {
241+
midiPos = (pos + 2) / 3 * 4;
242+
if (pos + 3 >= length) {
243+
// last data
244+
switch (pos % 3) {
245+
case 0:
246+
midi[midiPos ] = 7;
247+
midi[midiPos + 1] = buffer[pos ];
248+
midi[midiPos + 2] = buffer[pos + 1];
249+
midi[midiPos + 3] = buffer[pos + 2];
250+
break;
251+
case 1:
252+
midi[midiPos ] = 5;
253+
midi[midiPos + 1] = buffer[pos ];
254+
midi[midiPos + 2] = 0;
255+
midi[midiPos + 3] = 0;
256+
break;
257+
case 2:
258+
midi[midiPos ] = 6;
259+
midi[midiPos + 1] = buffer[pos ];
260+
midi[midiPos + 2] = buffer[pos + 1];
261+
midi[midiPos + 3] = 0;
262+
break;
263+
}
264+
} else {
265+
// has more data
266+
midi[midiPos ] = 4;
267+
midi[midiPos + 1] = buffer[pos ];
268+
midi[midiPos + 2] = buffer[pos + 1];
269+
midi[midiPos + 3] = buffer[pos + 2];
270+
}
271+
}
272+
} else {
273+
// has more data
274+
midiLength = 64;
275+
for (int pos = i; pos < length; pos += 3) {
276+
midiPos = (pos + 2) / 3 * 4;
277+
midi[midiPos ] = 4;
278+
midi[midiPos + 1] = buffer[pos ];
279+
midi[midiPos + 2] = buffer[pos + 1];
280+
midi[midiPos + 3] = buffer[pos + 2];
281+
}
282+
}
283+
284+
if (host->bulkWrite(dev, bulk_out, (uint8_t *)midi, midiLength) != USB_TYPE_OK) {
285+
return false;
286+
}
287+
}
288+
return true;
289+
}
290+
return false;
291+
}
292+
293+
bool USBHostMIDI::sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) {
294+
return sendMidiBuffer(8, channel & 0xf | 0x80, note & 0x7f, velocity & 0x7f);
295+
}
296+
297+
bool USBHostMIDI::sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
298+
return sendMidiBuffer(9, channel & 0xf | 0x90, note & 0x7f, velocity & 0x7f);
299+
}
300+
301+
bool USBHostMIDI::sendPolyKeyPress(uint8_t channel, uint8_t note, uint8_t pressure) {
302+
return sendMidiBuffer(10, channel & 0xf | 0xa0, note & 0x7f, pressure & 0x7f);
303+
}
304+
305+
bool USBHostMIDI::sendControlChange(uint8_t channel, uint8_t key, uint8_t value) {
306+
return sendMidiBuffer(11, channel & 0xf | 0xb0, key & 0x7f, value & 0x7f);
307+
}
308+
309+
bool USBHostMIDI::sendProgramChange(uint8_t channel, uint8_t program) {
310+
return sendMidiBuffer(12, channel & 0xf | 0xc0, program & 0x7f, 0);
311+
}
312+
313+
bool USBHostMIDI::sendChannelPressure(uint8_t channel, uint8_t pressure) {
314+
return sendMidiBuffer(13, channel & 0xf | 0xd0, pressure & 0x7f, 0);
315+
}
316+
317+
bool USBHostMIDI::sendPitchBend(uint8_t channel, uint16_t value) {
318+
return sendMidiBuffer(14, channel & 0xf | 0xe0, value & 0x7f, (value >> 7) & 0x7f);
319+
}
320+
321+
bool USBHostMIDI::sendSingleByte(uint8_t data) {
322+
return sendMidiBuffer(15, data, 0, 0);
323+
}
324+
325+
/*virtual*/ void USBHostMIDI::setVidPid(uint16_t vid, uint16_t pid)
326+
{
327+
// we don't check VID/PID for this driver
328+
}
329+
330+
/*virtual*/ bool USBHostMIDI::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
331+
{
332+
// USB MIDI class/subclass
333+
if ((midi_intf == -1) &&
334+
(intf_class == AUDIO_CLASS) &&
335+
(intf_subclass == 0x03)) {
336+
midi_intf = intf_nb;
337+
return true;
338+
}
339+
340+
// vendor specific device
341+
if ((midi_intf == -1) &&
342+
(intf_class == 0xff) &&
343+
(intf_subclass == 0x03)) {
344+
midi_intf = intf_nb;
345+
return true;
346+
}
347+
348+
return false;
349+
}
350+
351+
/*virtual*/ bool USBHostMIDI::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
352+
{
353+
if (intf_nb == midi_intf) {
354+
if (type == BULK_ENDPOINT) {
355+
midi_device_found = true;
356+
return true;
357+
}
358+
}
359+
return false;
360+
}
361+
362+
#endif

0 commit comments

Comments
 (0)