flush incoming midi buffer at end of setup

Wondered if usbMIDI.setHandleRealTimeSystem(RealTimeSystem) might be the issue, so I changed the code to using usbMIDI.setHandleClock(myClock).
Changed void setup() to:
Code:
void setup() {
  Serial.begin(57600);
  Serial.println(F("serial ready"));
  delay(10000);                   
  while (usbMIDI.read()) {};                                         
  usbMIDI.setHandleClock(myClock);
  Serial.println(F("end setup"));
}

This is void myClock(), copied from void RealTimeSystem(byte realtimebyte):
Code:
void myClock() {
  currentRXtime = micros();
  Serial.print(counter); Serial.print("\t"); Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println(CLOCK, HEX);
  previousRXtime = currentRXtime;
  processClock();
  counter++;
  if (counter >= PPQ_96) counter = 0;
}

Result: fail, still a rush of F8's before the normal 21msec F8 cadence comes in.

Paul
 
OK, uploaded your latest sketch from message #23.
Saw this on the serial monitor:
View attachment 32347
This rush of F8's goes on for roughly 4 x 96 times before the normal 21msec F8 cadence comes in.
Setting enableActiveSensing to false or true did not matter.

Moved the handle usbMIDI.setHandleRealTimeSystem(RealTimeSystem) from void setupUsbClientMidi() to the last line of void setup(), but to no avail.

There must be some intermediate buffering going on somewhere that can not be cleared by while (usbMIDI.read()) {};.

Paul
yes I concur. @PaulStoffregen do you have any observations?
 
Maybe try something like this at the end of setup?

Code:
  elapsedMillis msec = 0;
  while (msec < 250) {
    usbMIDI.read();  // consume MIDI messages for 1/4 second
  };
 
Maybe try something like this at the end of setup?

Code:
  elapsedMillis msec = 0;
  while (msec < 250) {
    usbMIDI.read();  // consume MIDI messages for 1/4 second
  };
i just tried your suggestion, no good. Its still doing the rush. As we have found
usbMIDI.read();
seems to have no effect on these clock messages. Can you think of why they would be buffered in either the midi or USB libraries?
 
I'll throw my hat into the ring here . . .

Since calling while (usbMIDI.read()); in the setup() function has been demonstrated to be ineffective at flushing the buffered clock messages, that got me to wondering if maybe something happens WRT the usbMIDI interface between the time that the setup() function completes & the time that the loop() function begins. So, my radical idea was to attempt to empty the buffered clock messages on the very first pass (& only the first pass) thru the loop() function. I offer the following, which seems to effectively flush the buffered clock messages (I used the "MIDI Sync Transport" capability in MIDI-OX to continuously generate MIDI clock messages . . . hopefully with the same effect as your Reaper):

Code:
#include <USBHost_t36.h>
#include <MIDI.h>

USBHost thisUSB;
USBHub hub1(thisUSB);
MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

MIDI_CREATE_DEFAULT_INSTANCE();

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

void setup() {
   // start the USBhost for MIDI inputs/devices
   usbhostMIDI.begin();

   // Initiate MIDI communications, listen to all channels
   MIDI.begin(MIDI_CHANNEL_OMNI);

   Serial.begin(57600);

   delay(10000);  // wait 10 secs
}

void loop() {
   if (initial_pass_only)
   {
      // NOTE: proper credit to Paul Stoffregen for the method of flushing for a quarter second
      elapsedMillis msec = 0;
      while (msec < 250) {
         usbMIDI.read();  // consume MIDI messages for 1/4 second
      };
   }

   initial_pass_only = false;

   if (usbMIDI.read()) {
      if (usbMIDI.Clock) {
         currentRXtime = micros();
         Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
         previousRXtime = currentRXtime;
      }
   }
}

Someone with a more detailed understanding of exactly what's going on under the covers will have to explain why this approach appears to have the desired effect, as that level of understanding would be way above my pay grade !!

Mark J Culross
KD5RXT

P.S. The use of the following is leftover from my attempt at getting my USB MIDI keyboard to generate the clocks, & hopefully this is not influencing the results in any way:

Code:
USBHost thisUSB;
USBHub hub1(thisUSB);
MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

MIDI_CREATE_DEFAULT_INSTANCE();

MJC
 
Last edited:
I'll throw my hat into the ring here . . .

Since calling while (usbMIDI.read()); in the setup() function has been demonstrated to be ineffective at flushing the buffered clock messages, that got me to wondering if maybe something happens WRT the usbMIDI interface between the time that the setup() function completes & the time that the loop() function begins. So, my radical idea was to attempt to empty the buffered clock messages on the very first pass (& only the first pass) thru the loop() function. I offer the following, which seems to effectively flush the buffered clock messages (I used the "MIDI Sync Transport" capability in MIDI-OX to continuously generate MIDI clock messages . . . hopefully with the same effect as your Reaper):

Code:
#include <USBHost_t36.h>
#include <MIDI.h>

USBHost thisUSB;
USBHub hub1(thisUSB);
MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

MIDI_CREATE_DEFAULT_INSTANCE();

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

void setup() {
   // start the USBhost for MIDI inputs/devices
   usbhostMIDI.begin();

   // Initiate MIDI communications, listen to all channels
   MIDI.begin(MIDI_CHANNEL_OMNI);

   Serial.begin(57600);

   delay(10000);  // wait 10 secs
}

void loop() {
   if (initial_pass_only)
   {
      // NOTE: proper credit to Paul Stoffregen for the method of flushing for a quarter second
      elapsedMillis msec = 0;
      while (msec < 250) {
         usbMIDI.read();  // consume MIDI messages for 1/4 second
      };
   }

   initial_pass_only = false;

   if (usbMIDI.read()) {
      if (usbMIDI.Clock) {
         currentRXtime = micros();
         Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
         previousRXtime = currentRXtime;
      }
   }
}

Someone with a more detailed understanding of exactly what's going on under the covers will have to explain why this approach appears to have the desired effect, as that level of understanding would be way above my pay grade !!

