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

Thread: MIDI Message Queue

  1. #1
    Senior Member Davidelvig's Avatar
    Join Date
    Aug 2015
    Location
    Wisconsin
    Posts
    307

    MIDI Message Queue

    I'll be adding MIDI message queues to my MIDI wind controller.

    Any suggestions as to a useful, solid, fast queue library to use (such as this one that is one of several in Arduino libraries)?
    There will be a queue serving each outbound MIDI stream (usually one, sometimes two) and one for inbound MIDI.
    Messages in the queue will be 2-3 bytes, plus a timestamp. I need not cover SysEx messages.

    Is there a particular favorite for a Teensy 3.2 project?

    Thanks!

  2. #2
    What exactly do you need a queue for? Usually, you want to keep the latency as low as possible, so you'd avoid queuing up any MIDI data. The serial port or USB MIDI code you're using already has its own queue, for both sending and receiving data.
    Do you intend to use these queues inside of interrupt contexts?

    If you just want a working queue without having to install any third-party libraries, you can use the one in the C++ standard library: https://en.cppreference.com/w/cpp/container/queue
    Code:
    #include <queue>
    
    void setup() {
      Serial.begin(115200);
      while (!Serial);
    
      std::queue<int> q;
      
      q.push(42);
      q.push(43);
      q.push(44);
    
      while (!q.empty()) {
        Serial.println(q.front());
        q.pop();
      }
    }
    
    void loop() {}
    It does use dynamic allocations, but this shouldn't be an issue with the amount of RAM you have available on a Teensy 3.2.

    You could also very easily create a ring buffer using fixed-size storage in static memory, you'll find thousands of implementations online for this kind of thing. Pick one that includes some unit tests, and a fair bit of community interaction (multiple contributers, reaction to issues, stars, etc.).

    I wouldn't recommend SMFSW/Queue library you linked to, it's C++ code written by a C programmer, it has no type safety, the user has to cast back and forth between void pointers, you have to manually specify the size of the entries, etc.

    Pieter

  3. #3
    Senior Member Davidelvig's Avatar
    Join Date
    Aug 2015
    Location
    Wisconsin
    Posts
    307
    Thanks, Pieter!
    I'll check it out.

    I'm queuing because I have several inputs and several outputs (including the USB in and out)
    The outputs have different latency (USB, Serial and Bluetooth) and I want to de-serialize them - not waiting for bluetooth to complete message one before I start USB message two.

    I also what to try to batch bluetooth MIDI messages to minimize the effect of BT connection interval on BT latency.
    To some degree, implementing queues is a shot in the dark.
    I'm seeing 27ms latency (I think per message) with the way I'm implementing outbound BT MIDI.

    I'm using the unaltered AdaFruit Bluetooth UART Friend and its BLE libraries (including a layer for BT MIDI).
    For the time being, I'd like to leave that as a bundle... and yet get better performance out of it.

    The separate queues will allow me to decouple and debug better, at least.

  4. #4
    A latency of 27ms doesn't seem right at all. I'm not familiar with the Adafruit libraries you mention, but I did write a MIDI BLE library for ESP32. The way I handle it there is by buffering the Bluetooth packets, rather than the MIDI messages, I think this should simplify the rest of the code. The ESP32 has FreeRTOS and two cores, so it's relatively easy to handle sending Bluetooth data asynchronously, on Teensy, it'll be a bit harder, you could use a timer interrupt, but that gets complicated quickly.
    Basically, you have two threads, your main thread that wants to send MIDI messages, and a second thread that sends BLE packets in the background. When you write the first data to a BLE packet, the sender thread starts a timeout of a couple of milliseconds. As long as the timeout isn't reached yet, all incoming MIDI messages are added to the same BLE packet. When the timeout is reached, the sender thread sends the BLE packet. This means that the main thread never has to wait for the BLE transmission.

    You can find my implementation here if you're interested: https://github.com/tttapa/Control-Su...ace.cpp#L8-L80

    Looking at the implementation of the Adafruit MIDI BLE library, it seems like most actual MIDI BLE stuff happens on the nRF51 itself, not on the “master” MCU.
    Two things stood out to me though:
    For communicating with the Bluefruit UART LE, the library seems to use SoftwareSerial @ 9600 baud. This is extremely slow. Since you probably have a free hardware UART on your Teensy, you could look into using that instead, although you might not be able to use a higher baud rate, see this comment https://github.com/adafruit/Adafruit..._UART.cpp#L109. The overhead for sending MIDI data to the nRF51 is at least 12 bytes for the string “AT+BLEMIDITX”, then probably some other overhead, and at least 5 bytes of data (for a 3-byte MIDI message and 2 bytes for the header/timestamp). Let's say you need to transmit at least 20 bytes for a single 3-byte MIDI message, that'll take approximately 20 ms (1000(8+2)20/9600), just to send it over the serial link to the BLE adapter, that's not really acceptable IMHO.
    Second, the MTU seems to be fixed to 20 bytes for some reason, see https://github.com/adafruit/Adafruit....cpp#L116-L133. This will introduce extra latency as well. On my ESP32, I use an MTU of over 500 bytes, which allows you to fit much more MIDI data into a single BLE packet.

    Adding a queue for buffering might prevent the rest of your code from being slowed down by the BLE code, but it won't solve the underlying problem with the Teensy <-> nRF51 link being way too slow to be usable for MIDI.

  5. #5
    Senior Member Davidelvig's Avatar
    Join Date
    Aug 2015
    Location
    Wisconsin
    Posts
    307
    That's very helpful! Thanks!
    I'll digest this.

    I expected that the AdaFruit implementation through AT commands would have a fair amount of overhead. Thanks for quantifying it.

    I have the queues working, so I'll be able to isolate the BT MIDI from the rest of the app. That's a win!

    Queue guidance was the initial "ask" in this tread. I think that is solved for me.

    As for BT MIDI, I have plenty to do, and it sounds like you've made things happen there.
    I'm relying on Adafruit for most of that solution (I just send message bytes at this point... it handles the time-stamping and and beyond)

    I would like to further that discussion in this forum - I'll do more forum searching and start a new thread as needed.

    Thanks again.

Posting Permissions

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