from what I understand, * makes sure that the signal is passed to in0 and in1 of FreeVerb2; the total signal it first concatenated (++) with constant signals [1, 1, 0.2, 1e4]?
Indeed this short writing using syntactic sugar (coming from a SC tweet?) needs a closer look.
.) A ‘*’ before an array inside a functional writing of args splits the array items and passes them to the args. It becomes more clear if you separate it, e.g. by doing this with the method Array.with which genererates a new array and expects separated items. Here you see that it does something slightly different from what you describe, the inner array is first built and then split:
Also interesting what’s going on inside the LPF: PinkNoise.ar(1!2) is very short writing for getting decorrelated stereo pink noise. Instead you could equivalently write
[PinkNoise.ar(1), PinkNoise.ar(1)]
or
[PinkNoise.ar, PinkNoise.ar]
or
{ PinkNoise.ar } ! 2
or
{ PinkNoise.ar }.dup
but not
PinkNoise.ar ! 2
which generates a correlated pink noise.
Concerning
PinkNoise.ar (1!2) + 0.2 * Dust.ar(0.1)
SC’s left-right precedence causes an offset of 0.2 to be added (to stereo noise) before the multiplication with dust impulse. The sigmoid function tanh keeps the signal between -1 and + 1.
Concerning the offset of 0.2. I have only an assumption: the help file of PinkNoise says:
NOTE: The values produced by this UGen were observed to lie with very high probability between approximately -0.65 and +0.81 (before being multiplied by mul).
So it might be that the intention was to keep the signal roughly below 1 with very high probability also.
@dkmayer answered this very thoroughly, so just one addition:
Multichannel / array expansion in SuperCollider is one of the most powerful features of the language, but it’s easy to lose track of what it’s doing no matter your experience level. It helps in the case you described to break things out into separate pieces and use a straightforward postln to see if you’re getting what you expect. You can add a .postln without affecting the value of it’s object - it’s effectively transparent.
sig = PinkNoise.ar(1 ! 2).postln;
sig = LPF.ar (sig + 0.2 * Dust.ar(0.1), 60).postln;
sig = (sig ++ [1, 1, 0.2, 1e4]).postln;
sig = FreeVerb2.ar(*sig).postln;
… you get the picture. I still do this regularly with my own synths to track down issues.
This will allow you to assert the channel count you expect in your SynthDef, and will error out if it’s ever not the case. Often, I have things correct initially but when I make some changes later I accidentally change the channelization (e.g. from mono to stereo) - this is often hard or impossible to hear. I’ve been involved in whole performances where we were just pumping out an extra 4 channels that weren’t getting heard because of a weird multichannel expansion case… I’ll usually use this in particularly complex sections of a synth, to ensure I’m getting what I think I’m getting.
// yup, my signal is mono
sig = Dust.ar(1).assertChannels(1);
// yup, my signal is stereo
sig = FreeVerb2.ar(Dust.ar(1)).assertChannels(2);
// oops, feeding two channels into FreeVerb2 leaves you with 4 channels - this throws an error
sig = FreeVerb2.ar(Dust.ar([1, 1])).assertChannels(2);