Reading MIDI data from USB Host when plugged in and discarding

UHF

Well-known member
I have a Teensy-based synth and when my MIDI controller is plugged-in to the T4.1 USB Host, it automatically sends several CC data messages that I want to ignore as it affects the settings on the synth. I can detect when the controller is plugged-in to the port but I can't see a way to read-off the incoming data and discard it. Any ideas what I should use? The basic code is below with the section where the read-off should be:

Code:
#include <USBHost_t36.h>
#include <MIDI.h>

// USB HOST MIDI Class Compliant
USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
MIDIDevice midi1(myusb);

boolean usbHostPluggedIn = false;


void checkUSBHostStatus()
{
    if (midi1 && !usbHostPluggedIn)
    {
        usbHostPluggedIn = true;
        Serial.println("usbHostPluggedIn");
        
		//REMOVE DATA FROM MIDI DEVICE 
    }
    else if (!midi1 && usbHostPluggedIn)
    {
        usbHostPluggedIn = false;
        Serial.println("usbHostUnplugged");
    }
}

void setup() {
      myusb.begin();
}

void loop(){
	checkUSBHostStatus();
    //USB Host MIDI Class Compliant
    myusb.Task();
    if (usbHostPluggedIn)
		midi1.read(MIDI_CHANNEL_OMNI);
}

Thanks.
 
I would like to help, but I don't really understand the problem you have.
When you plugin a midi device into your USB Host port, nothing happens with incoming midi as long as your code does not process it. So as long as you don't explicitly read the incoming CC messages and do something with it, incoming CC wont affect your synth.

But as you seem to have issues, you either have written code yourself, that is processing the incoming midi (to play the synth I guess) or you are using a library for that. Without knowing about that, it is hard to help your, because with the code you provide in your post, everything that is sent to USB Host is ignored.

The MIDI implementation on Teensy works (independent of Serial, USB Device or USB Host) in a way, that you either set a callback to the midi object, that is called for incoming messages, as soon as you read() them, or you have no callbacks, so after every read() you can handle the messages by yourself:

For the first:
Code:
void playANote( byte channel, byte note, byte velocity) {
 // Tell the synth engine to trigger the Amp Envelope, set pitch etc
}
void stopANote( byte channel, byte note, byte velocitY) {
  // Tell the synth to end playing a note
}

void setup() {
  midi.setHandleNoteOn( playANote);
  midi.setHandleNoteOff( stopANote);
}

void loop() {
while( midi.read());
}
as long as you have not setHandleControlChange set, CC messages will be ignored.
You can remove a callback by calling setHandleControlChange( nullptr);

for the second approach:

Code:
void loop() {
 while( midi.read()) {
  switch( midi.getType()) {
    case midi::MidiType::NoteOn:
      // Tell the synth engine to trigger the Amp Envelope, set pitch etc
      break;
    case midi::MidiType::NoteOff:
      // Stop playing the note
      break;
    case midi::MidiType::ControlChange:
      // do nothing
      break;
  }
 }
}
 
I'm using the callbacks. This approach doesn't work, there is data waiting on the buffer at the moment midi1.setHandleControlChange(...) is set. Also calling midi1.setHandleControlChange(...) again when the USB is plugged in again, crashes it.

Code:
FLASHMEM void checkUSBHostStatus()
{
    if (midi1 && !usbHostPluggedIn)
    {
        usbHostPluggedIn = true;
        Serial.println("usbHostPluggedIn");
        delay(4000);
        midi1.setHandleControlChange(myControlChange);
    }
    else if (!midi1 && usbHostPluggedIn)
    {
        midi1.setHandleControlChange(nullptr);
        usbHostPluggedIn = false;
        Serial.println("usbHostUnplugged");
    }
}
 
Last edited:
I still don't understand what you are doing, and why you are doing it.

