hey y’all – ahead of tomorrow’s release exposing the global clock, i put together a bit of a “how I migrated code I hadn’t looked in months from beatclock to global clock, survived, and thrived!”: Norns: migrating scripts from `beatclock` to the new global clock (a how-to!)
it’s worth mentioning that since its first release the clock module has got many important fixes by @Dewb and @ngwese, documentation by @okyeron and @dan_derks, and of course thanks to @tehn who implemented the params menu integration and did the whole release management for this update! cheers to you all, guys!
I’m curious if it would it be better to use the new clock system in my code where I have multiple sequences running off of a master metro at different clock divisions.
Typically I’ve been following this pattern in my main metro callback:
for i=1,#sequencers do sequencers[i].div_count = sequencers[i].div_count % clock_division + 1 if sequencers[i].div_count == 1 then -- do sequencer stuff end end
clock_division is 4, the sequencer will only play every 4 iterations when the modulo calculation equals 1.
My understanding is with the new system I could instead define multiple coroutines for each sequence, where each one syncs to master tempo at a different division. Is this more stable and/or performant? Are there benefits for me to refactor this to use coroutines other than it maybe looking cleaner and being more readable?
there are no performance or stability benefits. if you don’t use the coroutine features extensively and don’t need external sync sources, it’s just cleaner code.
Is there a way to query which source the clock is using? The use case here would be to allow a user to change clock tempo with an encoder if the source is internal. I see a setter, but no getter.
everything’s in the params, so I think you can just call
params:get("clock_source"). If it returns
1 the source is internal. You can see all the param IDs here
ah! Thank you. I’ve somehow overlooked
params:string all this time. Meanwhile I’m over here trying to keep track of all these dang indices
Very cool. I just updated my sequencer script. Drop-in replacement for what I was doing before.
This is great. Excited to see Norns development is continuing in a direction that the apps I’m interested in writing will greatly benefit from.
One issue I’m running into (this is my first app so it may be the way I’ve written my sequencer): When synced to external midi and setting
clock.sync within a
while true do loop, there is a tiny delay on the 1st downbeat every time I start my external sequencer. If I move the engine.hz outside of the while loop then it sounds the moment I hit
start on my sequencer (but only once obviously).
Here is the relevant code:
function clock.transport.start() clock_id = clock.run(sequencer) end function sequencer() sequencer_pos = 0 while true do clock.sync(1/4) engine.hz(get_note()) end end
Any help would be greatly appreciated - thanks!
Completely untested random thought - try putting
engine.hz inside a function that is used in the while loop?
The behavior you are experiencing makes sense to me. According to the docs,
clock.sync suspends the coroutine until the given duration has passed. Therefore, on transport start, you are immediately calling
sequencer(), which will first suspend for a quarter note (first iteration of the while loop), and then call
engine.hz, hence the delay. That’s my guess. There would be multiple ways to fix it, swap
.hz or call
.hz once before the
while loop, etc.
Thanks for the thought @okyeron!
engine.hz is actually inside of a separate function - I just simplified the code for the sake of this thread.
@Artaos I think I had actually tried both of those suggestions with no luck, but I’m not positive I had tried to call
.hz once outside of the loop. The delay doesn’t seem to be a quarter note’s length (and the length seems to change on every start message). If neither of those suggestions work I’ll take a short video demonstrating the behavior. Thanks for the help!
Swapping it does indeed have the first note land on the 1, but there is still sometimes (maybe 10% of the time) an unquantized gap between the 1st and 2nd note and then things seem to even out. Here’s a super short video: https://imgur.com/a/d4vr9Dg
I’ll experiment with the rest of the code and report back if I gain some insight in case anyone else runs into this issue.
What’s the expected type of data for
Looks to be a big float like
130.00013500014 when I do
screen.text(clock.get_tempo()) and taking clock from Link.
Best practice here would be to use
util.round (number, quant) perhaps when displaying the tempo?
Should that value be left alone as a float when setting tempo? like
params:set("clock_tempo",clock.get_tempo()+delta) or better to round it first?
What level of precision would be recommended here?
I’m curious about that, too.
I noticed that external clock values are always rounded to set the “clock_tempo” param, which is updated every 1 s:
Link transport control?
remote start/stop events can be generated by link or external midi.
but how do you do that?
I have these functions setup, but don’t get any transport start/stop from link. Do I need something else?
function clock.transport.start() print("transport.start") id = clock.run(pulse) end function clock.transport.stop() print("transport.stop") clock.cancel(id) end
EDIT - top post says:
no start/stop synchronization in link
Has this been added since then or is it still not functional?
Is this little delay related to this Github item
starting coroutines inside transport callbacks may be a bit tricky. it’s safe to assume that
clock.sync(1/4) in your couroutine will wake up at 0.25 beats, and not at 0 beats as expected.
it might help to start the coroutine with the engine.hz call as follows:
function sequencer() engine.hz(get_note()) while true do clock.sync(1/4) engine.hz(get_note()) end end
or wait for 1 or more whole beats before you start your norns sequencer:
function sequencer() clock.sync(1) while true do engine.hz(get_note()) clock.sync(1/4) end end
we’re working on fixing the syncing behaviour on transport start, but for now i don’t recommend relying on it for all use cases.
clock.get_tempo() returns a float. so the answer to your question really depends on what you want to show in your script. if you want to round the number down to n digits, use string.format.
clock_tempo parameter is updated every 1 second so it’s in sync with the external source tempo. the value is rounded indeed (parameter type is number and can only be increased/decreased by 1).
should work (but see the transport quirk described above). do you also get the tempo and beats synchronized via link? does your other link client show the norns node in “links” counter?