20 characters of yes!
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
current beat: 1830.0018375 delayed till: 1836.003975
I’ve been experimenting with recording midi (which is obviously the gold standard ) 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
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! thank you for closing the loop with your results
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 )
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
sleep for most cases, as it guarantees stable rhythms in the script with graceful handling of tempo and source changes and better transport syncing.
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
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.
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.
hope all’s well
1-300bpm is the supported range — lmk if there was a cc2 issue tho in the thread and i’ll take a peek!
Are there any big things to avoid when working with clock coroutines? I am trying to use clock.sync to synchronize 2 pattern_time patterns. Everything works fine if I’m only using one of the patterns in clock-synced mode, i.e. only one clock coroutine running at a time, but as soon as I try to use both synced to the clock (2 clock coroutines running at once) I get some very strange behavior.
I do have both patterns stored in a table, and the coroutines both access the table to modify(record/start/etc) a pattern at index n, but both should be accessing different indices and different patterns.
maybe share the code? sorry i don’t quite get it.
… actually that’s ok, it’s enough i think. i’ll try and reproduce or make a working example.
It’s quite messy, and I’ll try to make a distilled version to illustrate what I’m talking about. There’s a table called “patterns”, indices 1 and 2 store tables with a pattern and a couple state variables, such that you can access pattern n with “patterns[n].pattern”.
All the clock synced stuff happens in record_start_sync(), record_stop_sync(), start_sync(), stop_sync(), and reset_sync(). All but reset_sync are started in g.key. Reset_sync is started by a special event I put near the end of the patterns, it is started at the top of pattern_note().
The gist of the script is that columns 2-16 is a grid of notes you can play, column 1 is control functions, (1,3) controls pattern 1, (1,4) controls pattern 2. Holding (1,7) while pressing a pattern button clears that pattern starts recording unsynced, pressing the pattern button again stops recording and then controls stopping/starting. Holding (1,6) while pressing a pattern button does the same, but synced to the clock. You can toggle the grid flashing to the clock by pressing key2 on norns while holding (1,7).
Feel free to wait for me to give you a distilled example, my code is pretty rough.
… here’s an example of two parallel clock-driven sequences. one is a steady 16th note and the other steps between a few different sync divisions in sequence.
… does that help at all?
as far as race conditions…
well, our lua VM is single threaded, the question is can we guarantee an order of execution for clock routines that are sync’d to the same beat. that’s a good question; i’ll take a quick look and see if there is an obvious answer.
for now best, to assume that you can’t and try and avoid the possibility of race conditions. (i mean once you get into any sort of complex rhythmic relationship you probably want to assume this anyway.)
so, on the other side of it i will make a brief attempt to grok your script’s management of runtime state. we’ll see.
going on your general description - it seems fine for multiple clock threads to mutate different fields in a table. it might be a generally bad idea to have both threads adding or removing elements in the same table, but even then it depends on the method - there are ways to make it safe.
Thanks for your help - I guess I should finally start making some diagrams so that I can fully grok the script’s management of runtime state…
This is the kind of stuff that was in the last CS course I took, and failed. Time to dig back into it I guess
ah well, i didn’t mean anything complicated and probably phrased that badly.
so far i haven’t spotted anything obviously scary. ah, well ok, i am skeptical of / would investigate if/how
pattern_reset_sync(n) and one or more
pattern_*_start_sync(n) are interacting when they run simultaneously with the same
n. that seems like it coud easily become hard to predict.
but, i guess you said it’s working with only one pattern… so, sorry - nothing obvious (assuming the
ns really are distinct and really are getting passed on to the clock update functions correctly.)
like ok, here’s my dummy check: add print statements (print
n in each coro, print the returned id of each coro) to make triple sure that the
ns are distinct. (who knows, we could have some bug in the way the clock wrappers handle varargs or something.)
I’ve added the print statements: orison/orison.lua at b56b0ddd0708860a1dbcd41a5e1735064ff13d25 · evannjohnson/orison · GitHub
There’s some kind of mixup happening with the patterns, but I’m pretty sure
n is being properly passed to all functions. Here’s a sample of the output:
encountered syncer event for pattern 1 pattern reset sync1 742 encountered syncer event for pattern 1 pattern reset sync1 743 pattern_time: not playing pattern rec start sync2 pattern_time: not playing 744 encountered syncer event for pattern 1 pattern reset sync1 745 pattern rec start pat2.rec 0 encountered syncer event for pattern 1 pattern reset sync1 746 encountered syncer event for pattern 1 pattern reset sync1 747 encountered syncer event for pattern 1 pattern reset sync1 748 encountered syncer event for pattern 1 pattern reset sync1 749 encountered syncer event for pattern 1 pattern reset sync1 750 pat1.rec 1
Pattern 1 was playing synced, you can see the syncer event being processed. Then I attempt to start recording pattern 2 synced, and it does print
pattern rec start sync2, so it’s getting the correct
n. You can then see further down that pattern_time has printed
pattern rec start, on beat. But,
pat2.rec = 0 and
pat1.rec = 1, so something has gone wrong here, despite
pattern_record_start_sync correctly being passed 2. I can’t figure out why.
̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶ ̶
I got it working. First tried moving all variables out of the table to eliminate weirdness from 2 coroutines accessing the table at the same time. That didn’t work, the pattern_sync functions would still cause weirdness when being run at the same time. So I made separate sync functions for the patterns, to try and divorce the flow of execution (am I using that term right?) for the coroutines as much as possible. And now it seems to be working as intended.
It feels pretty clumsy, but it’ll have to do until I better understand coroutines.
Here’s the working code: orison/orison.lua at d995ec527f93c55ec94ca1c71571a7f9adcbc642 · evannjohnson/orison · GitHub
apologies if there’s a better place to find this, but: has anyone had luck sending clock from norns to Logic Pro X? The following steps aren’t working for me:
- Set norns clock to source: internal, whatever tempo. Set norns clock midi out to e.g. port 1
- Open up Logic Pro X, and in File > Project Settings > Synchronization, try Sync Mode: MTC. Also go to “MIDI” and turn on “Listen to MIDI Machine Control (MMC) Input”, just for kicks.
Logic does not seem to receive norns’ tempo or transport messages with these settings
i think Logic doesn’t receive sync from anything unless it’s MTC (MIDI Time Code), which norns doesn’t currently transmit. seems like for regular MIDI clock devices, Logic needs to be the ‘central transmitting device’: Sync multiple MIDI devices to Logic Pro - Apple Support
edit: maybe there’s a way to send pulses to Logic’s Tempo Interpreter?
seems like the ‘tap tempo’ command is just a keyboard shortcut. weiiiird.
I’m very new to norns and trying to get an overview of the current sync options. Is the idea of having a global synced transport still alive – or a long term plan? Until then: are there any sequencer apps with synced start/stop when setting the global clock to Ableton Link?
norns supports transport sync on the API level, but doesn’t enforce scripts to support it, given that different scripts can have very different ways of following time and user input.
for instance, some scripts will trigger sample playback when a button on the grid is pressed, so there will be no notion of “transport start” in such script completely.
i am not using any scripts synced to transport myself, but i am sure some of them do it.
I’d bet most norns sequencers do. I know Cyrene: a drum sequencer based on Mutable Instruments Grids does