Teensy 2.0, USB MIDI and SysEx

Status
Not open for further replies.

blake

Member
I recently purchased a Teensy 2.0 as I am migrating a project that started with an Arduino but I wanted to keep my hardware at +5 Vdc while making use of the extra output pins and the USB MIDI features. I am hitting wall with trying to read System Exclusive messages from a DAW on a macintosh and need some guidance.

Firstly, the serial MIDI library supports a callback function to read System Exclusive messages but I don't see that functionality duplicated with USB MIDI. Is this correct? If so, is it forthcoming?

Secondly, as a test I wrote a very small program to test and see if an incoming message is System Exclusive and I seem to be getting a lot of data with nothing else on my computer sending MIDI. I'm making use of the macintosh utility MIDI Monitor to keep an eye on things and it looks like the Teensy is currently seeing a ton of phantom SysEx data.

Here's my simple program:

Code:
void setup() {
  // put your setup code here, to run once:
    Serial.begin(38400);
}

void loop() {
  // put your main code here, to run repeatedly:
  usbMIDI.read();
Serial.print("Message is of type ");Serial.println(usbMIDI.getType());
}

The output to serial is attached as a screencap. Any advice would be greatly appreciated!

Screen Shot 2016-10-18 at 9.55.26 PM.png
 
You need to check the number returned by usbMIDI.read(). Zero (false) indicates the PC hasn't sent anything new. You don't want to keep printing info and calling things like usbMIDI.getType() when there's nothing new to read.
 
Thank you, Paul! Clearly a rookie mistake. This should clean things up quickly.

Let's see what I can break next.

Thanks again!
 
Firstly, the serial MIDI library supports a callback function to read System Exclusive messages but I don't see that functionality duplicated with USB MIDI. Is this correct? If so, is it forthcoming?

usbMIDI page does reference usbMIDI.getSysExArray() but doesn't really show you how to use it. The trick I stole is to use this line of code that defines a data array variable as a pointer.

byte *sysExBytes = usbMIDI.getSysExArray();

