Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 15 of 15

Thread: USBhost_t36 MIDI losing midi note on / off events (Teensy 3.6) during modwheel change

  1. #1
    Senior Member
    Join Date
    Feb 2021
    Posts
    129

    USBhost_t36 MIDI losing midi note on / off events (Teensy 3.6) during modwheel change

    I'm using the USBhost_t36 lib to connect the popular Arturia minilab mk II midi keyboard/controller to a synth I have developed for Teensy 3.6. Unfortunately note on and off events are lost when I play keys and move the "modwheel" (really a touch slider) quickly at the same time. Interestingly, I see the issue only with the modwheel and not other controllers, including the pitch wheel which is also a touch slider. Maybe the minilab is sending modwheel data at a higher rate, but it seems a bit odd to me that the USBhost_t36 would not be able to keep up with any midi controller. I have tested the synth with the (non-host) USB midi connection, connectng the minilab to a computer and then the computer passing the data on to the Teensy via midiOX, and it works just fine via that midi path.

    So the problem appears to be with the USBhost_t36 lib code rather than my software. And indeed I can replicate the same problem using the "InputFunctions" example code provided in the library. I stripped that example down to the bare minimum and left in only the callbacks for midi note on/off and modwheel CC. I added one variable that keeps track of the number of notes being held down (NumNotes) that is incremented for note on events and decremented for note off (or 0-velocity note on) events. When I release all the keys on the controller, that number should then be 0, but it is not always when I move the modwheel quickly whilst playing notes. The serial print shows that both note off and on events can be lost during modwheel movements. It even happens when I do nothing inside the modwheel callback (commenting out its serial print messages).

    Any suggestions for things to try are appreciated. I reinstalled the latest versions of Arduino / Teensyduino jus in case, but no change.


    Here is the modified example code:

    Code:
    #include <USBHost_t36.h>
    
    USBHost myusb;
    MIDIDevice midi1(myusb);
    
    void setup() {
      Serial.begin(115200);
      delay(1500);
      myusb.begin();
    
      midi1.setHandleNoteOn(myNoteOn);
      midi1.setHandleNoteOff(myNoteOff); 
      midi1.setHandleControlChange(myControlChange);
    }
    
    int NumNotes=0;
    
    void loop() {
      myusb.Task();
      midi1.read();
    }
    
    
    void myNoteOn(byte channel, byte note, byte velocity) {
      Serial.print("Note On, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.println(velocity, DEC);
      
      Serial.print(">>>>>>>> ");
      if(velocity>0)
        Serial.println(++NumNotes);
      else
        Serial.println(--NumNotes);  
    }
    
    void myNoteOff(byte channel, byte note, byte velocity) {
      Serial.print("Note Off, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.println(velocity, DEC);
      
      Serial.print(">>>>>>>> ");
      Serial.println(--NumNotes);
    }
    And here is an example of a section of the the Serial output showing the problem for a missing note-on event:


    Note On, ch=1, note=59, velocity=95
    >>>>>>>> 1
    Note Off, ch=1, note=59, velocity=0
    >>>>>>>> 0
    Note On, ch=1, note=62, velocity=125
    >>>>>>>> 1
    Note Off, ch=1, note=60, velocity=0
    >>>>>>>> 0
    Note Off, ch=1, note=62, velocity=0
    >>>>>>>> -1


    p.s. In this library example code, what is the purpose of myusb.Task() in the main loop? I can comment it out with no effect on the code's function, so I'm unclear on when this line would be needed and when it would not.
    Last edited by rvh; 05-11-2022 at 02:12 AM.

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,864
    I tried your code with an M-Audio keyboard which has a modwheel. No matter how much I roll the wheel and play chords and arpeggios, the total note count is always zero at the end.
    You didn't include your myControlChange function. I just copied this from an example:
    Code:
    void myControlChange(byte channel, byte control, byte value) {
      Serial.print("Control Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", control=");
      Serial.print(control, DEC);
      Serial.print(", value=");
      Serial.println(value, DEC);
    }
    Is your version any different?

    Pete

  3. #3
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    Quote Originally Posted by el_supremo View Post
    I tried your code with an M-Audio keyboard which has a modwheel. No matter how much I roll the wheel and play chords and arpeggios, the total note count is always zero at the end.
    You didn't include your myControlChange function. I just copied this from an example:
    Code:
    void myControlChange(byte channel, byte control, byte value) {
      Serial.print("Control Change, ch=");
      Serial.print(channel, DEC);
      Serial.print(", control=");
      Serial.print(control, DEC);
      Serial.print(", value=");
      Serial.println(value, DEC);
    }
    Is your version any different?

    Pete
    Thanks Pete,
    Yes that's the same function, sorry I missed that. But even if I comment everything out in that function I can lose note events if changing controls very quickly. I've experimented some more and can get it to happen not just on the modwheel but also with other controls if I change them quickly enough whilst pressing down or releasing a key. The number of notes played is not so much the issue. I wonder if the m-audio sends changes more slowly. Certainly the small touch modwheel slider on the minilab can produce very fast changes. I do have to try harder to get it to lose note events by turning the rotary controls.

  4. #4
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    I've now tried two Teensy 3.6 boards, one USB powered, and the other with an external 5V supply, and get the same missing note events in both cases when I change controllers fast enough.
    And no matter how hard I try, running my synth via the non-host USB midi port with the minilab II controller connected via midiOX doesn't lose any note events. So I don't think I have hardware issues.

    Cheers,
    Richard

  5. #5
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    I found an older Novation REMOTE SL37 keyboard controller and like Pete's M-Audio keyboard it behaves just fine too.

    Maybe the Arturia minilab II, being a newer controller, is expecting midi USB host devices to be faster these days. Does anyone who knows the workings of the USBhost_t36 code think it plausible that it might not be fast enough for recent controllers?

  6. #6
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    I have not been able to make the Arturia minilab II work reliably with midi via the USB host port, so it seems the T36 host lib as it stands cannot be used in midi products that would be released to the general public to be used with contemporary keyboard controllers. I'm surprised there's not more interest in this?

  7. #7
    Senior Member
    Join Date
    Apr 2020
    Location
    DFW area in Texas
    Posts
    386
    Quote Originally Posted by rvh View Post
    I have not been able to make the Arturia minilab II work reliably with midi via the USB host port, so it seems the T36 host lib as it stands cannot be used in midi products that would be released to the general public to be used with contemporary keyboard controllers. I'm surprised there's not more interest in this?
    @rvh: Regarding your earlier question on the use/need for calling myUSB.Task(), the USBhost source shows:

    Code:
    /**
     * \brief USB main task, responsible for enumeration and clean up stage.
     *
     * \note Must be periodically called from loop().
     */
    void USBHost::Task(void)
    You'd probably only notice a difference between including/excluding this call if/when connecting/disconnecting USB devices while running.

    As for the main problem that you are observing, unfortunately, I don't have any direct suggestions to offer towards solving what you are experiencing. However, as an alternate data point, I can report the following: when exercising my TeensyMIDIPolySynth (using T4.1) using a MIDIPLUS AKM322 mini keyboard connected via the USBhost port, I am not able to reproduce any lost noteOn / noteOff messages with the simultaneous use of any other controls. This particular keyboard has a pitchbend wheel (reporting +8192 to -8192 counts), a modwheel (reporting 0 to 127 counts), a rotary encoder (reporting 0 to 127 counts), 3 digital pots (each reporting 0 to 127 counts), and a slider (reporting 0 to 127 counts). No matter how quickly I use any/all of these controls to generate CC messages while playing notes, I am not able to cause any message losses.

    Because of the immense amount of source code (currently 33000+ LOC) for my TeensyMIDIPolySynth, I am compiling using "Smallest Code". You might try different optimization levels to see if that makes any difference in what you see. In addition, if you have a way to cool your T3.6, could you try over-clocking to see if that makes any difference ?? Have you checked your audio processing load ?? As a last resort, you might try a T4.x.

    Good luck & have fun !!

    Mark J Culross
    KD5RXT

  8. #8
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    Quote Originally Posted by kd5rxt-mark View Post
    @rvh: Regarding your earlier question on the use/need for calling myUSB.Task(), the USBhost source shows:

    Code:
    /**
     * \brief USB main task, responsible for enumeration and clean up stage.
     *
     * \note Must be periodically called from loop().
     */
    void USBHost::Task(void)
    You'd probably only notice a difference between including/excluding this call if/when connecting/disconnecting USB devices while running.

    As for the main problem that you are observing, unfortunately, I don't have any direct suggestions to offer towards solving what you are experiencing. However, as an alternate data point, I can report the following: when exercising my TeensyMIDIPolySynth (using T4.1) using a MIDIPLUS AKM322 mini keyboard connected via the USBhost port, I am not able to reproduce any lost noteOn / noteOff messages with the simultaneous use of any other controls. This particular keyboard has a pitchbend wheel (reporting +8192 to -8192 counts), a modwheel (reporting 0 to 127 counts), a rotary encoder (reporting 0 to 127 counts), 3 digital pots (each reporting 0 to 127 counts), and a slider (reporting 0 to 127 counts). No matter how quickly I use any/all of these controls to generate CC messages while playing notes, I am not able to cause any message losses.

    Because of the immense amount of source code (currently 33000+ LOC) for my TeensyMIDIPolySynth, I am compiling using "Smallest Code". You might try different optimization levels to see if that makes any difference in what you see. In addition, if you have a way to cool your T3.6, could you try over-clocking to see if that makes any difference ?? Have you checked your audio processing load ?? As a last resort, you might try a T4.x.

    Good luck & have fun !!

    Mark J Culross
    KD5RXT
    Thanks Mark,

    I very much appreciate any and all suggestions, as I'd really like to have the option of connecting controllers to my synth without a computer. But this is a synth I'd like to make into a product so (for now) I can't use the host midi port if it doesn't work with the Arturia Minilab II. It's a popular controller with a very small form-factor that pairs well synths because it has 16 rotary controllers as opposed to the more usual 8 or so.

    I'm guessing it sends out midi data quicker than some other controllers being more recent, having more controllers, and two small touch sliders for pitch bend and modwheel that allow you to go from one extreme to the other very quickly. The fact that the Arturia works fine with the regular usb midi port on the teensy 3.6 points to the t36 host lib code being the bottle neck, but I'm hoping someone who knows its workings can confirm that's plausible (and ideally fixable). I've also looked at the output from the Arturia with midi OX and see nothing indicating the Arturia is doing anything it shouldn't be.

    If you look at the example code above, it's a completely minimal t36 USB host library example that only prints incoming midi note events. There's no audio or anything else running, yet it easily loses midi note on/off events using the Arturia but not my older controllers, and even when there is no code being executed in the midi CC callback.

    If I get a chance I may check if that example on a Teensy 4.1 has the same problem, but unfortunately I can't use the 4.1 for my application because I need the integrated DAC from the the 3.6 for its ability to play audio samples at any time rather than at a steady sample rate.

    Cheers,
    Richard
    Last edited by rvh; 05-20-2022 at 11:50 PM.

  9. #9
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    I tried overclocking the T36 at 240Mhz (rather than 180Mhz) but it didn't solve my problem.

  10. #10
    Senior Member
    Join Date
    Apr 2020
    Location
    DFW area in Texas
    Posts
    386
    Quote Originally Posted by rvh View Post
    Thanks Mark,

    I very much appreciate any and all suggestions, as I'd really like to have the option of connecting controllers to my synth without a computer. But this is a synth I'd like to make into a product so (for now) I can't use the host midi port if it doesn't work with the Arturia Minilab II. It's a popular controller with a very small form-factor that pairs well synths because it has 16 rotary controllers as opposed to the more usual 8 or so.

    I'm guessing it sends out midi data quicker than some other controllers being more recent, having more controllers, and two small touch sliders for pitch bend and modwheel that allow you to go from one extreme to the other very quickly. The fact that the Arturia works fine with the regular usb midi port on the teensy 3.6 points to the t36 host lib code being the bottle neck, but I'm hoping someone who knows its workings can confirm that's plausible (and ideally fixable). I've also looked at the output from the Arturia with midi OX and see nothing indicating the Arturia is doing anything it shouldn't be.

    Cheers,
    Richard
    Richard:

    I took another look at my TeensyMIDIPolySynth source & realized that there is one additional difference in my implementation of the USBHost interface that might be pertinent. Because I have only used T4.x for all of my projects, I don't know if this is even valid and/or would make any difference, but could you try the following when declaring your USBHost interface:

    Code:
    USBHost myusb;
    MIDIDevice_BigBuffer midi1(myusb);     // <<<=====
    In searching the forum for more clues, it's easy to find other reports of occasional MIDI data loss & the common answer is to use the "MIDIDevice_BigBuffer" declaration (which is probably how I ended up with that in my source). See <this> example post & <this> post, both by Paul, as just two examples w/ explanation of the difference.

    Hope that helps . . .

    Mark J Culross
    KD5RXT

  11. #11
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    Quote Originally Posted by kd5rxt-mark View Post
    Richard:

    I took another look at my TeensyMIDIPolySynth source & realized that there is one additional difference in my implementation of the USBHost interface that might be pertinent. Because I have only used T4.x for all of my projects, I don't know if this is even valid and/or would make any difference, but could you try the following when declaring your USBHost interface:

    Code:
    USBHost myusb;
    MIDIDevice_BigBuffer midi1(myusb);     // <<<=====
    In searching the forum for more clues, it's easy to find other reports of occasional MIDI data loss & the common answer is to use the "MIDIDevice_BigBuffer" declaration (which is probably how I ended up with that in my source). See <this> example post & <this> post, both by Paul, as just two examples w/ explanation of the difference.

    Hope that helps . . .

    Mark J Culross
    KD5RXT
    Thanks Mark, yes I had seen those threads and have tried that. Unfortunately it makes no difference with my issue.

  12. #12
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    p.s. if anyone else happens to have an Arturia Minilab II and is willing to try the above example code whilst playing notes and sweeping the modwheel slider it would be good to get verification of the problem.

    Or if you don't have one, but could do with a small 2-octave 16-knob controller for just US$99...

  13. #13
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    I just tried the simple example on a Teensy 4.0 and get the same result as with the T3.6, with missing note events if I simultaneously vary the modwheel touchpad rapidly on the minilab.

    So perhaps it's not a processing speed issue per se, but some kind of queuing/buffering problem despite including the _BigBuffer option?

    Here is the complete code again including the function I missed last time, and including the BigBuffer option that uses the higher bus speed:

    Code:
    #include <USBHost_t36.h>
    
    USBHost myusb;
    //MIDIDevice midi1(myusb);
    MIDIDevice_BigBuffer midi1(myusb); 
    
    void setup() {
      Serial.begin(115200);
      delay(1500);
      myusb.begin();
    
      midi1.setHandleNoteOn(myNoteOn);
      midi1.setHandleNoteOff(myNoteOff); 
      midi1.setHandleControlChange(myControlChange);
    }
    
    int NumNotes=0;
    
    void loop() {
      myusb.Task();
      midi1.read();
    }
    
    
    void myNoteOn(byte channel, byte note, byte velocity) {
      Serial.print("Note On, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.println(velocity, DEC);
      
      Serial.print(">>>>>>>> ");
      if(velocity>0)
        Serial.println(++NumNotes);
      else
        Serial.println(--NumNotes);  
    }
    
    void myNoteOff(byte channel, byte note, byte velocity) {
      Serial.print("Note Off, ch=");
      Serial.print(channel, DEC);
      Serial.print(", note=");
      Serial.print(note, DEC);
      Serial.print(", velocity=");
      Serial.println(velocity, DEC);  
      Serial.print(">>>>>>>> ");
      Serial.println(--NumNotes);
    }
    
    
    void myControlChange(byte channel, byte control, byte value) {
    
    }

  14. #14
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    10,533
    Responding to PM: Sorry know very little about using MIDI and do not have any test setups... So not sure how much help I could be.

    About the only way I see that I could potentially take a look, is if there is some specific setup, that reproduces the issue.

    For example if you somehow capture the midi information that is going to the T3.6 or T4.x over the USBHost and for example program another Teensy to generate that midi stream, that does not require any additional hardware, that you could plug into the USB and reproduce the issue with simple program like you show in the previous post.
    Along with details on what you are seeing and what expected.

    Now if it were me, and were trying to debug it...
    1) My guess is this runs at 480mbs so LA can not capture the USB traffic... So that is probably out. And I don't have USB analyzer... So would need a different approach.
    2) I would try to capture, what information the USBHost is seeing and see if it actually sees the data... Again not sure what that data is
    My guess is printing all of this to USB Serial may change timing and make it work differently. SO I would probably capture the information to memory...
    Would probably try on T4.1 with PSRAM,

    In some cases I have hacked up a memory stream to handle this... SOmething like:
    Code:
    // Quick and Dirty memory Stream...
    class RAMStream : public Stream {
    public:
      // overrides for Stream
      virtual int available() { return (tail_ - head_); }
      virtual int read() { return (tail_ != head_) ? buffer_[head_++] : -1; }
      virtual int peek() { return (tail_ != head_) ? buffer_[head_] : -1; }
    
      // overrides for Print
      virtual size_t write(uint8_t b) {
        if (tail_ < sizeof(buffer_)) {
          buffer_[tail_++] = b;
          return 1;
        }
        return 0;
      }
    
      enum { BUFFER_SIZE = 32768 };
      uint8_t buffer_[BUFFER_SIZE];
      uint32_t head_ = 0;
      uint32_t tail_ = 0;
    };
    Again if I thought I had tons of data that might be received, I would make the storage in PSRAM... and maybe up to a couple Megabytes...

    Then do all of the debug output to the stream...

    And when you detect the event, then have some way to then tell this stream to print out, in an example sketch that I used the above in.
    I was doing this as Serial was not valid yet (initializing MTP), so then after Serial was valid, I did something like:
    Code:
      int ch;
      while ((ch = rstream.read()) != -1)
        Serial.write(ch);
    Obviously could be done more efficiently, but gets the job done.

    But the hard part is figuring out what data to capture... Also if this generates 1000s of lines of output, may want to make stream code smarter, and only dump out a range of
    the data...

    But again sorry, without knowing a lot more about MIDI and your setup, not sure anything specific I can say to help

  15. #15
    Senior Member
    Join Date
    Feb 2021
    Posts
    129
    Thanks Kurt,

    I'll have a think about how to capture a stream that produces the host port data loss so it can be reproduced without the midi controller.

    Cheers,
    Richard

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •