PDA

View Full Version : Multiple virtual MIDI ports over USB with Teensy 2.0? (Or 3.0?)



MuShoo
02-21-2013, 07:02 AM
I apologize in advance if any of my terminology is wrong - I'm just a hobbyist coder/arduino-head, and most of the talk about USB HID Descriptors and Endpoints and such just confuses me!

Anyway, I'm building a MIDI control surface for digital audio workstations - To interface it cleanly with one of those workstations, I need my device to show up as 3 separate MIDI ports over USB (IE, Port A in, Port A out, Port B in, etc etc). Ideally I could then write/read from each virtual port by way of something like usbMIDI_A.noteOn() or some such. From what I can tell, the usbMIDI class was written for the Teensy 1.0, (some of the comments reference AT90USB162, which has only 4 programmable endpoints, while the Teensy 2.0 uses the AT90USB1286, which has six).

Re-writing the usbMIDI class is most likely well beyond my own abilities/sanity levels - I suppose I just want to check if it's POSSIBLE to do what I need - have three different MIDI in/out pairs described to the computer, and access each one separately from within the arduino coding language.


Any help would be much appreciated!

Nantonos
02-21-2013, 12:29 PM
Are you wanting multiple ports to get more than 16 channels?

MuShoo
02-21-2013, 03:25 PM
Nope - I'm just emulating three different control surface MIDI protocols (they each do different things, I need different features from each one - one allows for scrubbing, another allows for better plugin interaction in the DAW, etc) at the same time. The DAW needs each protocol to be set up as it's own 'device' with it's own midi in/out pair.

CheapB
02-22-2013, 12:21 AM
Not sure what your hardware situation is, but unless you need more that 16 channels, you should be able to to run several control surfaces via a single MIDI. The teensy can act as a midi device. If you do need more, You may want to check this out. https://www.sparkfun.com/products/9598 I have not used it, but i understand it will convert a serial connection to MIDI.

MuShoo
02-22-2013, 12:26 AM
Ah, there must be some confusion from what I wrote - I don't need to pickup MIDI signals from three devices and read them into the Teensy - I need the Teensy to act, over USB, like three different MIDI devices. So you plug the Teensy in, and the computer sees Teensy MIDI A, Teensy MIDI B, and Teensy MIDI C as separate MIDI ports it can send and receive information from - but all three of those ports lead to the Teensy, and I can then access them separately in the code. No external DIN connectors on the Teensy, different sensors on the control surface I'm building will feed to different virtual MIDI ports.

I suppose in a sense I do need 48 channels (3x16) of MIDI in and 48 of MIDI out.

CheapB
02-22-2013, 01:47 AM
I don't think anyone is suggesting anything different. Teensy will be recognized as a midi device. I believe the intent of the above breakout would add another

MuShoo
02-22-2013, 04:39 AM
As far as what I can tell, for what I need that breakout board doesn't do anything for me? While yes, it would conceivably allow me to have another MIDI output - that output would be out of the 5-pin DIN connector on the MIDI Breakout Board, and NOT out of the USB cable connected to the Teensy - please correct me if I'm wrong. Using the breakout boards would require me to use six cables - one USB, two pairs of MIDI cables into another MIDI-to-USB interface that then goes out another USB cable, and then into the computer.

The Teensy 2.0 should have enough USB endpoints to allow for writing the usbMIDI library such that it can appear as 3 separate MIDI devices. From the Teensy usb_midi hardware core code, hardware/teensy/cores/usb_midi/usb.c:


// Standard MS Interface Descriptor,
9, // bLength
4, // bDescriptorType
MIDI_INTERFACE, // bInterfaceNumber
0, // bAlternateSetting
2, // bNumEndpoints
0x01, // bInterfaceClass (0x01 = Audio)
0x03, // bInterfaceSubClass (0x03 = MIDI)
0x00, // bInterfaceProtocol (unused for MIDI)
0, // iInterface

I read this as, there are 9 items in this list, the interface's number is whatever the variable 'MIDI_INTERFACE' is, no alternate setting, and takes 2 endpoints. This bit describes the way the Teensy is enumerating it's MIDI i/o to the computer, and each MIDI I/O takes 2 endpoints.


// MIDI IN Jack Descriptor, B.4.3, Table B-7 (embedded), page 40
6, // bLength
0x24, // bDescriptorType = CS_INTERFACE
0x02, // bDescriptorSubtype = MIDI_IN_JACK
0x01, // bJackType = EMBEDDED
1, // bJackID, ID = 1
0, // iJack