Mark J Culross
KD5RXT

P.S. The use of the following is leftover from my attempt at getting my USB MIDI keyboard to generate the clocks, & hopefully this is not influencing the results in any way:

Code:
USBHost thisUSB;
USBHub hub1(thisUSB);
MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

MIDI_CREATE_DEFAULT_INSTANCE();

MJC
Hi Mark, i will give this a go but i have already tried this in loop using a "firstloop" check. But did not wrap it in a timer. Will report back....
 
@kd5rxt-mark I have tried your suggestion, but its still doing the rush.

here is the updated test sketch for anyone to try, @PaulS does this work for you?

C++:
#include <MIDI.h>                                                         

unsigned long currentRXtime;
unsigned long previousRXtime;
uint8_t counter = 0;
unsigned long lastUsbMidiActiveSensingRefreshTime = 0;
unsigned long usbMidiActiveSensingDelay = 300;                          // how often to send a midi active sensing command
bool enableActiveSensing = true;
elapsedMillis currentMillis = 0;
elapsedMicros currentMicros = 0;                                        // greater resolution for internal clock BPM

bool firstLoop = true;        // to track the first time we run loop after setup

//-------------------------------------------------------------------------  MIDI byte addresses

const byte CLOCK = 248;

#define PPQ_96 96

//-------------------------------------------------------------------------

void setup()
{
  Serial.begin(57600);
  Serial.println(F("serial ready"));

  setupUsbClientMidi();

  if (enableActiveSensing) usbMidiActiveSensing();

  delay(10000);                                                         // wait 10 secs to allow clocks to buffer
  Serial.println(F("end setup"));

  while (usbMIDI.read()) {};                                            // has no effect to clear the clocks that have buffered
}

//-------------------------------------------------------------------------

void loop()
{
  if (firstLoop)
  {
    elapsedMillis msec = 0;
    while (msec < 250)
    {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    }
    firstLoop = false;
  }
 
  /*
     this next line I have had in place for a long time and it fixed random rush events experienced during loop().
  */
  while (usbMIDI.read()) {};                                            // fix: https://forum.pjrc.com/threads/28282-How-big-is-the-MIDI-receive-buffer - this fixes random rushes of clocks during loop()

  usbMidiActiveSensingHandler();
}

//-------------------------------------------------------------------------

void RealTimeSystem(byte realtimebyte)
{
  switch (realtimebyte)
  {
    case CLOCK:
      currentRXtime = micros();
      Serial.print(counter); Serial.print("\t"); Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println(CLOCK, HEX);
      previousRXtime = currentRXtime;

      processClock();

      counter++;

      if (counter >= PPQ_96) counter = 0;
      break;
  }
}

void processClock()
{
  /*
   *  silly little drum pattern generator to hear the rush
   *  example maps notes to free VSTi tromine c78 on cable 0, channel 1. https://www.kvraudio.com/product/tromine-c-by-marvin-vst
   */
 
  if (counter % 24 == 0)                          // kick on beat
  {
    sendMidiNoteOn(36, 100);
    sendMidiNoteOff(36);
  }
  if (counter % 4 == 0)                           // hat 1/6
  {
    if(random(0, 4) == 1)                         // 1 in 4 probability
    {
      sendMidiNoteOn(44, 110);
      sendMidiNoteOff(44);
    }
  }
  if (counter % 8 == 0)                           // random snare based on 1/3
  {
    if(random(0, 4) == 1)                         // 1 in 4 probability
    {
      sendMidiNoteOn(38, 127);
      sendMidiNoteOff(38);
    }
  }
  if (counter % 12 == 0)                          // random metallic based on 1/2
  {
    if(random(0, 4) == 1)                         // 1 in 4 probability
    {
      sendMidiNoteOn(51, 40);
      sendMidiNoteOff(51);
    }
  }
  if (counter % 6 == 0)                           // random rimshot based on 1/4
  {
    if(random(0, 8) == 1)                         // 1 in 8 probability
    {
      sendMidiNoteOn(13, 127);
      sendMidiNoteOff(13);
    }
  }
}

void usbMidiActiveSensingHandler()               
{
  if (enableActiveSensing)
  {
    if ((currentMillis - lastUsbMidiActiveSensingRefreshTime) > usbMidiActiveSensingDelay)
    {
      lastUsbMidiActiveSensingRefreshTime = currentMillis;

      usbMidiActiveSensing();
    }
  }
}

void sendMidiNoteOn(uint8_t note, uint8_t velocity)
{
  usbMIDI.sendNoteOn(note, velocity, 1, 0);     // send note on over midi channel 1, cable 0
}

void sendMidiNoteOff(uint8_t note)
{
  usbMIDI.sendNoteOff(note, 1, 0);              // send note off over midi channel 1, cable 0
}

void setupUsbClientMidi()
{
  Serial.print(F("setup midi stuff"));
  usbMIDI.setHandleRealTimeSystem(RealTimeSystem);
  Serial.println(F(" - setup midi stuff done"));
}

void usbMidiActiveSensing()
{
  usbMIDI.sendRealTime(usbMIDI.ActiveSensing);
}

In any event I would not really want to have to run this check for firstLoop on every loop, but certainly these attempts to track it down are worthwhile :)
 
here is the updated test sketch for anyone to try, @PaulS does this work for you?
No, that didn't work either. Still 4x96 F8's before the normal 21msec F8 cadence comes in.

1699808496422.png


usbMIDI.read(); does not clear the buffered MIDI clock messages.

Paul
 
OK, so I think we can agree that
Code:
usbMIDI.read()
is not affecting the clock. I am taking a leap here and going with the notion it does not effect any realtime messages.
 
No, that didn't work either. Still 4x96 F8's before the normal 21msec F8 cadence comes in.

View attachment 32348

usbMIDI.read(); does not clear the buffered MIDI clock messages.

Paul

@PaulS:

Did you try the sketch that I posted earlier & if so, did it work at your end ?? Note that I also tried the sketch that snowsh posted in #32 above, but it still showed the failure to clear the buffer for me as well.

