Hi guys,
now if you use \fliter role you might get a control name clash, becuse of same control names in different slot’s functions/synthdefs
how do you think is it possible to create such role, that would prepend the control names with a slot number by default and allow provide a custom one as an argument to ‘put’ method?
I did something like this:
Override the Symbol.kr method which creates a NamedControl parameter. Inside that look for an environment variable. If it exists append it to the name
+ Symbol {
kr { | val, lag, fixedLag = false, spec |
var name = "%%".format(this, ~num ?? {""});
^NamedControl.kr(name, val, lag, fixedLag, spec)
}
}
Then when you add a filter you can specify an environment using inEvir
Ndef(\test).filter(1, {|in| \test.kr(1); in }.inEnvir( (num: 3) ) );
Ndef(\test).controlNames.postln
Not sure if this would suffice for your situation but this doesn’t require any changes to the NodeProxy interfaces.
@droptableuser mm interesting, will look into it, thanks))
took a closer look at the \filter role’s code
looks like if we could just restructure the def function on the fly that would do it
but never done it
the code of the role if this as I got it
filter: #{ | func, proxy, channelOffset = 0, index |
var ok, ugen;
if(proxy.isNeutral) {
ugen = func.value(Silent.ar);
ok = proxy.initBus(ugen.rate, ugen.numChannels + channelOffset);
if(ok.not) { Error("NodeProxy input: wrong rate/numChannels").throw }
};
{ | out |
var env, ctl = NamedControl.kr("wet" ++ (index ? 0), 1.0);
if(proxy.rate === 'audio') {
env = ctl * EnvGate(i_level: 0, doneAction:2, curve:\sin);
XOut.ar(out, env, SynthDef.wrap(func, nil, [In.ar(out, proxy.numChannels)]))
} {
env = ctl * EnvGate(i_level: 0, doneAction:2, curve:\lin);
XOut.kr(out, env, SynthDef.wrap(func, nil, [In.kr(out, proxy.numChannels)]))
};
}.buildForProxy( proxy, channelOffset, index )
}
so if one could take the func, access the controls and redo the func, that would be it
hmm. not sure how to go about rewriting the filter func itself especially if you have an open function…but you definitely can inject variable into the filter function which you can then access to create the unique control names
One way is to inject the slot index as an envir variable
(
AbstractPlayControl.buildMethods['filter'] = #{ | func, proxy, channelOffset = 0, index |
var ok, ugen;
if(proxy.isNeutral) {
ugen = func.value(Silent.ar);
ok = proxy.initBus(ugen.rate, ugen.numChannels + channelOffset);
if(ok.not) { Error("NodeProxy input: wrong rate/numChannels").throw }
};
{ | out |
var env, ctl = NamedControl.kr("wet" ++ (index ? 0), 1.0);
if(proxy.rate === 'audio') {
env = ctl * EnvGate(i_level: 0, doneAction:2, curve:\sin);
// pass the index as an envir variable to the filter function
XOut.ar(out, env, SynthDef.wrap(func.inEnvir( (index:index) ), nil, [In.ar(out, proxy.numChannels) ]))
} {
env = ctl * EnvGate(i_level: 0, doneAction:2, curve:\lin);
// pass the index as an envir variable to the filter function
XOut.kr(out, env, SynthDef.wrap(func.inEnvir ( (index:index) ), nil, [In.kr(out, proxy.numChannels) ]))
};
}.buildForProxy( proxy, channelOffset, index )
}
)
Then you can access the index as an envir variable in your filter function
~func = {|in| "mycontrol_%".format(~index.debug("index").asSymbol.kr(1) }
Ndef(\test).filter(120, ~func);
Ndef(\test).filter(220, ~func);
Ndef(\test).controlNames
Another way would be to pass the index in the Syththdef.wrap call…
(
AbstractPlayControl.buildMethods['filter'] = #{ | func, proxy, channelOffset = 0, index |
var ok, ugen;
if(proxy.isNeutral) {
ugen = func.value(Silent.ar);
ok = proxy.initBus(ugen.rate, ugen.numChannels + channelOffset);
if(ok.not) { Error("NodeProxy input: wrong rate/numChannels").throw }
};
{ | out |
var env, ctl = NamedControl.kr("wet" ++ (index ? 0), 1.0);
if(proxy.rate === 'audio') {
env = ctl * EnvGate(i_level: 0, doneAction:2, curve:\sin);
// pass the index as an argument in the wrap function
XOut.ar(out, env, SynthDef.wrap(func, nil, [In.ar(out, proxy.numChannels), Ref(index) ]))
} {
env = ctl * EnvGate(i_level: 0, doneAction:2, curve:\lin);
// pass the index as an argument in the wrap function
XOut.kr(out, env, SynthDef.wrap(func, nil, [In.kr(out, proxy.numChannels), Ref(index) ]))
};
}.buildForProxy( proxy, channelOffset, index )
}
)
Then you can access it as an argument to the filter function.
~func = {|in, index| "mycontrol_%".format(index.value.debug("index")).asSymbol.kr(1) }
Ndef(\test).filter(320, ~func);
Ndef(\test).filter(420, ~func);
Ndef(\test).controlNames
Maybe this is getting closer. Need to see if there is away to get to the internals of the function so that it happens automatically…
A problem here is that you can read argument names from the function def, but NamedControls don’t exist until the function executes so they can’t be known in advance.
Another approach might be to buildForProxy
and then modify the control names in the resulting SynthDef, before sending to the server. After the SynthDef is built, the relationships between control names and UGen inputs are already set – so there is no longer any requirement that the control name match the argument name. (Actually that was never really a requirement – it’s rather that, in the normal SynthDef case, generating differences between arg names and control names would only add confusion, so, no point in doing it. In the NodeProxy case, there is a point.)
hjh
A quick demo – it’s a hack but it does work:
(
d = SynthDef(\test, { |out, frq = 440, amp = 0.1|
Out.ar(0, SinOsc.ar(frq, 0, amp).dup)
});
// normally we just 'add' but we can sneak in post-hoc changes
d.allControlNames[1].name = \freq;
d.add;
)
x = Synth(\test, [frq: 880]); // 440 Hz: frq was ignored
x.set(\freq, 880); // 880: but it responds to freq
x.free;
SynthDescLib.at(\test)
->
Controls:
ControlName P 0 out control 0.0
ControlName P 1 freq control 440.0 // yup, it's changed
ControlName P 2 amp control 0.10000000149012
O audio Out 0.0 2
hjh