[ANN] Squine: sine-saw-square-pulse morphing oscil with hardsync [RC4]

Great - and I just tried it. It’s awesome. Good job!!

1 Like

@required-field
It seem to output messages to the post window:

Min_Sweep: 11.000000
Min_Sweep: 11.000000
Min_Sweep: 8.000000
...

can this be disabled?

Of course, will be for release.

(It’s debug debris, also cos I’m still on the fence whether to allow fractional MinSweep. I don’t think that it would break anything, but needs to be tested)

Cheers,
/rasmus

why do you get no sound at all for specific freq / index settings using Squine? (no problem with SinOsc):

(
Ndef(\xfm, { arg freqA = 32, freqB = 9, modAtoB=540, modBtoA=240;
	var fbIn = LocalIn.ar(2);
	var sigs = Squine.ar([freqA, freqB] + (fbIn.reverse * [modBtoA, modAtoB]));
	LocalOut.ar(sigs);
	sigs * 0.5;
}).play;

Spec.add(\freqA, [1, 10000, \exp]);
Spec.add(\freqB, [1, 10000, \exp]);
Spec.add(\modBtoA, [0, 1000, 5]);
Spec.add(\modAtoB, [0, 1000, 5]);

Ndef(\xfm).gui;
)

Hi!
I think you’re feeding it negative frequencies.
Squine clips freq to [0, sr/2], at 0 it outputs a flat line (current phase value).

If i replace your var sigs = Squine.ar ... line with this:

var freqs = [freqA, freqB] + (fbIn.reverse * [modBtoA, modAtoB]);
var sigs = SinOsc.ar(freqs);
freqs.scope;

I get a scope that shows one flat line and one varying with neg values.
( Not sure I’m doing the scoping right - you probably know? )

So, a limitation and difference from other Oscs.
Perhaps worth mentioning in the docs, think I should do that!

In testing I didn’t like the result of inverting output for negative freqs - FM sounded bad and it’s actually not correct with non-zero skew values - so thought it better to just clip the freq.

Hope you can find some use with a slight variation on your patch :slight_smile:

Cheers,
/rasmus

hey, thanks for your answer.

I have assumed that for high index FM the modulated frequency of the carrier signal would oscillate between negative and positive values. I think I dont understand why its necessary to prevent any negative frequencies to occur by clipping or inverting them.

With this cross synthesis patch its not clear to me if the negative frequencies which are clipped to 0 and outputting a flat line of Osc A and modulating the frequency of Osc B or the static negative frequency values of Osc B which are modulating the frequency of Osc A are causing the blowup. Even if you just swap one of the Squines for SinOsc the problem doesnt exist anymore.


(
Ndef(\xfm, { arg freqA = 32, freqB = 9, modAtoB=540, modBtoA=240;
	var fbIn = LocalIn.ar(2);
	var sigs = [
		SinOsc.ar(freqA + (fbIn[1] * modBtoA)),
		Squine.ar(freqB + (fbIn[0] * modAtoB))
	];
	LocalOut.ar(sigs);
	sigs * 0.5;
}).play;

Spec.add(\freqA, [1, 10000, \exp]);
Spec.add(\freqB, [1, 10000, \exp]);
Spec.add(\modBtoA, [0, 1000, 5]);
Spec.add(\modAtoB, [0, 1000, 5]);

Ndef(\xfm).gui;
)

Did you try to .abs the frequency? This should work fine.

Sam

hey thanks.
yes, i have done that, which would be equivalent to inverting the frequencies like @required-field was considering in the design of the Osc.
I doesnt create the same amount of dead latent space but i have to admit it really sounds different and i dont know why its necessary in the first place in FM you always deal with negative frequencies.

why its necessary to prevent any negative frequencies to occur by clipping or inverting them.

Not necessary, but hard to implement in the Squine code.
But this is great feedback, thanks!

So here’s a question:
What waveform is output when feeding negative freq to a left-facing saw?
In a phase-driven digital osc, it would obviously be a right-facing saw,
but I realise I have no idea what happens in various physical/electric oscillators.
Anyone has a clue?

(GPT4 only talks about “phase inversion” and supports left-right inversion for all cases, but hedges wildly when I ask about VCO:s and negative voltage :roll_eyes:)

Anyway, it’s easy to just abs the freq on input.
That should support your patch better and (I see now) just produce more sensible output.
I thought it would be correct to also reverse the waveform; this needs a little more thought, and testing.
Any opinions on this, aside from AI guesstimates?

Regards,
/rasmus

See my post here:

FM - Analog Synth Style

about FM using analog synths.

I was going to say this earlier, but lots of digital oscillators don’t accept negative frequencies. Our own Saw does not and neither do the bandlimited oscillators in Faust.

Sam

Thanks for mention, good post!
I still went through and changed the source, I think it doesn’t change anything wrt your technique.

New build for Mac is up, RC3. Hope it works.
Releases · required-field/squinewave · GitHub (draft; only visible on releses page)
Will update Windows later today.

This doesn’t make Squine (as sine) an undetectable drop-in replacement for SinOsc: there’s still the high-freq limiter logic (i think. hope it’s not a typo :sweat_smile:).

there is actually a term for this i didnt know about its called “through-zero frequency modulation”

also found this comment on modwiggler:

Imagine a carrier at 100Hz, sounds like a bass tone. Modulate it with a slow rate and linear depth of 10hz, so it goes between 90 and 110. The centre/average still sits at 100 we hear a 100 Hz tone with strong vibrato. You can speed the modulator up to faster vibrato, trill, until it fuses into creating sideband frequencies, but the average feels like 100hz still. You can turn up the linear depth, say to +/-50hz, now you are swinging between 50 and 150, but still centred on 100. Turn up the depth to +/-100, now you swing between 0hz and 200hz, still has an average of 100. But if you try to push it deeper, say to +/-150hz, then the ideal swing should be -50 to 250hz. Hence negative frequency.
If the oscillator core can’t produce (or emulate) this, what will it do? Maybe it stalls at 0hz, your swing centre is now 125hz. Maybe it reflects back to positive and you get 50hz instead of -50, so now your average is 150hz. Either way, your centre/average pitch has drifted up from 100hz.