Mark J Culross
KD5RXT
 
@snowsh Your notion is right.
Used one Teensy to send RT messages every 21ms:
C++:
#include <MIDI.h>

IntervalTimer myTimer;

void setup() {
  myTimer.begin(sendMIDIclock, 20833); // 120BPM = 24ppq/500ms = 20833us/clock
}

void loop() {
  while (usbMIDI.read()) {} // ignore incoming messages
}

void sendMIDIclock() {
  usbMIDI.sendRealTime(usbMIDI.Clock);
  usbMIDI.sendRealTime(usbMIDI.Start);
  usbMIDI.sendRealTime(usbMIDI.Continue);
  usbMIDI.sendRealTime(usbMIDI.Stop);
  usbMIDI.sendRealTime(usbMIDI.ActiveSensing);
  usbMIDI.sendRealTime(usbMIDI.SystemReset);
  usbMIDI.send_now();
}
and had another Teensy receiving the RT messages, using a delay of 10secs and a while (usbMIDI.read()) {}; in void setup():
C++:
#include <MIDI.h>

unsigned long currentRXtime = 0;
unsigned long previousRXtime = 0;

void setup() {
  delay(10000);
  while (usbMIDI.read()) {};
}

void loop() {
  if (usbMIDI.read()) {
    currentRXtime = micros();
    uint8_t msgtype = usbMIDI.getType();    // which MIDI message, 128-255
    uint8_t channel = usbMIDI.getChannel(); // which MIDI channel, 1-16
    uint8_t data1 = usbMIDI.getData1();     // first data byte of message, 0-127
    uint8_t data2 = usbMIDI.getData2();     // second data byte of message, 0-127

    Serial.print(currentRXtime - previousRXtime); Serial.print("\t");
    Serial.print(msgtype | channel - 1, HEX); Serial.print(" ");
    if (data1 <= 0x0F) Serial.print("0");
    Serial.print(data1, HEX); Serial.print(" ");
    if (data2 <= 0x0F) Serial.print("0");
    Serial.println(data2, HEX);

    previousRXtime = currentRXtime;
  }
}
.
Here is the output on the serial monitor:
1699815679519.png
1699815619406.png

A rush of buffered RT messages first and then nicely bunched every ~21ms.

Paul
 
Last edited:
@PaulS:

Did you try the sketch that I posted earlier & if so, did it work at your end ?? Note that I also tried the sketch that snowsh posted in #32 above, but it still showed the failure to clear the buffer for me as well.

Mark J Culross
KD5RXT
Mark, are you connecting using midi USB host or USB client? there may be a difference here that could give us the clue we need to track this down.
 