I used it in my Footsy project and the code shows how you then can refer to the individual bytes. Here's the snippet:
Code:
//************SYSEX SECTION**************
void doSysEx(){
  byte *sysExBytes = usbMIDI.getSysExArray();
  if (sysExBytes[0] == 0xf0 
  && sysExBytes[15] == 0xf7 
  && sysExBytes[1] == 0x7D // 7D is private use (non-commercial)
  && sysExBytes[2] == 0x4C // 4-byte 'key' - not really needed if via USB but why not!
  && sysExBytes[3] == 0x65
  && sysExBytes[4] == 0x69
  && sysExBytes[5] == 0x66){ // read and compare static bytes to ensure valid msg
      for (int i = 0; i < 10; i++) {
      EEPROM.write(i, sysExBytes[i+6]);
      mem[i] = sysExBytes[i+6];
    }  
    byte data[] = { 0xF0, 0x7D, 0xF7 }; // ACK msg - should be safe for any device even if listening for 7D
    usbMIDI.sendSysEx(3, data);         // SEND
    for (int i = 0; i < 3; i++) {
      toggled[i] = false; // for consistant behaviour, start in OFF position
    }
  }
But that's looking for a known length message... I never did experiment with how you scan for the end byte efficiently.

I believe this was length limited at some point but I think that may have been fixed.

BTW the code writes a simple chain of settings to EEPROM so my controller can be configured via midi sysex and retain settings after reboot.
 
After some time being pulled and distracted by various projects, I finally have time to work on this again.
Unfortunately, it appears the teensy is dropping sysex packets. I'm not exactly sure what to try to fix and I'm hoping someone smarter than me may have a suggestion. I'm sending sysex messages from protools. In addition to the teensy, I'm using the macOS native application MIDI Monitor to help keep track of things.

Here's my code…

Code:
unsigned long time;


void setup() {
  // put your setup code here, to run once:
    Serial.begin(250000);
    Serial.println("midiUsbSysexVer03");
}

void loop() {
  // put your main code here, to run repeatedly:
  usbMIDI.read();
  if (usbMIDI.read() == 1 && usbMIDI.getType() == 7 ) {//check for sysex 
    handleSysEx();
    }

}

void handleSysEx(){
  Serial.print(millis());Serial.print("\t");
  byte inSize = usbMIDI.getData1();
  byte *sysExBytes = usbMIDI.getSysExArray();
      Serial.print("The sysex length is ");Serial.print(inSize);Serial.print(" bytes\t");
      Serial.print("MIDI sysex message   \t");
       for(int i=0; i!=inSize; i++){
    Serial.print(*sysExBytes++, HEX);Serial.print(" ");
    }
   
   Serial.println(" ");
      
}

The following two screencaps come from MIDI Monitor and the Arduino serial monitor. Notice the various sysex messages that are in the MIDI Monitor screencap that don't make it to the Teensy.
MIDI monitor.pngArduino serial monitor.png
Thoughts?
 
If I try to recreate this problem, what software do I use to sent the sysex messages? Remember, I'm not a musician and I don't regularly use this sort of software... so specific info & steps would help.
 
Hi, Paul. This may not be so easy to replicate on your end as I am using ProTools to send Mackie HUI messages.

An explanation of the spec (via a pdf link) can be found at this page http://forum.cockos.com/showthread.php?t=101328

In other news, I borrowed a friend's teensy 3.2 and ran my code on it. Both teensy's exhibit the same behavior.
 
Paul,

I'm kinda stuck on things. If I remove the sysex test "usbMIDI.getType() == 7" I see TONS of MIDI data, much of it repeated multiple times. When I add back in "usbMIDI.getType() == 7", I see packets of sysex data getting dropped on occasion. Is it possible that the "usbMIDI.getType() == 7" function (or however that may work) is occasionally not seeing new sysex data arrive?

My next step would be to keep reading "usbMIDI.getSysExArray()" into two different pointers and attempt to compare new vs old data. I'm currently a bit hung up. Any thoughts?
 
Sorry, I don't know anything about midi code, so probably won't help much.

But looking at your loop code. You first do a read not testing anything, then you do read checking for result of 1, followed by your other test. If both reads do actually read a packet might you be skipping some?
 
Hi all,

I'm new to this forum and this thread is one of the reasons I signed up. I'm also working on MIDI controllers and the Teensy seems like the perfect choice for this application, especially with the newer versions with the larger RAM.
To give an idea, here's a picture of a project I'm working on:

ZeusPC3.jpg

A little device for sending program change messages to hardware synths, what sets this one apart is the (planned) ability to show the program names. For this I need to be able to send a program dump request(got that working) and extract the program names from the response. The current limit of 60 bytes is far too small, a typical program dump is about 10-20kB. A Teensy 3.2 should be able to handle this, of course the Arduino Uno on the right has far to little memory for this.
Using blake's code I can get this response:
Code:
18580	The sysex length is 60 bytes	MIDI sysex message   	F0 3E 0 0 50 20 0 40 2 0 0 9 4A 3 36 0 40 0 42 2 0 0 9 4A 3 36 0 0 1E 3F 6A 40 40 40 0 0 40 0 40 1 5 3F 0 40 40 40 0 0 40 0 40 0 0 70 0 0 7F 70 40 40
When I change the limit in usb_midi.h to 16000 or even 160 the sysex is no longer processed. Any help would be greatly appreciated.
Tools for sending MIDI sysex mesages:
For Windows: MIDI-OX
For Mac: SysEx Librarian
Examples of large MIDI sysex files can be found here: Waldorf Microwave I Sounds, get the plain_sysex zip. The content can be loaded in one of the tools and sent to the Teensy.

When I get this up and running I would be more than happy to document it somewhere so that others can have a look at it, use it, learn from it or whatever. Any suggestions as to what would be a good place to document such a project?

king regards,

Gerrit
 
byte inSize = usbMIDI.getData1();

You will overflow this byte after 256 bytes of data.... not sure why it would fail by 160 bytes.

https://forum.pjrc.com/threads/32369-Teensy-3-2-MIDI-SysEx-size-over-255-bytes

(...and do you need to use .getData1 to read the size of the array or could you just read from the pointer until you hit the terminating byte?)

Note also that KurtE is correct that Blakes code should not use two .read commands and that is almost certainly where his missing sysex messages were going.
 
Last edited:
Maybe you're not reading the sysex quickly enough? Could all the writing to the serial monitor be taking to long? Why don't you simply track the lengths of the messages?
 
Also, wouldn't it make more sense to do this for your loop. Perhaps it's possible the usbMIDI.read() returns something that's not 1.
Code:
if (usbMIDI.read() && usbMIDI.getType() == 7 )
 
Thanks for the replies!

To simplify things I reduced the code in the main loop to:
Code:
  while (usbMIDI.read()) {
   if (usbMIDI.getType() == 7 ) {   //check for sysex 
    Serial.print("The sysex length is ");Serial.print(usbMIDI.getData1());
    Serial.println(" "); 
   }
  }
Now it works as expected up to sysex length 255:
Code:
The sysex length is 255
When the size in usb_midi.h is 256 or more I get:
Code:
The sysex length is 7
The 7 bytes are always the last seven bytes of the 11527 byte large file irrespective of the size set in usb_midi.h:
Code:
.....
2CD0  00 07 40 15 01 00 28 00  32 00 40 00 00 00 40 00  |  @   ( 2 @   @ |
2CE0  40 00 20 01 00 33 36 61  20 50 50 47 20 77 61 76  |@    36a PPG wav|
2CF0  65 20 32 2E 33 00 00 00  00 00 00 00 00 00 00 00  |e 2.3           |
2D00  00 00 00 00 55 4C F7                              |    UL |
This is from the mwave2_3.syx file contained in the examples I mentioned earlier.

IMHO this looks like there's a byte limitation somewhere or am I missing something?

kind regards,

Gerrit
 
The max length is defined as a byte, so you are capped at 256 unless you're ready to edit the usb_midi.h and usb_midi.c files.
 
The max length is defined as a byte, so you are capped at 256 unless you're ready to edit the usb_midi.h and usb_midi.c files.
Well, that explains it. Thanks.
I'll look if I can figure out the required changes although I think the 255 byte limit should not be there in the first place. A conservative default size is a good idea though, it doesn't even need to be as much as 255 bytes, 160 bytes is enough for simple sysex applications and even Mackie/HUI control emulations.

kind regards,

Gerrit
 
That library was written back in the day when on board memory was in short supply.

I've rewritten parts of the library so that I can forward full length sysEx messages from the USB port to a MIDI din out, and vice versa from a MIDI din In to USB MIDI.
 
...and do you need to use .getData1 to read the size of the array or could you just read from the pointer until you hit the terminating byte?...
Can anyone say whether this is feasible... to just increase the memory limit and exit a 'while' loop when the extracted byte equals F7?
 
That library was written back in the day when on board memory was in short supply.

I've rewritten parts of the library so that I can forward full length sysEx messages from the USB port to a MIDI din out, and vice versa from a MIDI din In to USB MIDI.

Are these changes available somewhere? Given the increased amount of RAM it makes sense to me to incorporate these changes in the core.

kind regards,

Gerrit
 
It's been customized to meet my particular needs and might not be suitable for everyone as it adds a bit of complexity. And in terms of RAM, it actually uses less. I'll think about posting it.
 
...and do you need to use .getData1 to read the size of the array or could you just read from the pointer until you hit the terminating byte?...
Perhaps, but what's the problem with using getData1?
Other than it doesn't work?

If you use a 'do while' you should only need to alter one line in the .h file:

#define USB_MIDI_SYSEX_MAX 60 // maximum sysex length we can receive

Unless there's a way to do it in the sketch like with the MIDI device display string, which would be better still.
 
Status
Not open for further replies.
Back
Top