Waiting for ("joining") a `par` spawned stream in Pspawner

Hi there, I’m using Pspawner to play a mixture of sequential and parallel streams. What I would really like would be to launch a stream using par() and then (at some later point in the Routine) wait for it to end. So, similar to what seq() does, except that it would reference a par stream, and one would be able to wait for it to end before continuing.

In the following example, I can use par for the first four, and seq for the last iteration (I know it will be the last to end playing, since they happen to all have the same duration).

~melo = Pbind(\degree, Pseq([0,4,1,-1], 4), \dur, 1/4, \octave, 4, \scale, Scale.minorPentatonic);

(
Pspawner({ |sp|
	5.do{ |i|
		var pat = Padd(\degree, i*3, ~melo);
		i.debug("starting");
		if (i == 4) { sp.seq(pat) } { sp.par(pat); sp.wait(1); };
	};
	"Done parallel streams".postln;
}).play;
)

Another option would be to just put a wait of the correct duration, as in the next example. However, having to know the overall duration of every Pattern you schedule would be a nuisance (and possibly not knowable, e.g. if randomness is involved)!

(
Pspawner({ |sp|
	var meloStreams = 5.collect{ |i|
		var pat = Padd(\degree, i*3, ~melo);
		var stream;
		i.debug("starting");
		stream = sp.par(pat);
		sp.wait(1);
		stream
	};
	sp.wait(3); // have to figure this duration out for yourself
	// or can stop par streams explicitly (possibly before they're done playing)
	// meloStreams.do(sp.suspend(_));
	"Done parallel streams".postln;
}).play;
)

It would be nicer to launch a bunch of par streams (stored in an array) and then wait for all of them to end (just waiting for a specific one to end would be enough, you could loop on them)… kind of like join on a thread in other languages.

Is there a way to do this now?

Thanks,
Glen.

1 Like

this is the only way i could think to do it…

~melo = {|rep| Pbind(\degree, Pseq([0,4,1,-1], rep), \dur, 1/4, \octave, 4, \scale, Scale.minorPentatonic) };
(
Pspawner({ |sp|
    
    var num = 5;
    var count = 0;
    
    NotificationCenter.register(\a, \b, \c, {
        if (count == (num-1)) {
            "Done parallel streams".postln;
        };
        count = count + 1;
    });
    
    num.do{ |i|
        var pat = Padd(\degree, i*3, ~melo.( rrand(4, 8).debug("rep") ));
        i.debug("starting");
        sp.par( Pseq([ pat, Pfuncn({ NotificationCenter.notify(\a, \b); nil }, 1) ], 1) );
    };
    
    inf.do({
        // keep alive
        sp.wait(1)
    });
}).play
)

This would work (but it might require rethinking your design a bit):

(
s.waitForBoot({
	var pat;
	~melo = {|rep| Pbind(\degree, Pseq([0,4,1,-1], rep), \dur, 1/4, \octave, 4, \scale, Scale.minorPentatonic) };
	pat = Pspawner({ 
		|sp|
		
		var meloStreams = 5.collect{ |i|
			var pat = Padd(\degree, i*3, ~melo.(rrand(4, 8)));
			var stream;
			i.debug("starting");
			stream = sp.par(pat);
			sp.wait(1);
			stream
		};
	});
	Pseq([
		pat, 
		Pfunc {"Done parallel streams".postln; nil}]).play; 
});
)

Thanks to you both, good ideas! I hadn’t thought of using a Pspawner inside another one. A simpler version of what you suggest, @shiihs, would be like this (which is pretty nice and without much extra code or management):

(
~melo = Pbind(\degree, Pseq([0,4,1,-1], 4), \dur, 1/4, \octave, 4, \scale, Scale.minorPentatonic);

Pspawner({ |sp|
	var meloPat = Pspawner({ |sp|
		5.do{ |i|
			var pat = Padd(\degree, i*3, ~melo);
			i.debug("starting");
			sp.par(pat);
			sp.wait(1);
		}
	});
	sp.seq(meloPat);
	"Done parallel streams".postln;
}).play;
)

(It would still be nice to be able to sp.wait() on a previously saved par stream, though. :wink: )

Have you tried it out @totalgee?

Your call sp.seq will start playing 1 second after the last parallel stream is started, not when all parallel streams have finished.

Yes, I did indeed try it before posting here, and (to my surprise) it worked correctly. Did you try it and find otherwise? The sp.seq call will actually be called before the par and wait and stuff above, since that’s only defining a Pattern that hasn’t been played yet, then the seq will play it and wait for it to end. So, I was kind of surprised at first, but in the end it makes sense.

I did try it out and it didn’t work for me.

But just now I realize I misread your code (you have 2 Pspawners now!) and I initially tried something different (putting sp.seq inside a single Pspawner).

Your solution is ok!

1 Like