Go Down

Topic: arduino master MIDI clock unaccuracy (Read 1 time) previous topic - next topic

leroidubuffet

Dec 29, 2017, 06:16 pm Last Edit: Dec 31, 2017, 12:39 pm by leroidubuffet
Hello everyone,

I am building a master MIDI metronome on an Arduino MEGA 2560 to control my drum machine and other electronic devices using a standard 5 pin midi connection.

My code is almost 100% functional, but the clock timing is not 100% accurate, thus causing a fluctuating BPM value.


A sample of the time between clock messages in micros at 60BPM taken directly from the serial monitor:

micros()    elapsed time
6688928   3380
6692308   3380
6695688   3380
6699068   3380
6702448   3384
6705828   3376
6709208   3380
6712588   3380
6715968   3384
6719348   3376
6722728   3380
6726108   3380
6729488   3380
6732868   3380
6736248   3380
6739628   3380
6743008   3380
6746388   3380
6749768   3380
6753148   3380
6756528   3380
6759908   3384
6763288   3376

The resulting BPM value is not accurate either.

Attached there is an image of my breadboard. The in MIDI in connection and the display are out of use right now.

Any ideas?

I am also learning to code so corrections will be appreciated.

Code: [Select]

//From the book Arduino for Musicians
//Listing 4.9: Metronome 3 (Complete Metronome)
//Added midi output functionality for master clock use

//LEDS;
const int ledPin[] = {10, 9, 8, 7};
int ledState[] = {LOW, LOW, LOW, LOW};

//midi_clock values
byte midi_clock = 0xf8; //Declare the hex value of a midi clock message
byte midi_start = 0xfa; //Declare the hex value of a midi clock start message
byte midi_stop = 0xfc; //Declare the hex value of a midi clock stop message

//Potentiometer and smooth filter
const int potPin = 0;
const int numReadings = 20;    // a higher number make smoother readings
int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
unsigned long total = 0;        // the running total
unsigned long average = 0;      // the average
int preValue;                   // Variable to track the value of the pot

//Switch pin:
const int pushButtonPin = 2 ;

//Variable to track pushbutton status
int pushButtonStatus = LOW;

//A boolean variable to track if metronome is on or off:
boolean on = false;

//Variables to track tempo and time delay
unsigned int beats_per_minute = 60;
unsigned int milliseconds_per_minute = 1000 * 60;
unsigned int MS_per_beat = milliseconds_per_minute/beats_per_minute;

//Variables to track time
long last_time = 0;
int counter = 0;//Counts the number of ticks
int ledOn = 0;

byte offSequence = 1;

int previousLed = 0;


void setup()
{
    //interrupts();
    //Initialize serial port
    Serial.begin(31250); //Change to 38400 for debugging. MIDI 31250
    
    // set the digital pins 10 to 13 as output for leds:
    
    pinMode (ledPin[0], OUTPUT);
    pinMode (ledPin[1], OUTPUT);
    pinMode (ledPin[2], OUTPUT);
    pinMode (ledPin[3], OUTPUT);

    //Set the digital pin for input and pullup resistor:
    pinMode(pushButtonPin, INPUT_PULLUP);

    // initialize all the readings to 0:
    for (int thisReading = 0; thisReading < numReadings; thisReading++)
    {
      readings[thisReading] = 0;
    }
}

void loop()
{
    smoothFilter();
    checkPushButton();
    checkPot();
    
    long current_time = millis();    //Time reference. Elapsed time since board is on
    int ticks = MS_per_beat/24;      //24 ticks per beat
    int elapsed_time = current_time - last_time;
    
    //Midi clock sending routine
    if(elapsed_time >= ticks && on == true)
    {
      //Serial.write(midi_clock);
      last_time = current_time;
      counter++;
      ledBlink (ledOn);

      if (counter >= 24)
      {
        ledOn ++;
        counter = 0;
      }
      if (ledOn >= 4)
      {
        ledOn = 0;
      }    
    }
    //Switch off every led
    if (on == false)
    {
    
      if (offSequence)
      {
        for (int i = 0; i <=3; i++)
        {
        digitalWrite(ledPin[i], LOW);
        }
       offSequence = offSequence -1;
      }
      counter = 0;
      ledOn = 0;
      previousLed = 0;
    }
}

void ledBlink (int ledOn)//Led blink routine
{
  previousLed = ledOn - 1;
  if (previousLed == -1)
  {
    previousLed = 3;
  }    
    ledState[ledOn] = HIGH;
    digitalWrite(ledPin[ledOn], ledState[ledOn]);
    digitalWrite(ledPin[previousLed], LOW);
}

void checkPot()
{
    int value = average;

    //Re-calculate the tempo if the value has changed.
    if(value != preValue)
    {
       //Map the value to a reasonable metronome range of 30 BPM to 350 BPM
       beats_per_minute = map(value, 0, 1023, 60, 240);

       //Recalculate the delay time
     MS_per_beat = milliseconds_per_minute/beats_per_minute;
       //Update preValue
       preValue = value;
    }

void smoothFilter()
{
  //From http://www.arduino.cc/en/Tutorial/Smoothing
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(potPin);

  // add the reading to the total:
  total = total + readings[readIndex];
  
  // advance to the next position in the array:
  readIndex ++;

  // if we're at the end of the array...
  if (readIndex >= numReadings)
  {
    // ...wrap around to the beginning:
    readIndex = 0;
  }
  // calculate the average:
  average = total / numReadings;
}

void checkPushButton()
{
  //Check digital pin 2 for a button press
  int button_state = digitalRead(pushButtonPin);
    
  //Check for button press
  if(button_state == LOW && button_state != pushButtonStatus)
  {
    //Send start message
    Serial.write(midi_start);

    //Switch on the first led
    ledBlink (0);

    //Swap on/off states
    on =! on;
      
    //update buttonStatus;
    pushButtonStatus = button_state;
    //Use delay for a kludgy form of debouncing
    delay(20);
   }
    
  //Check for button release
  if(button_state == HIGH && button_state != pushButtonStatus)
  {
    //Send stop message
    Serial.write(midi_stop);
    //Reset counter
    offSequence = 1;
        
    //Updtate the pushbutton status to off
    pushButtonStatus = button_state;
    delay(20);
   }
}

TonyWilk

Hi,
Your list of sample times range from 3376 to 3384, that's only +-4 millionths of a second.

Also notice that the deviations come in pairs:

6699068   3380
6702448   3384      +4
6705828   3376      - 4
6709208   3380

The resolution of the micros() probably only counts in 4's so you occasionally see plus or minus 1 count.

Don't worry about it.

Yours,
  TonyWilk
 

Go Up