This is describing the virtual MIDI port that Teensy shows up as when you connect it to a computer in USB_MIDI HID mode. In particular, it's the 'In' jack (IE, Teensy MIDI In) as it pops up on my computer. It's an 'embedded' type of jack - not one that references a physical input.

From hardware/teensy/cores/usb_midi/usb_private.h:


// These buffer sizes are best for most applications, but perhaps if you
// want more buffering on some endpoint at the expense of others, this
// is where you can make such changes. The AT90USB162 has only 176 bytes
// of DPRAM (USB buffers) and only endpoints 3 & 4 can double buffer.


// 0: control
// 1: debug IN
// 2: debug OUT
// 3: midi IN
// 4: midi OUT

The comment in this piece of the hardware core, as well as the description of what each endpoint is doing, leads me to believe that the Teensy USB MIDI Library/Core was written for the old Teensy 1.0 (Which had an AT90USB162. That chip only has 4 programmable endpoints (and one non-programmable). However, the Teensy 2.0, which is an AT90USB1286, has 6 programmable endpoints.

So, my questions are these: Can the USB HID descriptor for the Teensy usb_midi hardware core be rewritten to have 3 pairs of "midi IN" and "midi OUT"?

And can the rest of the core usb_midi_class class (hardware/teensy/cores/usb_midi/usb_api.h) be written so that you can have 3 different instances of the usbMIDI object, each referencing it's own pair of USB endpoints?

I'm not asking if anyone out there is willing to do this for me, I just want to know if it's feasible/possible before I (probably badly) try and attempt it. Also note that I would prefer for this to not need any additional software/drivers (that I'd then have to write and distribute) to, say, parse a serial signal from the teensy and convert it to multiple MIDI outs. That is my backup plan, but an entirely 'in the box' solution fits my needs better.

MuShoo
02-22-2013, 09:04 AM
A small amount of my last post was incorrect - the Teensy 2.0++ uses the AT90USB1286, the Teensy 2.0 uses the ATMega32u4 - both chips have 6 programmable endpoints, however, so the point still stands.

Nantonos
02-22-2013, 06:20 PM
Yes, I suspect that a new descriptor could be writen that has six MIDI endpoints (3 in and 3 out). You might find that some of the other descriptors which declare multipe devices (like keyboard+mouse+joystick) to be helpful as examples.

What I don't know is whether the computer OS will recognise the device without a driver (i.e. whether it will be class compliant).

(And yes, adding an Arduino DIN-MIDI shield would not help you here).

PaulStoffregen
02-22-2013, 07:46 PM
The USB MIDI protocol can support up to 16 "jacks" or "cables" (both terms are used in the specification). The idea is to represent up to 16 virtual MIDI cables. I have personally never used this. I do not know if Mac, Windows and Linux are compatible with this feature, or if they are, how the virtual cables are presented to the user. This is all based only on the USB MIDI spec, as published at www.usb.org (and which I used to write Teensy's USB MIDI code).

In USB MIDI, messages are packed into 32 bits. The first byte uses 4 bits to specify which type of message is in the other 3 bytes, and the other 4 bits specify which virtual cable. Teensy's USB MIDI implementation always sets the cable number to 0. Likewise, the Teensyduino functions to send do not have fields to specify which virtual cable, and the receive callbacks and functions do not provide the virtual cable. I considered this. I talked with numerous people interested in USB MIDI. Nobody really wanted this feature, and several people were confused by the question (talk of the 16 MIDI channels was the common response, similar to messages in this thread). I quickly came to the conclusion that adding virtual cable parameters would add more complexity than it was worth. So far, you're the very first person to ask for this virtual cable feature!

If you want to implement 3 virtual USB MIDI cables, you certainly can. You'll simply have some work to do. Since you mentioned building a controller, I'll only mention the sending functions....

First, you'll need to edit hardware/teensy/cores/usb_midi/usb.c. Here's the descriptors that currently exist:



// Standard MS Interface Descriptor,
9, // bLength
4, // bDescriptorType
MIDI_INTERFACE, // bInterfaceNumber
0, // bAlternateSetting
2, // bNumEndpoints
0x01, // bInterfaceClass (0x01 = Audio)
0x03, // bInterfaceSubClass (0x03 = MIDI)
0x00, // bInterfaceProtocol (unused for MIDI)
0, // iInterface

// MIDI MS Interface Header, USB MIDI 6.1.2.1, page 21, Table 6-2
7, // bLength
0x24, // bDescriptorType = CS_INTERFACE
0x01, // bDescriptorSubtype = MS_HEADER
0x00, 0x01, // bcdMSC = revision 01.00
0x41, 0x00, // wTotalLength

// MIDI IN Jack Descriptor, B.4.3, Table B-7 (embedded), page 40
6, // bLength
0x24, // bDescriptorType = CS_INTERFACE
0x02, // bDescriptorSubtype = MIDI_IN_JACK
0x01, // bJackType = EMBEDDED
1, // bJackID, ID = 1
0, // iJack

// MIDI IN Jack Descriptor, B.4.3, Table B-8 (external), page 40
6, // bLength
0x24, // bDescriptorType = CS_INTERFACE
0x02, // bDescriptorSubtype = MIDI_IN_JACK
0x02, // bJackType = EXTERNAL
2, // bJackID, ID = 2
0, // iJack

// MIDI OUT Jack Descriptor, B.4.4, Table B-9, page 41
9,
0x24, // bDescriptorType = CS_INTERFACE
0x03, // bDescriptorSubtype = MIDI_OUT_JACK
0x01, // bJackType = EMBEDDED
3, // bJackID, ID = 3
1, // bNrInputPins = 1 pin
2, // BaSourceID(1) = 2
1, // BaSourcePin(1) = first pin
0, // iJack

// MIDI OUT Jack Descriptor, B.4.4, Table B-10, page 41
9,
0x24, // bDescriptorType = CS_INTERFACE
0x03, // bDescriptorSubtype = MIDI_OUT_JACK
0x02, // bJackType = EXTERNAL
4, // bJackID, ID = 4
1, // bNrInputPins = 1 pin
1, // BaSourceID(1) = 1
1, // BaSourcePin(1) = first pin
0, // iJack

// Standard Bulk OUT Endpoint Descriptor, B.5.1, Table B-11, pae 42
9, // bLength
5, // bDescriptorType = ENDPOINT
MIDI_RX_ENDPOINT, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk)
MIDI_RX_SIZE, 0, // wMaxPacketSize
0, // bInterval
0, // bRefresh
0, // bSynchAddress

// Class-specific MS Bulk OUT Endpoint Descriptor, B.5.2, Table B-12, page 42
5, // bLength
0x25, // bDescriptorSubtype = CS_ENDPOINT
0x01, // bJackType = MS_GENERAL
1, // bNumEmbMIDIJack = 1 jack
1, // BaAssocJackID(1) = jack ID #1

// Standard Bulk IN Endpoint Descriptor, B.5.1, Table B-11, pae 42
9, // bLength
5, // bDescriptorType = ENDPOINT
MIDI_TX_ENDPOINT | 0x80, // bEndpointAddress
0x02, // bmAttributes (0x02=bulk)
MIDI_TX_SIZE, 0, // wMaxPacketSize
0, // bInterval
0, // bRefresh
0, // bSynchAddress

// Class-specific MS Bulk IN Endpoint Descriptor, B.5.2, Table B-12, page 42
5, // bLength
0x25, // bDescriptorSubtype = CS_ENDPOINT
0x01, // bJackType = MS_GENERAL
1, // bNumEmbMIDIJack = 1 jack
3, // BaAssocJackID(1) = jack ID #3


You'll need to add more "jacks". I can't help you with the exact bytes to add. You'll need to get the "Universal Serial Bus Device Class Definition for MIDI Devices" from www.usb.org which is probably in the audio class section. At least those comments in the code can point you to the relevant sections. Honestly, all that virtual cable and jacks stuff seems confusing to me, not being a MIDI expert. Hopefully it'll make more sense for you... or you can try stuff until you find settings that work? Just remember, the USB descriptors don't actually "do" anything, they're only binary data which your computer reads to learn what type of USB device you've implemented.

Once you've created descriptors that tell your PC about the other virtual cables, then all you need to do is modify the code in hardware/teensy/cores/usb_midi/usb_api.cpp to send the virtual cable numbers, instead of putting zeros into those fields. You'll see all the send functions look like this:



void usb_midi_class::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel)
{
send_raw(0x08, 0x80 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);
}
void usb_midi_class::sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel)
{
send_raw(0x09, 0x90 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);
}


