First of all, I'm using latest version of MIDI library (3.2). I've already built MIDI controller using that same library and everything works pretty well, even the MIDI In (I use it to control LEDs - messages are coming from Traktor Pro software). Now, depending on messages Arduino receives, LEDs can either blink or just be turned on all the time. What I'm trying to do is to send MIDI clock from Traktor to Arduino, calculate BPM and then calculate time after which the timer turns LEDs on/off, in case they're blinking. So in short I want that MIDI clock to be in sync with LEDs blinking. I actually managed to achieve all this, but it's very buggy and for some reason works stable only at 60 and 120BPM. Here is my code:
void handleStart() {
previousClockTime = 0;
clockCounter = 0;
averageClockTime = 0;
}
This function gets called when Arduino starts receiving MIDI clock, it just resets the variables that get changed during receiving to 0.
void handleClock() {
uint32_t currentTime = micros();
uint32_t timeDiff = currentTime - previousClockTime;
if (previousClockTime != 0) {
averageClockTime += timeDiff;
clockCounter++;
}
previousClockTime = currentTime;
}
Based on my research of MIDI clock, it works by sending 24 messages per tick, so if my BPM is 120, there would be 2880 of those messages per minute or 48 per second. Which means that messages are 20833 microseconds apart. Yay, that's everything I need! But somehow this doesn't quite work. What this function does is that it calculates the time difference between current and previous clock message, adds it to averageClockTime variable and increments clock counter. Finally, it updates previousClockTime with current. I'm working with micros here to gain some accuracy.
void handleStop() {
averageClockTime /= clockCounter;
//get timer value in milliseconds
uint16_t timerValue = 1000/(1000000/(averageClockTime*24));
if (timerValue > 4000 || timerValue < 100) timerValue = 700;
//disable global interrupts while we setup timer
cli();
//set entire TCCR1A register to 0
TCCR1A = 0;
//same for TCCR1B
TCCR1B = 0;
//set compare match register to desired timer count
OCR1A = timerValue / TIMER_RESOLUTION - 1;
//turn on CTC mode
TCCR1B |= (1 << WGM12);
//set CS10 and CS12 bits for 1024 prescaler:
TCCR1B |= (1 << CS10);
TCCR1B |= (1 << CS12);
//allow interrupts
sei();
}
This final function gets called at the end of receiving the clock input. First, it calulates average time between messages by dividing total sum of differences by clockCounter and then it calculates time for timer in milliseconds.
1000/(1000000/(averageClockTime*24));
Going back to 120BPMs, time difference between messages in that case is 20833 in microseconds. If we assume that averageClockTime equals to that, timer value is going to be 499,992 milliseconds (I don't know if this equals to 499 or 500, probably the latter).
So in theory this should work but it doesn't. Works only with 60 and 120, and even that doesn't always work, so I have to send clock several times before it gets the right value. When it does though, it works perfectly.
I must be doing something wrong here and I hope there is someone who can tell me what.