If I understand you correctly: you don't want incoming CC Message to be handled that are coming from USB Host?
Why are you using the ControlChange callback on USBHost, if you don't want them to do anything?
The USB Serial Buffer is handled by calling .read() on the usb object (on all of them). There don't need to be a callback set for every message type. You can leave them "empty" if you dont want the messages to be handled (I can say that for sure, in 99% of the times, I am dropping every single midi message as long as my sequencer is not in record mode, and the sequencer can run for days in that state, so nothing is filling up or so. My USB MIDI-Hub that acts as connection between DIN, USB Host and USB Device, I am constantly reading and either dropping or forwarding messages on all 3 different midi types. I have not had a single issue with buffers overflowing, or any problems, when I skip handling message types completely).

On top of that, in your second example: why would you change the callback dependent on the status of the USB Host? If you assign the callback as soon as you plugin the device (first if), it is totally understandable, that the incoming messages are sent there.

Maybe it is really easier, if you write down (or post code) what you are currently doing, because this is some meta-discussion without knowing anything behind that.
 
Yes I want CC messages. I don't want the messages that get sent by some MIDI controllers when they get plugged in and start up - they're irrelevant. Perhaps I should mention that I'm taking MIDI in from USB Host, USB Client and Serial (5 pin DIN). I'll try using a dedicated CC handler for USB Host and only forwarding to the main CC handler that the other two use, after a delay when the host is plugged in to allow the irrelevant CC messages to be dealt with and hopefully ignored. Like this:

Code:
midi1.setHandleControlChange(usbHostControlChange);



FLASHMEM void usbHostControlChange(byte channel, byte control, byte value)
{
    if (usbHostPluggedIn)
    {
        myControlChange(channel, control, value);
    }
}

FLASHMEM void checkUSBHostStatus()
{
    if (midi1 && !usbHostPluggedIn)
    {
        Serial.println("usbHostPluggedIn");
        delay(4000);//Wait for irrelevant CC messages to arrive
        usbHostPluggedIn = true;
    }
    else if (!midi1 && usbHostPluggedIn)
    {
        usbHostPluggedIn = false;
        Serial.println("usbHostUnplugged");
    }
}
 
Last edited:
Oh! Now I see what I missed. I am so sorry! You don't want to ignore ALL messages but just the first few one that the controller send on startup!

You are totally right with the assumption, that the buffer is your problem here. during your delay(4000) nothing is executed on the Teensy (of your main code). So midi1.read() is not called, so everything that happens is, that the buffer fills up and you handle the messages a few seconds later, so you just need to use a different way to wait.

My idea would be to use elapsed time to determine if you want to handle the messages or not.

Code:
elapsedMillis usb_host_wait_timer;
FLASHMEM void checkUSBHostStatus()
{
   
    if (midi1 && !usbHostPluggedIn)
    {
        Serial.println("usbHostPluggedIn");
        usb_host_wait_timer = 0;
        usbHostPluggedIn = true;
    }
    else if (!midi1 && usbHostPluggedIn)
    {
        usbHostPluggedIn = false;
        Serial.println("usbHostUnplugged");
    }
}
FLASHMEM void usbHostControlChange(byte channel, byte control, byte value)
{
    if (usbHostPluggedIn && usb_host_wait_timer > 4000)
    {
        myControlChange(channel, control, value);
    }
}

This has an issue tho, as microseconds overflow after 49 days. so if you let the device run for 49 days without plugging in or out the usbhost, you might have a 4 second gap, where this code acts differently. You could avoid this, by resetting the variable before it overflows. This might be a quite hacky way, but it should work.

Code:
FLASHMEM void checkUSBHostStatus()
{
    if( usb_host_wait_timer > 0xF000000) {
        usb_host_wait_timer = 5000; // Reset to avoid 4 second long missbehaviour after 32bit overflow.
    }
    if (midi1 && !usbHostPluggedIn)
    {
        Serial.println("usbHostPluggedIn");
        usb_host_wait_timer = 0;
        usbHostPluggedIn = true;
    }
    else if (!midi1 && usbHostPluggedIn)
    {
        usbHostPluggedIn = false;
        Serial.println("usbHostUnplugged");
    }
}

Something you might also consider: midi1 knows the name of the plugged in device. I use that to show in the message I show to the user, when I recognize the usbhost. You could also use that to handle different devices differently (just reset the timer for the device that sends irrelevant data on startup)

The USBDriver (that is the base for USBMidi) has a method product:

Code:
const uint8_t *product();

you can cast that to const char* and use it as string.
 
