Having an issue with volume

As you can see in the last line , there is an addition of 2 operators op4+op6
The individual output of these is relatively low , but when they are added together on the last line , it is way out of proportion (esp.op4 )
Sure adding makes things louder , but this is nuts
Mind that op6 has op5 and op4 as modulator but it’s relatively quit , when adding op4 (which has op3 as mod ) on the last line it just play way too loud
Can someone tell me what’s going on ( don’t mind the crappy fm sounds )

(
{
    var op1,op2,op3,op4,op5,op6,trig;
    trig=Impulse.kr(7);
    op1=SinOsc.ar(1000)!2*EnvGen.kr(Env.perc(0.001,0.500),gate:trig);
    op2=SinOsc.ar(220)!2*EnvGen.kr(Env.perc(0.001,0.250),gate:trig);
    op3=SinOsc.ar(300)!2*EnvGen.kr(Env.perc(0.001,0.250),gate:Impulse.kr(20))*0;
	op4=SinOsc.ar(440,op3)!2*EnvGen.kr(Env.perc(0.001,0.250),gate:Impulse.kr(3))*0.5;
    op5=SinOsc.ar(440,op4)!2*EnvGen.kr(Env.perc(0.001,0.500),gate:trig);
	op6=SinOsc.ar(220,(op5*2))!2*EnvGen.kr(Env.perc(0.001,0.200),gate:Impulse.kr(4))*0.1;
	op6+op4
}.play
)

I think there is somehing weirg going on

Only op4 is playing on the last lane
Op 3 is set in the phase argument of op4 , no phase modulation is happening becasue op3 output is set to zero .
Now delete op3 from op4 phase argument , notice how the sound becomes quieter .
How is this possible ?

(
{
    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
)

Upon further investigation it seems this only happes when osc’s are set to stereo ‘!2’ , when using the brackets for stero signal freq [200,200} , it does’t happen .

Run these one by one:

a = 0 ! 2;  // btw spaces improve readability

b = a ! 2;

c = b ! 2;

d = c ! 2;

Based on those results, does ! 2 really mean “set to stereo”? Or does it mean something else (and, what might that be)?

hjh

Ok , It makes arrays , but should that matter since the op3 ouput is set to zero ( multiplied by 0)
Removing op3 from op4’s phase argument alters it volume
I’m still in the dark why the !2 hs an effect on this

afer while…and some hair scratching

! 2 , means make an array of the value associated with a = 0 , [0,0]
Next one , b = make 2 arrays of a ( which is already an array [0,0] , , so b =[[0,0],[0,0]]
Iow doubling of arrays , dependent of the sign after !
Correct .?

But still the increase in output doesn’t make sense ( to me ) , especially when it’s removed from the phase argument

p.s. …lots of editing for the past 30 minutes :slight_smile:

So freq is (400)!2 is the same as [400,400]
Then why is there a difference ,showcased in my example ?

Here’s the last example
first one uses an array for osc’s pitches [200,200]
Second example uses (200)!2 …and acording to the console these are the same

Both examples differ in volume , and both examples have op3 as phase argument for op4 , but it’s output is set to 0 (op3)

(
{
    var op1,op2,op3,op4,op5,op6,trig;
    trig=Impulse.kr(1);
  
	op3=SinOsc.ar([200,200])*EnvGen.kr(Env.perc(0.001,0.250),gate:Impulse.kr(20))*0.0;///notice how output is nill
	op4=SinOsc.ar([440,440],op3)*EnvGen.kr(Env.perc(0.001,0.250),gate:Impulse.kr(3))*0.5;
 
	  op4
}.play
)
//////
(
{
    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
)

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

1 Like

Most of it make sense ( will probably re-read it couple of times ) , but why is it then not happenng in the configuration (200,200) but only in ( 200)!2
Both are doubling , right ?

(
a = 1; a.debug("a");
b = (a + 1) ! 2; b.debug("b");
c = (b + 1) ! 2; c.debug("c");

"\nBut...".postln;
a = 1; a.debug("a");
b = a + [1, 1]; b.debug("b");
c = b + [1, 1]; c.debug("c");
)

Same principle: there’s a difference between array-duplicating the result of an operation (!) and feeding an array into an operation.

hjh

1 Like