Note: earlier version was exhibiting a bug with NdefMixer; likely it would have happened with explicit use of ProxySpace as well, although I’ve stayed away from using that one explicitly myself insofar. I’ve added a check for that now in the first method below (Symbol.mangle
).
I’ve got the NamedControl pre-mangling duplication check solved, i.e. now it’s a post-mangling check as it should be. It was quite a bit of code to change for that. Basically it needs to flag whether the mangling happened already in NamedControl.
+ Symbol {
mangle {
if(currentEnvironment.notNil) {
if(~controlNameMangler.notNil) {
// This is mostly a fix for NdefMixer which creates ControlNames
// while use()-ing ProxySpace. And in ProxySpace every environment
// variable key is mapped to a non-nil value! Furthermore, default
// values differ between keys, so can't check even "==" equality
// with the value mapped to a "random" key. But it's reasonably safe
// to check that a random key is mapped to a non-nil value, to
// detect proxy spacess.
//this.dumpBackTrace;
if(~aiospd345fjaiohtgXXO.isNil) {
//("cnm:" + ~controlNameMangler + "this:" + this).postln;
^~controlNameMangler.value(this).asSymbol.postln
} {
// "Detected ProxySpace".postln;
}
}
}
^this
}
+ ControlName {
*new { arg name, index, rate, defaultValue, argNum, lag, preMangled = false;
^super.newCopyArgs(
if(preMangled) { name.asSymbol } { name.asSymbol.mangle },
index, rate, defaultValue, argNum, lag ? 0.0)
}
}
+ SynthDef {
// this is actually called from Control with a ControlName object
// after it is built, so it needs no changes really, just noting that here
addControlName { arg cn;
controlNames = controlNames.add(cn);
allControlNames = allControlNames.add(cn);
}
// allow incremental building of controls
// CHANGED: all these (except addNonControl) are called from NamedControl,
// so they now need to know if preMangled
addNonControl { arg name, values, preMangled = false;
this.addControlName(ControlName(name, nil, 'noncontrol',
values.copy, controlNames.size, 0.0, preMangled));
}
addIr { arg name, values, preMangled = false;
this.addControlName(ControlName(name, controls.size, 'scalar',
values.copy, controlNames.size, 0.0, preMangled));
}
addKr { arg name, values, lags, preMangled = false;
this.addControlName(ControlName(name, controls.size, 'control',
values.copy, controlNames.size, lags.copy, preMangled));
}
addTr { arg name, values, preMangled = false;
this.addControlName(ControlName(name, controls.size, 'trigger',
values.copy, controlNames.size, 0.0, preMangled));
}
addAr { arg name, values, preMangled = false;
this.addControlName(ControlName(name, controls.size, 'audio',
values.copy, controlNames.size, 0.0, preMangled))
}
}
+ NamedControl {
*new { arg name, values, rate, lags, fixedLag = false, spec;
var res;
this.initDict;
/* just this line is CHANGED in this whole method (but more in init) */
name = name.asSymbol.mangle;
if (spec.notNil) {
spec = spec.asSpec;
if (values.isNil) {
values = spec.default;
};
};
res = currentControls.at(name);
lags = lags.deepCollect(inf, {|elem|
if (elem == 0) { nil } { elem }
});
if (lags.rate == \scalar) {
fixedLag = true;
};
if(res.isNil) {
values = (values ? 0.0).asArray;
res = super.newCopyArgs(name, values, lags, rate, fixedLag).init;
currentControls.put(name, res);
} {
values = (values ? res.values).asArray;
if(res.values != values) {
Error("NamedControl: cannot have more than one set of "
"default values in the same control.").throw;
};
if(rate.notNil and: { res.rate != rate }) {
Error("NamedControl: cannot have more than one set of "
"rates in the same control.").throw;
};
};
if(res.fixedLag and: lags.notNil) {
if( res.lags != lags ) {
Error("NamedControl: cannot have more than one set of "
"fixed lag values in the same control.").throw;
} {
^res.control;
}
};
if(spec.notNil) {
res.spec = spec; // Set after we've finished without error.
};
^if(lags.notNil) {
res.control.lag(lags).unbubble
} {
res.control
}
}
init { /* CHANGED: all callbacks to buildSynthDef need to pass preMangled: true */
var prefix, str;
name !? {
str = name.asString;
if(str[1] == $_) { prefix = str[0] };
};
if(fixedLag && lags.notNil && prefix.isNil) {
// not sure why next line doesn't pass lags, by the way (might be no-op downstream)
buildSynthDef.addKr(name, values.unbubble, preMangled: true);
if(rate === \audio) {
control = LagControl.ar(values.flat.unbubble, lags)
} {
control = LagControl.kr(values.flat.unbubble, lags)
};
} {
if(prefix == $a or: {rate === \audio}) {
buildSynthDef.addAr(name, values.unbubble, preMangled: true);
control = AudioControl.ar(values.flat.unbubble);
} {
if(prefix == $t or: {rate === \trigger}) {
buildSynthDef.addTr(name, values.unbubble, preMangled: true);
control = TrigControl.kr(values.flat.unbubble);
} {
if(prefix == $i or: {rate === \scalar}) {
buildSynthDef.addIr(name, values.unbubble, preMangled: true);
control = Control.ir(values.flat.unbubble);
} {
buildSynthDef.addKr(name, values.unbubble, preMangled: true);
control = Control.kr(values.flat.unbubble);
}
}
};
};
control = control.asArray.reshapeLike(values).unbubble;
}
}
With all that now this bit works properly:
(~swrap = { arg func, suffix;
var envir = (controlNameMangler: { arg name; (name ++ suffix).postln; });
SynthDef.wrap { envir.use { func.value } }; // This eats args!
})
~f2 = { SinOsc.ar(\freq.ar(333)) } // works now because it deduplicates suffixed
d = SynthDef(\LRtest, { Out.ar(0, [~swrap.(~f2, "Left"), ~swrap.(~f2, "Right")]) })
d.allControlNames