Oversampling oscillators

Thats awesome. I Would Love to help, but my c++ is very Limited i can only compare it to my sc implementation but don’t know where i would find all the necessary Bits and pieces in the cpp and hpp files. Maybe we could go through every step of the sc implementation and think why it works and how it has to be implemented across platforms.

is that the foldover of the harmonic that you mean? I think the sinc mip mapping is implemented correctly but the aliasing is coming from the phase itself.

b = Buffer.loadCollection(s, Signal.sineFill(4096, [1]));

(
{
	var phase = Phasor.ar(0, 440 * SampleDur.ir);
	var sig = OscOS.ar(b,
		phase,
		buf_divs: 1, buf_loc: 0, oversample: 0
	);
	sig!2 * 0.1;
}.play;
)

s.freqscope;

b = Buffer.loadCollection(s, Signal.sineFill(4096, [1]));

(
{
    var rate, phase, sig;

	rate = SinOsc.ar(0.1, 1.5pi).linlin(-1, 1, 100, 8000);
    phase = Phasor.ar(DC.ar(0), rate * SampleDur.ir);
	sig = OscOS.ar(~sndBuf, phase, 1, 0, 0);
	//sig = BufRd.ar(1, ~sndBuf, phase * BufFrames.kr(~sndBuf), 1, 4);

    sig = LeakDC.ar(sig);
    sig!2 * 0.1;
}.play;
)

with the Sine wave, you really only see it up around 8K, but that is right where the mipmap should be making a sine wave in all cases:

b = Buffer.loadCollection(s, Signal.sineFill(4096, [1]));

(
{
	var phase = Phasor.ar(0, MouseX.kr(100,16000).poll * SampleDur.ir);
	var sig = OscOS.ar(b,
		phase,
		buf_divs: 1, buf_loc: 0, oversample: 0
	);
	sig!2 * 0.1;
}.play;
)

It is a third harmonic distortion. Frequency is 3x the fundamental.

you can hear it maybe at the root frequency of this:

p = Platform.resourceDir +/+ "sounds/a11wlk01.wav";
b = Buffer.read(s, p);
(
    {
	var fund_of_buf = 1/BufDur.kr(b);
        var phase = LFSaw.ar(MouseY.kr(fund_of_buf/8,fund_of_buf*(2**10.5), 1).poll).range(0,1);
        var osc = OscOS.ar(b,phase,1,MouseX.kr,0);
        osc.dup*0.2
    }.scope
)

But it is pretty cool to be able to shift a 4 second sound file up 10 octaves without foldover.

I don’t know. All these things have trade-offs. If there is anything I have learned from making these libraries is that digital oscillators, like analog oscillators, have character, and that should be embraced.

Sam

hey,

Im more then thankful for all the work you have been putting in that and im very frustrated that i cant offer additional help, but i think a frequency sweep with a sine wave up to 8000 hz should not cause any aliasing. The last time imo aliasing has had any character was in y2k.

lol. well, then the whole point of this library is to get this programming language out of the 90s.

but i’ll figure it out. i’m doing something dumb. i just need to run the symposium now.

sam

1 Like

This is a heavy burden to carry for sure. I have devoted countless hours the last years to try to make a contribution to that on the synthesis side of things and im more then happy if we could figure out what could be done next.

hi @Sam_Pluta,

i just tried the new OscOS package and played a longer sound file with more harmonic material with the oscillators.
there seems to be some strong artifacts/aliasing, which is quite clear when compared to a simple playbuf. heres the example and the sound i used.

b = Buffer.read(s,"/Users/jan/Sound/Recordings/harmsnd.aif");

(
    {
	//var osc = OscOS.ar(b,Phasor.ar(0,BufRateScale.kr(b)*0.7,0,BufFrames.kr(b))/BufFrames.kr(b),1,0,1);
	var osc = OscOS3.ar(b,-1,0.7/BufDur.kr(b),0,1,0,1,1,1,0,0,1);  //either of the OscOS has artifacts
	osc
    }.play
)

//compare to

{PlayBuf.ar(1,b,0.7,1,0,1)*1}.play

harmsnd.aif (1.5 MB)

maybe this is the same issue that is already pointed out but i thought id just report this for the use case of a PlayBuf alternative.

Thanks!

thank you for this. that is helpful.

it is not a replacement for PlayBuf, but that is the distortion. Knowing it is in both really helps.

Ack.

Sam

Couple odd things.

  • In OscOS, shouldn’t buf_loc have a default = 0? OscOS.ar(b, phase) → ERROR: K2A arg: ‘in’ has bad input: nil
  • Should OscOS guard against the buffer being freed before the synth is fully gone?
(
s.waitForBoot {
	b = Buffer.alloc(s, 2048, 1, completionMessage: { |buf|
		// btw I see everyone is doing loadCollection for this
		// but you can turn off wavetable format in the b_gen message
		buf.sine1Msg([1], asWavetable: false);
	});
	s.sync;
	a = {
		var freq = LFDNoise3.kr(8).exprange(200, 800);
		var phase = Phasor.ar(0, freq * SampleDur.ir, 0, 1);
		(OscOS.ar(b, phase, buf_loc: 0) * 0.1).dup
	}.play;
};
)

// later
b.free;

--> Server 'localhost' exited with exit code 0.

Of course it’s wrong to run a wavetable oscillator without a wavetable, but silence would be a better outcome than kaboom, your set died onstage.

hjh

Thanks for those. Will implement.

Sam

OK. All these things should be fixed.

Notes:

  1. OscOS now operates differently when it uses oversampling and when it doesn’t. In non-OS mode, it removes the top octave of harmonics (like @dietcv’s SC example). It should be no different form dietcv’s example, except when the fundamental frequency is in the top octave. In oversampling mode, it puts the top octave back into the signal and removes the foldover with oversampling.

In the future, I would like to use ffts for the sinc interpolation and use a bigger sinc function. This is not happening any time soon though.

  1. OscOS3 uses quadratic interpolation only.
  2. fixed the crash when buffer disappears issue for all wavetable UGens.
  3. Shaper, ShaperOS, and SergeFoldOS now all use quadratic interpolation
  4. OscOS only uses a 8 point sinc interpolation, so it is great on short files, but won’t replace PlayBuf. The distortion we are experiencing on long files is because of this, but it is not an issue with shorter files. Here is a nice example, though, to get around that:
p = Platform.resourceDir +/+ "sounds/a11wlk01.wav";
b = Buffer.read(s, p);
(
    {
    var fund_of_buf = 1/BufDur.kr(b);
        var freq = MouseY.kr(fund_of_buf/8,fund_of_buf*(2**10.5), 1);
        var phase = LFSaw.ar(freq).range(0,1);
        
        var osc = OscOS.ar(b,phase,1,MouseX.kr,0);
        var br = BufRd.ar(1, b, phase*BufFrames.kr(b), 1, 4);

        //will crossfade with 
        var cross = LinXFade2.ar(br, osc, (freq.abs-1).clip(0,400).linlin(0,8,-1,1).poll);

        cross = HPF.ar(cross, 20);
        
        cross.dup*0.2
    }.scope
)

Sam

3 Likes

Is there a possibility to set the default value for the Buchla and Serge Wavefolder normalized to 1?
I think it would be desirable that a value of a zero waveshaping amount would mean no waveshaping but with full amplitude of the dry signal.

So, with an amplitude of up to 1 there is no distortion and then distortion starts when the signal is above/below 1/-1? Think that makes sense.

Yes, exactly. I think when adding distortion you don’t want to Attenuate the Signal below 1/-1.