They all call send_raw() to transmit 4 bytes. That first byte is the one with the cable number and message type. Just put your cable number into the upper 4 bits. Easy, right?

Of course, with USB MIDI, the hardest part is finding what is actually supported on the 3 operating systems. The drivers are remarkably poor, especially Microsoft's (big surprise, right?!)

Good luck. If you get it to work, I hope you'll share the modified code with some details about which system you tested and how the virtual cables appear?

Nantonos
02-22-2013, 09:25 PM
It would be nice to see this added and tested. I suggest that a '4 cable MIDI' set would be good; as an example, the Kontakt sampler (very popular, on Windows and Mac) supports 4 groups of 16 channels so hardware could be tested with that. (I have Kontakt).

PaulStoffregen
02-22-2013, 09:46 PM
If there are descriptors that work on all 3 operating systems, I'd be happy to include it in a future Teensyduino release.

Nantonos
02-22-2013, 10:02 PM
So, iOS, Android *and* BeOS? Sheesh.

Qumefox
02-23-2013, 05:46 AM
So, iOS, Android *and* BeOS? Sheesh.

No silly. He means IRIX, OS/2 and OpenVMS of course.

MuShoo
02-23-2013, 08:30 AM
Thanks Paul! I'm glad I was finally able to make sense. I've been digging into that USB Class Definition doc (was screwing around with it yesterday but with little success).

