[PD] achieving smooth tempo change

Roman Haefeli reduzent at gmail.com
Thu Feb 11 09:44:10 CET 2016


Hi Liam

A thread that covers the same topic:
http://lists.puredata.info/pipermail/pd-list/2016-01/112812.html

On Thu, 2016-02-11 at 05:14 +0000, Liam Goodacre wrote:
> Any DAW or sequencer has a global setting which allows you to change
> the tempo smoothly across all elements. But fool around on PD for a
> while and you will realize that this is not a simple operation. If you
> have more than one time object going at once, it is very hard to keep
> them in time with each other while changing the period.
> 
> Consider the example of two metros set to 500 and 1000 ms, banging in
> synch with one another. The "cold" inlet of both are connected to a
> float, with the second one being multiplied by 2 along the way. I'm
> sure you don't even need to try this to know that the two metros will
> quickly faze out quite if you scroll through the value. The metro
> duration updates only  after the  cycle is complete, so one updates
> before the other and it quickly gets chaotic, especially if you drag
> the value for more than one complete metro cycle. Of course you could
> keep them in time by using a trigger to reset the metros, but this is
> not good from a musical perspective, because I don't want to go
> restart the bar every time I change the tempo. Another possibility is
> to store the new time value and load it only when both cycles
> coincide. This should keep both metros in time, although the
> transition wouldn't be very smooth.

Checkout the help of [delay] (>= Pd 0.45). It illustrates a new 'tempo'
method that tackles this exact problem, i.e. using 'tempo' allows to
change time intervals in the middle of a running interval. The same
method can be used for [metro] and [timer], too. 


> [line] works differently, in that the time is specified in the "hot"
> inlet. But this also doesn't update smoothly if you interrupt it with
> a new message before the cycle has finished, because the new value
> sent to it will take the last point as a starting point. The total
> duration of its cycle will be {elapsed time} + {new time}, which isn't
> very good if you want to change tempo smoothly. I worked on trying to
> solve this for a while, and came up with a prototype solution which I
> am attaching. It works by taking the new time and subtracting it from
> elapsed time for the new value, so that the line finishes its cycle as
> if the new time value had been given at the beginning. The patch works
> reasonably well for one iteration, but completely fails for more than
> one (ie. if you scroll through the time values). I still haven't
> figured out why.

I think you're on the right path. If an update happens, measure the time
since the start of the ramp, calculate the current position, generate a
new ramp that starts at the current position and takes the proportional
rest of the time interval to complete. I cannot think of another way to
achieve that.

I think this is supposed to work even when updating more than once
during one ramp.

> Before I open up for suggestions, here are a few other points:
> 
> 1. The simplest solution to this would be to have one and only one
> time object, set to say 1/16th beat or whatever is the smallest time
> interval you want, acting globally on all other sequenced events. This
> would work simply and elegantly, but unfortunately it is not an option
> for my project. Keeping time locally is essential to me, and I would
> sooner give up tempo change altogether than give this up.

And it doesn't work well, if you want to have many different ratios of
beats in parallel. Soon your common partial interval will be so small,
that [metro] significantly hogs the CPU. If you're interested in keeping
many different timing ratios in sync, checkout master.pd and
abs/master-poly.pd from https://github.com/reduzent/netpd-instruments/ .
While [master] provides the master clock, any instance of [master-poly]
can derive any arbitrary integer ratio time interval for it (for
instance, [master-poly 9 5] would create 9 ticks withing the time of 5
master ticks).

> 2. Another option would be to use the [timer] object, either as an
> alternative to the others, or to help calculate feedback for them. I'm
> hesitant to do this because a: it seems like it would require more CPU
> and b: it it seems like an empirical solution to a deterministic
> problem.

I don't know how this is a deterministic problem. You don't know when
the user decides to make a tempo change. So you're left with measuring
the time elapsed since ramp start or last interval or whatever. If you
know beforehand, when a tempo change is going to occur, then you don't
need [timer] to get elapsed time.

>  My intuition says that this
> problem can be solved mathematically, without resorting to
> measurement. However, I am willing to be convinced that this is the
> right way to go.
> 
> 3. I assume that [line] and [line~] will work identically in this
> regard,

No, [line~] does start and end ramps exactly on block boundaries, while
[vline~] is sub-sample precise. Only [vline~] takes into account the
precision of [metro] and [delay]. For the things you're working on, I
strongly suggest using [vline~], since [line~] would cause all kinds of
glitches.

>  and that [delay] and
> [metro] will work similarly. But [line] and [delay] work in very
> different ways, and so the solution, if it exists, is likely to be
> quite different for these objects. Ultimately I want to find a
> solution for all time objects, so I'm willing to hear anything you've
> got! 

Checkout the 'tempo' method of [metro], [delay], [timer].

Roman


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: This is a digitally signed message part
URL: <http://lists.puredata.info/pipermail/pd-list/attachments/20160211/16965cbb/attachment-0001.sig>


More information about the Pd-list mailing list