cl-patterns icon indicating copy to clipboard operation
cl-patterns copied to clipboard

Instantaneous tempo changes

Open ntrocado opened this issue 1 year ago • 3 comments

Is it possible to have a new tempo start immediately (restarting/resyncing the clock), instead of only changing on the next beat?

ntrocado avatar Jul 03 '22 19:07 ntrocado

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.

defaultxr avatar Jul 04 '22 01:07 defaultxr

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...

ntrocado avatar Jul 04 '22 10:07 ntrocado

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.

defaultxr avatar Jul 04 '22 23:07 defaultxr