Ndef.xset on a \filter index retriggers the Ndef primary index. Why?

Hi,

In this little example, the .xset and .set at the level of sub-index don’t have the same behaviour. The xset is retriggering the Ndef’s primary index. Is it logic ?

~flute = Buffer.read(s, "/path/to/some/longwavefile.wav");

(
Ndef(\flute).play;
Ndef(\flute).fadeTime=1;

Ndef(\flute,{ |bufnum = 0, amp=1, pan=0|
	var sig;
	// sig = PlayBuf.ar(1, bufnum, BufRateScale.ir(bufnum),LastValue.kr(bufnum)-bufnum,0,1);//, doneAction: 2);
	sig = PlayBuf.ar(1, bufnum, BufRateScale.ir(bufnum),bufnum,0,1);//, doneAction: 2);
	sig * amp;
});
Ndef(\flute).set(\bufnum,~flute.bufnum);
Ndef(\flute).set(\wet1,0);
Ndef(\flute)[1]=\filter -> { |in| Select.ar(in.ceil, [in.abs * \amount.kr(1), in]) }
)

// activate the filter while reading the buffer. No transition. --> OK
Ndef(\flute).set(\wet1,1); 
// activate the filter while reading the buffer. With transition. --> KO, the buffer gets re-read from start.
Ndef(\flute).xset(\wet1,1); 
// deactivate the filter
Ndef(\flute).set(\wet1,0); 

Why is it so at a filter level ?

[EDIT]
Modifying the filter’s function at [1] DOES NOT retrigger the synth at [0].
So it seems unlogical that a .xset(\wet1, xyz) does.

This one is really bothering me. I couldn’t manage to make it work
Any idea on how to avoid that a xset(\wet1,1) retriggers the Ndef[0] ?

Haven’t look at this in detail, but my understanding is that xset creates two synths and cross-fades between them. Probably what happens is that the entire “chain” inside the Ndef, meaning all sources array, get duplicated in this process, which is why your base signal gets restarted. I guess Ndef would have to be “smarter” to avoid this and figure out which control belongs two which source in the array and only duplicate that one for the xfade.

I think the simple workaround is to separate your filter to a separate Ndef and use external signal routing.

Indeed. The whole chain is being duplicated. From my point of view this is a mistake.

I’ve solved this this way:

(
~xset={
	|ndef,param,value,dur|
	var from, v,to;
	var e;
	var levels=10;
	dur=dur?ndef.fadeTime;
	e=Env.pairs([[0,0],[dur,1]],\sine).asStream;
	from=ndef.get(param);
	to=value;
	{
		(levels+1).do({
			var r=e.next;
			var v=((to-from)*r)+from;
			ndef.set(param,v);
			(dur/levels).wait;
	})}.fork
};
)

with the simple usage :

~xset.(Ndef(\flute),\wet1,1,4);

I think I’m going to move this as a custom extension of the NodeProxy class.

1 Like

You could also have a look at ProxyPreset to see if that one manages the transitions better in your case, but I rather doubt it.

In which extension is it ?

final version

+NodeProxy {
	/**
	* Fluid XSET, that doesn't duplicate the whole ndef but just smoothly change the desired parameters
	* LVR: 11/12/21
	* Parameters are:
	*  - a suite of key, value. See NodeProxy set for more info.
	*  - on optianal duration representing the fadeTime. If ommitted, then the Ndef.fadeTime will be used.
	**/
	xfset {	| ... args|
		var ndef, dur;
		var values;
		var e;
		var levels=10;
		// ndef=args.removeAt(0);
		ndef=this;
		if ((args.size.odd),
			{
				dur=args.removeAt(args.size-1)
			},
			{
				dur=ndef.fadeTime
			}
		);

		values=args.reshape((args.size/2).asInteger,2);
		// values.postln;

		values=values.collect({|a| a.insert(1,ndef.get(a[0])?0)});
		// values.postln;


		e=Env.pairs([[0,0],[dur,1]],\sine).asStream;
		{
			(levels+1).do({
				var r=e.next;
				var v=values.collect({|a| [a[0],((a[2]-a[1])*r)+a[1]]});
				// v.postln;
				v.do({|a| ndef.set(a[0].asSymbol,a[1])});
				(dur/levels).wait;
		})}.fork
	}
}
2 Likes

In the obscurely name JITLibExtensions. Part of JITlib comes with SC, but the more “experimental” part, which includes that class, is only shipped as quark.

1 Like

Actually it does manage the transitions !!
However, the developers and I don’t have apparently the same use cases. So I submitted a modification of the quarks to take more cases into account.

1 Like