Get current argument value from Synth

Hi there,

I have this below Synth with DynKlank. I would like to retrieve what the current values are in the ‘\freqs’ argument. I tried the ‘.trace’ method, but it seems to return all the Synth’s input and output values and not just the ‘freqs’ values.

(
x = {
	x = WhiteNoise.ar * Impulse.kr(1);
	x = DynKlank.ar(`[
		\freqs.kr(({rrand(100,1e3)}!10).postln),
		1!10,
		1!10
	],x);
	x = x * Env.asr.kr(2,\gate.kr(1));
	Pan2.ar(x * -48.dbamp);
}.play;
)
x.trace(\freqs);
x.setn(\freqs, ({rrand(40,2e3)}!10).postln);
x.set(\gate, 0);

I tried to understand some relevant comments from other threads here in the forum. E.g. the one by @jamshark70 where - from what I understand - it is said that this seems to be the way the Synth object is constructed, ie. that it doesn’t store values by default, that’s why people try to refer to the Server to retrieve such information/data.

I’m not sure I understand the above linked discussion fully, but I’m wondering if there was any short hand way of retrieving current info/data from a specific argument in the Synth instead of the whole data package?

I tried to look into how to do this via registering the Synth first with the ‘.register’ method, but I only see this enabling the ‘.isRunning’ or ‘.isPlaying’ methods, not registering/retrieving arguments’ current values. But I might be wrong on this.

All-in-all, would appreciate a short-hand solution to this if there’s any. :thinking:

Thanks,
cd

Out of curiosity, in what situation would you not know the value, or have access to it?

Check the Server Command Reference – there is a /s_get command (or /s_getn for multiple values).

But…

Jordan’s point is a good one: You set the values initially using language side code – therefore language side code knows what the values are – therefore, really, the language side code could / should store those values, and then the problem of getting them from the server disappears.

A bit of a hacky way to do it might be:

(
var pairsToDict = { |defName, withDefaults = true, args|
	var argDict = IdentityDictionary.new;
	var desc = SynthDescLib.at(defName);
	
	if(withDefaults and: { desc.notNil }) {
		desc.controls.do { |cn|
			argDict.put(cn.name.asSymbol, cn.defaultValue)
		};
	};
	
	args.pairsDo { |key, value, j|
		if(key.isNumber and: { desc.notNil }) {
			key = desc.controls[key];
			if(key.notNil) { key = key.name };
		};
		if(key.notNil) {
			argDict.put(key.asSymbol, value);
		};
	};
	
	argDict
};

~newSynth = { |defName = \default, args, target, addAction = \addToHead|
	var synth = Synth(defName, args, target, addAction);
	var argDict = pairsToDict.(defName, true, args);
	
	Library.put(\synthArgs, synth, argDict);
	
	OSCFunc({
		Library.global.removeEmptyAt(\synthArgs, synth);
	}, '/n_end', synth.server.addr, argTemplate: [synth.nodeID])
	.oneShot;
	
	synth
};

~synthSet = { |node ... args|
	var argDict = pairsToDict.(node.defName, false, args);
	var d = Library.at(\synthArgs, node);
	argDict.keysValuesDo { |key, value|
		d.put(key, value);
	};
	node.set(*args);
};
)

x = ~newSynth.(\default, [freq: 100]);

Library.at(\synthArgs, x);
Library.at(\synthArgs, x, \freq);
-> 100

~synthSet.(x, \freq, 300);

Library.at(\synthArgs, x, \freq);
-> 300

x.free;

Library.at(\synthArgs, x);  // nil, gone after 'free'

hjh

This method seems to require a lot more stuff than the default, making it more practical to ask the server maybe?

One concrete difference is that asking the server is asynchronous, so, it wouldn’t be possible to use the query results in a Pfunc for instance. So it depends on the use case. If asynchronous reply doesn’t matter, then, sure.

Of “a lot more stuff” – ~newSynth.(\default, [freq: 100]) vs Synth(\default, [freq: 100]) is not that much of a burden. That is – of course – 99% of programming is abstracting complexity away into more convenient calls. If it’s very important to someone to have synchronous access to synth control values, then they could make a “has-a” wrapper for Synth that does all of this in a class definition.

I was about to say “I haven’t done this because in 20+ years, I never needed to” but then remembered – in ddwPlug, I needed to be able to access plug objects assigned to synth controls. So I ended up keeping a dictionary anyway:

x = Syn(\default, [freq: 330]);
x.argAt(\freq);
-> 330

That strikes me as even more convenient than asking the server. (Currently this doesn’t get default values from a SynthDesc, so it can report only those values from the arg list. I could add defaults if someone wants it.)

(Going forward, just for fun:)

x.set(\freq, Plug { LFDNoise3.kr(LFDNoise3.kr(0.2).exprange(0.8, 10)).exprange(50, 1000) });

x.argAt(\freq)
-> Plug[16A84514]

// only way to access the concrete Plug instance
x.argAt(\freq).source = { LFDNoise3.kr(LFDNoise3.kr(0.2).exprange(10, 35)).exprange(50, 1000) };

x.release;

hjh

1 Like

Touché. You have your point. Looks like a good direction.