@PaulS Did you try the sketch that I posted earlier & if so, did it work at your end ??
Hi Mark, no, I did not. It threw a lot of errors during compilation:
C++:
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp: In member function 'void USBHIDParser::out_data(const Transfer_t*)':
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:258:33: error: 'tx1' was not declared in this scope
  258 |         if (transfer->buffer == tx1) txstate &= ~1;
      |                                 ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:258:38: error: 'txstate' was not declared in this scope; did you mean '_tx_state'?
  258 |         if (transfer->buffer == tx1) txstate &= ~1;
      |                                      ^~~~~~~
      |                                      _tx_state
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:259:33: error: 'tx2' was not declared in this scope
  259 |         if (transfer->buffer == tx2) txstate &= ~2;
      |                                 ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:259:38: error: 'txstate' was not declared in this scope; did you mean '_tx_state'?
  259 |         if (transfer->buffer == tx2) txstate &= ~2;
      |                                      ^~~~~~~
      |                                      _tx_state
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp: In member function 'bool USBHIDParser::sendPacket(const uint8_t*, int)':
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:275:14: error: 'tx1' was not declared in this scope
  275 |         if (!tx1) {
      |              ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:280:17: error: 'tx2' was not declared in this scope
  280 |                 tx2 = tx1 - out_size;
      |                 ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:282:14: error: 'txstate' was not declared in this scope; did you mean '_tx_state'?
  282 |         if ((txstate & 3) == 3) return false;   // both transmit buffers are full
      |              ^~~~~~~
      |              _tx_state
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:285:22: error: 'tx1' was not declared in this scope
  285 |         uint8_t *p = tx1;
      |                      ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:286:14: error: 'txstate' was not declared in this scope; did you mean '_tx_state'?
  286 |         if ((txstate & 1) == 0) {
      |              ^~~~~~~
      |              _tx_state
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:289:22: error: 'tx2' was not declared in this scope
  289 |                 if (!tx2)
      |                      ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:292:21: error: 'tx2' was not declared in this scope
  292 |                 p = tx2;
      |                     ^~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp: At global scope:
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:303:6: error: no declaration matches 'void USBHIDParser::setTXBuffers(uint8_t*, uint8_t*, uint8_t)'
  303 | void USBHIDParser::setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb)
      |      ^~~~~~~~~~~~
In file included from C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\hid.cpp:25:
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\USBHost_t36.h:694:14: note: candidate is: 'void USBHIDParser::setTXBuffers(uint8_t*, uint8_t*, uint8_t, uint8_t*, uint8_t*)'
  694 |         void setTXBuffers(uint8_t *buffer1, uint8_t *buffer2, uint8_t cb,
      |              ^~~~~~~~~~~~
C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\USBHost_t36\USBHost_t36.h:689:7: note: 'class USBHIDParser' defined here
  689 | class USBHIDParser : public USBDriver {
      |       ^~~~~~~~~~~~
Multiple libraries were found for "SdFat.h"
 Used: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\SdFat
 Not used: C:\Users\Paul\Documents\Arduino\libraries\SdFat_-_Adafruit_Fork
Error compiling for board Teensy 4.1.

Only tested snowsh's code of message #32 since he copied the relevant code from your message #30.

Paul
 
@kd5rxt-mark I have tried your suggestion, but its still doing the rush.

here is the updated test sketch for anyone to try, @PaulS does this work for you?

C++:
#include <MIDI.h>                                                        

unsigned long currentRXtime;
unsigned long previousRXtime;
uint8_t counter = 0;
unsigned long lastUsbMidiActiveSensingRefreshTime = 0;
unsigned long usbMidiActiveSensingDelay = 300;                          // how often to send a midi active sensing command
bool enableActiveSensing = true;
elapsedMillis currentMillis = 0;
elapsedMicros currentMicros = 0;                                        // greater resolution for internal clock BPM

bool firstLoop = true;        // to track the first time we run loop after setup

//-------------------------------------------------------------------------  MIDI byte addresses

const byte CLOCK = 248;

#define PPQ_96 96

//-------------------------------------------------------------------------

void setup()
{
  Serial.begin(57600);
  Serial.println(F("serial ready"));

  setupUsbClientMidi();

  if (enableActiveSensing) usbMidiActiveSensing();

  delay(10000);                                                         // wait 10 secs to allow clocks to buffer
  Serial.println(F("end setup"));

  while (usbMIDI.read()) {};                                            // has no effect to clear the clocks that have buffered
}

//-------------------------------------------------------------------------

void loop()
{
  if (firstLoop)
  {
    elapsedMillis msec = 0;
    while (msec < 250)
    {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    }
    firstLoop = false;
  }
 
  /*
     this next line I have had in place for a long time and it fixed random rush events experienced during loop().
  */
  while (usbMIDI.read()) {};                                            // fix: https://forum.pjrc.com/threads/28282-How-big-is-the-MIDI-receive-buffer - this fixes random rushes of clocks during loop()

  usbMidiActiveSensingHandler();
}

//-------------------------------------------------------------------------

void RealTimeSystem(byte realtimebyte)
{
  switch (realtimebyte)
  {
    case CLOCK:
      currentRXtime = micros();
      Serial.print(counter); Serial.print("\t"); Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println(CLOCK, HEX);
      previousRXtime = currentRXtime;

      processClock();

      counter++;

      if (counter >= PPQ_96) counter = 0;
      break;
  }
}

void processClock()
{
  /*
   *  silly little drum pattern generator to hear the rush
   *  example maps notes to free VSTi tromine c78 on cable 0, channel 1. https://www.kvraudio.com/product/tromine-c-by-marvin-vst
   */
 
  if (counter % 24 == 0)                          // kick on beat
  {
    sendMidiNoteOn(36, 100);
    sendMidiNoteOff(36);
  }
  if (counter % 4 == 0)                           // hat 1/6
  {
    if(random(0, 4) == 1)                         // 1 in 4 probability
    {
      sendMidiNoteOn(44, 110);
      sendMidiNoteOff(44);
    }
  }
  if (counter % 8 == 0)                           // random snare based on 1/3
  {
    if(random(0, 4) == 1)                         // 1 in 4 probability
    {
      sendMidiNoteOn(38, 127);
      sendMidiNoteOff(38);
    }
  }
  if (counter % 12 == 0)                          // random metallic based on 1/2
  {
    if(random(0, 4) == 1)                         // 1 in 4 probability
    {
      sendMidiNoteOn(51, 40);
      sendMidiNoteOff(51);
    }
  }
  if (counter % 6 == 0)                           // random rimshot based on 1/4
  {
    if(random(0, 8) == 1)                         // 1 in 8 probability
    {
      sendMidiNoteOn(13, 127);
      sendMidiNoteOff(13);
    }
  }
}

void usbMidiActiveSensingHandler()              
{
  if (enableActiveSensing)
  {
    if ((currentMillis - lastUsbMidiActiveSensingRefreshTime) > usbMidiActiveSensingDelay)
    {
      lastUsbMidiActiveSensingRefreshTime = currentMillis;

      usbMidiActiveSensing();
    }
  }
}

void sendMidiNoteOn(uint8_t note, uint8_t velocity)
{
  usbMIDI.sendNoteOn(note, velocity, 1, 0);     // send note on over midi channel 1, cable 0
}

void sendMidiNoteOff(uint8_t note)
{
  usbMIDI.sendNoteOff(note, 1, 0);              // send note off over midi channel 1, cable 0
}

void setupUsbClientMidi()
{
  Serial.print(F("setup midi stuff"));
  usbMIDI.setHandleRealTimeSystem(RealTimeSystem);
  Serial.println(F(" - setup midi stuff done"));
}

void usbMidiActiveSensing()
{
  usbMIDI.sendRealTime(usbMIDI.ActiveSensing);
}

In any event I would not really want to have to run this check for firstLoop on every loop, but certainly these attempts to track it down are worthwhile :)

@snowsh:

Did you try the actual simplified sketch that I posted ?? For comparison, I also tried the sketch that you posted in #32 above, and I can confirm that it still showed the failure to clear the buffered clock messages in my environment (using MIDI-OX to continuously generate MIDI clock messages), whereas the simplified sketch that I posted did successfully clear the buffered clock messages. There is still some fundamental difference between your "full" sketch, which shows the problem, and the "simplified" sketch, which seems to resolve the problem, which may ultimately provide some useful clues.

As a side note, checking the "first_time_only" boolean every time thru the loop, as inefficient as it appears on the surface, is not really any different than checking "enableActiveSensing" every time the usbMidiActiveSensingHandler() function is called, which is also done every time thru the loop() function.

Mark J Culross
KD5RXT
 
OK, so I think we can agree that
Code:
usbMIDI.read()
is not affecting the clock. I am taking a leap here and going with the notion it does not effect any realtime messages.
Just guessing wildly, but maybe if you don't enable realtime messages until later, the clock messages will be in the queue read by usbMidi.read()?

Also, I don't know if it matters, but your code does not wait for Serial to actually be ready, as shown below. Maybe there is something about initializing usbMidi before Serial is ready that is having an effect?

Serial.begin(57600); while (!Serial) {} // wait for Serial Serial.println(F("serial ready"));
 
Hi Mark,
Copied the relevant code from your message #30 into the code below:
C++:
#include <MIDI.h>

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

void setup() {
  delay(10000);  // wait 10 secs
}

void loop() {
  if (initial_pass_only)
  {
    // NOTE: proper credit to Paul Stoffregen for the method of flushing for a quarter second
    elapsedMillis msec = 0;
    while (msec < 250) {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    };
  }
  initial_pass_only = false;

  if (usbMIDI.read()) {
    if (usbMIDI.Clock) {
      currentRXtime = micros();
      Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
      previousRXtime = currentRXtime;
    }
  }
}
This seems to effectively clear the buffered MIDI clocks as seen on the serial monitor:
1699817427591.png


So we have a workaround apparently!?

Paul
 
Last edited:
A simplified test program (cleaned up) with the timed attempt to clear buffered clock messages commented out (to demonstrate how the problem presents itself):

Code:
#include <MIDI.h>

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

void setup() {
   Serial.begin(57600);

   delay(10000);  // wait 10 secs
}

void loop() {
//   if (initial_pass_only)
//   {
//      elapsedMillis msec = 0;
//      while (msec < 250) {
//         usbMIDI.read();  // consume MIDI messages for 1/4 second
//      };
//   }

   initial_pass_only = false;

   if (usbMIDI.read()) {
      if (usbMIDI.Clock) {
         currentRXtime = micros();
         Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
         previousRXtime = currentRXtime;
      }
   }
}

And the results from the simplified test program with the timed attempt to clear buffered clock messages commented out:

Code:
10442000    F8
3    F8
2    F8
2    F8
3    F8
2    F8
9    F8
623    F8
308    F8
263    F8
999    F8
138    F8
142    F8
131    F8
135    F8
185    F8
126    F8
136    F8
172    F8
124    F8
169    F8
119    F8
177    F8
128    F8
172    F8
120    F8
182    F8
141    F8
122    F8
169    F8
124    F8
178    F8
118    F8
168    F8
117    F8
120    F8
194    F8
119    F8
180    F8
126    F8
196    F8
112    F8
158    F8
115    F8
84    F8
120    F8
214    F8
139    F8
407    F8
394    F8
199    F8
189    F8
172    F8
226    F8
715    F8
247    F8
312    F8
190    F8
257    F8
258    F8
190    F8
237    F8
368    F8
432    F8
225    F8
311    F8
301    F8
200    F8
384    F8
295    F8
236    F8
316    F8
370    F8
268    F8
162    F8
213    F8
142    F8
212    F8
123    F8
202    F8
178    F8
163    F8
120    F8
170    F8
153    F8
132    F8
192    F8
191    F8
120    F8
164    F8
126    F8
174    F8
130    F8
145    F8
135    F8
135    F8
136    F8
129    F8
207    F8
177    F8
131    F8
161    F8
125    F8
177    F8
124    F8
188    F8
136    F8
200    F8
137    F8
153    F8
173    F8
128    F8
198    F8
139    F8
182    F8
180    F8
176    F8
128    F8
195    F8
134    F8
187    F8
125    F8
128    F8
201    F8
128    F8
190    F8
123    F8
143    F8
173    F8
147    F8
284    F8
416    F8
265    F8
230    F8
227    F8
194    F8
184    F8
211    F8
226    F8
270    F8
189    F8
125    F8
171    F8
131    F8
207    F8
127    F8
186    F8
123    F8
122    F8
182    F8
130    F8
174    F8
199    F8
132    F8
187    F8
180    F8
125    F8
198    F8
139    F8
134    F8
195    F8
125    F8
208    F8
174    F8
136    F8
189    F8
127    F8
135    F8
178    F8
125    F8
185    F8
208    F8
125    F8
173    F8
123    F8
200    F8
126    F8
187    F8
176    F8
120    F8
188    F8
137    F8
174    F8
124    F8
190    F8
174    F8
127    F8
183    F8
124    F8
149    F8
120    F8
174    F8
147    F8
128    F8
174    F8
130    F8
160    F8
156    F8
126    F8
130    F8
214    F8
124    F8
174    F8
123    F8
208    F8
205    F8
193    F8
182    F8
125    F8
228    F8
120    F8
193    F8
124    F8
209    F8
191    F8
184    F8
177    F8
132    F8
192    F8
132    F8
201    F8
138    F8
196    F8
124    F8
172    F8
137    F8
174    F8
248    F8
247    F8
177    F8
180    F8
211    F8
220    F8
287    F8
235    F8
226    F8
139    F8
181    F8
182    F8
128    F8
190    F8
140    F8
188    F8
131    F8
175    F8
179    F8
122    F8
173    F8
113    F8
176    F8
134    F8
143    F8
7005    F8
24615    F8
24997    F8
24480    F8
26047    F8
25125    F8
24663    F8
24112    F8
25030    F8
25970    F8
24038    F8
26117    F8
24971    F8
24123    F8
25598    F8
24353    F8
24974    F8

The same simplified test program with the timed attempt to clear buffered clock messages uncommented (to demonstrate that the problem seems to be resolved):

Code:
#include <MIDI.h>

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

void setup() {
   Serial.begin(57600);

   delay(10000);  // wait 10 secs
}

void loop() {
   if (initial_pass_only)
   {
      elapsedMillis msec = 0;
      while (msec < 250) {
         usbMIDI.read();  // consume MIDI messages for 1/4 second
      };
   }

   initial_pass_only = false;

   if (usbMIDI.read()) {
      if (usbMIDI.Clock) {
         currentRXtime = micros();
         Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
         previousRXtime = currentRXtime;
      }
   }
}

And the results from the simplified test program with the timed attempt to clear buffered clock messages uncommented:

Code:
10689999    F8
26279    F8
26819    F8
26372    F8
21328    F8
26877    F8
26311    F8
21886    F8
26339    F8
25317    F8
25148    F8
24459    F8
25334    F8
25058    F8
26033    F8
21592    F8
26253    F8
25917    F8
25224    F8
24675    F8
25060    F8
25161    F8
24489    F8
24971    F8
25144    F8
24945    F8
25326    F8
24728    F8
24888    F8
25326    F8
24820    F8
24868    F8
25557    F8
24824    F8
25138    F8
24474    F8
25959    F8
26320    F8
21555    F8

And finally, the local results from running the test program posted by snowsh in #32 above (which still shows the problem locally):

Code:
serial ready
setup midi stuff - setup midi stuff done
end setup
0    10419002    F8
1    4    F8
2    3    F8
3    3    F8
4    4    F8
5    3    F8
6    3    F8
7    346    F8
8    250    F8
9    195    F8
10    750    F8
11    117    F8
12    167    F8
13    145    F8
14    137    F8
15    143    F8
16    122    F8
17    141    F8
18    169    F8
19    80    F8
20    119    F8
21    185    F8
22    122    F8
23    162    F8
24    378    F8
25    221    F8
26    238    F8
27    250    F8
28    129    F8
29    174    F8
30    118    F8
31    119    F8
32    151    F8
33    120    F8
34    183    F8
35    80    F8
36    121    F8
37    173    F8
38    136    F8
39    137    F8
40    123    F8
41    147    F8
42    123    F8
43    124    F8
44    131    F8
45    187    F8
46    117    F8
47    276    F8
48    270    F8
49    269    F8
50    225    F8
51    400    F8
52    292    F8
53    459    F8
54    422    F8
55    577    F8
56    205    F8
57    295    F8
58    292    F8
59    333    F8
60    195    F8
61    124    F8
62    195    F8
63    332    F8
64    235    F8
65    127    F8
66    188    F8
67    175    F8
68    120    F8
69    186    F8
70    124    F8
71    204    F8
72    124    F8
73    228    F8
74    123    F8
75    265    F8
76    135    F8
77    129    F8
78    166    F8
79    178    F8
80    131    F8
81    175    F8
82    208    F8
83    225    F8
84    191    F8
85    143    F8
86    125    F8
87    207    F8
88    174    F8
89    189    F8
90    184    F8
91    181    F8
92    143    F8
93    126    F8
94    179    F8
95    180    F8
0    119    F8
1    193    F8
2    126    F8
3    138    F8
4    168    F8
5    121    F8
6    208    F8
7    163    F8
8    134    F8
9    224    F8
10    218    F8
11    233    F8
12    219    F8
13    246    F8
14    178    F8
15    181    F8
16    192    F8
17    232    F8
18    124    F8
19    152    F8
20    124    F8
21    149    F8
22    133    F8
23    180    F8
24    138    F8
25    212    F8
26    125    F8
27    135    F8
28    126    F8
29    170    F8
30    253    F8
31    183    F8
32    200    F8
33    128    F8
34    205    F8
35    125    F8
36    224    F8
37    274    F8
38    191    F8
39    237    F8
40    205    F8
41    235    F8
42    225    F8
43    198    F8
44    207    F8
45    138    F8
46    214    F8
47    183    F8
48    177    F8
49    142    F8
50    124    F8
51    182    F8
52    151    F8
53    131    F8
54    146    F8
55    126    F8
56    196    F8
57    178    F8
58    131    F8
59    202    F8
60    122    F8
61    185    F8
62    193    F8
63    127    F8
64    162    F8
65    130    F8
66    289    F8
67    248    F8
68    170    F8
69    117    F8
70    219    F8
71    122    F8
72    196    F8
73    210    F8
74    178    F8
75    130    F8
76    160    F8
77    157    F8
78    200    F8
79    126    F8
80    175    F8
81    131    F8
82    169    F8
83    133    F8
84    210    F8
85    274    F8
86    269    F8
87    229    F8
88    158    F8
89    194    F8
90    158    F8
91    243    F8
92    205    F8
93    190    F8
94    204    F8
95    194    F8
0    162    F8
1    143    F8
2    202    F8
3    127    F8
4    198    F8
5    183    F8
6    147    F8
7    143    F8
8    197    F8
9    134    F8
10    195    F8
11    133    F8
12    134    F8
13    232    F8
14    141    F8
15    234    F8
16    156    F8
17    238    F8
18    189    F8
19    207    F8
20    179    F8
21    179    F8
22    165    F8
23    244    F8
24    189    F8
25    180    F8
26    131    F8
27    197    F8
28    152    F8
29    123    F8
30    245    F8
31    121    F8
32    140    F8
33    153    F8
34    155    F8
35    177    F8
36    290    F8
37    223    F8
38    216    F8
39    250    F8
40    209    F8
41    291    F8
42    209    F8
43    124    F8
44    160    F8
45    121    F8
46    219    F8
47    134    F8
48    202    F8
49    137    F8
50    13596    F8
51    24806    F8
52    24232    F8
53    26312    F8
54    24184    F8
55    25565    F8
56    24832    F8
57    24654    F8
58    25389    F8
59    25103    F8
60    23978    F8
61    25915    F8
62    24929    F8
63    24264    F8
64    25214    F8
65    25281    F8
66    25253    F8
67    24912    F8
68    24275    F8
69    25988    F8

Mark J Culross
KD5RXT
 
@snowsh:

Did you try the actual simplified sketch that I posted ?? For comparison, I also tried the sketch that you posted in #32 above, and I can confirm that it still showed the failure to clear the buffered clock messages in my environment (using MIDI-OX to continuously generate MIDI clock messages), whereas the simplified sketch that I posted did successfully clear the buffered clock messages. There is still some fundamental difference between your "full" sketch, which shows the problem, and the "simplified" sketch, which seems to resolve the problem, which may ultimately provide some useful clues.

As a side note, checking the "first_time_only" boolean every time thru the loop, as inefficient as it appears on the surface, is not really any different than checking "enableActiveSensing" every time the usbMidiActiveSensingHandler() function is called, which is also done every time thru the loop() function.

Mark J Culross
KD5RXT
wow. (wasn't the exact phrase i used. :) )

OK, so I had to add a few bits just to help me track it.... and yes you are right, it seems to fix the issue.


C++:
#include <USBHost_t36.h>
#include <MIDI.h>

USBHost thisUSB;
USBHub hub1(thisUSB);
MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

MIDI_CREATE_DEFAULT_INSTANCE();

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

uint8_t counter = 0;
#define PPQ_96 96

void setup() {
  // start the USBhost for MIDI inputs/devices
  usbhostMIDI.begin();

  // Initiate MIDI communications, listen to all channels
  MIDI.begin(MIDI_CHANNEL_OMNI);

  Serial.begin(57600);

  Serial.println(F("alive"));
 
  delay(10000);  // wait 10 secs
 
  Serial.println(F("end setup"));
}

void loop() {
  if (initial_pass_only)
  {
    // NOTE: proper credit to Paul Stoffregen for the method of flushing for a quarter second
    elapsedMillis msec = 0;
    while (msec < 250) {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    };
  }

  initial_pass_only = false;

  if (usbMIDI.read()) {
    if (usbMIDI.Clock) {
      currentRXtime = micros();
      Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
      previousRXtime = currentRXtime;

      processClock();

      counter++;

      if (counter >= PPQ_96) counter = 0;

    }
  }
}

void processClock()
{
  /*
      silly little drum pattern generator to hear the rush
      example maps notes to free VSTi tromine c78 on cable 0, channel 1. https://www.kvraudio.com/product/tromine-c-by-marvin-vst
  */

  if (counter % 24 == 0)                          // kick on beat
  {
    sendMidiNoteOn(36, 100);
    sendMidiNoteOff(36);
  }
  if (counter % 4 == 0)                           // hat 1/6
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(44, 110);
      sendMidiNoteOff(44);
    }
  }
  if (counter % 8 == 0)                           // random snare based on 1/3
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(38, 127);
      sendMidiNoteOff(38);
    }
  }
  if (counter % 12 == 0)                          // random metallic based on 1/2
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(51, 40);
      sendMidiNoteOff(51);
    }
  }
  if (counter % 6 == 0)                           // random rimshot based on 1/4
  {
    if (random(0, 8) == 1)                        // 1 in 8 probability
    {
      sendMidiNoteOn(13, 127);
      sendMidiNoteOff(13);
    }
  }
}


