Sound differences SAPF - SC

Hi there,

I was watching this intro video to SAPF and I can not understand the big sound difference between SAPF and SC regarding phase modulation.

Why does SAPF’s
220 ((110 0 sinosc) (0.1 0 0 10 lfo) *) sinosc play
sound so different from SC’s
{SinOsc.ar(220, SinOsc.ar(110) * SinOsc.ar(0.1).range(0,10))}.play; ???

I tried changing sample rate to match but still the difference persists.

Even on SAPF when I switch from lfo to a sinosc it still sound super different from SC:
220 ((110 0 sinosc) (0.1 0 sinosc 1 + 10 *) *) sinosc play

Is this a difference on the synthesis engine? what is the difference of a “sine wave lfo with linear range” from a regular SinOsc? And the difference between “sine wave lfo with exponential range” from a regular SinOsc ?

Im Not sure about that Syntax. For Phase Modulation You have to add your modulated Phase increment per sample instead of scaling your Running total with a factor.

Its Phase + (mod * Index) and not phase * Index. Phase * Index is modulating the number of cycles. When doing this you have to Latch your Multiplicator for every cycle. But again This is Not Phase Modulation.

In SinOsc.ar(freq, mod * index), the phase + part is implicit from the nonzero frequency. To do Phase + (mod * Index), the oscillator’s frequency should be set to 0.

hjh

Yeah, thanks thats true :slight_smile: maybe beeing reminded by the General Formular helps for getting it right for Both Syntax.

Since no one else jumped in… I’m not sure, because I haven’t used sapf and can’t find more info about ugen input ranges.

A likely culprit, though, could occur if sapf sinosc expects a different phase range from SC.

In SinOsc.ar(freq, phase), the phase wraps around at 2pi.

In sapf, in the source code, there’s phase(sc_wrap(iphase, 0., 1.) * kTwoPi) which suggests the phase input might go 0 to 1 instead of 2pi. So the intensity of the modulation would be 2pi times more intense. But I’m not sure if that wrap(0., 1.) * 2pi really means what I think it does.

hjh

Thanks a lot!

I tried the FM PM conversion formulas with no good equivalent results.


{SinOsc.ar(0, (220 + (SinOsc.ar(110) * SinOsc.ar(0.1, -0.25).range(0,10))).mod(2pi) )/2}.play

(
{
	var carr = 220;
	var mod = 110;
	var index = SinOsc.ar(0.1).range(0,10);
	var phi = ((1 - SinOsc.ar(mod, pi/2)) * index);
	SinOsc.ar(carr, phi.mod(2pi))/4
}.play;
)

Here is the SAPF recording:

And here is how they sound like in SC:

There is some kind of wobbling in the SAPF version which I am not able to reproduce…

I also could not properly find where in SAPF the ranges were defined, neither what would be the difference between an lfo and a sinosc…

The closest I could get was inserting a modulo operation to 1 and then scaling to [-8pi, +8pi]

{SinOsc.ar(0, ((220 + (SinOsc.ar(110) * SinOsc.ar(0.1, 3pi/2).range(0,10))).mod(1)).range(-8pi,8pi) )/2}.play

In SC, it should be modulo 2pi.

To my ear, this sounds indistinguishable from the sapf recording (which confirms my guess that sapf takes 0 <= phase < 1.0 while SC is 0 <= phase < 2pi, therefore in SC you should scale up the phase by a factor of 2pi to match sapf – same issue doing PM in Pure Data, where [cos~] expects a 0.0 to 1.0 input):

(
a = {
	var mod = SinOsc.ar(110) * SinOsc.ar(0.1, -0.5pi).range(0,10);
	// * 2pi assuming that in sapf you have 0 <= phase < 1.0
	// then % 2pi because SinOsc doesn't like too big a phase offset
	var sig = SinOsc.ar(220, mod * 2pi % 2pi);
	// then, I don't like killing only my left ear
	(sig * 0.1).dup
}.play
)

a.free;

hjh

2 Likes

Thanks a lot!!!

Only a last question, which range and scaling factors should I use for reproducing the same example but using PMOsc?

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)
	}

	... similar for kr...
}

So, carfreq = 220, modfreq = 110, pmindex = SinOsc.ar(0.1, -0.5pi).range(0,10*2pi).

But it won’t sound the same because SinOsc doesn’t work for phase < -8pi or phase > 8pi (documented in the help file). 20pi is well outside of that range. Because PMOsc doesn’t guarantee that phase stays within this valid range, it’s buggy for large mod index values.

(
a = {
	SinOsc.ar(
		220,
		(SinOsc.ar(110) * 20pi)
		// left channel wrap(-1000000, 1000000) = effectively, no modulo
		// right channel wrap(0, 2pi) same as modulo 2pi
		.wrap([-1e6, 0], [1e6, 2pi]),
		0.1
	)
}.play;
)

hjh