cl-patterns
cl-patterns copied to clipboard
Instantaneous tempo changes
Is it possible to have a new tempo start immediately (restarting/resyncing the clock), instead of only changing on the next beat?
In theory an event with :quant 0
should be processed immediately (and not quantized), but it looks like that is not what actually happens. I'll try to fix that soon.
I was thinking more on having e.g. (tempo 60/60)
changing tempo immediately instead of scheduling it for the next beat. I'm making a "tap tempo" thing and this would be useful, but also on other situations. I looked on the internals and I don't think this is currently possible. Another way of getting the same result would be to kill the current clock and start a new one, but the new one would have to keep the state (patterns playing) of the old one...
Well, (tempo 60/60)
is basically just shorthand for (play (event :type :tempo :tempo 60/60))
. So if you want a tempo change to be immediate rather than occurring on the next beat, you could just do (play (event :type :tempo :tempo 60/60 :quant 0))
instead. This doesn't work at the moment, though, since :quant 0
--at least for tempo events--is broken. I'm intending to refactor the clock code soon though so once that is done I will ensure all :quant 0
events are processed immediately.
You're correct that it's not currently possible, at least through cl-patterns' exported API. The clock keeps track of time by recording the beat and timestamp of the last tempo change (the timestamp-at-tempo
and beat-at-tempo
slots), so those slots have to be updated as well when changing the tempo. I played around with this a bit and it seems like this function may do what you're asking:
(defun immediate-tempo-change (new-tempo &optional (clock *clock*))
"Immediately change the tempo of CLOCK to NEW-TEMPO.
Note that this function may stop working in future versions of cl-patterns, after which the following should be used instead:
(play (event :type :tempo :tempo new-tempo :quant 0))"
(let ((beat (beat clock)))
(setf (slot-value clock 'cl-patterns:tempo) new-tempo
(slot-value clock 'cl-patterns::timestamp-at-tempo) (local-time:now)
(slot-value clock 'cl-patterns::beat-at-tempo) beat
(slot-value clock 'beat) beat)))
It's possible that this function could result in clock glitches, but just from testing a bit it seems to work.
As its docstring says, this function may stop working in the future, once I finish the clock refactor, but after that point just using :quant 0
in a tempo change event should work as expected instead.