void sendMidiNoteOn(uint8_t note, uint8_t velocity)
{
  usbMIDI.sendNoteOn(note, velocity, 1, 0);     // send note on over midi channel 1, cable 0
}

void sendMidiNoteOff(uint8_t note)
{
  usbMIDI.sendNoteOff(note, 1, 0);              // send note off over midi channel 1, cable 0
}

So lets see whats hapening differently here. You are instigating midi in a different way. Going to have to check one by one...
 
@snowsh:

Sorry for the red herring from the "extra" MIDI stuff !! I really should have cleaned out that test sketch before posting it last night. Please see the cleaner version in #42 above & the intermediate results that go along with it. We certainly haven't fully resolved the original problem, but hopefully, we're getting closer to the final understanding of the actual cause, with a corresponding solution that really works in all cases !!

Mark J Culross
KD5RXT
 
Just realized why while (usbMIDI.read()) {}; did not seem to work: it does actually work, but only for 1 buffered message since there is some time between messages (~250us).
Hence PaulStoffregen's code
C++:
    elapsedMillis msec = 0;
    while (msec < 250) {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    }
is able to clear all messages for a quarter sec. Smart!

Paul
 
@snowsh:

Sorry for the red herring from the "extra" MIDI stuff !! I really should have cleaned out that test sketch before posting it last night. Please see the cleaner version in #42 above & the intermediate results that go along with it. We certainly haven't fully resolved the original problem, but hopefully, we're getting closer to the final understanding of the actual cause, with a corresponding solution that really works in all cases !!