I think I ought to be able to pull this off - if I do I'll try and post my code. One last question! Is there a way to make a new usb core for Teensy? IE, "midi_usb_3ports" that would show up in the "Tools:USB Type" submenu in Teensyduino? Currently I've just been editing the usb_midi files from within teensy/cores directly, but that seems... dangerous.

And I knew MIDI well before I knew coding (going on... wow, like 16 years now. Makin' me feel old...) so all that jargon about jacks and cables makes perfect sense to me :D

MuShoo
02-23-2013, 08:35 AM
Oh, also - can I safely remove all code related to the DEBUG_INTERFACE? From my limited understanding, it's using two endpoints, and I'll need those to make other MIDI jacks out of.

PaulStoffregen
02-23-2013, 12:00 PM
It's easiest to just edit the exiting MIDI type. You can add more types, but it requires getting a number of things to match between boards.txt and several files. I'd suggest just editing the MIDI type, at least for getting started. If you want to use the original, just install another copy of Arduino in a different directory.

Using the virtual cables feature, you can have up to 16 cables all running from the same pair of endpoints normally used for only a single cable. Maybe that wasn't clear from my message?

Don't mess with the DEBUG_INTERFACE stuff. If you ever want to use Serial.print() to the Arduino Serial Monitor, you'll need it. If your changes will ever get merged into a future version of Teensyduino, that stuff must be preserved. Besides, you don't need more endpoints. The virtual cables is a 4 bit number in each 32 bit data field, all running within the same 2-endpoint interface.

VoltVisionFrenchy
02-23-2013, 02:46 PM
Hello! This thread is very interesting to me. The original poster mentioned that he is using a DAW (Digital Audio Workstation) which supports 3 different protocols over MIDI which require separate endpoints for each protocol. I see an additional significant benefit.... the ability to talk with more than one Midi Application at a time! Recently I did a big show where I had my MacBookPro running a DAW application (Reaper) for triggering audio samples via midi. The same MBP was running the main lighting control software for the event (QLC). When you use a standard USB Midi Controller like the Kenton Killamix Mini (http://www.kentonuk.com/products/items/midicontrol/kmix-mini.shtml), then whichever software "opens" the midi device first takes control of it and you cannot "open" the midi device in other software. I see a significant benefit using the Teensy2 or Teensy3 as a platform for developing new MIDI controllers which show up as multiple ports and can control different applications simultaneously. Perhaps this is specialty stuff here, but I see value in it.... of course we have to design the new Midi Controllers in a way that routes the various controller data to the appropriate ports in a useful and intuitive way.
Good thread! -frenchy

(ps-I would love to send OSC data (much higher resolution and flexible naming) using a Teensy over USB, but that is for another thread...)

MuShoo
02-23-2013, 07:15 PM
Update! I have successfully gotten Teensy to show up with 2 pairs of MIDI in/out jacks. Eventually I'll update it to 3 pairs (I'm just happy to have two working now!)

In Mac OSX 10.7.5, using MaxMSP as my MIDI host, they show up as 'Teensy Midi Port 1' and 'Teensy MIDI Port 2' (which is perfectly fine by me).

I had to tweak some things, I was using this forum post http://www.microchip.com/forums/m493322-print.aspx as a reference for the descriptor types (it does a few things differently). I still have to NOT include the AC Interface Descriptor stuff though (#if 0 section of the MIDI descriptor code).

Keeping track of the bLengths and wTotalLengths of all these descriptors is a huge pain in the ass. :)

