So the next troubleshooting thing you should look into is .trace
for synths.
To do that, you’ll need to change one habit – instead of writing only { ... }.play
, write a = { ... }.play
.
By not saving the synth in a variable, you limit your troubleshooting options.
(
a = {
var op1,op2,op3,op4,op5,op6,trig;
trig=Impulse.kr(1);
op3=SinOsc.ar(200)!2*EnvGen.kr(Env.perc(0.001,0.250),gate:Impulse.kr(20))*0.0;///notice how output is nill
op4=SinOsc.ar(440,op3)!2*EnvGen.kr(Env.perc(0.001,0.250),gate:Impulse.kr(3))*0.5;
op4
}.play
)
// when there's sound...
a.trace;
For this problem, the important bit is at the end:
unit 20 BinaryOpUGen
in 1 0
out 0
unit 21 Out
in 0 0 0
out
unit 22 BinaryOpUGen
in 1 0
out 0
unit 23 Out
in 0 0 0
out
There are two Out units.
This is doubling the output volume.
Now, how did you get to have two Out units?
op3 = SinOsc.ar(200) ! 2 ...;
Now you have a stereo signal – an array of [ a SinOsc, a SinOsc ]
.
op4 = SinOsc.ar(440, op3)
Let’s pause here for a minute. Maybe take a moment to read the help file called “Multichannel Expansion.”
Most UGen inputs expect a single signal. You can’t have one SinOsc with multiple frequencies, or multiple phases. The only way to handle multiple frequencies or multiple phases is to create multiple SinOscs.
op4
is receiving two phase inputs. So SinOsc.ar(440, op3)
itself produces [ a SinOsc, a SinOsc ]
.
op4
is already stereo.
Then you add ! 2
on top of that. So you’re taking a stereo signal and making it doubly stereo.
[ [ a SinOsc, a SinOsc ], [ a SinOsc, a SinOsc ] ]
Now. Out is a special UGen where, when you give it an array of signals, it spreads them out over consecutive buses: left channel = [ a SinOsc, a SinOsc ]
, right channel = [ a SinOsc, a SinOsc ]
.
But how can Out handle the array for the left channel?
This is where multichannel expansion comes in again. There are two things being put into the left channel, so SC requires two Out units to handle them. Same for the right channel.
If we rewrite it like this:
op4 contains [ [ SinOsc 1, SinOsc 2 ], [ SinOsc 3, SinOsc 4 ] ]
// this translates to
Out.ar(0, [ SinOsc 1, SinOsc 3 ]);
Out.ar(0, [ SinOsc 2, SinOsc 4 ]);
And you get double the volume you expected.
For good or ill, in SC, you have to be aware of the geometry of these multichannel arrays.
Some valid approaches are:
-
Keep everything mono at first, and expand to stereo only at the last output stage. (This makes sense if L and R are phase-correlated.)
-
Or, begin with a stereo oscillator at the top, and then do not expand at any other stage.
-
Or (I often do this), begin with a lot of detuned oscillators, mix them to stereo, and then do the rest of the processing in stereo (without further expansion).
It is not valid to write !2
everywhere. (But, it would be valid, though a lot of typing, to write .asArray.wrapExtend(2)
instead of !2
– and a stereo input would remain stereo.)
hjh