MIDI clock to TTL clock 24ppq ?

blakeAlbion

Well-known member
Hi! How it is done? How do I generate a locked, stable square wave at the same frequency as a stream of pulses coming from MIDI clock? Any references?
Thanks
 
I wrote this in a hurry!
What I mean more specifically is, MIDI clock was designed to operate at 24PPQN.
I need to drive my Roland CR-5000 drum machine at 24PPQN.

I get a MIDI clock pulse, but how do I turn a "one dimensional" clock pulse into a symmetrical square wave at the same frequency?
If I toggle the Teensy pin I am using to drive the CR-5000 clock each time I get a MIDI clock event, I get a very stable clock on the drum machine, but it's half the correct frequency.

bool clockState = LOW;

void myClock() {
if (CLOCK_RUNNING) {
digitalWrite(CLOCK_OUT, clockState);
clockState = !clockState;
}
}

If I make up some number, like I dunno 2200 microseconds, and I take the pin HIGH when I get a clock and then 2222 microseconds later I take the pin LOW, it kinda sometimes works.
But the CR-5000 seems very unforgiving of non-symmetrical clock pulses.
If I measure the interval between MIDI clock events, and turn off the pin at that duration/2, it sorta kinda sometimes works.

I have seen some Teensy projects that measure this duration and keep a running average of the tempo on a timer handler. But does that work well enough to keep non-stumbling time?

How can I get a stable clock for my drum machine, that keeps time to at least the 16th note, at the full clock speed?
Roland proposed the MIDI clock spec, I believe, and Roland & Korg made sync boxes that could read MIDI clock and output the Sync24 "DIN" sync signal.
These sync boxes worked great back then; how did they work?
I have a hard time believing they simply measured the tempo and estimated the output clock based on "dead reckoning", because no way is that good enough to keep time.
How can I make a Teensy keep track of a 5-pin MIDI clock signal and generate the TTL equivalent?
 
I need this to work consistently if the tempo varies. I need it be handle stop-continue from my master MIDI sequencer. And as I said above, I can't use USB MIDI for this. I must work with legacy MIDI devices.
 
Hi, cool that you have a CR 5000.

I'm not speaking from in-depth experience of doing this with teensy but a couple of points:
- Din sync just counts pulses (rather than frequency) - your myClock routine is only putting out one half of a pulse (alternating rising and falling edges) hence you only get half the tempo.
- I don't think you need 50% duty cycle, a pulse should be fine but should be longer, could you try 5 ms like:
Code:
void myClock() {
if (CLOCK_RUNNING) {
digitalWrite(CLOCK_OUT, HIGH);
delay(5);
digitalWrite(CLOCK_OUT, LOW);
}
}

If you've got other timing critical stuff running you might want to do something other than delay but should be fine for testing.

Cheers Paul
 
I would look at using elapsedmillis (or elapsedmicros if that's not accurate enough) to measure the interval between clock bytes coming down the pipe. Then wait that number / 2 (also using elapsedmillis) to set the pin low, not your set number of 2222 or whatever. Should square up the wave.

If that is too jittery for some reason, you can average the last few intervals to get a more stable beat at the cost of slight lag when the tempo changes.
 
I knew I had read about his somewhere, some useful real world testing detail (especially if you want to implement continue) in this link

If you get stuck there are still commercial products out there, I use Synchole which works for me.

cheers, Paul
 
I have tried this a few different ways, but never with elapsedMicros(). It just does not work at all. The clock drops, and drops, and drops. I think it's because my test clock source generates a logjam of MIDI garbage and buries the clock in there. I have seen and tested hand-made MIDI read code that seeks to the clock byte and ignores everything else. That didn't help either.
 
I didn't make this clear enough, sorry. I can read MIDI clocks and run my CR-5000 if it's a basic drum pattern and the MIDI source is really just another drum machine doing nothing but putting out MIDI clock and note on messages (example: Alesis SR-16). I can get it to work within a nominal tempo window. That's not the problem. The problem is when I use a noisy MIDI source (example: Boss DR-202) that's tossing out lots of garbage and then does some drum-and-bass rolls and CC sweeps. That chokes my little MIDI clock reader. Filtering out messages does not help. Optimizing the MIDI read loop does not help. I don't think I really understand the root cause of the problem.
 
Sounds like you need a clock recovery algorithm that learns the frequency and tolerates (upto some limit) delayed or missing MIDIclock messages.
Clock recovery is a big thing in comms, there'll be all sorts of publications about this.
 
Based on experimentation and input from forums, a few things have helped in small ways, and it adds up to a decent but not perfect solution.
1) put the MIDI.read() on a Teensy timer and don't be shy about cranking up the timer speed
2) while(MIDI.read()) should ensure you get the whole packet, but in practice it makes no difference
3) use elapsed time math instead of delayMicroseconds()
4) The one-shot time for my CR-5000 is between 2200-3500ms. Nothing else works but shorter time lets me play faster patterns.
5) I do not believe I am losing any MIDI data. GETTING the clock is clearly not the problem.
6) That might mean, for some reason, the one-shot code must be acting like a lowpass filter when clock pulses come early or late due to large MIDI packets from my (Florian Pilz called it 'dirty') groove box.

And I picked up a few antipatterns but of course they need more examination.
a) if you try to estimate clock frequency and duty cycle, good is the enemy of perfect and perfect is what you need, and yet -
b) MIDI has no way to "re-sync" once things get out of step, so a one-shot that can't deal with tempo change will ruin your whole day
 
For 120BPM a square clock pulse could be around 10ms at 48Hz. That just doesn't work in my tests. It needs to be narrower. But not too narrow. Sigh.
 
The problem with.a slight lag is this is a looping sequencer and it will never catch up. Device A plays a short loop, device B plays a short loop; either they line up perfectly or it’s unusable. So achieving the approximate tempo and tracking change is just not enough.
 
Yes, I have a timer callback doing this

void timer_handle_interrupts(int timer) {
MIDI.read();
if (CLOCK_RUNNING) {
if (clockFuture > 0 && (micros() - clockFuture) >= CLOCK_LENGTH) {
digitalWriteFast(CLOCK_OUT, LOW);
clockFuture = 0;
}
}
}
 
Back
Top