coroutine execution scheduler #712
Conversation
|
wow, this looks amazing. could you provide a few lua usage examples? |
6cb5ddf
to
e9886ce
649e5c7
to
e1fafae
|
This looks awesome. I was trying to figure out how to get bpm for incoming midi clock earlier today and then saw this had been merged. I'm playing with the sample script and can't quite get midi clock synced up with my TR-09 adding a 3rd tick box and then this:
It tracks tempo from the TR-09 roughly, but is not exactly in time and seems kinda drifty. Also - How do I set the INTERNAL tempo? |
|
you don't have any code to stop the tick tasks when you change the parameter value, are you sure that the drifting tracker comes from midi clock and not internal?
for now you can use a global |
|
Not really sure if I'm even approaching this the right way :) Any suggestions would be appreciated. I just tried mapping a clock.stop(task[3]) to a midi stop - which works. but it seems like a clock.start() is also needed to use with midi start/continue. Or is that clock.resume() ? That doesn't quite work as once I hit stop, A funny thing about midi clock is that it's always running/sending. So I don't understand how clock.run and clock.stop work in that context. FWIW - I just tired mapping _clock_internal_set_tempo to enc1, but while the value changed, the internal tempo did not change.
|
|
not sure why tempo changing doesn't work, i will be able to look into the issue next week. |
|
I'm happy to help out with testing on this (esp midi). I've just got to wrap my head around how it all works. Query: Would a |
|
yeah, it would be nice to have a global (system-level) parameter for source, so we can get its value at any time. i won't be able to help you much in the coming few days as i will not have good access to the internet, but will be able to look into any upcoming issues towards the end of next week. |
|
bookmarking this for later: Internal Clock seems to always be 120 due to this (?): norns/matron/src/clocks/clock_internal.c Line 41 in 7d61c27 |
|
I have some time later this week and want to revisit testing the clock co-routines. Has the above |
|
@okyeron this is the default tempo that's set during initialization, it's supposed to be like this. in your code above you assign the bpm value to the function: |
|
Aha! Fantastic. Thanks for that clarification. |
|
@artfwo I'm still having some issues changing the tempo. Trying the following code - the tempo appears to stay at 120/default. Does the _clock_internal_set_tempo need to be "applied" in some fashion in
|
no, i think the problem here is that you're not updating the global(?) |
|
Apologies - I was simplifying and left that out of the code
|
|
Started on work in progress here: https://github.com/okyeron/norns/tree/clock-get-source also added a wrapper for internal tempo (although I still can't make _clock_internal_set_tempo work).
|
|
there are matching constants in the clock module, that's why the source id is 0-based. i'll have a look at the issue with set_tempo, could be a problem with the resume scheduler. |
i don't think it is really necessary, as the clock source can be a script or a global param, which you normally only have to set every once in a while. for display getting the param value should be enough as well. |
|
Gotcha. I'll abandon that for now I guess. |
|
@okyeron i cannot reproduce the issue with tempo not being changed, do you have any |
|
Yes I do have a Gist of the test script I'm using (which is extended from your example script): https://gist.github.com/okyeron/a67b2405c64823cfe4528fa4bde5b32f |
|
Yeah, your script uses midi clock as sync source. Remove that line to sync to internal clock. |
|
OK - I'm a bit confused as I thought I would be able to switch between midi clock and internal clock and that my If I want to be able to switch between midi and internal - what would you suggest? |
|
well, your clock source param will be 1 or 2 so it never reaches the value 0, try the following code instead (i hope this mess will clear up when we have these params as matron-wide):
without incoming midi clock events, the clock will stick to last known tempo (default in this case), and setting internal tempo will do nothing, i'm sure that's the problem that you're having. |
|
Thanks for that. However, I must still be doing something else wrong as I can't make the internal tempo change on the running script using I added some debug to clock_internal_set_tempo and noticed this - which seems a bit odd to me - interval_nseconds is not getting any precision from interval_seconds (since that's coerced to (uint64_t) first)?.
Also - apologies if I'm being a bit thick. My lack of knowledge here is showing. |
|
good catch, the fix is coming.
i don't know how a host-host adapter helps that, but we also have an ableton link clock source in the works for timesyncing norns+norns or norns+PC. |
|
Looking forward to Link support for sure. Basically with 2 hosts you can't just send USBMIDI between them with a simple USB cable. I built a little adapter with a couple of Teensy LC's and tested this out with 2 RasPi's tonight (one running orca the other norns). Did not try clock sync between them yet, but it should work. There's a commercial host-host adapter from a company called SevillaSoft that does this, but I wanted to try DIYing one. |
|
midi clock source has a few problems now - needs a smoothing buffer for averaging interval measurements and doesn't react to "continue" event. |
|
Finally getting the chance to dig deeper into this. Looks awesome and will be a huge help on my poly arp project! Using it to trigger crow right now. It looks like lua errors inside coroutines are silently lost. Is there an easy fix for that? Also, like @okyeron I'm also not observing any change after calling |
Good catch! Yes, coroutine.resume will return false in case of an error, so we can intercept this as follows: #900 Can look into tempo setting problems later as well. |
This PR addresses #129 by adding a module for syncing to an external clock using Lua coroutines. It does not replace the current metro API.
The main rationale for using coroutines is that coroutines are a super-flexible tool for describing musical processes in code in a functional way: they are concurrent by nature, have multiple re-entry points, can execute other coroutines.
The clock module provides 2 main functions:
clock.run(function() ... end)- will create a coroutine from the given function and immediately run it. returns coroutine ID that can be used to stop it later.clock.sync(beats)- will pause execution until the given fraction of a beat is reached in time. must be called from within a coroutine launched withclock.run.Helper functions:
clock.sleep(s)- pause coroutine execution forsseconds.clock.stop(coro_id)- stop execution of a coroutine started usingclock.runclock.set_source(source)- select the sync source, currentlyclock.TEMPOandclock.MIDISynchronisation:
clock.syncschedules coroutine wakeup events usingclock_reference_tstructure inclock.c- the structure stores the count of beats (as double), tempo, and time of last update of the two former numbers. The scheduling logic is implemented inclock_schedule_resume_sync: it basically counts backwards to "beat zero", then calculates the time until the next given fraction of a beat.Clock sources: this PR only provides tempo and midi (needs more testing). Sources can update the reference at any suitable rate via
clock_update_reference_from- it will only accept updates from source selected byclock_set_source.Example script:
clock_test.lua - with generators
clock_test.lua - without generators