Mark J Culross
KD5RXT
yes, I have stripped out in an effort to simplify and retain the fix. This is what i have so far - excludes the serial and host midi stuff. includes my drum generator.

C++:
//#include <USBHost_t36.h>
#include <MIDI.h>

//USBHost thisUSB;
//USBHub hub1(thisUSB);
//MIDIDevice_BigBuffer usbhostMIDI(thisUSB);

//MIDI_CREATE_DEFAULT_INSTANCE();

unsigned long currentRXtime;
unsigned long previousRXtime;
bool initial_pass_only = true;

uint8_t counter = 0;
#define PPQ_96 96

void setup() {
  // start the USBhost for MIDI inputs/devices
//  usbhostMIDI.begin();

  // Initiate MIDI communications, listen to all channels
//  MIDI.begin(MIDI_CHANNEL_OMNI);

  Serial.begin(57600);

  Serial.println(F("alive"));
 
  delay(10000);  // wait 10 secs
 
  Serial.println(F("end setup"));
}

void loop() {
  if (initial_pass_only)
  {
    // NOTE: proper credit to Paul Stoffregen for the method of flushing for a quarter second
    elapsedMillis msec = 0;
    while (msec < 250) {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    };
  }

  initial_pass_only = false;

  if (usbMIDI.read()) {
    if (usbMIDI.Clock) {
      currentRXtime = micros();
      Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println("F8");
      previousRXtime = currentRXtime;

      processClock();

      counter++;

      if (counter >= PPQ_96) counter = 0;

    }
  }
}

