MIDI HW Clock GP Master Scriptlet

As there has been another flurry of interest in using GP for outputting a MIDI clock I thought I would get creative with a bit of scripting to see how to make a simple MIDI Clock Output. It exposes an enable/disable parameter for turning on and off the HW clock and a tune that speeds up the internal timing in mS. Note that unhandled messages are blocked.

Place a scriptlet in the wiring view (local rackspace or global rackspace depending on your use) and wire the Local GP Port Input and output as shown. Then connect the MIDI Out block of your choice.

Set the options of the input block as shown below.

Cut and paste the following code into the scriptlet and compile. This code will follow the BPM on the next beat and will respond to song, rackspace and global BPM changes.

// Declare a parameter representing a set of strings
var enable : Subrange Parameter 0..127 = 0 // Discrete named parameters
var tune : Subrange Parameter 0..50 = 0

var plus ("Late") : Continuous Parameter = 0.0
var minus ("Early") : Continuous Parameter = 0.0


const MIDI_CLOCK_BEAT : integer = 117
const MIDI_CLOCK_CHN : integer = 16
const MIDI_CLOCK_MSG : Integer = 248
const MIDI_CLOCK_QTR : Integer = 24

// Millisecond intervals 
var subBeat : double = 0.0
var beat : double = 0.0

// Static MIDI messages
var clock : midiMessage
var cc : midiMessage

// Stats
var cdmax : double = 0.0
var cdmin : double = 0.0
var blast : double = 0.0

Function SetStats()
    var now : double = TimeSinceStartup()
    var delta : double = 0.0
    If blast <> 0.0 Then
        delta = (now - blast - beat) / beat
        If plus < delta Then
            plus = delta
        elsif minus < -delta Then
            minus = -delta
        End
    End
    blast = now
End

Function SetIntervals()
    var bps : double = GetBPM()/60
    beat = (1000/bps)
    subBeat = beat/MIDI_CLOCK_QTR
End

On ParameterValueChanged matching enable
    SetIntervals()
    if enable >= 64 Then
        SendNow(cc)
        blast = 0.0
        plus = 0.0
        minus = 0.0
    End
End

On ParameterValueChanged matching tune
    SetIntervals()
End

On ControlChangeEvent(m : ControlChangeMessage) Matching MIDI_CLOCK_BEAT
    var n : integer
    if GetChannel(m) == MIDI_CLOCK_CHN Then
        for n=0; n< MIDI_CLOCK_QTR; n=n+1 Do
            SendLater(clock, (subBeat*n)+(subBeat/2))
        End
        If enable >= 64 Then
            SendLater(cc,beat-tune)
        End
        SetIntervals() 
        SetStats()
    End
End

// Called automatically after script is loaded
Initialization
    clock = MakeMidiMessage1(MIDI_CLOCK_MSG)
    cc = MakeControlChangeMessageEx(MIDI_CLOCK_BEAT,127,MIDI_CLOCK_CHN)
    SetIntervals()
End

Optimised some code sections and added BPM +/- error

Simply set the enable parameter and you should now have a MIDI Clock output from GP. I have used LoopMIDI for testing, this is connected to Protokol so I can see the MIDI output. If you don’t want the beat CC messages simply add a fliter block before the MIDI Output and block CC#117 messages.

I would be interested to know how stable this is, as I have just thrown this together and not spent hours testing.

This follows on from earlier topics:

5 Likes

That’s exactly what I wanted to ask :stuck_out_tongue_winking_eye:
Very nice idea :+1:

1 Like

Awesome, thanks a lot for this!!

One question - what would be the use case for the tune parameter?

1 Like

Your feedback is very valuable :slight_smile:
Thanks for sharing.

Interestingly, you found a solution for another use case I’ve been scratching my head over - sending beats to blink an LED with tempo on a hardware controller.

I currently do it with a widget mapped to the system actions “plugin” but hate that way of doing it with a passion - I really dislike using a pseudo plugin for this for aesthetic reasons, but more importantly it only works with global playhead in the “on” state only, which messes up a few things for me.

Sorry I can’t properly test this at the moment - my hardware will only arrive in a couple of weeks.

As an alternative for controlling hardware tempo, I was thinking about just sending sysex messages on tempo changes, maybe that will end up being a more accurate solution in the end, but sending ccs on every beat is still super useful.

So double thanks for the scriptlet, it kills two birds with one stone for me potentially!

One question - what would be the use case for the tune parameter?

You can use it to minimise the BPM error. As this is not a free running timer, the processing time of the script and GP impacts the actual interval times, which will also be dependent on the machine performance. I may have a look at automating the trimming depends how accurate the Clock timers are, It looks like there may be some sampling of the system timers somewhere in the subsystems.

The one drawback of this method is that is is not locked to the internal clock like the methods used in GP as MIDI Clock Master. So should not really be used if relying on a internal GP modulation or sequencer to stay in sync with external hardware.

Where is does score it is very simple to drive external gear from the GP BPM settings. For synchonisation to work you would need to have the playhead running (with different code) or use the Ableton Link/PD solution.

We’ll see. I personally don’t need sample perfect synchronization, it’s mostly for delays and tremolo like effects - tempo needs to be in te ballpark, but setting it just once in the beginning of a song doesn’t always work as drift can be significant.

Sample perfect stuff can be handled with midi automation.

I guess one way to tie it to the internal clock could be replacing the CC trigger with an action from a widget grouped with another widget mapped to system actions plugin, although the very thought of having such a contraption is cringey. But if it works then maybe it could be a workaround.

This is a clock, not a beat, so max drift will be +/- 1/48th of a beat, more than good enough for effects.

1 Like

Very nice. I got it to be relatively stable with ~ +/- 0.1 BPM jitter using a small amount of tuning.

3 Likes