Reusing Pattern streams in multiple Pbinds

I’m trying to wrap my brain around how to use a Pattern’s stream in multiple places across multiple Pbinds. Within a single Pbind, I could use Pkey() to access values from a Pattern associated with one key for a different key. I’d like to accomplish something like this that I could use across multiple Pbinds, preferably something that I could re-evaluate in one place, and have the (identical) changes take effect in multiple places.

Now I understand that Patterns are stateless, so assigning a nondeterministic Pattern (e.g, Pshuf) to a variable, then using that variable in multiple Pbinds results in different Pshuf permutations within each Pbind . This makes sense. But I would have thought that I could do something like:

a = Pshuf([1,2,3,4], inf).asStream;
b = a.shallowCopy;

to get two identical Routines. The Pattern may be stateless, but the stream shouldn’t be (at some point the Pshuf permutation is chosen, and it has some kind of internal iterator). But the result is two different permutations, as before.

Now I can get the effect I want by building/storing an array and putting it into a Pseq like this:

a = List[0,1,2,3].scramble;
~p = Pbind(
	\degree, Pn(Plazy { Pseq(a.()) }),

But I’m basically missing out on most of the benefits that patterns bring by doing the work myself and putting it into a Pseq at the last minute. If I wanted to re-use something like Pbjorkland1(Pwhite(2,13),16)), I’d need to roll my own Euclidean algorithm, instead of using the existing one.

Does anyone have any approaches to storing/reusing/re-evaluating Pattern output in multiple places?

See these threads, you can use PSx patterns (esp. PSdup) from miSCellaneous_lib, other options are listed there too.

1 Like

See Plambda which lets you Plet and Pget from different Pbinds as long as they are “bound” under the same Plambda. (Frankly the name for this wrapper is pretty uninspired, as it has to do with data sharing RAM-like style. Something like Pcommon would have been more fitting and Pput and Pget as they work like a common dictionary.)

Penvir with independent: false is somewhat similar but less fancy. Actually, you don’t even need use the independent :false unless you want to share across Penvirs. Contrast

e = (melody: Pshuf([1,2,3,4], inf).asStream);

(p = Penvir(e, Ppar([
	Pbind(\degree, Pfuncn({~cnote =}, inf), \dur, 0.2),
	Pbind(\degree, Pfuncn({~cnote}, inf), \ctranspose, 24, \dur, 0.2)

r = p.asStream;

r.nextN(2, ()) // hit a few times to see they are in sync

e[\cnote] // -> nil, private copy is made for Penvir by default

// in contrast...
(p = Ppar([
	Penvir(e, Pbind(\degree, Pfuncn({~cnote =}, inf), \dur, 0.2), false),
	Penvir(e, Pbind(\degree, Pfuncn({~cnote}, inf), \ctranspose, 24, \dur, 0.2), false)

r = p.asStream
r.nextN(2, ()) 

e[\cnote] // -> not nil now

Note that Pkey doesn’t work to access stuff that goes in Penvir.envir like ~cnote above. If you hate that syntactic asymmetry, and would rather have something more like Pkey, but alas still with a different name… you get Pget from Plambda.

(p = Plambda(Ppar([
	Pbind(\degree, Plet(\melody, Pshuf([1,2,3,4], inf)), \dur, 0.2),
	Pbind(\degree, Pget(\melody, repeats: inf), \ctranspose, 24, \dur, 0.2)

r = p.asStream
r.nextN(2, ()) 

(Beware that Pget only has repeat on its 3rd arg; the 2nd is the default value, unlike Pkey, which doesn’t come with that.)


You cannot actually copy Routines like that, you don’t get a real copy.

Thread : Stream {
	copy { ^this } // sorry cannot copy

Routine : Thread {
	// inherits Thread.copy 

And speaking of gotchas, if you try not to use any of the above “specials”, but think that mere Ppar combined with [P]chaining might do it, well it does work, but when using you need account for how many streams you into pull in parallel… Compare:

(p = Ppar([
	Pbind(\degree, Pkey(\melody), \dur, 0.2),
	Pbind(\degree, Pkey(\melody), \ctranspose, 24, \dur, 0.2)
])  <> Pbind (\melody, Pshuf([1,2,3,4], inf)) );

// with 

(p = Ppar([
	Pbind(\degree, Pkey(\melody), \dur, 0.2),
	Pbind(\degree, Pkey(\melody), \ctranspose, 24, \dur, 0.2)
]) <> Pbind (\melody, Pstutter(2, Pshuf([1,2,3,4], inf))) );

Only the 2nd Ppar works as intended for same-value sharing. It’s actually possible to avoid manual counting (of parallel streams) like that if using scztt’s PtimeClutch instead of Pstutter.

1 Like