Event Based Midi Read

Hi All,

I'm trying to use an Arduino accurately measure the timing of midi signals being produced by Traktor (DJ software). Unfortunately the only examples I can find of reading midi work by running in loop() and therefore reading from the midi buffer. When looking at a clock signal from Traktor, I'm seeing quite a bit of drift in the signal timing, with the interval changing from 14 to 25ms :o . I am assuming is coming from reading from the buffer at random times rather than drift on the DJ software (it is pro software used to sync equipment in large concerts, so the timing should be tight).

I'm really hoping for something like the void noteOn() and void noteOff() functions of themidiBus library in Processing, where the code is triggered as soon as midi note events are detected. I'm open to suggestions on how to implement something like in Arduino.

I'm currently using the MidiUsb library and my modified version of the MIDIUSB_read example, which I've posted below. I am happy to switch library if necessary.

Thanks for any suggestions.

Ad

#include "MIDIUSB.h"

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
}

int t_old = 0;

void loop() {
  midiEventPacket_t rx;

  do {
    rx = MidiUSB.read();

    // read the velocity value
    int Velocity = rx.byte3;
    int t_now = millis();
    int t_diff = t_now - t_old;
    
    if (rx.header != 0) {
      Serial.print("t_diff = ");
      Serial.print(t_diff);
      
      Serial.print("\t Received: ");
      Serial.print(rx.header, DEC);
      Serial.print("-");
      Serial.print(rx.byte1, DEC);
      Serial.print("-");
      Serial.print(rx.byte2, DEC);
      Serial.print("-");
      Serial.println(Velocity, DEC);

      if (Velocity == 127){
        digitalWrite(LED_BUILTIN,HIGH);
        } else{
        digitalWrite(LED_BUILTIN,LOW);
        Serial.println(" ");
        }

        t_old = t_now;
      }
  } while (rx.header != 0);

}

I think Traktor has a delay for MIDI clock... I'm interested in running a sync program similar to yours in my current project. The problem is that the MIDIUSB library doesn't compile to Uno boards.

It works with serial though, I tested with Ableton the other day running a MIDI signal from Live to loopmidi server and through MIDI-Serial Bridge to the Uno.

I am assuming is coming from reading from the buffer at random times

What ever it is, it is not that. What makes you think you are reading at random times?
The loop function can check the state of the serial buffer many hundreds of times in a millisecond.
If anything all that serial printing is slowing things down a lot.

Grumpy_Mike:
What ever it is, it is not that. What makes you think you are reading at random times?
The loop function can check the state of the serial buffer many hundreds of times in a millisecond.
If anything all that serial printing is slowing things down a lot.

I think he means the Traktor MIDI clock.. which could be possible if it's synced from an external source such as a drum machine or another software.

Which board are you using? Maybe Leonardo or Micro? Or one of the 32 bit boards like Zero?

Thanks for the replies everyone and sorry for the delay in responding. I thought the forum would automatically email me when I got a response! Guess I need to set up notifications.

pjrc and enfique - I'm using a Leonardo board as this supports the midi library.

Mike - ok, that's an interesting point, but why would the serial print cause a non-constant delay? It is the name number of characters on every cycle.

I guess one thing I could try is getting the Arduino to set a digital pin output high on each clock signal and then look at the timing with an external scope to see if the drift is coming from the serial output. Watch this space!

enfique - that's an interesting solution using a midi-to-serial bridge. May I ask which one you are using? I just did a quick search and found one called Hairless.

Thanks Ad

but why would the serial print cause a non-constant delay?

Two reasons.

  1. The variable number of characters needing to be printed.
  2. When you want to print the characters are put into a buffer and then the print function returns. If however you are sending very frequently you fill up the buffer and the print function has to wait until there is room in the buffer before it returns. So you can get longer delays as when the buffer is not full.

Hi Guys,

So I stripped out all the serial commands and set pin 6 to respond to the clock signals with a high pulse that lasts 1ms. Midi clock signals do not have an on and off component like midi note signals so I had to add the 1ms delay to allow my oscilloscope trigger to pick up the signal and let me do timing measurements.

Unfortunately it seems that the time variation is still there, though now it is smaller (the clock pulses vary from 20-27ms apart, it used to be 14-25ms). Here's a video where every time I hit the measure button the scope takes a time measure between the rising edges, which is printed at the bottom in yellow.

https://www.youtube.com/edit?o=U&video_id=JkzSw-U4EdU

I guess the next step is to measure the clock signal from some other music software like Ableton and see if the variation disappears. If so then Traktor is to blame instead of the Arduino.

