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

Hi,

Squine Ugen is a sine-square-saw-pulse morphing oscillator with hardsync.
It works well as LFO or to extend the palette of FM setups.
Output is bandlimited in all configurations including high index FM.

Files for Mac/Windows at Releases · required-field/squinewave · GitHub

The code is fairly stable, a version has been in Csound for a couple years.
(…and I very modestly think it should replace most sine/square oscils in every softsynth ever :wink: )

Btw,
Is here a good place for announcements?
I see very few similar posts - maybe writing and using externals is not a popular sport with SC users?
Is there some kind of process to make new Ugens available to users with minimal hassle?

Regards,
rasmus ekman

8 Likes

Looks good! The quarks are (at least mostly) only for language-side extensions written in sclang. Server plugins is a bit more complicated - there is the SC3-plugins package, which currently is in a semi-frozen state, unlikely to accept new contributions.

I recall there has been discussions about extending the quarks system to include server plugins, but it’s afaik not there yet. Meanwhile I guess the best option is to make your own binary release (as you’ve already done), and maybe make a pull request to the Awesome SuperCollider list?

Hi Rasmus
Great job on this one! Yes as @jpburstrom mentioned, feel free to add it to the awesome sc list!

I’ve added it to the Arch User Repository for those on Arch Linux based systems. They can now install via paru -S supercollider-squinewave-git. Looking forward to trying it out properly!

https://aur.archlinux.org/packages/supercollider-squinewave-git/

The best way atm is probably like you have done making prebuilt binaries available on the github repo. You can automate this using github actions (the cookiecutter template for sc plugins will be updated soon with an action built in): Auto build + release workflow of supercollider plugins

Thanks all for suggestions, I’ll work on those.

Mads, yep - I just looked at your page, so there… :slight_smile:

1 Like

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.