arduino-libraries / MIDIUSB

A MIDI library over USB, based on PluggableUSB
GNU Lesser General Public License v2.1
471 stars 90 forks source link

need delay to send messages reliably #44

Open chriswatrous opened 6 years ago

chriswatrous commented 6 years ago

Board: Arduino Due OS: Windows 10

I'm learning how to use this library and I'm having trouble getting messages sent in quick succession to send reliably. It seems I need to add a delay in between each message.

Is this expected behavior? Or is this a bug?

This is what I would expect to work:

#include <MIDIUSB.h>

void noteOn(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOff);
}

void setup() {
}

void loop() {
    noteOn(0, 52, 100);
    noteOn(0, 53, 100);
    noteOn(0, 57, 100);
    noteOn(0, 60, 100);
    MidiUSB.flush();
    delay(200);

    noteOff(0, 52, 100);
    noteOff(0, 53, 100);
    noteOff(0, 57, 100);
    noteOff(0, 60, 100);
    MidiUSB.flush();
    delay(200);
}

But only the first message of each group sends reliably. (recorded in FL Studio): image

I tried adding a flush after each event:

#include <MIDIUSB.h>

void noteOn(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOn);
    MidiUSB.flush();
}

void noteOff(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOff);
    MidiUSB.flush();
}

void setup() {
}

void loop() {
    noteOn(0, 52, 100);
    noteOn(0, 53, 100);
    noteOn(0, 57, 100);
    noteOn(0, 60, 100);
    delay(200);

    noteOff(0, 52, 100);
    noteOff(0, 53, 100);
    noteOff(0, 57, 100);
    noteOff(0, 60, 100);
    delay(200);
}

Same result: image

If I add a 50 microsecond delay it works.

#include <MIDIUSB.h>

void noteOn(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOn);
    delayMicroseconds(50);
}

void noteOff(byte channel, byte pitch, byte velocity) {
    midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
    MidiUSB.sendMIDI(noteOff);
    delayMicroseconds(50);
}

void setup() {
}

void loop() {
    noteOn(0, 52, 100);
    noteOn(0, 53, 100);
    noteOn(0, 57, 100);
    noteOn(0, 60, 100);
    MidiUSB.flush();
    delay(200);

    noteOff(0, 52, 100);
    noteOff(0, 53, 100);
    noteOff(0, 57, 100);
    noteOff(0, 60, 100);
    MidiUSB.flush();
    delay(200);
}

image

25 microseconds is not quite enough: image

Thanks for your help.

chriswatrous commented 6 years ago

I also looked at the messages in MIDI-OX and it confirmed what I saw in FL Studio.

facchinm commented 6 years ago

Hi @chriswatrous, it is probably related to the USB controller that drops (or, better, doesn't accept) data packets if they are too fast. Due is not our main dev target and its clock almost doubles the Zero family so probably the behaviour was unnoticed until now.

SorenAndreasen commented 6 years ago

Same issue here, also with a Due and Win 10. Putting a 50 microseconds delay in between each message is better, but it's still not 100% reliable. such a shame

Helguli commented 3 years ago

A workaround for this issue is sending multiple event packets at the same time with MidiUSB.write(*buffer, size).

Working code:

void sendNotes(midiEventPacket_t events[], size_t size) {
    uint8_t data[4 * size];
    for (unsigned int i = 0; i < size; i++) {
        data[0 + i * 4] = events[i].header;
        data[1 + i * 4] = events[i].byte1;
        data[2 + i * 4] = events[i].byte2;
        data[3 + i * 4] = events[i].byte3;
    }
    MidiUSB.write(data, 4*size);
}
ivanmart commented 2 years ago

I've tried everything: flush after each sendMIDI, flush after several sendMIDI, delay 500 microseconds, delay 1 millisecond, delay 10 milliseconds. But there was always more or less missing note-off events, though the debug messages stated that sendMIDI method have been invoked. It seems to be the library is not useable for Due at all for some reason.

Update: Even with simple Serial.write and Hairless midi<->serial bridge it works without any glitches, no need any delays... Why would a library won't do that?