norns: clock

@21echoes + @xmacex, thank you so much for testing + confirming! i’ve also confirmed this same behavior with crow as a clock source. internal and Link seem to test well, so the issues seems specific to those two sources. logged the issue.

up next: testing clock sent out via crow


@yoyosandshoes, multi-device midi clock output is not available through the system menus, no. this could be accomplished with some scripting (or daisy chaining your midi devices), but I agree that it’d be a good feature for the norns system.

6 Likes

Thanks @dan_derks Would scripting this be a beginner task?
And/or would this post be considered a feature suggestion. Or should be done elsewhere?

Edit to say I will add it to the Norns Ideas thread

it’d for sure be a great intro to learning the menus and the heuristics of system-level features. i recently did some work on extending MIDI device management to 16 devices, which did take a little bit of time poking and cross-referencing – much of the challenge for me was learning the dependencies across different files – but is 1000% doable and will really highlight the moving parts of norns :slight_smile:

all to say, it’d be great to have feedback on the feature request ticket either way. if you’re interested in working on the problem, @csboling’s guides to contributing to the main norns codebase (compiling + extending) are excellent and the community can help fill in the gaps :tada:

2 Likes

Thanks for the reply and opening the feature request Dan. I will investigate this over the coming week but given some life matters atm I actually can’t see me getting anywhere with it. Thanks for the encouragement though

1 Like

Just cross posting this to see if anyone here has a solution?
I noticed it still seems like an open issue on the github page. I’m having more sync issues with the latest update. It may have been like this for a while as I normally make slower ambient sounds, which make sync issues harder to hear, but yesterday I wrote some more rhythmic/percussive based stuff and could clearly hear sync issues between norns and ansible being clocked by PNW.
Really hoping this can be resolved.
Id love to see a more solid external sync and access to a global transport with some sort of reset.

hey sorry, i saw this and then we emailed and i totally spaced on replying here :slight_smile:

i know this one is frustrating. essentially, investigation is still underway – there was a change released in december 2020 which didn’t end up solving all cases. a solid set of repro cases have been logged and we’ll definitely keep folks updated as work develops! for now, clocking from norns (if possible) is the best approach. this has been logged, tho, and won’t get lost (it’s part of feasibility scoping for norns 3.0) – thanks for the patience + bump :rainbow:

(script authors can also bypass the norns system clock and listen directly for incoming crow pulses or midi clock messages when using those clocking methods – this is a workaround until the core library has been addressed)

as far as transport goes, methods are now outlined in the clock studies for script authors: clocks - docs. this is pretty much the method i used to add a global transport to cheat codes. if you have a favorite script that you want to talk to the transport, it’d be best to raise as a feature request with that author.

1 Like

Hey Dan,

Thats all good, I didn’t want to bog down our emails with Norns clock issues :upside_down_face:

Good to hear things are still in the works. Cant wait for this to be fixed properly.
I do try to keep my sync set up the same for studio to live performances which means running Norns as master isn’t ideal. I do love having a dedicated start/stop button as well as using all crow outputs for sequencing but i’ll give it a shot and see if its any better as a work around for now.

Quick question: Is sync via Link solid and can I send out clock from crow?

Your transport implementation in Cheat Codes is awesome!
Would love to see it in all scripts but I’ll make a few requests to the authors of my favourite scripts ad see what they say.

Thanks for continuing to work on this, very much appreciated :sparkles: :pray: :sparkles:

1 Like

Sync via link is super solid. Norns always takes the lead and changes the ableton clock when it first connects but no other issues. Haven’t tried crow yet in this format. I’m Just sending clock from ableton straight to eurorack via midi and to Norns.

1 Like

just a quick bump for visibility – @artfwo has done absolutely amazing work on the norns: update 210607 to address all of the issues described above. an incredible testing team (@Dewb , @license , @Quixotic7 , @anxietymachine , @proswell , @static , @midouest , @eigen , @tyleretters, @Wisdom.Water , @infinitedigits ) has put everything through its paces and it’s all come out very positively :slight_smile:

thanks to everybody for the patience + fantastic communication re: troubles over the last year – having clear repro cases has helped frame both criteria and testing :revolving_hearts:

19 Likes

Yay! Happy syncing and thanks a lot for all the work :slight_smile:

3 Likes

Can someone tell me if I’m misunderstanding clock.sync or if I’ve found a problem with it? I was seeing this in the last norns release so don’t think its related to the clock changes.

With a script:

function init()
  clock.run(step)
end

function step()
  while true do
    print(clock.get_beats())
    clock.sync(6)
  end
end

I’m expecting a wait of 6 beats each time but the first sync is always less e.g.

20444.842958333
20448.000958333
20454.000979167
20460.000333333

Every sync after that is perfect.

In my ‘In C’ script i’m playing notes in a loop like this and syncing after each note for the appropriate note length but getting unexpected results. I’ve switched to sync’ing every 0.25 beats and just looping round without playing a note if I need a longer note. That seems to work better but it would be good to understand what’s going on.

