-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Norns 'clock' module support on crow (global timing) #339
Comments
The above notes aren't really relevant to the actual solution. Open PR #352 solves this issue by directly translating the norns implementation to using the system clock on crow. The timing granularity is only 1ms so there is more jitter than on norns, but in general, it should be sufficient in all but the most extreme situations. There is an issue where the resumption of the clock routine is delayed slightly (by the event system), causing a subsequent clock sourcesThe main issue to be discussed is relating to clock-sources. Which ones, and how they should relate to normal crow usage: clock.internalThis is the currently implemented clock source and seems to be working well. It's just an internal timer that accumulates over time. Interface is: -- currently working
clock.set_source( 'internal' )
clock.internal.set_tempo( 160 ) clock.crowLike on norns, this uses the crow input jacks as the clock source. Implemented in the same way with smoothing and clock divider. When crow is being clocked by the input jack there's no real need for the input jack to be accessible to a crow script, because you can use a clock.set_source( 'crow', 1 ) -- awkward calling it 'crow' on crow
clock.set_source( input[1] ) -- requires some metatable magic It makes more sense for the user not to have the clock.midiTBD. This should be figured out once we have the USB MIDI working well, but will likely be a key usage of the MIDI connection (ie. to use crow to send MIDI synchronized clocks (whether with clock divs or swing etc). clock actionsOn norns, most of the clock interaction is handled by the menu system. setting tempo / reset etc is all done with a standardized menu, rather than from a script. obviously on crow we don't have a UI beyond the CV jacks, so any interface must be defined in-script. As such it makes sense to talk about what features are important to add to the documented API. clock.set_source( source ) -- 'internal', 'crow', 'midi', 'norns'
clock.reset() -- for 'internal' resets counter & restarts immediately, for 'crow' resets to count = 1 on next tick clock.outputOptions: crow, norns, or midi. clock.output.nornsWhen norns selects 'crow' as it's input device, that should tell crow to select clock source as input 1, and output as norns. This should perhaps not be a crow option? It will have no effect until norns selects the crow-input option, and when attached to norns, typically folks will see crow as a dumb expander (ie it should take directions, rather than send them to norns). For voltage output, this should likely be user-implemented as a clock routine over the output: -- directly set it up
clock.run(
function()
clock.sync(1)
output[1]( pulse() )
end)
-- can provide a helper function
function clock.crow.output( channel, sync )
return clock.run(
function()
clock.sync(sync)
output[channel]( pulse() )
end)
end
-- so user can call:
clock.crow.output( 1, 1/4 ) -- set output 1 to pulse every quarter beat
-- when norns sets 'crow out div $divs' it just calls
clock.crow.output( 1, 1/divs ) sending midi should work the same as norns just needs some lua syntax clock.midi.output( chan )
return clock.run(
function()
clock.sync(1/24)
-- TODO send MIDI clock signal. see midi issue
end)
end |
@tehn would love your input and thoughts about this if you have time! |
on it today!
…On Fri, Jul 24, 2020, 3:19 PM trent ***@***.***> wrote:
@tehn <https://github.com/tehn> would love your input and thoughts about
this if you have time!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#339 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAB4I4GXGMAIS4CJ5WAVRELR5HNFJANCNFSM4OFEM3ZA>
.
|
this is a very good solution in my opinion
i propose
i agree that setting an input as clock source should otherwise disable other operations. i guess they should still be able to query pin state... i doubt that's trouble though.
TBD (wait for the midi code first!) :) but it should be straightforward. the bytes are fixed with a specific timing division. (i did the midi clock norns code so i can clarify later)
this is actually a good issue to add to norns as well: ie, see https://github.com/monome/norns/blob/main/lua/core/clock.lua#L196 for list of stuff norns menu items do.
i guess
the norns option is a little complex:
this seems nice, but i think we'll have to save the clock as to cancel it before changing the value... because i see people wanting to dynamically change the subdivisions of a running output clock. i do agree that this part seems a little extra hand-holdy perhaps at the expense of flexibility (ie, actually just learning how to use clocks and do something more interesting than a 3-line script which does clock division!) the midi output is helpful, though. needs enable/disable and channel. norns clock output should have enable/disable and div. (i guess div can also be a fraction, becoming a mul) happy to clarify or revisit ideas here! |
Coming back to this issue. I've spent some time on the I just read back through the previous discussion and i've had some alternative ideas for how to implement some of the helper functionality, and how to refer to clock-source switching: For clock-output to a CV jack, i'm thinking we should add a helper fn to the -- example usage
output[1]:clock(div) -- where div is essentially the argument to `clock.sync()`
-- Output library code
function Output.clock(self, div)
self.clock_div = div or self.clock_div
if self._clock then clock.cancel(self._clock) end -- replace the existing coro
self._clock = clock.run(function()
while true do
clock.sync(self.clock_div)
self.asl:action()
end
end)
end Basically it's an inversion of control: Output leverages Clock to extend the functionality of Output (as opposed to the prior: Clock takes control of Output). Similarly I want to approach clock_source in the same way. Specifically focusing on CV input for controlling the global clock: -- set input mode to control global clock
input[1].mode('clock',div, avg) -- div should equate input rate with clock.sync() rate. avg is number of clocks to smooth over Here we use the -- input event only
input[1].mode('change')
input[1].change = do_thing
-- input drives clock, and creates events on each input pulse
input[1].mode('clock')
clock.run(function()
while true
clock.sync(1)
do_thing() -- should work identically to the above event
end
end)
There is some design questions about how to handle source switching (eg. what happens when The primary point being (again) that controlling what the inputs do, should be handled by the I know @tehn 's last message spoke about not focusing on a 3-line clock-divider script, but i think there is a benefit to making these timed events nearly effortless -- practically it provides 'clock division' as a first-class functionality, which means building more complex actions on top of clock mul/div is far easier & there's little boilerplate. Also, clock-multiplication is something people love to ask for, but nobody has provided a solid re-usable implementation. Clocks are a great solution. Anyway, here's the 4 line clock-divider script: function init()
input[1].mode 'clock'
output[1]:clock(2) -- where 2 is the division
end
-- can update the output clock_div on the fly:
output[1].clock_div = 4 Which gets me thinking about writing a simple quantization script, useful for working with decorrelated trigger streams: -- input[1] is timebase
-- input[2] is pulse-train to be quantized to the timebase
QUANT = 1/4 -- quantize to 4segments per beat
function init()
input[1].mode 'clock'
input[2].mode 'change'
input[2].change = quantize_event
output[1].action = pulse()
end
function quantize_event(dir)
clock.run(function()
clock.sync(QUANT) -- wait until next QUANT tick
if not dir then clock.sleep(0.01) end -- delay noteoff by 10ms to avoid on/off falling into same window
output[1]() -- pulse the output
end)
end So the last category of functionality (and the one i'm least confident designing) is interaction with a usb host. Specifically norns, and M4L. When sending clocks out of crow, driven from the host, I actually don't think clocks are necessary and we don't need to support following the clock. The specific reason being that most existing scripts take the approach of sending explicit commands on every event (rather than a description of when to send events in the future). I think that is absolutely fine and echoes existing note generation style (eg supercollider engines in norns, midi-notes in m4l). When the host is being clocked from crow's input, we currently just use the default I guess this means the upstream clock handling is mostly unchanged, we just use a new input mode. It would be easy to first set 'change' mode such that if an older version of crow was running with new norns/m4l, it would fall back to the current solution. would love any feedback on the above! |
i am hugely in favor of all of your points here! (particularly being able to optimize within the C layer.) please disregard my earlier assessment from last july. re: multiple clock inputs. is it worth even supporting clock from both jacks? i guess it's maybe interesting to have clock input streams in both jacks and being able to dynamically (via script) switch between jack sources... so yes? my impulse assumption:
re new clock input mode (sending via usb) i am all for some minor breaking changes to improve the norns-crow sync optimization (ie clock+). maybe just add a flag somewhere for enable/disable send TTY sync? |
fixed in #393 opening a new issue to handle different input sources |
Add norns 'clock' module support to crow.
This allows for a variety of clock synchronization functions, and introduces the notion of a global clock on crow.
While the implementation will be substantially different from the norns version (using a single hardware timer for the clock base instead), it should present the same interface to the user.
extended features
timeline support. ie a table of time, function pairs that will be executed. useful for 'composition' style sequencing.
The text was updated successfully, but these errors were encountered: