A useful pattern that doesn't have a class: Pkey(\dur).integrate (like Ptime but doesn't "keep running" for paused streams)

I often found myself writing a like-Ptime-but-doesn’t-add-pause-time idiom.

Basically the problem I had with Ptime is that it “keeps running” even when a stream is paused, e.g.

Pdef(\tt, Ptrace(Pbind(\trt, Ptime()))

fork { Pdef(\tt).play; 3.wait; Pdef(\tt).pause; 3.wait; Pdef(\tt).resume; }

You get something like

( 'trt': 0.0 )
( 'trt': 1.0 )
( 'trt': 2.0 )
( 'trt': 6.0 )
( 'trt': 7.0 )

I.e. Ptime “counts” the stream while is paused too.

The basic idea is, of course, to just add the time played. Ignoring variable duration (for now), i.e. just “counting events” is something like

Pdef(\tt, Ptrace(Plazy({var td = 0; Pbind(\trt, Pfuncn({td = td + 1}, inf))}))

As it turns out, there’s an “classless” pattern that does this kind of summing already, Pattern.integrate:

Pdef(\tt, Ptrace(Pbind(\trt, Pseq([1], inf).integrate)));

And to add the actual durations (instead of just counting each event as 1):

Pdef(\tt, Ptrace(Pbind(\trt, Pkey(\dur).integrate)))

Interestingly, a google search for Pkey(\dur).integrate didn’t return any real hits, so I’ve decided to post this “discovery”.

1 Like

As a bit of an aside here: you cannot restart/resume Clocks in SC. Once a Clock is stopped it’s “stopped forever”. You can do somewhat kludgy manipulations of beats, i.e. subtracting from them (e.g. the pause time) which can “make pretend” you’ve stopped a Clock for a while (and the resumed it), but the whole mechanism is quite brittle because there are quite a few sources that restore the beats from the current time (see the help page).

Actually there is a class for it: Pseries.

It’s often assumed that Pseries’s step should be a constant, but that isn’t the case. If it’s a pattern, then it behaves as an integrator.

There’s a slight difference: .integrate returns the values after adding each value, while Pseries returns the value before adding (starting at an arbitrary value that you specify). For the use case stated at the top of this thread, Pseries(0, Pkey(\dur), inf) is probably a bit better.


1 Like

By the way, in case you wonder what I’m using it for, it’s something like

Pdef(\p, Pbind(\amp, Penv([0.1, 1, 0.1], [3, 3], \exp), \dur, 6/20));

(fork {
	Pdef(\p).resume; // no coda

Whereas with

~env = Env([0.1, 1, 0.1], [3, 3], \exp);
(Pdef(\p, Pbind(
	\dur, Pn(~env.times.sum / 20, 20),
	\env, Pfuncn({~env}, inf), // protect Env from asStream
	\amp, Pbinop(\at, Pkey(\env), Pkey(\dur).integrate))))

it resumes from where it was at (using the same fork). The “fork” with fixed times is here to demonstrate the problem, normally the play/pause/resume are live/GUI events.