void processClock()
{
  /*
      silly little drum pattern generator to hear the rush
      example maps notes to free VSTi tromine c78 on cable 0, channel 1. https://www.kvraudio.com/product/tromine-c-by-marvin-vst
  */

  if (counter % 24 == 0)                          // kick on beat
  {
    sendMidiNoteOn(36, 100);
    sendMidiNoteOff(36);
  }
  if (counter % 4 == 0)                           // hat 1/6
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(44, 110);
      sendMidiNoteOff(44);
    }
  }
  if (counter % 8 == 0)                           // random snare based on 1/3
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(38, 127);
      sendMidiNoteOff(38);
    }
  }
  if (counter % 12 == 0)                          // random metallic based on 1/2
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(51, 40);
      sendMidiNoteOff(51);
    }
  }
  if (counter % 6 == 0)                           // random rimshot based on 1/4
  {
    if (random(0, 8) == 1)                        // 1 in 8 probability
    {
      sendMidiNoteOn(13, 127);
      sendMidiNoteOff(13);
    }
  }
}


void sendMidiNoteOn(uint8_t note, uint8_t velocity)
{
  usbMIDI.sendNoteOn(note, velocity, 1, 0);     // send note on over midi channel 1, cable 0
}

void sendMidiNoteOff(uint8_t note)
{
  usbMIDI.sendNoteOff(note, 1, 0);              // send note off over midi channel 1, cable 0
}