ChatGPT4 says:

  1. Handling TZFM:
    The key concept of TZFM is that the frequency of the carrier oscillator needs to cross through zero during modulation. This requires careful handling of phase offsets and sign changes.

Here’s a general algorithm for implementing TZFM:

  • Calculate the instantaneous frequency of the modulator oscillator at each sample point.
  • Calculate the phase increment for the carrier oscillator based on its instantaneous frequency and the sample rate.
  • Update the phase of the carrier oscillator using the phase increment.
  • Calculate the output of the carrier oscillator at the current phase.
  • Apply any additional processing (envelopes, filtering, etc.) to shape the final sound.
  1. Implementation Considerations:
  • To achieve a seamless through-zero transition, you’ll need to detect when the frequency of the carrier oscillator crosses zero and smoothly adjust the phase to ensure continuity.
  • You might need to employ techniques like linear interpolation between two phase values to avoid audible glitches during frequency transitions.
  • Consider using wavetables for more efficient and flexible oscillator implementations.
  • Experiment with different modulation waveforms and modulation depths to achieve different sonic results.

https://learningmodular.com/understanding-the-differences-between-exponential-linear-and-through-zero-fm/

it seems it became kind of a trend lately in modular https://www.youtube.com/watch?v=41uxRUgIJHQ

1 Like

Thanks, that was edifying, intimidating and slightly confusing!

So neg frequency is best viewed as the waveform running “backward”.

At freq input zero-crossing, when phase increment switches direction, some new shapes are formed dep on current phase:
– a low rounded m shape, or a full-amp M shape or no change.
– if wave is on the negative side, we may get similar rounded w and W shapes

This could be implemented in Squine with some sign-flipping and phase-jumping tricks.

Question is, is this very useful?
In discussons of TZFM it’s sometimes stated that it expands FM possibilities.

Squine already does that, since you can modulate the waveform shape at audio rate.
This produces new sets of sidebands, or “sonic possibilities”.
(also there’s hardsync, let’s not forget to tout that :smile: )

So I’m just noodling over what’s the most convenient and understandable out-of-the-box behavior?

“Well I think…” posts welcome at this point :wink:

Regards,
/rasmus

i think these are two different concepts, with the busy circuits TAZM-O for example one could beside having TZFM also modulate the shape or use sync like in Squine.

Im doing experiments with this backward / forward PM/FM modulation per grain with buffered single cycle waveforms for over a year now, which is a kind of scratching the waveform. Especially the zero crossings have been interesting, wasnt able to get the same behaviour with .abs. With tonal patches i think the advantages are obvious.

a bit off topic, just my personal use case:
My interest in Squine is thats its bandlimited for high index modulation and you can modulate the shape for more timbral possiblities with lesser control parameters then using a multiplexer to sequence some static buffered shapes with demand Ugens at audio rate.
Actually did some benchmarking of all patches ive created over the last year to investigate which buffered signals or windows i have been mostly using. Most of the waveforms have been kind of sinusoidal but here and there ive thrown in some more complex shapes in the sequence. So the sequencing for the waveforms is not necessary in my case (windows are another story and tended to be more complex) but additional modulation the shape of the waveform is great.

Thanks for your work story, great to hear real use cases in context!

Wrt TZ mod, switching direction on Squine looks like a complicated change, so I’m inclined to leave it as is for now (RC3).

Ie, input freq is treated as abs value, the waveform remains unchanged on negative frequency. (At least better than flatlining, no?)

I’ll be testing and wavering a bit more, but adding 2-3 sample-rate if-then’s and bunch of code (creating internal discontinuities but hoping the output still aligns) doesn’t taste so nice…

Regards,

  /rasmus

hey, thank you very much for thinking about my concerns and helping me out fixing the issue with my initial cross synthesis feedback FM patch using Squine.
Unfortunately im not familiar with C++, if there is a moment in the future where i can help you out with enabling TZFM in Squine i will let you know.
Would be cool to be able to look into some of these Eurorack Modules to know how it could be done. Just found this https://github.com/docb/dbRackModules/blob/main/src/Osc1.cpp which does TZFM in VCV Rack.
For now treating freqs as abs values is already better then having a flatline output.
Is there already a windows version available of RC3 ?

Yep, Windows version is there.
Thanks so much for your input, best way to make it stable and useful is by getting other uses than mine!

(The Rack opcode is very different. I know what to do for TZmod, but it’s risqué :wink:
Squine maintains 2 internal phases, neg freq means everything has to be inverted, doubling 150 lines of code. There’s a neat shortcut, but less stable; rounding errors could introduce micro-noises that I worked so hard to get rid of.)

Cheers,
/ r

Release RC-4 up now.

Changes:
Fixed Through-Zero FM, ie waveform runs “backward” on negative input frequency.

((turned out to be an isolated and simple change, no significant complexity increase or risk of destabilizing the algorithm))

This doesn’t do a whole lot to the sound, negative frequency input and negative skew take out each other, so nothing new really.
But it might be regarded as more correct behavior than previous versions.

Again, thanks to @dietcv for input and nudge.

Note, this release will change your sounds if your FM ever dips below zero.
(esp different from RC1-2, which clipped neg freq to 0)

Cheers,
/rasmus

2 Likes

awesome! made already some RC 1-2 / RC-3 / RC-4 comparisons with the same patch.