Tuesday, May 27, 2008

How to Deal with MIDI Clock Signals in Arduino

It is often desirable to synchronise something like a homemade sequencing circuit or a Commodore 64 to MIDI clock signals.

I thought I might share some generic Arduino skeleton code that could be used to synchronise
many different types of things to MIDI clock (and therefore ProTools, Ableton Live etc -- any type of host sequencer).

So, what exactly is MIDI clock? Well, it is a set of specific bytes that form part of the MIDI protocol. The great thing is that MIDI clock messages are only one byte long (instead of two or three bytes like other MIDI messages), so the code is very simple.

Here are the main, useful MIDI clock bytes:

start = 0xfa
stop = 0xfc
clock tick = 0xf8
continue = 0xfb

Twenty-four clock tick bytes are sent per quarter note when the host sequencer is running. Every time the sequencer is stopped, the stop byte is sent. Every time the sequencer is started after being stopped, the start byte is sent. Every time the sequencer is started after being paused, the continue byte is sent.

Using this information, it is possible to write a simple Arduino sketch to handle MIDI clock bytes and "do something" for every clock tick. The trick is to work out what you want to do with every clock tick, and then simply adjust the code below to add this desired functionality.

byte midi_start = 0xfa;
byte midi_stop = 0xfc;
byte midi_clock = 0xf8;
byte midi_continue = 0xfb;
int play_flag = 0;
byte data;

void setup() {
Serial.begin(31250);
}

void loop() {
if(Serial.available() > 0) {
data = Serial.read();
if(data == midi_start) {
play_flag = 1;
}
else if(data == midi_continue) {
play_flag = 1;
}
else if(data == midi_stop) {
play_flag = 0;
}
else if((data == midi_clock) && (play_flag == 1)) {
Sync();
}
}
}

void Sync() {
// do something for every MIDI Clock pulse when the sequencer is running
}

40 comments:

Joel said...

Another very handy post, thanks! From only an initial look through, what is the purpose of nano_flip? Also, presumably the Sync function will need to count to 24 then do something if the circuit being controlled is expecting a clock, is that right?

Thanks again, Joel

Sebastian Tomczak said...

Haha, yeah I gutted some code I wrote for a nanoloop sync device (whose output would run at 12ppqn instead of MIDI clock's 24 ppqn), hence the nano_flip byte! I thought I had deleted all traces of the nanoloop code, but obviously I hadn't :)

Yes you are correct, that one would need to keep track of the MIDI clock pulse byte count in order to do something for every quarter note, say, instead of every pulse. But this is something that can be added quite easily, and I didn't want to confuse the code in the post, which is very barebones to serve as an example.

bitrex said...

Is it possible to attach an interrupt to the serial port input pin, so that the microcontroller can be freed up for other tasks in the time between clock pulses instead of polling the port in a loop?

Sebastian Tomczak said...

I guess so.

But in this situation, I don't really see the point.

okvern said...

bitrex asked about attaching an interrupt to the serial RX pin--as far as I know, attachInterrupt can only use digital pins 2 and 3 (which it thinks of as 0 and 1):

//where "onInterrupt" is the function
//you want to run when
//the interrupt occurs.
attachInterrupt(0, onInterrupt, CHANGE);

This is as of Arduino 011, anyway. You could have Serial.available set one of these pins high and then handle that as an interrupt, but, as far as I know, you couldn't attach an interrupt to the serial RX pin itself. If there's some way to attach an intterupt to pins other than 2 and 3 that I've missed, I'd love to hear about it!

Sebastian, your blog absolutely rocks. Keep up the good work!

Thanks,

Olav Martin Kvern

Sebastian Tomczak said...

Olav Martin Kvern!
Thanks for your comment.

I have not used interrupts for the Arduino yet because I have not felt a need to. For example, when dealing with MIDI clock signals I might make be making something that simply takes MIDI clock and converts it into some other timing protocol (DIN 24, LSDJ, Nanoloop, binary counting for digital logic circuits, etc). So you can see, the things I make are very simple and straightforward.

:-)

Thanks again,

Sebastian Tomczak

Joel said...

