Collect into function Node Proxy Arguments

Hello, I am switching the function of an Ndef programatically, doing something like this

Ndef(\sdm, ~sdmFunc.choose);

Where ~sdmFunc is an array containing various functions with ugens generating sound, and I was wondering if there is a method which can allow me to assign control values to all arguments of my ndef. For example, some pseudo code to elaborate a bit more

Ndef(\name).params = rrand(0.1, 1.0);

Thanks

You can do something like:

Ndef(\sdm).source.argNames.do{|n| Ndef(\sdm).set(n, rrand(0.1, 1.0))}

Might make more sense to use Spec to define the ranges, unless all args are designed to take a normalized value.

1 Like

Yes, for the future reader.

Spec.add(\par1, [120. 1220.0]);
~specs = (par1: par1.asSpec)
Ndef(\sdm).source.argNames.do{|n| Ndef(\sdm).set(n, ~specs[n].asSpec.map(rrand(0.1, 1.0)))};

This seems to be working, not sure the most elegant but seem okay.

Why doesn’t it work to just use Ndef(\sdm).set(<the control values here>) and then you do your switching with Ndef(\sdm, ~sdmFunc.choose)? The control values should automatically map to your synth that comes from the function.

I think this is a question of efficiency. Arguments are not universal in all functions meaning different functions embed different parameters. Without checking first which arguments are inside the function it will mean that each time running just .set will try to change parameters that doesn’t exist imo.

Spec.add(\freq, [20, 2000]);
Spec.add(\amp, [0.1, 1.0]);
Spec.add(\pan, [-1.0, 1.0]);

~specs = (
    freq: \freq.asSpec,
    amp: \amp.asSpec,
    pan: \pan.asSpec
);

~sdmFunc = [
    { |freq, amp, pan| SinOsc.ar(freq, 0, amp) * pan },
    { |freq, amp| Saw.ar(freq) * amp },
    { |freq| Pulse.ar(freq) }
];

Then, would setting parameters dynamically sort work? Or some macro-like code generation hack? (not tested, it’ s sort of pseudo-code)


// Function to set parameters based on available arguments
~setParams = { |ndef, func|
    func.argNames.do { |arg|
        if (~specs[arg].notNil) {
            ndef.set(arg, ~specs[arg].map(rrand(0.1, 1.0)));
        }
    };
};

// Switch the Ndef function and set parameters
Ndef(\sdm, {
    var func = ~sdmFunc.choose;
    ~setParams.(Ndef(\sdm), func);
    func.value;
});

EDIT: I don’t use much Ndef or specs, but I think referring to Ndef(\sdm, ...), choosing a function that will kind of " pattern match" the elements in~setParams, will set only params that exist in that one function, and avoiding potential errors. Code I posted (not tested, sorry) can have mistakes, but the idea makes sense to me if you want to experiment a little with that.

1 Like

I wouldn’t care about efficiency too early. If the paramters are set, you don’t need to set them again, new functions pick them up. And if you set a parameter that the synth doesn’t have, it won’t do much harm, and they stay around for a future synth to pick up.

When you need high efficiency, you better use setGroup(<params>), also there you don’t have to care about non-matching control names.

Just to clarify, does this mean it needs to hardcode for each parameter?

Ndef(\sdm).set(\par, \par.asSpec.map(rrand(0.1, 1.0)));

Or adding another function which collects them like this:

~specs.keys.collect{|n| Ndef(\sdm).set(n, ~specs[n].asSpec.map(rrand(0.1, 1.0)))};

`Yes, that makes sense. I don’t know why I didn’t write this. But I guess that fits what you want in general? If you start exploring code generation, there are a lot of possibilities.

1 Like

No need for macros here, you can just roll some functions.




f = [
	{ var freq = \freq.kr(440, spec: \freq); Blip.ar(freq) * 0.1 },
	{ var freq = \kreq.kr(440, spec: ControlSpec(2, 45, \exp, 0.5)); Blip.ar(freq).squared * 0.5 }
];

Ndef(\x, f.choose).play;

Ndef(\x).specs; // look at the specs

~setMapped1 = { |proxy, key, val| var spec = proxy.specs.at(key); if(spec.notNil) { proxy.set(key, spec.map(val)) } };
~setMapped = { |proxy ... pairs| pairs.pairsDo { |key, val| ~setMapped1.(proxy, key, val) } };


~setMapped.(Ndef(\x), \freq, 0.5, \kreq, 0.2);
fork { 5.do { ~setMapped.(Ndef(\x), \freq, exprand(0.01, 1), \kreq, exprand(0.01, 1)); 0.2.wait } };




It may be useful to have node proxy setMapped as a method, I’ll think about that.

1 Like

It’s way more idiomatic, for sure. That’s why I wrote the example in a lisp, because I don’t know how that would be in sc ))

Again, this verges on the off topic, at least as far as the title of the topic goes. Now @smoge what about this: make a new one with this interesting theme of macro-thinking and link back to the current one?

The topic-drifting is much less straining if you do it yourself, but for the others it may feel like someone taking over the steering wheel and driving into the bush. I kind of like it, but only when it is not enforced on others. We should have a special category of topic for that (maybe like the cadavre exquis / code tennis ones).

1 Like

@julian I only know one other member who ever discussed macros with me in this forum, and he seems to have left. It would make sense otherwise, and I would not feel like I’m “pushing” something into the group. I think I was into the limits of the discussion since the idea was in the air.

(I suspect you misunderstood my sense of humor; I did NOT suggest anything to anyone)

Sounds like a great candidate for an off-topic twist on Emaaaacs (swoosh sound) editors tab (un)assuming this exists.

1 Like

Do you mean this can be added as a feature/future attribute in Node Proxies, that would be great addition to the already powerfull ecosystem of JIT.

yes, but not yet well defined. It is unclear what should happen with an old setting if you change or even remove the spec from the inside. Also a proxy may have settings that partly have been set with spec, others without.