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

Thread: Teensy3.6 UsbHost MIDI Performance Issues

  1. #1
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54

    Teensy3.6 UsbHost MIDI Performance Issues

    Hi there;

    I'm trying to control a 64 led matrix (on my push2) that is controlled simply via midi noteOn() massages.

    I have this code :
    Code:
    #include <Arduino.h>
    #include "USBHost_t36.h"
    
    USBHost myusb;
    USBHub hub1(myusb);
    USBHub hub2(myusb);
    MIDIDevice_BigBuffer midi1(myusb);
    
    
    bool led_state = false;
    int row;
    int column;
    
    void performanceTest(){
    
      elapsedMillis took_time;
    
      led_state = !led_state;
    
      row = 0;
      while(row<8){
    
        column = 0;
        while(column<8){
          midi1.sendNoteOn(36 + (row * 8) + column, led_state, 1);
          column++;
        }
        row++;
      }
    
      Serial.print("took time_: ");
      Serial.println(took_time);
    
    }
    
    void setup() {
    
      while(!Serial);
      Serial.begin(115200);
    
      delay(1500);
      Serial.println("USB Host InputFunctions example");
      delay(10);
      myusb.begin();
    }
    
    void loop() {
    
      performanceTest();
    
    }
    this gives me pretty much exact 320ms per function call, or 5ms for a midi noteOn() call.

    Can this be improved?

    thanks and greets!

    PS. a interesting readout about latency in MIDI: https://www.soundonsound.com/techniq...latency-part-2
    Last edited by Van; 02-09-2021 at 11:13 AM.

  2. #2
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    I've tested it against a "normal" midi usb with my computer and there is no problem regarding the speed ( much higher speed that 5ms per NoteOn)...

    Does this have something to do with the limitation I'm experiencing:

    in function
    Code:
    write_packed(uint32_t data){
    ...
    // TODO: start a timer, rather than sending the buffer
    // before it's full, to make best use of bandwidth
    Last edited by Van; 02-09-2021 at 02:16 PM.

  3. #3
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    So I'm talking to myself here, but it seems to be USB Host speed issue I can't resolve by myself.. :/

    This is one of the debug outputs I'm getting :
    Code:
    MIDIDevice transmit complete
      MIDI Data: 09 90 5D 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    So a bunch of nulls..


    Edit1: Aha, so I'm sending one(!) midi noteOn per buffer(512)???
    Last edited by Van; 02-09-2021 at 04:03 PM.

  4. #4
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    Is here someone wo can tell what the above transmission means and why my USB Host speed is so slow?
    Last edited by Van; 02-10-2021 at 09:28 AM.

  5. #5
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    .. or should I open an issue on github?

  6. #6
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,569
    Code:
    // TODO: start a timer, rather than sending the buffer
    // before it's full, to make best use of bandwidth
    This means that, at the moment, it sends a message immediately in which case having a very large buffer is counter-productive, as you've noticed. Just use "normal" midi.

    Pete

  7. #7
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    hey Pete,
    thanks for replaying!!
    I have to use the BigBuffer because the MIDI device I'm interfacing (Push2) requires a 480 Mbit/sec protocol.
    Is there a way to optimize the transfer?
    Greets!

  8. #8
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,569
    I don't think the buffer size has anything to do with the 480Mbit/sec transfer rate.
    I may be wrong, but I think the larger buffer size just allows you to send larger SYSEX messages in one large chunk.

    Pete

  9. #9
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    if I don't assign it to a BugBuffer, the device want work at all. it doesn't get recognized by the teensy..
    I've had a quetsion about this in antoher tread and Paul answered:
    "Are you using the "BigBuffer" version of the MIDI device driver? This is a 480 MBit speed device, so the normal small buffer driver isn't enough."
    Perhaps the core usbHost code needs some love?

    Otherwise i can't explain the odd behavior..

    cheers!

  10. #10
    I've been working on a more efficient implementation of the USB host MIDI driver. It uses alternating transmit buffers and disables interrupts only for a minimal amount of time, which allows for high throughput. It also implements a timeout that's started whenever an empty transmit buffer is started to be filled. If more data is written before the timeout fires, it's all sent in a single USB packet. When the timeout fires, the data is sent even if the buffer wasn't full yet.

    You can find the code here: https://github.com/tttapa/Control-Su...I/Teensy-host/

    I tried the following test code based on the code in the OP:
    Code:
    #define NEW_IMPL // Comment out to try original USBHost_t36 implementation
    
    #include <Arduino.h>
    #include "USBHost_t36.h"
    
    #ifdef NEW_IMPL
    #include <Control_Surface.h>
    #include <MIDI_Interfaces/USBHostMIDI_Interface.hpp>
    #endif
    
    USBHost myusb;
    USBHub hub1(myusb);
    USBHub hub2(myusb);
    
    #ifdef NEW_IMPL
    USBHostMIDI_Interface_BigBuffer midi1{myusb};
    #else
    MIDIDevice_BigBuffer midi1(myusb);
    #endif
    
    bool led_state = false;
    int row;
    int column;
    
    #ifdef NEW_IMPL
    void noteOn(uint8_t note, uint8_t velocity, uint8_t channel) {
      midi1.sendNoteOn({note, Channel::createChannel(channel)}, velocity);
    }
    #else
    void noteOn(uint8_t note, uint8_t velocity, uint8_t channel) {
      midi1.sendNoteOn(note, velocity, channel);
    }
    #endif
    
    void performanceTest(){
      elapsedMicros took_time;
    
      led_state = !led_state;
    
      row = 0;
      while(row<8){
    
        column = 0;
        while(column<8){
          noteOn(36 + (row * 8) + column, led_state, 1);
          column++;
        }
        row++;
      }
    
      Serial.print("took time: ");
      Serial.println(took_time);
    }
    
    void setup() {
      while(!Serial);
      Serial.begin(115200);
    
      delay(1500);
      Serial.println("USB Host InputFunctions example");
      delay(10);
      myusb.begin();
      delay(1000); // wait for connection
    }
    
    void loop() {
      performanceTest(); // sends 64 note messages at a time
      delay(1000); // slow down!
    }
    Running on a T4.1 with a T4.0 connected to its host port, I get 31,000 Ás using the USBHost_t36 MIDIDevice_BigBuffer driver, and 17 Ás using my own driver.
    This is somewhat misleading, though, because this is just the time it takes to fill the buffer with 64 note messages. If you remove the delay, you'll get alternating results of 17 Ás and 982 Ás: after writing 256 note messages, both buffers are full, and you have to actually wait for them to be sent, which seems to take approximately 980 Ás. Then filling the free buffer takes 128 messages before you have to wait 980 Ás again.

    Code:
    took time: 17
    took time: 17
    took time: 17
    took time: 17
    took time: 945
    took time: 17
    took time: 981
    took time: 17
    took time: 981
    took time: 17
    took time: 981
    took time: 17
    took time: 981
    took time: 17
    took time: 981
    took time: 17
    With large buffers (8 KiB IIRC), I've seen throughput of over 30 Mbps. This is of course much lower than the 480 Mbps of the USB connection, but improving that would involve optimizing large parts of the low-level USB code for both USBHost_t36 and the USB device stack for the second Teensy, which I don't think is useful (at least not for MIDI).
    Setting a pin high whenever I enqueue a data transfer and setting it low again when I get the callback for it reveals that pretty much all time is spent in the low-level USB code, not in my driver.

    I've paid attention to atomic accesses and memory order in my MIDI driver, which has been verified using the ThreadSanitizer. However, I've noticed that this is not the case for some parts of the USBHost_t36 library, none of the variables shared between the main code and the USB ISRs are atomic (reading and writing aligned integers and pointers on Teensy is atomic, but without any memory ordering guarantees), and some aren't even volatile (e.g. rx_head and rx_tail in the MIDI driver). This will most likely lead to data races, which is an issue. It might work fine at low speeds, but chances are they'll manifest themselves when the rate of interrupts increases.
    Unfortunately, I don't have enough experience with the low-level USB code to fix this.

    A question to Paul or anyone more knowledgeable about USB than I am: how are the packet sizes “negotiated” between host and device? Let's say I have a device with buffers of 64 bytes and a host with buffers of 512 bytes, what happens when I write “queue_Data_Transfer(txpipe, buffer, 512, this);” on the host? Similarly, what happens if the device has larger buffers than the host?

    Pieter

  11. #11
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,974
    Quote Originally Posted by PieterP View Post
    Running on a T4.1 with a T4.0 connected to its host port, I get 31,000 Ás using the USBHost_t36 MIDIDevice_BigBuffer driver, and 17 Ás using my own driver.
    Can you give me the exact code you're running on the Teensy 4.0, so I can exactly recreate your test results?

    Even if the other code on Teensy 4.0 is trivial, I always ask this because over and over when people have reported problems, usually I fail to recreate the same problem if I have to guess and write some of the code.

    Please, give me exactly the code to run on both sides, and any other necessary details, so I quickly get to the exact same test you're running.


    ... and to specifically answer your question:

    A question to Paul or anyone more knowledgeable about USB than I am: how are the packet sizes ônegotiatedö between host and device?
    During enumeration the USB host reads an "interface descriptor" from the USB device. It is actually many smaller descriptors concatenated together. Among those is an "endpoint descriptor" for each pipe / endpoint. Inside each of those descriptors is a 16 bit "wMaxPacketSize" field. It's documented on page 271 of the USB 2.0 spec. Here's a direct link to just the PDF, so you can quickly turn to page 271...

    https://www.pjrc.com/teensy/beta/usb20.pdf

    You'll see is says "Refer to Chapter 5 for more information", but doesn't tell you where in the lengthy chapter 5 to look. For bulk transfer type, the restrictions on sizes are near the top of page 53.

    (these are the page numbers as printed inside the PDF - not the page index your PDF reader uses, due to many pre-1 pages for the table of contents and other fluff at the beginning of the document)

  12. #12
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    @PeterP Hey, I've already found your repo ( you've made a pull request on the USBHost_t36, just about few days, right? )
    I've tested your version and there is a difference like night and day!
    All 64 LED's from my Push2 device (with a large buffer) are blinking simultaneously with no noticeable delay!!!
    Can this make it into the official USBHost_t36 ?

    Unfortunately I do not have any C/C++ skills, besides those I need for some simple audio-interface-workflow-arduino-modular-diy-stuff.
    I hope someone with more knowledge will chime in soon and help!

    hope you all are well; cheers!

    my code so far (You'll need this branch https://github.com/tttapa/Control-Su...tree/new-input):

    Code:
    #include <Arduino.h>
    #include "USBHost_t36.h"
    
    #include <Control_Surface.h>
    #include <MIDI_Interfaces/USBHostMIDI_Interface.hpp>
    
    USBHost myusb;
    USBHub hub1(myusb);
    USBHub hub2(myusb);
    USBHostMIDI_Interface_BigBuffer midi1(myusb);
    
    
    bool led_state = false;
    int row;
    int column;
    
    
    volatile int count = 0;
    
    void performanceTest(){
    
        elapsedMillis took_time;
    
        led_state = !led_state;
    
        row = 0;
        while(row<8){
    
        column = 0;
        while(column<8){
    
          midi1.sendNoteOn(36 + (row * 8) + column, led_state ? column+1 : 0);
          
          column++;
        }
        row++;
    
        }
    
        Serial.print("took time_: ");
        Serial.println(took_time);
      
      
    
    }
    
    void setup() {
    
      while(!Serial);
      Serial.begin(115200);
    
      delay(1500);
      Serial.println("USB Host InputFunctions example");
      delay(10);
      myusb.begin();
    }
    
    void loop() {
    
      // count
      performanceTest();
      delay(1000);
    
    }

  13. #13
    Quote Originally Posted by PaulStoffregen View Post
    Can you give me the exact code you're running on the Teensy 4.0, so I can exactly recreate your test results?
    Even if the other code on Teensy 4.0 is trivial, I always ask this because over and over when people have reported problems, usually I fail to recreate the same problem if I have to guess and write some of the code.
    Please, give me exactly the code to run on both sides, and any other necessary details, so I quickly get to the exact same test you're running.
    Sure, the code for the T4.0 simply discards any incoming MIDI USB data (compiled with USB type “MIDI”, of course):
    Code:
    void setup() {
      pinMode(LED_BUILTIN, OUTPUT);
      digitalWrite(LED_BUILTIN, HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN, LOW);
    }
    
    void loop() {
      (void)usb_midi_read_message();
    }
    Quote Originally Posted by PaulStoffregen View Post
    During enumeration the USB host reads an "interface descriptor" from the USB device. It is actually many smaller descriptors concatenated together. Among those is an "endpoint descriptor" for each pipe / endpoint. Inside each of those descriptors is a 16 bit "wMaxPacketSize" field. It's documented on page 271 of the USB 2.0 spec. Here's a direct link to just the PDF, so you can quickly turn to page 271...

    https://www.pjrc.com/teensy/beta/usb20.pdf

    You'll see is says "Refer to Chapter 5 for more information", but doesn't tell you where in the lengthy chapter 5 to look. For bulk transfer type, the restrictions on sizes are near the top of page 53.

    (these are the page numbers as printed inside the PDF - not the page index your PDF reader uses, due to many pre-1 pages for the table of contents and other fluff at the beginning of the document)
    Thanks a lot, I think it's clear now.
    If I understand correctly, this wMaxPacketSize is stored in the “tx_size” variable here https://github.com/PaulStoffregen/US.../midi.cpp#L169.
    I'll have to fix my driver tomorrow, it's always sending up to the full buffer size 512 bytes, completely ignoring tx_size

    Quote Originally Posted by Van View Post
    @PeterP Hey, I've already found your repo ( you've made a pull request on the USBHost_t36, just about few days, right? )
    I've tested your version and there is a difference like night and day!
    All 64 LED's from my Push2 device (with a large buffer) are blinking simultaneously with no noticeable delay!!!
    Indeed, that pull request is mine

    I'm glad to hear it's working! Do keep in mind that I haven't been able to test this extensively yet, I've just tested it with Teensies connected to Teensies, no “real” MIDI hardware yet, and I definitely have to fix the tx_size and rx_size bugs first.
    Edit: this refers to the driver posted above, not the pull request, the changes in the pull request are fine
    Last edited by PieterP; 02-14-2021 at 11:15 PM.

  14. #14
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    @paul any chance this driver is going to get into the official lib?

    @PieterP how can I set up midi callbacks like in the USBHost_t36 lib ?

    i.e.:
    Code:
    midi1.setHandleNoteOn(myNoteOn);

  15. #15
    You would inherit from the MIDI_Callback class and implement its onChannelMessage method. Then attach the callback to the MIDI interface using setCallbacks.
    For example:
    Code:
    #include <Control_Surface.h>
    #include <MIDI_Interfaces/USBHostMIDI_Interface.hpp>
    
    USBHost myusb;
    USBHub hub1(myusb);
    USBHub hub2(myusb);
    
    USBHostMIDI_Interface_BigBuffer midi1{myusb};
    
    struct MyCallbacks : MIDI_Callbacks {
      // Callback for channel messages (notes, control change, pitch bend, etc.).
      void onChannelMessage(MIDI_Interface &, ChannelMessage cm) override {
        if (cm.getMessageType() == MIDIMessageType::NOTE_ON) { // similar for NOTE_OFF
          Serial << "Note On, note: " << cm.getData1()
                 << ", velocity: " << cm.getData2() 
                 << ", channel: " << cm.getChannel().getOneBased() << endl;
        }
      }
    } callbacks;
    
    void setup() {
      while(!Serial);
      Serial.begin(115200);
    
      delay(1500);
      Serial.println("USB Host callback example");
      delay(10);
      myusb.begin();
      midi1.setCallbacks(callbacks);
      delay(1000); // wait for connection
    }
    
    void loop() {
      midi1.update();
    }
    This example might be useful as well: https://tttapa.github.io/Control-Sur...o-example.html

  16. #16
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    very nice, thanks a lot!
    greets!

  17. #17
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,974
    I've reproduced the problem here. Working on it now.

    Part of the reason for a single MIDI message per packet is lingering bugs in the USBDriverTimer class....

  18. #18
    What exactly is the problem with the USBDriverTimer class? Is there anything you could use help with?

  19. #19
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,974
    Ok, I've committed a fix on github.

    https://github.com/PaulStoffregen/US...54350fc9430db4

    This should allow transmitting about 800K MIDI messages/sec on Teensy 3.6 or about 3.3M on Teensy 4.0 & 4.1. That's still only about 1/4 of the theoretical 480 Mbit bandwidth, but to go faster would require faster receiving by Teensy 4.0 and more buffering on the transmitting side.


    What exactly is the problem with the USBDriverTimer class?
    I wasn't able to reproduce it today. Maybe I'm just remembering the painful early days of struggling to get EHCI working? Long ago, it would have trouble when multiple drivers were making heavy use of differing timeouts.

  20. #20
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    thank you very much!
    greets

  21. #21
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,974
    Please let me know when/if you have tried the new code. How is the speed for your project?

    I'm keeping this on my list of known issues for a few more days. A confirmation (or clear report of any trouble) would be nice, otherwise later this week I'll assume "no news is good news".

  22. #22
    Member Van's Avatar
    Join Date
    Mar 2018
    Location
    Dresden, Germany
    Posts
    54
    Quote Originally Posted by PaulStoffregen View Post
    Please let me know when/if you have tried the new code. How is the speed for your project?
    yes, I've testet this version in my project. The transfer is much faster!

    I can't say what and if it is a difference with PeterP's lib. ()

    Seems to work fine!

    Here again is my test code:

    Code:
    //#define NEW_IMPL // Comment out to try original USBHost_t36 implementation
    
    #include <Arduino.h>
    #include "USBHost_t36.h"
    
    #ifdef NEW_IMPL
    #include <Control_Surface.h>
    #include <MIDI_Interfaces/USBHostMIDI_Interface.hpp>
    #endif
    
    
    USBHost myusb;
    USBHub hub1(myusb);
    USBHub hub2(myusb);
    
    
    #ifdef NEW_IMPL
    USBHostMIDI_Interface_BigBuffer midi1{myusb};
    #else
    MIDIDevice_BigBuffer midi1(myusb);
    #endif
    
    
    #ifdef NEW_IMPL
    void noteOn(uint8_t note, uint8_t velocity, uint8_t channel) {
      midi1.sendNoteOn({note, Channel::createChannel(channel)}, velocity);
    }
    #else
    void noteOn(uint8_t note, uint8_t velocity, uint8_t channel) {
      midi1.sendNoteOn(note, velocity, channel);
    }
    #endif
    
    bool led_state = false;
    int row;
    int column;
    
    void performanceTest(){
    
      elapsedMillis took_time;
    
      led_state = !led_state;
    
      row = 0;
      while(row<8){
    
        column = 0;
        while(column<8){
          int note = 36 + (row * 8) + column;
          int color = led_state ? 122 : 0;
          int channel = 1;
          noteOn(note, color, channel);
          column++;
        }
        row++;
      }
    
      Serial.print("took time: ");
      Serial.println(took_time);
    
    }
    
    void setup() {
    
      while(!Serial);
      Serial.begin(115200);
    
      delay(1500);
      Serial.println("USB Host InputFunctions example");
      delay(10);
      myusb.begin();
    }
    
    void loop() {
    
      performanceTest();
      delay(1000);
    
    }
    Thank you for your time!!!

  23. #23
    Changing the Interrupt Threshold Control value seems to alter the timing and might expose some data races.
    Occasionally, packets seem to be dropped when sending very long SysEx messages using my driver. I added some print statements to make sure it wasn't my code that was dropping them, and I can see that I'm calling queue_Data_Transfer with the correct data, and I do get a callback for it, but the packet never arrives on the other side.

    I don't have too much time to look into it right now, I'll have to study the datasheet to understand what's going on in ehci.cpp, but it might be good to be aware of it when other drivers might fail as well, since it's very timing sensitive, even adding a Serial.print() of a single character in certain places causes the problem to disappear.
    This might be related to the issues you were seeing with the USBDriverTimer class?

    I'll see if I can write a simpler reproducible example.




    Ok, after writing that, I did some further testing, and the problem might be the receiving Teensy 4.0, not necessarily the USB Host code on the T4.1.

    I've tried the following code on a Teensy 4.0 with USB mode MIDI, connected to my laptop (Dell XPS15 9560, Ubuntu 20.04 5.4.0-65-lowlatency #73-Ubuntu SMP PREEMPT), using Teensyduino 1.53:
    Code:
    #define BLINK 0
    
    void setup() {
    #if BLINK == 1
      pinMode(LED_BUILTIN, OUTPUT);
    #endif
    }
    
    void loop() {
    #if BLINK == 1
      static elapsedMillis m;
      if (m >= 500) {
        m = 0;
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
      }
    #endif
      if (uint32_t r = usb_midi_read_message())
        usb_midi_write_packed(r);
    }
    After uploading the code, use “amidi -l” to list the MIDI devices and find the port number of the Teensy, if it's the only MIDI device connected to your computer, it will be "hw:1,0,0".
    Then run the following shell script:
    Code:
    #!/usr/bin/env bash
    
    cd "$(dirname "${BASH_SOURCE}")"
    
    oport=${1:-hw:1,0,0}
    iport=${2:-${oport}}
    
    chg="${3:-MIDI-long-challenge.syx}"
    rsp="/tmp/response.syx"
    
    amidi -p "${iport}" -d -t 1 > /dev/null # flush buffer before starting test
    amidi -p "${iport}" -r "$rsp" -t 1 & # receive response
    /usr/bin/time -f "Duration: %E" amidi -p "${oport}" -s "$chg" # send challenge
    wait
    wc -c "${chg}"
    wc -c "${rsp}"
    sha256sum -b "${chg}"
    sha256sum -b "${rsp}"
    cmp "${chg}" "${rsp}"
    It sends a long file with MIDI messages to the Teensy, reads it back from the Teensy and compares the two. I've pushed all code and the MIDI file to GitHub because it was too large to upload to the forum:
    https://github.com/tttapa/Teensy-MIDI-stresstest
    Run the script using: ./MIDI-stresstest.sh hw:1,0,0 hw:1,0,0 MIDI-long-challenge.syx, replacing the port number if necessary.

    With #define BLINK 1, I get output similar to
    Code:
    ./MIDI-stresstest.sh hw:1,0,0 hw:1,0,0 MIDI-long-challenge.syx
    Duration: 0:00.57
    
    2535844 bytes read
    2617684 MIDI-long-challenge.syx
    2535844 /tmp/response.syx
    14558f9f8340ee40c05862ca78f1004c98a2f7b9bd98ad077b6b441f9495a284 *MIDI-long-challenge.syx
    4d79866a4cdffe53f51075840ffd241180341df8e365bf8fa35e428715343e72 */tmp/response.syx
    MIDI-long-challenge.syx /tmp/response.syx differ: byte 38919, line 336
    This means that some packets or bytes are dropped, but most of them seem to arrive eventually.

    However, with #define BLINK 0, it's even worse, and I get:
    Code:
    ./MIDI-stresstest.sh hw:1,0,0 hw:1,0,0 MIDI-long-challenge.syx
    Duration: 0:00.32
    
    36515 bytes read
    2617684 MIDI-long-challenge.syx
    36515 /tmp/response.syx
    14558f9f8340ee40c05862ca78f1004c98a2f7b9bd98ad077b6b441f9495a284 *MIDI-long-challenge.syx
    80a356e00e50b03c0e7ca08a33e25f53b5842e2edc587b065d4373c608c90a7a */tmp/response.syx
    MIDI-long-challenge.syx /tmp/response.syx differ: byte 22388, line 196
    It drops 99% of the data! After the first test with BLINK 0, the Teensy seems to lock up, and it no longer sends any MIDI data back.

    If I add a delayMicroseconds(10) in the loop, all data arrives without issues (but much, much slower, of course):
    Code:
    ./MIDI-stresstest.sh hw:1,0,0 hw:1,0,0 MIDI-long-challenge.syx
    Duration: 0:09.13
    
    2617684 bytes read
    2617684 MIDI-long-challenge.syx
    2617684 /tmp/response.syx
    14558f9f8340ee40c05862ca78f1004c98a2f7b9bd98ad077b6b441f9495a284 *MIDI-long-challenge.syx
    14558f9f8340ee40c05862ca78f1004c98a2f7b9bd98ad077b6b441f9495a284 */tmp/response.syx
    Given the fact that adding the blinking LED or the delay changes the behavior dramatically, I'm afraid it might be a data race in the (MIDI) USB device code. Any idea what could cause this?

Posting Permissions

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