I'm working on processing incoming MIDI signals (not for a specific purpose yet, so currently just flashing LEDs based on clock and noteOn messages etc) using a PIC microcontroller, and this post came in very handy as predicted!! Also, some good info on noteOn processing can be found in this random blog post. Best wishes, Joel

Anonymous said...

Oh well i found a free software to monitor midiclock. maybe that helps ???

http://users.telenet.be/sdrsite/sites/serge/midiclockdetect/

william said...

Great post, thanks!

Sorry for my programming noobness, but could someone explain how to keep count of the pulses and do something?

I'm a bit stuck conceptually.

Thanks.

Sebastian Tomczak said...

Hi William,
Could you give me an example of something you want to do?

Cheers,
Seb

william said...

Thanks for the quick reply.

I trying to midi sync a relay, to trigger every 4 bars (384 pulses?)

Thanks again...

Sebastian Tomczak said...

Hi William.

I am not sure how long you want the relay to stay open or closed etc, but here is something to get you started.

It uses pin 2 as the relay pin, and holds the relay pin high for the first two bars of every four bars. The relay pin is held low for the last two bars of every four bars.

1. Add the following code to the start of the code:
int counter = 0;
int relayPin = 2;


2. Add the following where the code says "// do something for every MIDI Clock pulse when the sequencer is running":

if(counter < 384) {
counter = counter + 1;
digitalWrite(relayPin, 1 - (counter / 192));
}

else{
counter = 0;
}



Note that this code is untested, so im not sure if it will work. But it should at least be something to get you started.

william said...

Awesome, thanks!!!

gregory laplace said...

Yeah that's a great post.

You know, for debugging sync issues
I use 2 freeware program midiclock and midiclockdetect.

http://nic-nac-project.org/~rocket/midi/midiclockdetect/
http://nic-nac-project.org/~rocket/midi/midiclock/

But of course a hardware interrupt based timing is even more accurate !

Anonymous said...

This code helped me a lot with my analog clock module. Unfortunately, the sync code is not working properly. Here's the correct code:

void loop() {
if(Serial.available() > 0) {
data = Serial.read();
if(data == midi_start || data == midi_continue) {
play_flag = 1;
}
else if(data == midi_stop) {
play_flag = 0;
}
else if((data == midi_clock) && (play_flag == 1)) {
Sync();
}
}
}

Cheers,
ToAd
www.thebigear.be

Rich said...

Hi Sebastian,
Was just wondering how the code could be changed, to produce an output for driving an 8 step sequencer?

Sorry to appear dense, still getting to grips with this stuff!

Thanks in anticipation.

Sebastian Tomczak said...

Hi Rich,
Would you be using a multiplexer to sequence? If so, I can write it up and post it later today or tomorrow.

Rich said...

Sebastian,
to be honest, it's a very simple circuit with no multiplexing: all that's needed is a pulse to drive a 4017, which has 8 outputs, each providing a control voltage via a level pot (the count advances on each pulse).

Hope that makes sense!

Sebastian Tomczak said...

Ah okay, so you want to pulse the 4017 every, what, quarter note? eighth note?

Rich said...

I guess it would be in that region - enough to pulse all eight outputs on a single beat, say.

Sebastian Tomczak said...

hmm okay, i don't my arduino with me at this moment, so why don't you try replacing void sync () { } with:

void Sync() {
if(counter == 2) {
counter = 0;
digitalWrite(output_pin, HIGH);
}

else if(counter == 1) {
counter = counter + 1;
digitalWrite(output_pin, LOW);
}

else {
counter = counter + 1;
}
}




and add the following line to the top of the code:

int output_pin = 12; // set output pin




and the following code directly after Serial(31250);:

pinMode(output_pin, OUTPUT);

Rich said...

Hi, I compiled the code and did not get any errors, but did not get any output. Perhaps I left something out? (sorry to keep bothering you with this!):


int counter = 0;
int output_pin = 12; // set output pin
byte midi_start = 0xfa;
byte midi_stop = 0xfc;
byte midi_clock = 0xf8;
byte midi_continue = 0xfb;
int play_flag = 0;
byte data;

