Freeing the last synth when a pattern is stopped

In the example below,
what is the best way to free the synth at the same time the Pdef is stopped?
(without using command-period)

//Server.supernova;

Pdef(\test,
	Pbind(
        \group, ParGroup.new,
		\amp, 0.5,
		\freq, 440,
		\dur, 100
	);
);

Pdef(\test).play;
Pdef(\test).stop;

Thanks

Stopping an EventStreamPlayer does not free any of the Synths that have already been created by it. It only stops the process of creating new Synths. Your ‘dur’ value is 100, which means each Synth produced by the EventStreamPlayer will be released 100 beats after it was created. If you want to fade these Synths earlier, one option is to do it manually:

(
~grp = Group.new;

Pdef(\test,
	Pbind(
        \group, ~grp,
		\amp, 0.5,
		\freq, 440,
		\dur, 100
	);
);
)

Pdef(\test).play;

(
Pdef(\test).stop;
~grp.set(\gate, 0);
)

There might be a more sophisticated way to do this, but I find this approach to be direct and simple enough.

Eli

Not sure why I was trying to start a new group for each note, that should work fine.
Great.
ps. thanks for the online videos…

Btw the original example isn’t creating a new group per note.

hjh

Btw the original example isn’t creating a new group per note.

Oh, I don’t understand…
Wouldn’t it create a new target node each time the event is created?

No, it doesn’t.

It’s creating one new group at the time of creating the pattern object, and hard coding a reference to that one group into the pattern. Every event produced by that Pbind will keep a reference to the single group.

Similarly:

// hardcode the result of an operation into the pattern
p = Pbind(
	\a, rrand(1, 10)
).asStream;

p.next(());
-> ( 'a': 4 )

p.next(());
-> ( 'a': 4 )  // well that doesn't seem very random


// the pattern includes a reference to the operation itself
// instead of the operation's result
p = Pbind(
	\a, Pfunc { rrand(1, 10) }
).asStream;

p.next(());
-> ( 'a': 5 )

p.next(());
-> ( 'a': 2 )

The first case computes the result of rrand and puts this result into the definition of the Pbind. The second one includes the operation, but not its result, in the Pbind – so the resulting events get different numbers every time.

Note also that if you plan to have a new group per Event, then the SynthDef(s) should use doneAction: Done.freeGroup. Otherwise empty groups will leak on the server.

SynthDef(\sinblip, { |out, freq = 440, amp = 0.1|
	var eg = EnvGen.kr(Env.perc(0.01, 0.1), doneAction: Done.freeGroup);
	Out.ar(out, (SinOsc.ar(freq) * eg * amp).dup);
}).add;

p = Pbind(
	\instrument, \sinblip,
	\group, Pfunc { Group.new },
	\freq, Pexprand(200, 800, inf),
	\dur, 0.125
).play;

// 3 or 4 groups active at any time

p.stop;  // back down to 2 groups

hjh

Yes of course, that makes sense now you’ve explained it…
Thanks.

FWIW, it’s simple to free the Synth when the stream player is stopped. When .stop is called, all cleanup actions for all events are called immediately, so all you need to do is free your Synth as a cleanup action (via the \addToCleanup key).

Pdef(\test,
	Pbind(
        \group, ParGroup.new,
		\amp, 0.5,
		\freq, 440,
		\dur, 100,
		\callback, {
			|event|
			event[\addToCleanup] = { 
				event.free
			}
		}
	);
);

Q: Why do I set the \addToCleanup key in the \callback function?
A: Because \callback is called after the event is played - at this point, the Event knows about any running Synths on the server, so you can call Event:free. The event is passed in as the first arg to callback - we NEED this finished Event to call free on it.

You can make this the default behavior by doing:

Event.parentEvents.default[\callback] = Event.parentEvents.default[\callback].addFunc({
    |event|
    event[\addToCleanup] = { 
        event.free
    }
})
1 Like

I was just going to post a question about this…
Many thanks