Here is my code if you are interested.

#include "MIDIUSB.h"

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(6, OUTPUT);
}

int t_old = 0;

void loop() {
  midiEventPacket_t rx;

  do {
    rx = MidiUSB.read();

    // read the velocity value
    int Velocity = rx.byte3;
    
    if (rx.header != 0) {

      if (Velocity == 127){
        digitalWrite(LED_BUILTIN,HIGH);
       
        digitalWrite(6,HIGH); // triggers on clock signals
        delay(1);
       
        } else {
        digitalWrite(LED_BUILTIN,LOW);
        

        }

      }
  } while (rx.header != 0);

   digitalWrite(6,LOW);

}

Thanks for the Serial tips Mike. The character count was always the same so I guess the buffer must be the cause.

Ad

HauntedPliers:
enfique - that's an interesting solution using a midi-to-serial bridge. May I ask which one you are using? I just did a quick search and found one called Hairless.

Yes Hairless is the one I've been using with loopmidi. It's a loopback MIDI server, see loopMIDI | Tobias Erichsen

As to Traktor being at fault; it could be one of the aforementioned or your setup is causing the program to run slow.. I haven't personally experienced such but it's possible, if you're running several remix decks for instance.. I've been using Traktor for 10 years now and have never run into problem like that. Out of curiosity, what sort of computer are you using?

Ok great, I'll check out Hairless. Thanks for the recommendation.

I'm also a long term Traktor user so am really surprised by this fluctuation. My computer is a 2 month old i7 Dell laptop with 16Gb of RAM and an SSD plugged into a Native Instruments Audio 6 soundcard (so native Traktor support), so it should be able to run all this. For the clock signal test above I had no tracks playing and only 1 3min track loaded into Deck A.

Overall audio seems to play and mix fine, it's just the midi output that is weird. In addition to the clock signal being strange I've also noticed this variation on the beat phase monitor output (which sends a Note On & Note Off on every beat).

I guess a good idea would be to also try this out on the laptop I've been gigging with for the last few years (I do midi jogwheel scratching with this machine so the midi input is certainly tight).

Thanks,

Ad

while (rx.header != 0);

I don't understand why you do this. You do know it means that while the rx.header has something in it loop round and hold on this instruction. But I can see nothing that would actually change the value of rx.header, so this could potentially permanently hang up your program.

Hi Mike,
Good spotting! That was taken from the Midi Library built in example for reading Midi data. Each message is only printed once (when I include serial commands) so I guess the buffer is getting cleared somehow, though you are right that this could be a source of error. Can you suggest a better way of doing this? I appreciate the support btw.
Thanks,
Ad

Sorry my bad, I just looked up the MIDIUSB_read example and I see now it is the final part of a do ... while code structure not a while loop in its own right. In which case the the MidiUSB.read at the start of the do will change this variable.

Sorry again for leading you astray.

Hey Guys,

So I ran my setup monitoring the clock signal from Ableton (a different professional audio software) and I get the same variation in timing as with Traktor (it's actually kind of worse).

Ableton and Traktor cannot both have bad clock signals so it's either my computer or something to do with the USBMidi library.

I'll keep exploring options and post what I find in case it is useful to someone else in the future.

Best,

Ad

USB serial interface is not as far as I know designed for accurate timing, but for throughput.

Does this need to be live?

If not then sending MIDI file is a possibility, that contains all the detailed timing information
although you need some more parsing code to pick this out from the MIDI commands.

Hi Mark,

Yes I'm afraid it does have to be live. I am building a performance interface. Thanks for the suggestion about the pre-recorded file though.

If USB to midi interfaces do not have very accurate timing could you recommend another way to get accurate midi information out of my laptop?

Ad

I think the trouble with USB in general is that it gathers things into a buffer and only transfers it when the host asks it if it has any data. This is how all USB interfaces work.

How often it is polled is down to the host PC and the USB client. I think the client requests a polling rate and then PC accepts it or not. It is all part of the USB enumeration process.

In theory you should be able to hack the Arduino USBMIDI libiary to increase the polling rate. Otherwise I think you might be stuck because computers don't have serial ports anymore, and adding one to the PC is normally done through USB.

OSC ( Open Sound Control ) might be the way to go, this goes through the Ethernet port, but again their is no guarantee on when Ethernet stuff arrives also.

This ancient library might be useful perhaps: The Hairless MIDI<->Serial Bridge

At least it mentions the issue of low latency/jitter with FTDI based USB serial for MIDI