yes this fixes the rush. So my observations on the differences:

This working version does not use the interrupt attached functions, you directly use USBmidi.read(). So lets assume this is happening in whatever library is handling the interrupts. I would surmise that the USBmidi library hands off the clock calls to the interrupt handler as soon as they are defined, at this point they are buffered so the read() has no effect. I will try a USBmidi.read() loop just before I declare my interrupt handlers and see what that does.
 
ok, i tried using while (usbMIDI.read()) {};, no good. The idea of using a timer to force the read seems to do the job. but as you can see I have placed it in setup before we attach interupts right at the end of setup()

C++:
#include <MIDI.h>

unsigned long currentRXtime;
unsigned long previousRXtime;
uint8_t counter = 0;
unsigned long lastUsbMidiActiveSensingRefreshTime = 0;
unsigned long usbMidiActiveSensingDelay = 300;                          // how often to send a midi active sensing command
bool enableActiveSensing = true;
elapsedMillis currentMillis = 0;
elapsedMicros currentMicros = 0;                                        // greater resolution for internal clock BPM

bool firstLoop = true;        // to track the first time we run loop after setup

//-------------------------------------------------------------------------  MIDI byte addresses

const byte CLOCK = 248;

#define PPQ_96 96

//-------------------------------------------------------------------------

void setup()
{
  Serial.begin(57600);
  Serial.println(F("serial ready"));

  if (enableActiveSensing) usbMidiActiveSensing();

  delay(10000);                                                         // wait 10 secs to allow clocks to buffer
  Serial.println(F("end setup"));

  //while (usbMIDI.read()) {};                                            // fail. has no effect to clear the clocks that have buffered at this point

  elapsedMillis msec = 0;
  while (msec < 250)
  {
    usbMIDI.read();  // consume MIDI messages for 1/4 second
  };

  setupUsbClientMidi();

}

//-------------------------------------------------------------------------

void loop()
{
  if (firstLoop)
  {
    elapsedMillis msec = 0;
    while (msec < 250)
    {
      usbMIDI.read();  // consume MIDI messages for 1/4 second
    }
    firstLoop = false;
  }

  /*
     this next line I have had in place for a long time and it fixed random rush events experienced during loop().
  */
  while (usbMIDI.read()) {};                                            // fix: https://forum.pjrc.com/threads/28282-How-big-is-the-MIDI-receive-buffer - this fixes random rushes of clocks during loop()

  usbMidiActiveSensingHandler();
}

//-------------------------------------------------------------------------

void RealTimeSystem(byte realtimebyte)
{
  switch (realtimebyte)
  {
    case CLOCK:
      currentRXtime = micros();
      Serial.print(counter); Serial.print("\t"); Serial.print(currentRXtime - previousRXtime); Serial.print("\t"); Serial.println(CLOCK, HEX);
      previousRXtime = currentRXtime;

      processClock();

      counter++;

      if (counter >= PPQ_96) counter = 0;
      break;
  }
}

void processClock()
{
  /*
      silly little drum pattern generator to hear the rush
      example maps notes to free VSTi tromine c78 on cable 0, channel 1. https://www.kvraudio.com/product/tromine-c-by-marvin-vst
  */

  if (counter % 24 == 0)                          // kick on beat
  {
    sendMidiNoteOn(36, 100);
    sendMidiNoteOff(36);
  }
  if (counter % 4 == 0)                           // hat 1/6
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(44, 110);
      sendMidiNoteOff(44);
    }
  }
  if (counter % 8 == 0)                           // random snare based on 1/3
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(38, 127);
      sendMidiNoteOff(38);
    }
  }
  if (counter % 12 == 0)                          // random metallic based on 1/2
  {
    if (random(0, 4) == 1)                        // 1 in 4 probability
    {
      sendMidiNoteOn(51, 40);
      sendMidiNoteOff(51);
    }
  }
  if (counter % 6 == 0)                           // random rimshot based on 1/4
  {
    if (random(0, 8) == 1)                        // 1 in 8 probability
    {
      sendMidiNoteOn(13, 127);
      sendMidiNoteOff(13);
    }
  }
}

void usbMidiActiveSensingHandler()
{
  if (enableActiveSensing)
  {
    if ((currentMillis - lastUsbMidiActiveSensingRefreshTime) > usbMidiActiveSensingDelay)
    {
      lastUsbMidiActiveSensingRefreshTime = currentMillis;

      usbMidiActiveSensing();
    }
  }
}

void sendMidiNoteOn(uint8_t note, uint8_t velocity)
{
  usbMIDI.sendNoteOn(note, velocity, 1, 0);     // send note on over midi channel 1, cable 0
}

void sendMidiNoteOff(uint8_t note)
{
  usbMIDI.sendNoteOff(note, 1, 0);              // send note off over midi channel 1, cable 0
}

void setupUsbClientMidi()
{
  Serial.print(F("setup midi stuff"));
  usbMIDI.setHandleRealTimeSystem(RealTimeSystem);
  Serial.println(F(" - setup midi stuff done"));
}

void usbMidiActiveSensing()
{
  usbMIDI.sendRealTime(usbMIDI.ActiveSensing);
}


Can we confirm this is working?
 
Back
Top