void setup() {
Serial.begin(31250);
pinMode(output_pin, OUTPUT);
}

void loop() {
if(Serial.available() > 0) {
data = Serial.read();
if(data == midi_start) {
play_flag = 1;
}
else if(data == midi_continue) {
play_flag = 1;
}
else if(data == midi_stop) {
play_flag = 0;
}
else if((data == midi_clock) && (play_flag == 1)) {
Sync();
}
}
}

void Sync() {
if(counter == 2) {
counter = 0;
digitalWrite(output_pin, HIGH);
}

else if(counter == 1) {
counter = counter + 1;
digitalWrite(output_pin, LOW);
}

else {
counter = counter + 1;
}
}

Rich said...

Sebastian,
Just to clarify things, is the code above to be used on an Arduino board, or on a standalone Atmega with crystal etc. (because the pinouts would be different)?

erinb said...

what is the hardware set up for the incoming midi signal?

erinb said...

what is the hardware set up for the incoming midi signal?

kabadada said...

hello
i am trying get an tight midi sync without luck

for the midi-in i use this scheme: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1187962258/0
without the 100k resistor because it didnt work with it
i could use an 110k instead but it seems not make any difference if i leave it away

for controlchange an notes its fine
but for midi sync i run out of ideas
as master clock source i tried ableton live with an rme device or my micromodular

i want to use my arduino as sequenzer for midi, cv/gate and sync to nanoloop 2.2
any idea what else i could try ?

this is my curent sync method:
void Sync() {
if(counter < 24) {
counter++;
digitalWrite(8, LOW);
digitalWrite(13, LOW);
}
else {
counter = 0;
digitalWrite(8, HIGH);
digitalWrite(13, HIGH);
noteOn(0x80, 60, 0x00);
noteOn(0x90, 60, 0x40);
}
}

void noteOn(byte cmd, byte data1, byte data2) {
Serial.print(cmd, BYTE);
Serial.print(data1, BYTE);
Serial.print(data2, BYTE);
}

thanks for any help

john said...

hi Sebastian,
sorry for bringing this topic back up but i am trying to get my Arduino to send clock signals out to my kaoss pad to change the bpm, as a tap tempo setup.

if you could please contact me at spooky_bonus@yahoo.com i would love to ask you a few questions about the code.

im going to have an external controller with a xy touch screen and 2 switches sending cc, an encoder sending program changes and hopefully i can get the tap tempo in there as well.

thank you very much,
-John

Term Papers said...

good posts..

did said...

Hi all, can someone explain what the "int counter" in the sync() function and how it counts midi ticks?
I can blink an led with that code but I don't understand all of it.
Thanks.
Did

Essays said...

This is the exact way that people expecting we can see most of the comments are true and towards the positive opinion..This is the exact way that people expecting we can see most of the comments are true and towards the positive opinion.

richard upchurch said...

would you mind explaining how to set up the counter to simply count the quarter notes. I can't seem to get a proper counter working. I have midi coming in but it seems I am getting all the midi_clock bytes.

Anonymous said...

Would you mind please explaining how to set up a counter to output quarter notes based on the midi_clock. I have midi_clock driving an led display but it seems to be outputing straight midi_clock.

Thanks so much for you blog. It's fantastic! Really nice work.

autoum said...

Nice one mate. Once you pay attention it is very easy to follow your settings

Web Design said...

Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon.

Study in UK said...

I would like to thank you for the efforts you've made in writing this posting. I'm hoping the same very best function from you inside future too.

Anonymous said...

Incidentally, I like the way you have structured your site, it is super and very easy to follow. I have bookmarked you and will be back regularly. Thank you


UK Education Consultants

alex said...

This is fantastic! I have my arduino locked to ableton's master clock now!
One question: How would you go by calculating the clock's BPM in arduino?
Cheers
Alex

Business Logo Design said...

You have provided very prominent and conspicuous stuff. I really enjoyed that. Thanks for sharing this precious post.

website design company bangalore said...

I really enjoyed this site. This is such a Great resource that you are providing and you give it away for free. It gives in depth information. Thanks for this valuable information.

Software Test Metrics said...

This is pretty cool! I like the idea a lot.