Newbie question: returning value from Synth.get to variable

Hello all, I feel like I must be missing something obvious, so apologies in advance.

I’d like to be able to programmatically check the value of a running synth’s control, i.e. in the context of a UnitTest. I can easily check the control value visually using .postln, but I am struggling to save the value to a variable!

(
SynthDef.new(\testGet, {
    | ctrl1 |
}).add;
);

~testSynth = Synth(\testGet, [\ctrl1, 99]);

// visual confirmation
~testSynth.get(\ctrl1, { | val | val.postln});
// 99.0

// but how can I save val to a variable?
~x = ~testSynth.get(\ctrl1, { | val | val });
// Synth('testGet' : 1000)

~x = ~testSynth.get(\ctrl1, { | val | val }).value();
// Synth('testGet' : 1000)

~testFunc = ~testSynth.get(\ctrl1, { | val | val});
~testFunc.value();
// Synth('testGet' : 1000)

// desired functionality, e.g. within a unit test
//...
//var val = testSynth.get(\ctrl1, | val | val });
//this.assertEquals(val, 99.0, "value of synth control should be 99");
//...

Thanks!

Assign inside the completion:

~testSynth.get(\ctrl1, { | val | ~x = val });

Thanks Josh, that totally does it.

As a follow up question, can you help me understand the pattern I’m seeing below where this works on a global variable (with ~x), but not on local variable (var y)? Surely I’m missing something about how Environments and scope work in SuperCollider…

s.reboot;

(
SynthDef.new(\testGet, {
    | ctrl1 |
}).add;
);
~testSynth = Synth(\testGet, [\ctrl1, 99]);

(
~testSynth.get(\ctrl1, { | val | ~x=val });
(~x == 99.0).postln;
~x.postln;
);
// true
// 99.0

(
var y;
~testSynth.get(\ctrl1, { | val | y=val });
(y == 99.0).postln;
y.postln;
);
// false
// nil

Single letter variables are also environment variables - in general, I don’t use them because scope can sometimes be confusing.
Try giving it a new like
var myPolledVal;

Then

myPolledVal =

In the block. Then, anything within the code block that var is scoped for (basically, the parens that create the code block the var is within) will have access to it.

If you need things outside that code block to have access to that memory, then you can use the ~ environment variable approach (which I avoid, but many others use quite well).

~myEnvironmentVar = val;

Inside the completion message would store the value to the environment var.

Also - keep in mind the get completion is asynchronous - it takes time to ask the server for the value and to set it - in your code block, the code runs for your postln before you get the value back.

Thanks again Josh, appreciate all your help explaining this. I think failing to account for asynchronous timing was probably the primary culprit, but using the single letter var was definitely sloppy and made it easy to mistake whether I was really isolating local vs. global variables. Here’s my working example, in case it can help someone else in the future.

s.reboot;

(
var yy;
SynthDef.new(\testGet, {| ctrl1 |}).add;
fork {
    0.1.wait;
    ~testSynth = Synth(\testGet, [\ctrl1, 99]);
    0.1.wait;
    ~testSynth.get(\ctrl1, { | val | yy=val });
    0.1.wait;
    (yy == 99.0).postln;
    yy.postln;
};
);

I think using s.sync would be better:

(
fork {
	var setVal = 99, gotVal;
	SynthDef.new(\testGet, { |ctrl1| }).add;
	s.sync;
	~testSynth = Synth(\testGet, [\ctrl1, setVal]);
	s.sync;
	~testSynth.get(\ctrl1, { |val| gotVal = val });
	s.sync;
	(gotVal == 99.0).postln;
	gotVal.postln;
};
)
1 Like