Unexpected restart of Synth on Pdef reassign

I got a Synth that reads a long buffer (3’).
I’ve also defined 3 Pdef, \clean where the buffer is read without effect, \delayed where a delay is applied, \main which is one that is played and that I define as either \clean or \delayed.
I expected that switching from \clean to \delayed would simply activate/deactivate the delay, but, no, it restarts the reading of the buffer from the start. And I don’t understand why.

Can someone explain me what I should change to seamlessly activate/deactivate the delay ?

~flute = Buffer.read(s, "G:/Enregistrements/Temp/Supercollider/flute.wav");
~flute.bufnum.postln;
SynthDef(\flute, {
	var out = \out.kr(0), 
	amp=\amp.kr(1), pan=\pan.kr(0);
	var sig;
	var bufnum=~flute.bufnum;
	sig = PlayBuf.ar(1, bufnum, BufRateScale.ir(bufnum), doneAction: 2);
	sig = sig * amp * EnvGate.new(fadeTime: 3);
	Out.ar(out,Pan2.ar(sig,pan));
}).add;

Pdef(\base, Pmono(\flute, \amp, 1, 	\dur, 20 ));
Pdef(\clean, Pdef(\base));
//Pdef(\delayed, Pfx(Pdef(\base),\delay));
Pdef(\delayed, Pfx(Pdef(\clean),\delay));
)

// Create and/or empty the main pattern
Pdef(\chain, nil).quant = 0; 

// Start the main pattern. 
// ?? Should remain silence until I explicitly assign a Pdef to \chain. But it always start with the previous definition, even tough I assigned it to nil !!??
q=Pdef(\chain).play(); 

// active "Clean"
Pdef(\chain, Pdef(\clean));  // Does always restart the \flute synth from the start. Why ? I expect it to Activate/Deactivate the delay without impacting the \flute synth...

 // active "Delay" 
Pdef(\chain, Pdef(\delayed));

Every time you create a Pfx, it internally generates a new stream from your source pattern. This means that Pdef(\clean) will be asked to generate a new stream, and it will then
I don’t know if there are (and what would be the best) ways around it, but my guess is that you can achieve seamless switching using Ndef slots:

// warning: this starts immediately
Ndef(\chain, Pdef(\clean)).play
// insert fx
Ndef(\chain)[1] =  \filter -> {|in| in + CombC.ar(in, 0.2, 0.2, 1) }
// change fx
Ndef(\chain)[1] =  \filter -> {|in| distort(in*100) * Amplitude.ar(in,0.01,0.1) }
// remove fx
Ndef(\chain)[1] =  nil

Unfortunately, as far as I understand, you will have to adapt your effects from being written as SynthDefs to be written as Functions as in the example above.

Also, concerning Pdef(\chain, nil), nil gets ignored, like if you wrote just Pdef(\chain) without any argument. It is like this so that when you do, for instance, Pdef(\chain).play you don’t reset the Pdef to point to nil (since here the second argument is nil). To remove the link to Pdef(\clean) use:

  • Pdef(\chain).source = nil, which doesn’t change this Pdef’s playing state, or
  • Pdef(\chain).clear, which also stops it

Every time you create a Pfx, it internally generates a new stream from your source pattern. This means that Pdef(\clean) will be asked to generate a new stream, and it will then

Is it related to the Pfx ? Because Pdef(\chain, Pdef(\clean)); also restarts the synth from start…

In your suggestion with Ndef, I found it hard to control the effect,

Ndef(\chain)[1] =  \filter -> {|in| in + CombC.ar(in, 0.2, 0.2, 1) }

The filter is new temporary node, difficult to reach in order to modify its parameters.

The best I could achieve is this:

// v1: (+) Complete control, (-) Doesn't stop nicely
// play the synth
~sy=Synth(\flute);

// start the effect and control it
(
~fx=Synth.after(~sy,\delay);
~fxp=Pbind(\type, \set, \id, ~fx.nodeID,
	\args, #[\damp],
	\damp, Pseq([-24,-12,-6,-3,0].mirror,1).trace,	\dur,4
).play;
)

// stop the effect
~fx.release; // leads to errors on the effect EventStreamPlayer
// .. alt
(
Pbind(\type, \off, \id, ~fx.nodeID,
	\finish, {~fxp.stop} // does not work as expected, the EventStreamPlayerkeeps on running
	// \callback, {~fxp.stop} 
).play;
)

// v2:  (+) Nice, Stops nicely, (-) Only partial on the effect control. The effect stops at the end of the Pmono, and impossible to grab the effect to affect its paremeters

(
p = Pmono(\flute, \amp, 1,\dur, 5);

q = Pmono(\delay,\addAction,\addToTail,
	\damp, Pseq([-24,-12,-6,-3,0].mirror,1).trace,
	\dur,1
);
)

// play and trigger manually
r = p.play;
t = q.play;
r.stop;
t.stop;

although not ideal…

I never use Pfx because I find it too rigid. I find the most flexible way to do fx/routing/modulation is to have them side by side (in Ppar or independent Pdef) in patterns instead of enclosing them in special fx patterns

This is node order on the server which will determine to which sound the fx will be applied on

(
SynthDef(\wah, { arg out=0, gate=1;
	var env, in;
	env = Linen.kr(gate, 0.05, 1, 0.4, 2);
	in = In.ar(out, 2);
	XOut.ar(out, env, RLPF.ar(in, LinExp.kr(LFNoise1.kr(2.3), -1, 1, 200, 8000), 0.1).softclip * 0.8);
}, [\ir, 0]).add;
)

(
Pdef(\demo, 
	Pbind(
		\instrument, \default,
		\degree, Pseq((1..10),inf),
		\dur, 1/4,
		\amp, 0.1,
	),
).play;
);

// add fx
(
Pdef(\fx, 
	Pmono(\wah,
		\addAction, \addToTail,
		\lag, 0.0005,
		\dur, 1,
	)
).play;
);

// remove fx
Pdef(\fx).stop;

It’s not related to Pfx specifically, it happens all over the place. Actually I would say it’s more related to Pdef: Pdef has a single EventStreamPlayer that you access through its .play method, you can have more players using .fork, but every time you embed a Pdef in another pattern you get a new stream, “disconnected” from any other one you created before. Nevertheless, those streams get updated when you update the “source” Pdef.

However, here is another thing from Ndef world: you can add nodes and patterns to Ndef slots. Those pattern can control parameters from function in any slot, so you can control source and fx parameters from the same pattern:

Ndef(\chain)[0] = { |freq = 440| SinOsc.ar(freq) };
Ndef(\chain[10] = \filter -> { |in, amp = 0.1| in * amp };
Ndef(\chain)[100] = \set -> Pbind(\dur, 1, \freq, Pwhite(20,20000), \amp, Pexprand(0.01,1));

Here the trick is that when you create the Pbind, the Ndef needs to have fx parameters already defined (try to clear Ndef(\chain) and execute line 3 before line 2: amp doesn’t change because when you create the Pbind at line 2, the Ndef doesn’t have an \amp parameter yet).

Hope it is clear enough, and can be helpful!

1 Like

True, I got sidetracked a bit ^^

Ndef seems the most adapted to have the kind of signal-paradigm shown in the original question. Anyway I just want to add that you can use Pbindef to modify a pattern stream without resetting it

@hemiketal I feel it’s important to make sure: I hope my post didn’t come out as like saying that yours was off topic! I didn’t think you were sidetracked at all, and appreciated your contribution. I just wanted to add some to mine! :slight_smile:

1 Like

@elgiano no, your post was fine don’t worry :slight_smile: I’m watching you on twitch right now ^^

1 Like