Find out, if there are multiple nodes on the server with the same name (and different id)

Hey,

I have a SynthDev and a Pdef playing a pattern with the SynthDev. The SynthDev’s envelope frees the Dev’s node, after release time (doneAction: 2). When the pattern repeats the SynthDev sound faster than its release time, I have obviously multiple nodes with the same name and unique id on the server.

I need to write a function to find out, if there is another node existing on a server with the same name (but different id). This function should be executed from within the SynthDev. If the answer is true, the Synth Dev should ask itself, if it is the “last” node on the server.

I need this, because I’m sending the SynthDef’s envelope via OSC. But when two SynthDef nodes are active at the same time, they both send out their envelope with the same OSC address. But I just want to send the envelope of the last activated SynthDef.

I guess I have to work with the
s.queryAllNodes
but I don’t know how to go forther…??

I’m afraid that synths on the server cannot evaluate arbitrary client-side functions directly. You might be able to send a message to the client to do it, but there will be a short lag.

An alternate approach would be to build a control into the synth – when set to zero, it suppresses the outgoing messages (multiply the trigger by it). When you’re creating a synth, first set this control to 0 on the previous one.

That is, instead of asking “is there a problem?” and correcting the problem, don’t allow the problem to occur in the first place.

hjh

That makes sense, but in my usecase I’m not sure how to implement it:
I have a SynthDef and a Pdef - how can I tell the Pdef to set a control to 0 of the previous created SynthDef?

The code simplified looks like this:

SynthDef.new(\mySynthDef, {
....
env = Env([0, 1, 0], [att, rel], [c1, c2]).kr(doneAction: 2);
	oscBridge.sendSignal(myName, env);
....
	Out.ar(out, sig);
}).add;

//Pattern:
Pdef(\pattern,
	Pbind(
		\instrument, \mySynthDef,
		\dur, 2 * Pseq([1, 0.25, 0.75], inf),
		\amp, 0.8,
		\freqA, 700,
		\att, 0.035,
		\rel, 1;
	)
).play;

The OSCBridge looks like this:

OSCBridge{
	var oscDest;

	init {
		oscDest = NetAddr("127.0.0.1", 10000);
	}

	sendSignal {
	arg address = '/test', signal, refreshRate = 60;
	// osc listener for sendReply
	OSCdef(\listener, {|msg|
		var data = msg[3..];
		oscDest.sendMsg(address, data[0]);
	}, address);
	//create sendreply
	SendReply.kr(Impulse.kr(refreshRate), address, signal, -1);
	address;
	}
}

There’s one trick which, for years, I didn’t think of – but it’s actually a very convenient way to handle this case.

We usually think of setting controls on a Synth node – but you can also set controls on a Group (which passes the control value to any nodes within the group).

If you put all of your synths into this specific group, then you don’t need to know the IDs of the exact synths that need to be deactivated. Instead:

  1. When it’s time to create a new node, first send a message slightly early to set all of the previous nodes so that they stop sending messages.

  2. Then, create the new node with normal timing (slightly later than the “stop” message, so, unaffected by it).

(
SynthDef(\boops, { |out, gate = 1, mask = 1, freq = 440, amp = 0.1, decay = 2, trigFreq = 10|
	var masterEg = EnvGen.kr(Env.asr(0.005, 1, decay), gate, doneAction: 2),
	trig = Impulse.kr(trigFreq),
	// note trig * mask: if mask == 0, then no triggers pass through
	boopEg = EnvGen.kr(Env.perc(0.01, 0.08), trig * mask),
	sig = SinOsc.ar(freq);
	Out.ar(out, (sig * (amp * boopEg * masterEg)).dup);
}).add;
)

(
p = Pbind(
	\instrument, \boops,
	\freq, Pexprand(200, 800, inf),
	\trigFreq, Pexprand(8, 14, inf),
	\dur, 1
).play;
)

p.stop;

^^ Here, each node’s decay overlaps with the next node (not what you wanted).

g = Group.new;

(
p = Pbind(
	\instrument, \boops,
	\freq, Pexprand(200, 800, inf),
	\trigFreq, Pexprand(8, 14, inf),
	\dur, 1,
	\group, g,
	\finish, {
		s.makeBundle(s.latency - 0.01, { g.set(\mask, 0) });
	}
).play;
)

p.stop;

^^ But here, the trigger mask is set to 0 before creating the new node. Once the new one starts, the older synths’ output is suppressed.

hjh

Thanks @jamshark70, that’s working perfectly.
Just a question for better understanding: Where is the \finish attribute documented? I didnt know about it. Is it an attribute of Pbind or of SynthDef? When exactly is the function defined in \finish evaluated?

It’s an attribute of the default Event prototype. It’s evaluated just before calling the play function for the event type.

hjh

So in this case it’s an attribute of the SynthDef, right?
I still can’t find anything in the documentation about it. How did you find out about it?

No, it isn’t an attribute of the SynthDef.

The Pbind generates information and puts it into an Event object.

Then, each of the resulting Events is played – .play.

The job of .play is to translate the Event data into a concrete action – usually, to play a new Synth.

\finish is part of this .play process.

So it’s the Event that evaluates \finish.

There are many event types (different actions that can be taken in response to Event data). Some of them involve synths and SynthDefs. Others do not (e.g., \type, \midi). \finish is called for all of them. So, \finish is totally independent of any and all SynthDefs.

http://doc.sccode.org/Tutorials/A-Practical-Guide/PG_08_Event_Types_and_Parameters.html#User%20function%20hooks

But I wrote this part of the documentation. I guess, where I really found out about it is from reading the class library code.

			playerEvent: (

				type: \note,

				play: #{
					var tempo, server, eventTypes, parentType;

					parentType = ~parentTypes[~type];
					parentType !? { currentEnvironment.parent = parentType };

					server = ~server = ~server ? Server.default;

////// HERE:
					~finish.value(currentEnvironment);

					tempo = ~tempo;
					tempo !? { thisThread.clock.tempo = tempo };


					if(currentEnvironment.isRest.not) {
						eventTypes = ~eventTypes;
						(eventTypes[~type] ?? { eventTypes[\note] }).value(server)
					};

					~callback.value(currentEnvironment);
				},
... snip

hjh