Reusing Pattern streams in multiple Pbinds

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 = ~melody.next}, 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 = ~melody.next}, 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.)

Also

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