Thanks very much for this. It works to ignore the CC data when plugging in and then takes CC data after. However, it's freezing and crashing when unplugging and plugging back in about 80% of the time - it's not consistent. The test code I'm using is:


Code:
#include <MIDI.h>
#include <USBHost_t36.h>

boolean usbHostPluggedIn = false;

USBHost myusb;
USBHub hub1(myusb);
USBHub hub2(myusb);
MIDIDevice midi1(myusb);

elapsedMillis usb_host_wait_timer;

FLASHMEM void myControlChange(byte channel, byte control, byte value)
{
  Serial.printf("Ch:%u: cc=%d, v=%d\n", channel, control, value);
}

FLASHMEM void usbHostControlChange(byte channel, byte control, byte value)
{
  if (usbHostPluggedIn && usb_host_wait_timer > 4000)
  {
    Serial.println("usbHostControlChange");
    myControlChange(channel, control, value);
  }
}

FLASHMEM void checkUSBHostStatus()
{
  if (midi1 && !usbHostPluggedIn)
  {
    Serial.println("usbHostPluggedIn");
    usb_host_wait_timer = 0;
    usbHostPluggedIn = true;
  }
  else if (!midi1 && usbHostPluggedIn)
  {
    usbHostPluggedIn = false;
    Serial.println("usbHostUnplugged");
  }
}

void setup() {
  while (!Serial) {
  }
  Serial.print(CrashReport);
  myusb.begin();
  midi1.setHandleControlChange(usbHostControlChange);
  Serial.println(F("USB HOST MIDI Class Compliant Listening"));
}

void loop() {
  checkUSBHostStatus();
  // USB Host MIDI Class Compliant
  myusb.Task();
  if (midi1 && usbHostPluggedIn)
    midi1.read(0);
}
 
I just flashed that code onto my sequencer hardware and plugged in and plugged out my Launchpad Pro 20 times in a row, with me waiting, with me not waiting a bit to hopefully see where it crashed, but I had no crash at all.
Are you able to use a different USB MIDI device to see if it is related to the device you are testing with?

Another thing:
As far as I understand, how that USB Host stuff works, is that the Task() call is updating the internal state of the USB devices, so you should call this before you update your local status, because the way you are doing this now, at least usbHostPluggedIn should have a wrong state for one loop.
 
Oh, I forgot: If you go the the USBHost_t36.h file and uncomment line 62, you will see a lot of debug messages from the USB Host library. Maybe that might help to locate where it crashes.

Code:
// Uncomment this line to see lots of debugging info!
#define USBHOST_PRINT_DEBUG
 
Again, thanks very much for your help. The intermittent crashing when plugging in must be some timing/threading/race condition? issue. Having USB debug info doesn't help, it crashes before anything comes out to Serial. I tried a digital piano that has a USB output and this is always fine, it's an Arturia Minilab problem perhaps.
 
When your teensy crash BEFORE the USB Host library is able to print out debugging messages, there might be a deeper problem than some race-conditions with interrupts. As far as I know, the usb system initializes and prints out data, before it powers up the external device itself. So if you are seeing not a single message in serial, the key lab causes issues (maybe too much power required?) that are way before your code doing anything.
 
Again, thanks very much for your help. The intermittent crashing when plugging in must be some timing/threading/race condition? issue. Having USB debug info doesn't help, it crashes before anything comes out to Serial. I tried a digital piano that has a USB output and this is always fine, it's an Arturia Minilab problem perhaps.

Maybe try adding the following to setup() to aid in narrowing down where the crash is occurring (this capability provides info gathered during the crash, but reported as part of the next boot):

Code:
   if (CrashReport) {
      Serial.print(CrashReport);
   }

And <here's> a link with an example of how to use addr2line.

Hope that helps . . .

Mark J Culross
KD5RXT
 
... (maybe too much power required?)...

I think this is the problem. I run my synth via a USB isolator (to remove USB noise from the analogue audio) that drops some voltage. If I plug/unplug the Minilab, which is entirely USB powered, without the isolator it's fine. Time to get a better quality isolator!

Yes, I'm using CrashReport. The output varies each time it crashes.
 
Back
Top