MuShoo
02-23-2013, 08:05 PM
Gotten it up to 3 virtual in/out ports, which is my required limit (I think I understand it well enough that I can write up an explanation of what you need to change if you want to add more - there's a LOT of variables that are related to adding/removing ports.

Currently I'm now attacking the bitwise math/logic I need to make the usb_midi_class output to multiple ports (I am not good with bitwise math or logic). My ideal for using this would actually be to construct 3 instances of the class, usbMIDI1, usbMIDI2, and usbMIDI3, and have the constructor for each one assign it's Jack/Cable data there - instead of having to do it each time I call usbMIDI.sendControlChange(), I'd just call usbMIDI1.sendControlChange() or usbMIDI2.sendControlChange().

Is that feasible?

Also, I'm having some trouble parsing usb_midi_class::read():


if (type1 == 0x09 && type2 == 0x90) {
if (b3) {
msg_type = 1; // Note on
if (handleNoteOn) (*handleNoteOn)(c, b2, b3);
} else {
msg_type = 0; // Note off
if (handleNoteOff) (*handleNoteOff)(c, b2, b3);

That bold 0x09 is where I'd insert my jack ID nibble, correct?

MuShoo
02-24-2013, 01:31 AM
Alrighty, I've got everything _mostly_ working the way I want it to now. Except for reading in MIDI data on any of the ports, that seems to be acting very strange, and I'm not sure why. It's all still pretty rough and I'm sure there's some cleanup/efficiency stuff that could be done to it.

I've uploaded the current state of it to: *See a later post! Page 2!*

That .zip contains the current state of my 3-port USB files, as well as what appears to me to be fully functional MIDI sends on any of the three ports (oddly, it wouldn't let me instantiate my multiple instances of the usb_midi_class class unless I did it within the sketch! definitely look at the example/test sketch included.)

After instantiating your usb_midi objects (up to three) like so:


usb_midi_class usbMIDIa = usb_midi_class();
usb_midi_class usbMIDIb = usb_midi_class();
usb_midi_class usbMIDIc = usb_midi_class();

You'll need to put something like this:


usbMIDIa.setPortNum(0);
usbMIDIb.setPortNum(1);
usbMIDIc.setPortNum(2);

inside of your setup() function. I'm guessing there's an easier way to do this ("usb_midi_class usbMIDIb = usb_midi_class(1);" perhaps?) but I didn't really want to muck about with constructors too much.

Anyway, now you can use all the usbMIDI.sendWhatever() functions to any of the three virtual MIDI out ports that are set up.

However, as should hopefully be apparent with the Test sketch, reading doesn't work that well, and I'm not sure why. My completely uneducated guess is that the way pointers are being passed around to reference the various functions (such as OnNoteOn, which I've attempted to rectify by making OnNoteOnA and OnNoteOnB) isn't actually allowing for multiple handler functions.

Last note, there's a quick, rough MaxMSP-based standalone application included in the .zip file, for Mac (I don't have a windows machine to test this on with, and so I can't build windows MaxMSP apps) - it'll let you easily verify that all three in/out ports are appearing, and sending a MIDI note to one of the first two ports will make the third port spit back out some information. If you don't have a Mac, you can still see the problem by sending a note to one of the first two Teensy MIDI ports. A note sent to the first MIDI port should ONLY turn the LED ON, while a note to the second port should ONLY turn the LED OFF. Sadly that's not the case.

Any thoughts?

MuShoo
02-24-2013, 02:45 AM
Oh, another note - not sure if it's my fault or not, but Serial.print() and so forth don't seem to work anymore. Not really sure what I did, I don't think I touched the debug settings at all.

MuShoo
02-24-2013, 05:32 AM
*See later posts!*

SUCCESS. Though, it's still only been tested on a Mac - here's what I was doing wrong in the read() before:

By calling read(); for each of the three ports, I was inadvertently dumping the buffer data (UEDATX) _every_ time - so sometimes (50% of the time, when reading from two ports) I'd get buffer data that matches the channel I needed, and the other half I wouldn't. So now there's a (possibly not supposed to do this) *global* array that holds the buffer data (MIDIdata[]) - you read() only once for any loop, and it can be done from any of the 'ports' - speaking of which, I fixed the issue about having to define the usbMIDI objects in the sketch - so now you get usbMIDIa, usbMIDIb, and usbMIDIc just by having your Teensy type defined as MIDI.

Because of the changes to the way read() works, you now call something like usbMIDIa.read(); (it can be any of the three MIDI ports, but only call it ONCE per loop) to grab the buffer data into the MIDIdata array.

After you've read() from the buffer, you can use


usbMIDIa.parse(channel);
usbMIDIb.parse(channel);
usbMIDIc.parse(channel);

which functions the SAME as the previous usbMIDI implementation's 'read(channel);' function - ie:



const int ledPin = 11; // Teensy has LED on 11, Teensy++ on 6

int pinStatus = 0;

void setup()
{

pinMode(ledPin, OUTPUT);
usbMIDIa.setPortNum(0);
usbMIDIb.setPortNum(1);
usbMIDIc.setPortNum(2);

usbMIDIa.setHandleNoteOn(OnNoteOnA);

usbMIDIb.setHandleNoteOn(OnNoteOnB);

usbMIDIc.setHandleNoteOn(OnNoteOnC);

}

void loop(){
usbMIDIa.read(); // download MIDI data from
usbMIDIa.parse(); // parse midi data on port a
usbMIDIb.parse(); // parse midi data on port b
usbMIDIc.parse(); // parse midi data on port c
}

void OnNoteOnA(byte channel, byte note, byte velocity) {
pinStatus = 1;
digitalWrite(ledPin, HIGH);
}

void OnNoteOnB(byte channel, byte note, byte velocity) {
pinStatus = 0;
digitalWrite(ledPin, LOW);
}

void OnNoteOnC(byte channel, byte note, byte velocity) {
if (pinStatus)
digitalWrite(ledPin, LOW);
else
digitalWrite(ledPin, HIGH);
}

Writing MIDI to the various ports is simply of matter of using the identifier for the port you want to use, and using the same functions as Paul's MIDI api. For example, to send a midi note out of port B, and then a control change out of port C:


usbMIDIb.sendNoteOn(64, 127, 1); //send a note on out of port b
usbMIDIc.sendControlChange(37, 65, 15); //send control change #37 value 65, on channel 15, out port c

Anyway, like I mentioned earlier - I've only been able to test this on a Mac. I have no idea if the Ports will show up correctly on Windows or Linux - if anyone is feeling adventurous and wants to test those, let me know how it goes! (I honestly have no idea how midi is implemented within windows or linux, I'm mainly a mac audio geek)

nick
03-09-2013, 08:34 AM
Well done, but I get 404 error when i try to download the file.

ploveday
03-20-2013, 11:58 AM
Me too.

Any chance you can update your link? I need multiple USB MIDI ports for my project also.

- Peter

adrianfreed
04-01-2013, 08:33 PM
I just sent an alpha version of oscuino for Teensy 3.0 to CNMAT's release process.
Oscuino has to be tested on a rather large number of platform/device combinations so if you need a copy earlier
please e-mail me: adrian@cnmat.berkeley.edu.

Update: http://github.com/CNMAT/OSCuino is our release candidate
It runs on Teenys's and all the Arduino's I could lay my hands on except for the Due.

I will get to the Due eventually but Arduino for the Due is still too fragile and slow to build in.

MuShoo
05-05-2013, 06:17 AM
Alrighty, sorry about the lack of information. I'd somehow broken the Serial/Debug interface before, and I got busy with something so I didn't have a chance to update it.

Anyway, this one should work for everyone! Serial/Debug connection intact. Some notes!

If you want to change the name of your device, that's in 'usb_private.h' - line 47 (#define STR_PRODUCT). However, as noted in the file, windows (and mac!) cache USB device info - so I've made this have a different product ID than the regular Teensy MIDI setup. If you change the vendor ID, the serial connection will not work. Don't touch it! Changing the product ID seems to work fine, though.

The functionality of this is slightly different than the built-in Teensy MIDI system. (There's probably a better way to do this, but this seems to work OK for me). The usbMIDI.read() function has changed. Instead of calling a read() for each of the 3 virtual ports, you call read on just one of them - doesn't really matter which, but you only need one. Then, you call usbMIDIa.parse(), usbMIDIb.parse(), or usbMIDIc.parse(), which splits all the incoming bits into their correct ports for you.

Essentially, this:

void loop(){
usbMIDI.read();
}

has become this:


void loop(){
usbMIDIa.read();
usbMIDIa.parse();
usbMIDIb.parse();
usbMIDIc.parse();
}

Everything else should work pretty much the same as the normal Teensy MIDI code - although you have to change any instances of usbMIDI to usbMIDIa, b, or c. Otherwise it's all the same - set up different note handlers for each port, whatever you want to do with it. I'll admit I have not tested this extensively, so there could be giant hidden monsters somewhere that I didn't notice.

446

MuShoo
05-05-2013, 07:42 AM
Aaand - already found an issue! Working to resolve it, though I'm not entirely clear on what's happening yet.

Essentially it seems to be possible to overload the system with too much MIDI information, and then it struggles to catch up... So if you're doing something with a large amount of MIDI data sent to the Teensy, don't use this... yet.

MuShoo
05-05-2013, 08:25 AM
Fixed! I think. Maybe.

This is what you get when you let a non programmer play with data registers!

For those of you that understand this stuff at this level (Paul?) -

The last version, I was doing this:


if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B; //if read/write endpoint not allowed, release the buffer
SREG = intr_state;

MIDIdata[0] = UEDATX;
MIDIdata[1] = UEDATX;
MIDIdata[2] = UEDATX;
MIDIdata[3] = UEDATX;

Now I'm doing this:


MIDIdata[0] = UEDATX;
MIDIdata[1] = UEDATX;
MIDIdata[2] = UEDATX;
MIDIdata[3] = UEDATX;

if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B; //if read/write endpoint not allowed, release the buffer
SREG = intr_state;

From my (limited) understanding, I was checking if the buffer was empty (and releasing it if it was) before I actually pulled anything out of the buffer. Then I'd pull stuff out of the buffer - so the buffer would never actually clear (until, I assume, some other failsafe function came along and forcefully cleared it?) Now I pull data out of the buffer, THEN check if it's empty. Seems to work much more smoothly and responsively now.

Paul, if you've got the time, I invite you to pore over it and see if there's anything you might do differently. Due to the way (I think) the USB buffer stuff works, I had to set it up so that you only read from the buffer once, and then parse each 'port' separately - it's basically storing the USB buffer in a global array, and then leaving it up to each of the three MIDI ports to do anything with it. It *feels* ugly to me, but I can't think of a better solution.

As a side note, all this UEINTX and UEDATX and SREG and RWAL stuff makes my head hurt.

447

Wabelto
05-18-2013, 02:36 PM
Hi,

first of all, many thanks to Paul and MuShoo, great work.

My problem is that MuShoos' solution does not work under Windows (XP and Win7 tested).

Has anyone solve this?

MuShoo
05-19-2013, 09:36 AM
Wabelto - can you tell me what happens under windows? I never tested it under windows at all.

Wabelto
05-19-2013, 10:59 PM
MuShoo - I found the mistake:
in usb_private.h:

#define NUM_INTERFACE 4 //virtual MIDI in/out pairs, and the debug interfacethis is wrong. The count of interfaces is still 2:

#define NUM_INTERFACE 2
now it works (Windows XP):
476

tony666
09-19-2013, 11:20 AM
Is this also working on Teensy 3.0 ? Does anybody have a working example perhaps ?

tony666
09-20-2013, 10:15 AM
If I'm correct the teensy 3.0 uses the descriptors in the file ...Arduino\hardware\teensy\cores\teensy3\usb_desc. c . So i replaced the midi descriptors with those that Mushoo provided . Then I changed #define NUM_INTERFACE to 4 and #define NUM_ENDPOINTS to 5 (under USB MIDI in the file usb_desc.h) . But this doen't seem to work ....

MuShoo
09-20-2013, 09:09 PM
Sadly, I have no idea how to get this to work on a Teensy 3.0. I haven't used them at all yet. I'm sure I'll be using one eventually (and likely attempt to make multi-port MIDI work on them), but I have no idea when that'll be.

demon740
12-02-2013, 10:13 AM
My main aim of this thread is not same as my question but I found this thread while googling for TEENSY1.0 and arduino.

I want to build an USB MIDI cable with my TEENSY 1.0 (AT90USB162).

I have installed arduino 1.0.5 and teensyduino_v1.16 but I have not MIDI option in tools->usb
I have changed 'Arduino\hardware\teensy\boards.txt' and I have added these lines
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>
teensy1.menu.usb.midi.name=MIDI
teensy1.menu.usb.midi.build.define0=-DUSB_MIDI
teensy1.menu.usb.midi.fake_serial=teensy_gateway
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<
but during compile I got many errors. it seems in file 'Arduino\hardware\teensy\cores\usb_midi\usb_privat e.h' there is not any option for AT90USB162.

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> >>>>>>>>>>>>>>
...
// 0: control
// 1: debug IN
// 2: debug OUT
// 3: midi IN
// 4: midi OUT

#if defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)

// Some operating systems, especially Windows, may cache USB device
// info. Changes to the device name may not update on the same
// computer unless the vendor or product ID numbers change, or the
// "bcdDevice" revision code is increased.

#define STR_PRODUCT L"Teensy MIDI"
#define ENDPOINT0_SIZE 64
...
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< <<<<<<<<<<<<<<

Questions :

Did someone use AT90USB162 with USB MIDI in ARDUINO ?
Which files must be changed?

PaulStoffregen
12-02-2013, 11:13 AM
PJRC no longer supports any new developments on Teensy 1.0, which hasn't been sold in over 4 years.

If you have one of the original Teensy 1.0 boards purchased from PJRC, please email me directly (paul at pjrc dot com) with your original order number or invoice number, and details of the original order (eg, billing or shipping address). If Robin can look up your original order, I will send you a new board at no charge. I'll even pick up the shipping cost.

If you're using someone else's board or making your own, you are allowed to use the open source software I've published. But it comes with absolutely no technical support for use on non-PJRC products.

MuShoo
08-31-2014, 10:39 AM
Reviving this old dead thread, as I'm updating my 3-port MIDI descriptors and code to work on Teensy 3.1, and I'm a little confused.

In Teensy 2.0-land, the code for sending a NoteOff is:


send_raw(0x08, 0x80 | ((channel - 1) & 0x0F), note & 0x7F, velocity & 0x7F);

But in Teensy 3.1-land, it's:


usb_midi_write_packed(0x8008 | (((channel - 1) & 0x0F) << 8) | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));

Now, obviously the usb_midi_write_packed is the ARM version of sendRaw, but I'm confused as to why the first 16 bytes appear to be reversed in terms of sending order (ie, 0x08,0x80 on the old code, and 0x8008 in the new code).

I ask because my modified Teensy 2.0 code needs to pack the port number into the 0x08 portion of that, and I want to make sure I actually put it in the right place. Actually, early on in this thread, Paul mentions "They all call send_raw() to transmit 4 bytes. That first byte is the one with the cable number and message type. Just put your cable number into the upper 4 bits. Easy, right?" From some research, it looks like 0x80 is the MIDI command (send note off), and that would make the 0x08 be the portion containing the Port number, and... something else. Not sure. Highly confused.

MuShoo
08-31-2014, 10:57 AM
Wait, wait... I THINK I get it... maybe. all the << ops should have been a hint to me. essentially, T3 MIDI packs everything... backwards? So instead of, say, 08803C00 (Note Off, note 60, velocity 0) as it is in T2.0 MIDI, it's 003C8008? Which is the opposite of how the MIDI specs I've been reading seem to delineate things. Something something endians?

I'm still a little confused but I think this is right...

MuShoo
08-31-2014, 11:08 AM
...tested it briefly, I WAS right! Got that little chunk working, still a long ways to go...

Nantonos
08-31-2014, 02:18 PM
That confused me too and yes it is endianness.

TronicLabs
12-06-2014, 11:00 PM
Hi MuShoo, your updated code work with Teensy 3.1?

nlecaude
10-01-2015, 06:51 PM
Hello, I have the same question, were you able to make it work on Teensy 3.1 ?
Thanks !

fundorin
01-26-2017, 10:18 PM
Hi. Is it safe to directly rewrite contents of the installed usb_midi folder with the one from MuShoo?
I think that there could be some changes done by Paul in those files, since 2013 and I don't want to ruin anything (of cause, I've made a full backup of the original folder).
It would be nice if multiple ports functionality is available by default, as well as midi device + keyboard functionality. Thanks in advance.

UPD. Changed every "usbMIDI" to "usbMIDIa" in my sketch and got this error. I wonder why MuShoo didn't use usbMIDI/usbMIDI2/usbMIDI3 instead of usbMIDIa/usbMIDIb/usbMIDIc. It could be possible to use single port sketches with multiple port configuration without any changes.
9508
9509

Gerrit
03-13-2017, 10:42 AM
Is there any news on this subject, are there any plans to make multiple virtual midi ports part of the standard distribution?

Before diving into this I'd thought I'd check the status of the developments in this area. I would like to implement multiple ports on a Teensy 3.2 and 3.6. For my current projects two ports would suffice, but it would be great if more a available.
It would be really great if multiple virtual ports would be integrated in the current usbMIDI. As it is the Teensy is already the best option for creating simple to medium complexity MIDI devices. The addition of multiple virtual ports would make it the best choice for MIDI for any type of project as it would open up a whole range of new possibilities.

I may not be able to contribute to the codebase but I'd gladly help where I can, perhaps by writing examples and documentation.

Kind regards,

Gerrit

urbanspaceman
03-13-2017, 11:23 AM
+1 for me
interesting feature.