Callback at specified clock time?

I am trying to run a callback at a specified Clock Time (in milliseconds). Presently such a callback does not exist, correct? Ideally I could call ClockTime() to get the current time, then add some number of milliseconds to schedule a callback in the future.

If it does not exist, what is the best workaround?

  • Is there a way to use On SystemEvent?
  • Perhaps I could schedule a MIDI event and catch that
  • There is also the possibility of setting up a generator and using On TimePassing
  • On BeatChanged will not give me the granularity that I need for this project

Looking for the most efficient (least CPU) way to pull that off. Anyone have a recommendation?

Thanks!

Right now such a callback does not exist.
And a callback in the range of milliseconds is huge demanding on the system because of polling.

What do you want to do with such a callback?

Implementing my own tap tempo that looks at the timestamp of the last 8 taps and predicts the time and tempo for the next beat. Existing implementations of tap immediately alter BPM after a couple of taps and resulting BPM swings too much.

Not sure polling every ms is needed if one could set an interrupt to fire at a certain time. Also, there may be already that granularity of timing being done inside GP, in which case this would be an easier addition.

Pulling off events at a certain time are probably not that unusual.

I think you could implement that with a script.
Please wait, I will write a proof of concept

Here it is: One Button does an initialization, the other button is for calculating the bpm.
Quick & Dirty, but it should work.

var BUTTON : Widget
START  : Widget
Time   : Integer
index  : Integer

NW1    : Integer
NW2    : Integer
bpm    : double

on WidgetValueChanged (newValue : double) from BUTTON

 index = index + 1
 if index == 1 then
    NW1 = TimeNow()
 elsif index == 4 then
    NW2 = TimeNow()
    bpm = (600000.0 / (NW2-NW1)/4)
    SetBPM(bpm)
 end   

 
    
end

on WidgetValueChanged (newValue : double) from START 
 index = 0
end

This does not need the additional button:

var BUTTON : Widget
    index  : Integer
    
    NW1    : Integer
    NW2    : Integer
    bpm    : double

on WidgetValueChanged (newValue : double) from BUTTON

 index = index + 1
 if index == 1 then
    NW1 = TimeNow()
 elsif index == 4 then
    NW2 = TimeNow()
    bpm = (600000.0 / (NW2-NW1)/4)
    SetBPM(bpm)
    index = 0
 end   
    
end

on Activate
 index = 0
end

Thank you for your help and you have correctly found the BPM averaged over 4 taps. I am working on the next step which is to align the playhead to my taps.

Using a regression on the tap times I can easily calculate the time for the next beat. My thought was to set a callback for the desired time, then do an EnablePlayhead(). I was asking the group about the best way to pull that off.

Seems that setting up a generator may be overkill so I think I’ll schedule a MIDI event to happen at a specific time and get a callback on that. Happy to know if someone has a better idea!

Help me, I do not really understand.
What means “align the playhead…”?

Sure, that is if you give GP a tempo and meter it will count the beats for you. If you change tempo GP will count at the new BPM but it won’t necessarily be aligned to your taps. By doing an EnablePlayhead() at the right time you can have its downbeat match yours.

Ahh I think I understand.
You want to follow GP tempo your playing tempo and quantize the playing position?

Yes, I want to use arps and pulsing / rhythmic patches with a live band, which currently does not play to a click track. If the tempo is off by even a small bit my arps don’t sound tight. I’ve tried many different tap tempo implementations and most start trying to extrapolate tempo after two taps – this leads to wide swings in BPM – and they often don’t align to the beat in my DAW. It ends up sounding sloppy and so I have avoided rhythmic stuff altogether.

My tap tempo function will not react until I have tapped eight times, and it will do a best fit to the taps to line things up. Not sure if it is going to be reliable enough but I want to try it.

This reminds me to “Beat Seeker” which is a M4L patch for Ableton Live.
With this you can route for example the signal of the kick drum into Ableton Live.
This way Ableton Live follows the tempo of the drummer!

When the drummer is not a total beginner this works really well.

In my experience it is best you play with a click track :wink:

Which DAW are you using?

Hmm, I’m surprised you found this happening with GP’s Tap Tempo function. I’ve used it very successfully to sync up to drummers in one of my bands.

Tap Tempo Demo.zip (9 MB)

Built-in tap BPM progression (stdev=3.03):
109.3, 111.2, 115.8, 115.4, 115.5, 116.0, 116.1, 113.1, 115.1, 117.1, 116.5, 116.7, 116.4, 116.5, 116.4, 113.0, 117.0, 118.7, 117.9, 116.5, 116.2, 116.1, 126.0

Scripted tap BPM progression (stdev=0.18):
116.0, 116.2, 116.3, 116.5, 116.3

Part of the benefit here is that I don’t update until I get 8 taps, and then I do a linear regression on the tap times. Calculation is simplified since I know the X values are 0…7. Slope is the number of milliseconds per beat. Slope and intercept together tell the precise time when the next downbeat will occur.

What happens if the band tempo changes during the song? Do you have to tap 8 times again to get it synced up?

Yes. If I keep tapping I could adjust every two measures (4/4 time). That’s how it works now.

I may update it to use the last 8 taps so it is more of a moving average. One reason not to do that is I don’t want to reset the playhead every beat because that causes my arps to reset. I am using BlueARP and you can set up to 64 steps. The way it works now it gets through two measures of the arp before jumping back to step 1.

So if I was only updating the BPM then I would use the last 8 taps moving average. If I am also resetting playhead on the next downbeat then I am happy forcing another 8 taps.

I was ready to test it last weekend when my drummer surprised me and showed up with a metronome. :wink:

Often it is the case that a better idea hits me long after I hit “Reply” — a better solution is:

  1. don’t change BPM until I have two measures of taps (let’s say 8 taps for this example)

  2. update BPM using the slope of the line fit on the previous 8 taps (rolling basis)

  3. reset the playhead on the next downbeat following each two measures of taps (say, beat 9, 17, 25, etc.)

  4. if approx three beats goes by with no tap reset state machine so the next tap is tap number one

  5. i now do error checking so if calculated BPM would change by more than 1/8 its current value then skip the change, assuming the count got messed up. When playing live I expect gradual changes in tempo not drastic changes. And last thing I want is for my arps to get really out of whack because I accidentally tapped one time too many or I missed one

Here is a script that implements the above. It is a testament to GP Script that such a function can be built and run efficiently.

I noted that the playhead had to be disabled and then enabled in order to get it to start over at beat one. There was one surprise – I found I needed to trigger a callback to fire 420 ms before beat one in order to get it to align correctly.

LiveTap.gig (12.1 KB)

1 Like

Thanks for the kind words. . If you have some thoughts on what functionality would have made your script easier to develop, let us know ( no promises of course :pensive:)