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.
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.
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.