that’s the expected behavior. init() will be called at an undefined moment in time with regards to the beat coming from the clock source, and so will step(). the first thing step() does is printing current beat, which can be anything, as the clock isn’t reset on script reloading and such.

in order to do something in sync with the beat, make sure clock.sync is called before any actions that you expect to be synchronized.

it might be worth mentioning, that the absolute beat value is now returned from clock.sync after the coroutine is resumed, so the following code will also work:

function coro()
  local beat = clock.sync(x) -- wait and resume at the next Xth beat
  print(beat) -- will print the absolute beat
end
4 Likes

Thanks for the reply. I was expecting sync to wait for the specified number of beats but its waiting for the next time the beat counter is divisible by the specified number of beats. Is that right?

1 Like

20 characters of yes!

1 Like

for “wait for the specified number of beats” cases, using clock.sleep for the duration of a beat (or many beats) as measured by clock.get_beat_sec() could work:

function init()
  pending = false
end

function key(n,z)
  if z == 1 then
    if not pending then
      clock.run(play_after_x_beats,6)
      pending = true
    end
  end
end

function play_after_x_beats(x)
  clock.sync(1) -- quantizes to the nearest beat
  print("current beat: "..clock.get_beats())
  clock.sleep(clock.get_beat_sec() * x)
  print("delayed till: "..clock.get_beats())
  pending = false
end

will return:

current beat: 1830.0018375
delayed till: 1836.003975

Thanks @artfwo & @dan_derks.

I’ve been experimenting with recording midi (which is obviously the gold standard :slight_smile: ) into ableton and this pattern gave slightly better accuracy over the length of the sequence.

local waitCount = 0

while true do
  if waitCount == 0 then
	  -- turn off last note
	  -- play next note
	  waitCount = (<note length in beats> / 0.25) - 1
	else
	  waitcount = waitCount - 1
  end
  clock.sync(0.25)
end
1 Like

oh, hell yeah – i was actually writing a double-back post to mention that the code i shared is pretty literal (waiting a specified amount of time) and won’t likely perform exactly as desired in a traditional clock-sync situation where:

  • each beat coming from a leader might not an exact duration
  • we just want the things to happen at the same time, because that feels rhythmically “tight”

so yeah, an approach i’ve traditionally used is a counter-based system where a counter increments at your smallest desired beat duration and you track those for event execution, which is totally where you landed! :slight_smile: thank you for closing the loop with your results :sparkles:

artem also outlined a nice approach here, which is new to the clock improvements and leverages clock.sync()'s return of the current beat as a built-in counter method (which i need to get in the docs asap :sweat_smile: )

1 Like

with the new clock scheduler this shouldn’t be necessary, i.e. the following code:

local x = 1

clock.sync(x / 4)
if (clock.get_beats() > (x + wait_count)) then play() end
clock.sync(x / 4)
if (clock.get_beats() > (x + wait_count)) then play() end
clock.sync(x / 4)
if (clock.get_beats() > (x + wait_count)) then play() end
clock.sync(x / 4)
if (clock.get_beats() > (x + wait_count)) then play() end

wait_count = wait_count + 1

can be replaced with

local x = 1
clock.sync(x) -- guaranteed to resume at xth beat
play()

since the new scheduler follows the source very closely, tempo changes and midi signal jitter are less of a problem now.

in musical functions i recommend choosing sync over sleep for most cases, as it guarantees stable rhythms in the script with graceful handling of tempo and source changes and better transport syncing.

1 Like

with norns: update 210630 it’s possible to specify an offset for a sync position as follows:

 -- sync and resume at the next whole beat as usual
clock.sync(1)
-- sync and resume at the next whole beat with a half-beat delay
clock.sync(1, 0.5)
-- sync to the next 4th beat, but resume earlier by a quarter-beat 
clock.sync(4, -1/4)

-- create a swinging feel for a coroutine
-- by delaying even beats by half of the beat length
local beat = 1/4
local offset = beat / 2
local swing = 0

clock.run(function()
  while true do
    clock.sync(beat, offset * swing)
    -- do something
    swing = swing ~ 1
  end
end)

a bit of extra care should be taken with negative offsets, as they can lead to somewhat unexpected delays, e.g. clock.sync(2, -2.5) called at beat 0 will schedule resumption of the coroutine at beat 1.5, which is computed as 4 - 2.5, where 4 is the least possible beat divisible by the sync value of 2 which can also be scheduled in the future with an offset of -2.5 beats. hope this makes sense :slight_smile:

with positive offsets, sync will just be delayed by the time specified (in beats).

generally this allows relatively painless implementation of swinging rhythms in scripts and provides quite interesting results when different swinging patterns are used concurrently.

4 Likes

ran into a bit of an issue…
how slow can the norns BPM receive?

ran into an issue last night with Cheat Codes while trying to send it Ext MIDI Clock at 50bpm.
:stuck_out_tongue: