I don’t believe there’s an exact equivalent of this, because time.sleep blocks the thread and crow only has the one thread of execution. It’s possible to implement a sleep just by busy-waiting (spinning in a loop while keeping track of some counter) but this chews up CPU, is finicky to get totally accurate, and crow would spend all its time running the busy-wait loop. So while it was sleeping, it would not be able to continue running LFOs, reading inputs, or even communicating over the USB port.
crow can seem like it’s doing multiple things at once because it shares CPU time between handling different events. For this to work smoothly no given event handler (metro action or whatever) can take too long, or other event handlers will be left waiting. So generally you can’t say “pause for n seconds”, instead you ask the event loop to keep track of a timer and call you back n seconds in the future. In Python time.sleep avoids locking up your whole machine because the implementation of time.sleep asks the operating system to call Python back – the operating system is essentially processing its own event loop, and Python is sharing time with other programs.
You can however write asynchronous code in an imperative style in Lua, thanks to coroutines. A coroutine (wikipedia) is a generalization of a subroutine (function) that can basically return values multiple times, picking up where it left off when another value is requested. Using coroutines and delay you can write something like this:
function cometro(co)
local ok, tnext = coroutine.resume(co)
if ok and tnext then
delay(function () cometro(co) end, tnext)
end
end
output[1].action = lfo(1, 5)
output[1]()
cometro(coroutine.create(function ()
while true do
print'so. . .'
coroutine.yield(1)
print'. . .very. . .'
coroutine.yield(2)
print'. . .sleepy!'
coroutine.yield(3)
end
end))
If you run this you will get text printed to druid with pauses in between, and the LFO will keep running simultaneously. The way this works is kind of inside out: when coroutine.resume is called, the code wrapped by the coroutine argument co gets to run until the next coroutine.yield statement. coroutine.resume returns a flag indicating whether resuming the coroutine was successful (unpacked into the ok variable) followed by any arguments that were passed to coroutine.yield – here we’re using the argument to yield to indicate the number of seconds to delay. This takes a bit of getting used to but is a very powerful tool for advanced control flow.