Explanation of weird PMOsc behavior

Hello,

(
thisThread.randSeed =12;
Ndef(\pmtest,{
var a,c,mb,mt;
a=0.7;

c=Blip.ar([ 17.645452179362, 17.742460335684 ],Array.rand(5,9,150),0.3);

c=PMOsc.ar(0.01,(c*32),Array.rand(2,10,2210.1),(c/Array.rand(4,1,20)).mod(2pi),0.1);
//c=LeakDC.ar(c);
Splay.ar(c)* -6.dbamp;

}).play;
)

Give it a few minutes to run.

I am having trouble understanding why this PMOsc slowly filters and drifts out of audible range.

I know that high radian values translate to hi-freq/inaudible sounds but it seems they should stay there and not drift into silence as this system does. Even with mid-range values, the PMOsc does still eventually drift out, it just takes longer.

The sound does not reappear/re-enter and audible range so I think that is not affected by the carrier parameter of the PMOsc.

Why would blip as a modulator do this?
Could anyone explain why this slow filtering and “fade out” happens?
With other values, the sound does not always completely fade but it does always lose “energy.”
I would like to understand why this sort of drift happens with fixed values.

Thanks for your time!

so I found this
https://sc-users.bham.ac.narkive.com/WtpjibxF/pmosc-and-radians

which is great but I am still not sure why my code sounds the way it does

One approach to troubleshooting is to inspect the components of the sound. There are many ways to do this (polling, scoping etc.)… in this case I was curious what is the result of each Blip and each PMOsc.

There are five of each, so I allocated a 10-channel bus and scoped it. Also I wanted a look at some of the random numbers, so I moved those into variables (no difference in behavior – just to check out the numbers).

b = Bus.audio(s, 10);
b.scope;

(
thisThread.randSeed = 12;
Ndef(\pmtest, {
	var a, c, mb, mt;
	
	var rand5 = Array.rand(5, 9, 150);
	var rand2 = Array.rand(2, 10, 2210.1);
	var rand4 = Array.rand(4, 1, 20);
	
	rand5[0].debug("rand5[0]");
	rand2[0].debug("rand2[0]");
	rand4[0].debug("rand4[0]");
	
	a = 0.7;

	c = Blip.ar([ 17.645452179362, 17.742460335684 ], rand5, 0.3);
	
	Out.ar(b, c);
		
	c = PMOsc.ar(0.01, (c*32), rand2, (c / rand4).mod(2pi), 0.1);
	
	Out.ar(b.index + 5, c);
	
	Splay.ar(c) * -6.dbamp;
}).play;
)

The Blips turned out to be unsurprising. After a few minutes, some of the PMOscs had become almost DC:

pmoscs

PMOsc is actually not a C++ UGen – it’s made of a pair of SinOscs.

PMOsc  {

	*ar { arg carfreq,modfreq,pmindex=0.0,modphase=0.0,mul=1.0,add=0.0;
		^SinOsc.ar(carfreq, SinOsc.ar(modfreq, modphase, pmindex),mul,add)
	}

	...
}

Then I remembered something. SinOsc has a limitation – from its help file: “Phase values should be within the range ±8pi. If your phase values are larger then simply use .mod(2pi) to wrap them.”

Now in PMOsc: The modulator is fed directly into the carrier’s phase input, after being multiplied by pmindex, so the modulator signal will range -pmindex to +pmindex.

If the user supplies a pmindex > 8pi, then the modulator signal will go outside the bounds that SinOsc can handle for the phase input. At that point, correct behavior isn’t guaranteed.

Tracing the synth does reveal some SinOsc phases that are out of bounds… it might take a bit of luck to catch one in the act, but here, phase 67.2665 definitely exceeds 8pi!

  unit 16 SinOsc
    in  0.01 67.2665
    out 0.0456926

I’d suggest to log this as a bug at github – the modulator should have .mod(2pi) after it, but doesn’t.

As a workaround, you can unpack PMOsc’s logic and write the SinOscs directly:

(
thisThread.randSeed = 12;
Ndef(\pmtest, {
	var a, c, mb, mt;
	var modulators;
	
	var rand5 = Array.rand(5, 9, 150);
	var rand2 = Array.rand(2, 10, 2210.1);
	var rand4 = Array.rand(4, 1, 20);
		
	a = 0.7;

	c = Blip.ar([ 17.645452179362, 17.742460335684 ], rand5, 0.3);
	
	Out.ar(b, c);
	
	modulators = SinOsc.ar(c * 32, (c / rand4).mod(2pi), rand2);
	
	c = SinOsc.ar(0.01, modulators.mod(2pi), 0.1);
		
	Out.ar(b.index + 5, c);
	
	Splay.ar(c) * -6.dbamp;
}).play;
)

I’ve been running this one for probably 10 minutes now and the behavior isn’t degrading.

hjh

Oh, one more thing – for future posts, please use code formatting markup:

Writing text, `single backticks` become in-line code.

→ Writing text, single backticks become in-line code.

```
// a multi-line block should have
// three backticks at the beginning
// and another three at the end
"Hello world".postln;
```

// a multi-line block should have
// three backticks at the beginning
// and another three at the end
"Hello world".postln;

Look carefully at your first post in this thread – the code shows mixed formatting (the indented parts are formatted as code while the other parts are plaintext). That’s a sign that the backticks for code formatting are missing. Among other things, this can break double-quote marks in the code and make it more difficult for people to help you – so it’s to everybody’s benefit to use code markup.

hjh

Wow thanks so much! This was very helpful and I will format my code correctly for now on.

Thanks again