As an aside.
I assumed the audio rate bus mapping exemplified above would be dependent on order of execution. But that does not seem to be the case: Even if ~snd is instantiated after ~lfnoise in the example above, causing it to be added before ~lfnoise in the node tree, ~lfnoise still modulates ~snd hz argument:
(
s.waitForBoot {
// synth defs
SynthDef('snd', { arg hz=110;
var hzlag = Lag.ar(hz, 0.005);
Out.ar(0, (SinOsc.ar(hzlag) * 0.25).dup);
}, [\ar]).add; // <-- \ar creates audio rate control
SynthDef('lfnoise', { arg hz=12.0, out;
var cv = LFNoise0.ar(hz, 20, 40).midicps;
Out.ar(out, cv)
}).add;
0.1.wait;
~hzb = Bus.audio(s, 1); // .ar bus
~lfnoise = Synth('lfnoise', [\out, ~hzb.index]); // modulator
~snd = Synth('snd'); // add synth with argument after modulator
~snd.map(\hz, ~hzb); // mapping ar bus to arg
0.1.wait;
s.queryAllNodes(true); // ~snd is executed before ~lfnoise but ~lfnoise modulates ~snd
};
)
The modulation will not work if ~snd explicitly reads from ~hzb using In.ar, when it is executed before ~lfnoise.
(
s.waitForBoot {
// synth defs
SynthDef('snd', { arg hz_bus;
var hzlag = Lag.ar(In.ar(hz_bus), 0.005);
Out.ar(0, (SinOsc.ar(hzlag) * 0.25).dup);
}, [\ar]).add; // <-- \ar creates audio rate control
SynthDef('lfnoise', { arg hz=12.0, out;
var cv = LFNoise0.ar(hz, 20, 40).midicps;
Out.ar(out, cv)
}).add;
0.1.wait;
~hzb = Bus.audio(s, 1); // .ar bus
~snd = Synth('snd', [\hz_bus, ~hzb.index]);
~lfnoise = Synth('lfnoise', [\out, ~hzb.index]);
0.1.wait;
s.queryAllNodes(true); // ~snd is executed after ~lfnoise, so ~lfnoise can modulate ~snd
};
)
The modulation will work if ~snd explicitly reads from ~hzb using In.ar and is executed after ~lfnoise.
(
~snd.free;
~lfnoise.free;
~lfnoise = Synth('lfnoise', [\out, ~hzb.index]);
~snd = Synth('snd', [\hz_bus, ~hzb.index]);
)
s.queryAllNodes(true); // as expected, when ~snd is executed before ~lfnoise, ~lfnoise does not modulate ~snd
To me this is inconsistent, but perhaps not an issue in practice.
SuperCollider is fun but quirky. 