it can cause the host to hang
Final followup on this old thread. The MIDI transmit speed fix was merged and then the ITC setting was changed from 0 to 1 to prevent race conditions in some drivers.
//#define USE_USB_MIDI_SEND_NOW
//#define USE_UHP_MIDI_SEND_NOW
#define USE_UHP
int sendWait = 1000;
int noteLength = 1000;
bool noteIsOn = false;
uint8_t note = 0;
#ifdef USE_UHP
#include "USBHost_t36.h"
USBHost myusb;
USBHub hub1(myusb);
MIDIDevice_BigBuffer midi0(myusb);
MIDIDevice_BigBuffer midi1(myusb);
MIDIDevice_BigBuffer midi2(myusb);
MIDIDevice_BigBuffer midi3(myusb);
//POINERS TO THE UHP MIDI DEVICE SO WER CAN ENUMERATE
MIDIDevice_BigBuffer* uhpMIDI[4] = { 0 };
#endif
elapsedMicros timer;
void setup() {
#ifdef USE_UHP
uhpMIDI[0] = &midi0;
uhpMIDI[1] = &midi1;
uhpMIDI[2] = &midi2;
uhpMIDI[3] = &midi3;
#endif
// put your setup code here, to run once:
myusb.begin();
}
void loop() {
if (!noteIsOn && timer > sendWait) {
sendNoteOn(note, 5, 10);
noteIsOn = true;
timer = 0;
}
if (noteIsOn && timer > noteLength) {
sendNoteOff(note, 5, 10);
noteIsOn = false;
timer = 0;
note++;
if (note > 127) note = 0;
}
usbMIDI.read();
#ifdef USE_UHP
for (int i = 0; i < 4; i++) {
if (*uhpMIDI[i]) uhpMIDI[i]->read();
}
#endif
}
void sendNoteOff(uint8_t note, uint8_t vel, uint8_t chan) {
usbMIDI.sendNoteOff(note, vel, chan);
#ifdef USE_UHP
for (int i = 0; i < 4; i++) {
if (*uhpMIDI[i]) {
uhpMIDI[i]->sendNoteOff(note, vel, chan);
}
}
#endif
}
void sendNoteOn(uint8_t note, uint8_t vel, uint8_t chan) {
usbMIDI.sendNoteOn(note, vel, chan);
#ifdef USE_USB_MIDI_SEND_NOW
usbMIDI.send_now();
#endif
#ifdef USE_UHP
for (int i = 0; i < 4; i++) {
if (*uhpMIDI[i]) {
uhpMIDI[i]->sendNoteOn(note, vel, chan);
#ifdef USE_UHP_MIDI_SEND_NOW
uhpMIDI[i]->send_now();
#endif
}
}
#endif
}
// Tested on bare T3.2, 3.6 and 4.0, usbTYPE = MIDIx16.
#define USE_SEND_NOW
int NumClocks = 6; // 0 - 15
struct ClockData {
bool Started;
bool Paused;
unsigned long CurrentMicros;
unsigned long PreviousMicros;
unsigned long ClockInterval;
uint16_t ClockTempo;
int LedState;
int LedPinNum;
};
struct ClockData ClockData[16] = {
{false, false, 0, 0, 0, 60, HIGH, 13},
{false, false, 0, 0, 0, 60, HIGH, 14},
{false, false, 0, 0, 0, 60, HIGH, 15},
{false, false, 0, 0, 0, 60, HIGH, 15},
{false, false, 0, 0, 0, 60, HIGH, 17},
{false, false, 0, 0, 0, 60, HIGH, 18},
{false, false, 0, 0, 0, 60, HIGH, 19},
{false, false, 0, 0, 0, 60, HIGH, 20},
{false, false, 0, 0, 0, 60, HIGH, 21},
{false, false, 0, 0, 0, 60, HIGH, 22},
{false, false, 0, 0, 0, 60, HIGH, 23},
{false, false, 0, 0, 0, 60, HIGH, 0},
{false, false, 0, 0, 0, 60, HIGH, 1},
{false, false, 0, 0, 0, 60, HIGH, 2},
{false, false, 0, 0, 0, 60, HIGH, 3},
{false, false, 0, 0, 0, 60, HIGH, 4}
};
//**********************************************************
void setup()
{
delay(500);
for (int i = 0; i <= NumClocks; i++) {
pinMode(ClockData[i].LedPinNum, OUTPUT);
digitalWrite(ClockData[i].LedPinNum, LOW);
ClockData[i].Started = true;
}
}
//*********************************************************
void loop()
{
while (usbMIDI.read()) {
}
for (int i = 0; i <= NumClocks; i++) {
myBPM(i);
}
}
//****************************************************
void myBPM(byte clockNumber) {
{
if ((ClockData[clockNumber].Started) == (false) || (ClockData[clockNumber].ClockTempo) == (0)) {
ClockData[clockNumber].ClockInterval = -1;
}
else
ClockData[clockNumber].ClockInterval = (2500000 / ClockData[clockNumber].ClockTempo);
}
ClockData[clockNumber].CurrentMicros = micros();
if (ClockData[clockNumber].CurrentMicros - ClockData[clockNumber].PreviousMicros > ClockData[clockNumber].ClockInterval)
{
ClockData[clockNumber].PreviousMicros = ClockData[clockNumber].CurrentMicros;
if (ClockData[clockNumber].LedState == LOW)
ClockData[clockNumber].LedState = HIGH;
else
ClockData[clockNumber].LedState = LOW;
digitalWrite(ClockData[clockNumber].LedPinNum, ClockData[clockNumber].LedState);
usbMIDI.sendRealTime(usbMIDI.Clock, clockNumber);
#if defined USE_SEND_NOW
usbMIDI.send_now();
#endif
}
}
unsigned long ClockInterval
ClockData[clockNumber].ClockInterval = -1;
Ok, thanks for pointing out my next homework assignment.
Back to duplicate messages and playing around with your example above, found that any Midi message can be duplicated. Seems to be the first symptom of calling usbMIDISendNow() too frequently and if you increase that frequency further the Windows10 <>Teensy connection gets trashed and takes a reboot and a some extra time to sort itself out.
1 Ms, jitter?
1ms of jitter is not remarkable, but when it goes backwards in time, it certainly is. Here's a clearer image.
View attachment 26575
void usb_midi_flush_output(void)
{
if (tx_noautoflush == 0) {
tx_noautoflush = 1;
if (tx_packet && tx_packet->index > 0) {
tx_packet->len = tx_packet->index * 4;
usb_tx(MIDI_TX_ENDPOINT, tx_packet);
tx_packet = NULL;
}
tx_noautoflush = 0;
}
}
Furthermore, it looks like the problem with calling send_now() from user code can be resolved by changing the usb_flush_output() slightly.
Basically, I'm just using the tx_noautoflush as a sort of lock.
Code:void usb_midi_flush_output(void) { if (tx_noautoflush == 0) { tx_noautoflush = 1; if (tx_packet && tx_packet->index > 0) { tx_packet->len = tx_packet->index * 4; usb_tx(MIDI_TX_ENDPOINT, tx_packet); tx_packet = NULL; } tx_noautoflush = 0; } }
USBHost myusb;
USBHub hub1(myusb);
MIDIDevice_BigBuffer midiDevice(myusb);
MIDIDevice_BigBuffer midiDevice2(myusb);
MIDIDevice_BigBuffer* midiDeviceList[2] = { 0 };
...
midiDeviceList[0] = &midiDevice;
midiDeviceList[1] = &midiDevice2;
myusb.begin();
...
void sendMidiNoteOn(int channel, int noteNumber, int velocity) {
for (int i = 0; i < 2; i++) {
if (* midiDeviceList[i]) {
midiDeviceList[i]->sendNoteOn(noteNumber, velocity, channel);
}
}
}
void sendMidiNoteOff(int channel, int noteNumber, int velocity) {
for (int i = 0; i < 2; i++) {
if (* midiDeviceList[i]) {
midiDeviceList[i]->sendNoteOff(noteNumber, 0, channel);
}
}
}