Why is the synth argument set by a control bus not reflected when getting the value using the method '.get'?

Dear users and developers,

I have just realised the synth argument set by the control bus is not reflected when retrieving the value using the method ‘.get’ as in the code below.

Is it intended? It is a bit confusing for me.

(
SynthDef(\source, {
	|outBus, amp=0.2|
	Out.ar(outBus, WhiteNoise.ar(amp))
}).add;

SynthDef(\hpf, {
	|outBus=0, inBus, freq=5000|
	Out.ar(outBus, HPF.ar(In.ar(inBus, 1), freq, 1))
}).add;

~bus_hpf_snd = Bus.audio(s, 1);
~bus_hpf_ctl = Bus.control(s, 1).set(220)
)

(
~source = Synth(\source, [\outBus, ~bus_hpf_snd]);
~hpf = Synth.after(~source, \hpf, [\inBus, ~bus_hpf_snd])
)

~hpf.get(\freq, { |value| ("argument \\freq is set to" + value + "Hz").postln; });
// returns:
// -> Synth('hpf' : 1103)
// argument \freq is set to 5000.0 Hz // <- expected

// now mapping control bus to ~hpf:
~hpf.map(\freq, ~bus_hpf_ctl)
// returns:
// -> Synth('hpf' : 1103)

~hpf.get(\freq, { |value| ("argument \\freq is set to" + value + "Hz").postln; });
// returns:
// -> Synth('hpf' : 1103)
// argument \freq is set to 5000.0 Hz // <- I expect here 220.0 Hz set by the control bus ~bus_hpf_ctl.


~bus_hpf_ctl.set(1000)
// returns:
// -> Bus(control, 10, 1, localhost)

~bus_hpf_ctl.get
// returns:
// -> Bus(control, 10, 1, localhost)
// Bus control index: 10 value: 1000.0.

~hpf.get(\freq, { |value| ("argument \\freq is set to" + value + "Hz").postln; });
// returns:
// -> Synth('hpf' : 1103)
// argument \freq is set to 5000.0 Hz // <- I expect here 1000.0 Hz set by the control bus ~bus_hpf_ctl.


~hpf.set(\freq, 10000);
// returns:
// -> Synth('hpf' : 1103)

~hpf.get(\freq, { |value| ("argument \\freq is set to" + value + "Hz").postln; });
// returns:
// -> Synth('hpf' : 1103)
// argument \freq is set to 10000.0 Hz // <- expected

Good catch!

It looks like when a Control is mapped, that the value of the control preserved.

a=Synth(\default,[\freq,500])

a.get(\freq,_.postln) //500.0
a.map(\freq,b=Bus.control.set(200))
a.get(\freq,_.postln) // still 500.0
a.map(\freq,-1) // unsets the map - 500 again (is this in the docs anywhere? )

So the behavior is value = valueOfMappedBus ? value .

As far as getting the current value it looks like the Node doesn’t “know” the value of the bus it is mapped to.

But you can always just check the value of the bus in that case.

I don’t see any way to get information about whether a control is mapped from the server so I think you will have to keep track on the language side (someone may know more about this!)

Thank you for your answer!

The reason seems to be due to the ontological property of the class Bus.
It is a representation of a bus on the server, so a control bus directly controls the mapped argument on the server, but it does not set the instance of the Synth class, which is connected to the server-side node after having created it. Thus, the synchronisation of the two mentioned values is not automatically done. My question shows that I confused the server-side operation and client-side operation. The client-side instance of the Synth class cannot know the changed value on the server.

One more thing:
While testing it, I realised that the mapping of the control bus is automatically released when setting the argument on language:

a=Synth(\default,[\freq,500])

a.get(\freq,_.postln) //500.0
a.map(\freq, b=Bus.control)
{ Out.kr(b, MouseY.kr(250, 1000, 1)) }.play
a.get(\freq,_.postln) //500.0
a.set(\freq, 330) // moving the mouse pointer does not affect.
a.map(\freq, b) // moving the mouse pointer affects again.
1 Like

I don’t think the issue is server/language sync. Calling .get on a Synth instance sends an osc message to the server asking for the current value of some key of the node at the Synth instance’s nodeID. So when you have mapped \freq to a bus in my example, when you send the \get message to the node it still sends back ‘500.0’. That is the state of the node reported by the server. So the problem is not lack of sync. The problem is that the node is not somehow monitoring the value of the control bus and updating its own instance variables with those values - it doesn’t ‘know’ the value of the control bus and can’t report it to us - and sadly even the fact that it is mapped seems to be inaccessible to us. When we unmap by sending a \map message with bus number -1 it reverts to using its current saved value ‘500.0’. I could be wrong but that’s my sense (without looking at the server code!) of what’s going on.

check out the “server command reference” for more insight

1 Like

Oh, my supposition was wrong!
You seem to be right! I now agree to your opinion.

However, It would be nice if someone could explain this phenomenon one more!

In general, I think that we should not rely on server queries to get the state of nodes and groups, but rather track it in the client. John Murphy on this board identified one case where there really is no way except for querying – namely, recovering from an interpreter crash where nodes are still running on the server. I agree with him that server queries are needed here – and the current case (that s_get doesn’t return anything about control bus mapping) is a gap in query-ability which might need to be addressed.

With that said – if a node control is mapped to a bus, it’s because the user mapped it – therefore it’s the user’s responsibility to be aware of this.

Here, the SC class library has a design flaw in that we rely a lot on a lightweight class Synth for node messaging, but we need a class that handles messaging and tracks state. Synth itself maintains no state by default – you don’t even know if it’s still running or not, unless you .register-ed (not the default behavior). It doesn’t keep argument values or bus mappings, or its position in the node tree.

Because it doesn’t keep any of these things, users then think “I should ask the server,” and then run into cases like this where you can’t get all the information from the server. At that point, the assumption is that it’s the server’s fault – but I disagree here. It’s rather (or, also) that Synth is not a sufficient abstraction for people’s real needs … but we keep using it.

NodeProxy does keep track of any controls that have been set (.nodeMap). This is probably too heavy for every use, but it shows how it can be done without server queries. When you set or map a NodeProxy control, an entry gets saved into the nodeMap and you can use this to access the current state.

So another approach would be a lighter-weight class that lives in between Synth and NodeProxy – more thorough state tracking, but it wouldn’t need NodeProxy’s multiple slots and auto-reshape features.

hjh

2 Likes