Go Down

Topic: Event Based Midi Read (Read 674 times) previous topic - next topic

HauntedPliers

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


Code: [Select]
#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);

}

efinque

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.

Grumpy_Mike

Quote
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.

efinque

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.

pjrc

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

HauntedPliers

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

Grumpy_Mike

Quote
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.

HauntedPliers

#7
Sep 20, 2018, 04:00 pm Last Edit: Sep 20, 2018, 04:09 pm by HauntedPliers
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.


Code: [Select]
#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

efinque

#8
Sep 20, 2018, 04:51 pm Last Edit: Sep 20, 2018, 04:53 pm by efinque
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 https://www.tobias-erichsen.de/software/loopmidi.html

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?

HauntedPliers

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


Grumpy_Mike

Code: [Select]
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.

HauntedPliers

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

Grumpy_Mike

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.

HauntedPliers

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

MarkT

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.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up