onEnded function for patterns

I’m trying to trigger some action when a pattern has finished. In my example: stop a 2nd pattern from running and playing:

(
x=Pbind(\dur,Pwhite(3,5),\freq,Pn(440,4));
y=Pbind(\dur,Pwhite(1,5),\degree,Prand(Scale.minor.degrees,inf));
x.play;
y.play;
)

when x has finished, I’d like to stop y and release any synth running from that pattern.

Is there a way to attach some kind of “onEnded” function to the x pattern similar on the onFree of the Synth object ?

Today, I’m able to stop any running Synth but not the pattern:

(
~last=nil;
x=
Pseq(
	[Pbind(\dur,Pwhite(3,5),\freq,Pn(440,4)),
		Pbind(\type,\off,\id,Pfunc({~last['id'][0]})) // <-- Add an extra Step to release the last Synth played under y.
	]
);
y=Pmono(\default,\dur,Pwhite(1,5),\degree,Prand(Scale.minor.degrees,inf));
x.play;
y.collect({|event| ~last=event; }).play; // <-- remember the last played event, hence the last played Synth
)
1 Like

I still haven’t found a real “onEnded” for patterns, but I’ve got kind of a solution:

(
~last=nil;
~stream2=nil;

x=Pseq(
	[Pbind(\dur,2,\freq,Pn(220,2)),
		Pbind(\type,\off,\id,Pfunc({~stream2.stop;~last['id'][0]}) // <-- release last synth and stop the pattern
			,\dummy,Pn(1,1)) // <-- to have this Pbind running only once
	]
);
//y=Pmono(\default,
y=Pbind(
	\dur,3,\degree,Prand(Scale.minor.degrees,inf));
x.play;
~stream2=y.collect({|event| ~last=event; }).play;
)

But if someone has more info or another approach for a ´´´pattern.onEnded´´´ I’m still interested…

This is possible, but admittedly well-nigh impossible to figure out only from the documentation.

When you play a pattern, you get an EventStreamPlayer – this is the object scheduled on the clock, which gets events from the stream and plays them.

So, point #1 – it’s impossible to have ‘onEnded’ for patterns. Patterns define behavior, but don’t execute it. Only the “pattern performer” can end – so the responder would have to be installed on the performer instead.

Then… EventStreamPlayer is a subclass of PauseStream. PauseStreams (including Task) use dependent notifications to communicate their state. This is documented briefly in the Task help file, but without an example.

So…

(
// you can't do any of what you're asking about
// without variable references to the players!
var xPlayer, yPlayer;
var updater;

x = Pbind(\dur, Pwhite(3, 5), \freq, Pn(440, 4));
y = Pbind(\dur, Pwhite(1, 5), \degree, Prand(Scale.minor.degrees, inf));

xPlayer = x.play;
yPlayer = y.play;

updater = SimpleController(xPlayer)
.put(\stopped, {
	updater.remove;
	yPlayer.stop;
});
)

hjh

3 Likes

Or, another way (probably easier):

(
var yPlayer;

x = Pbind(\dur, Pwhite(3, 5), \freq, Pn(440, 4));
y = Pbind(\dur, Pwhite(1, 5), \degree, Prand(Scale.minor.degrees, inf));

x = Pfset(pattern: x, cleanupFunc: { yPlayer.stop });

x.stretch(0.2).play;
yPlayer = y.stretch(0.2).play;
)

And with early note release for y:

(
var yPlayer;
var yGroup = Group.new;

x = Pbind(\dur, Pwhite(3, 5), \freq, Pn(440, 4));
y = Pbind(\dur, Pwhite(1, 5), \degree, Prand(Scale.minor.degrees, inf));

x = Pfset(pattern: x, cleanupFunc: {
	yPlayer.stop;
	yGroup.set(\gate, 0);  // release synths
	{ yGroup.free }.defer(0.5);  // depends on synth release time
});
y = Pbindf(y, \group, yGroup);

x.stretch(0.2).play;
yPlayer = y.stretch(0.2).play;
)

hjh

2 Likes