PolyBLEP oscillators

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

All those box filtered waveforms are very dull at high frequencies. ;)

But i am looking forward for sine sync spectrums :)

Post

2DaT wrote:All those box filtered waveforms are very dull at high frequencies. ;)

But i am looking forward for sine sync spectrums :)
Well, yep, box filtering isn't the best filter when it comes to transition width. The first examples I posted also had a 8.5kHz low-pass on them, the last two I posted are raw ... and contain a higher amount of high frequency content.

Here are another two samples, this time (slave) sweeping up to 14kHz.

Sawtooth, 96kHz, no filters, flac (2.6MB)
Sine, 96kHz, no filters, flac (1.7MB)

Master: 220Hz, slave: 55Hz to 14.08kHz.
... when time becomes a loop ...
---
Intel i7 3770k @3.5GHz, 16GB RAM, Windows 7 / Ubuntu 16.04, Cubase Artist, Reaktor 6, Superior Drummer 3, M-Audio Audiophile 2496, Akai MPK-249, Roland TD-11KV+

Post

It has very low aliasing even if it does rolloff the highs a fair bit.

How does the integral oscillator work? I mean I can see that you are generating an integral signal and differentiating that to get the actual output. And I can see that you are hard syncing the integral signal. I dont get how you are making the whole thing band limited.

I think I remember reading about differentiated wavetables that were then integrated on playback, and that reduced aliasing, but I don't remember ever reading anything about integral based waveforms.

Thanks,
Chris Jones
www.sonigen.com

Post

sonigen wrote:I dont get how you are making the whole thing band limited.
It's direct quasi-bandlimited synthesis by basically just oversampling and averaging/box-filtering.

It might get clearer with a simple example:

Consider a naive, digital oscillator with an 8 bits accumulator. We now have e.g. a step size (phase increment) of 4. To reduce aliasing we oversample by, well, let's say a factor of 8 using a simple averaging filter.

You would now sum 8 sub-samples for one output sample and just divide the result by 8.

Now, we do have a fixed step size, which is 4 in our case. I.e. we increment the accumulator by 4 for each sample. This can also be represented by 4 times incrementing by 1 and this means we just have a sum of natural numbers ... and there's a formula to calculate the sum for natural numbers.

Instead of adding 8 sub-sample outputs and dividing by 8, we now just calculate the sum over the step width and divide by the step width.

And to get the sum over our step width we just need to do a:

Code: Select all

sum(phase + step) - sum(phase)
That's basically the whole trick about this ... it's just a pretty high oversampled, naive, digital oscillator decimated by an averaging/box filter, only that you're doing the oversampling by using formulas instead of loops.

You can compare this to the zero delay filters where you can e.g. solve the filter using an iterative approach (this is our loop case) or by solving for 'out' (this is the formula case).
sonigen wrote:It has very low aliasing even if it does rolloff the highs a fair bit.
Yeah, as I said, the box filter has a pretty bad transition width. My synths normally run internally at 96kHz and get resampled to the desired output rate, so in my case the attenuation in the highs can be neglected, as the strong attenuation then starts way beyond human hearing.
Last edited by neotec on Tue Oct 29, 2013 8:43 am, edited 1 time in total.
... when time becomes a loop ...
---
Intel i7 3770k @3.5GHz, 16GB RAM, Windows 7 / Ubuntu 16.04, Cubase Artist, Reaktor 6, Superior Drummer 3, M-Audio Audiophile 2496, Akai MPK-249, Roland TD-11KV+

Post

Ah right I see now, thanks.
Chris Jones
www.sonigen.com

Post

... my latest experiments also showed that you can expand this technique to give you PM without aliasing.
... when time becomes a loop ...
---
Intel i7 3770k @3.5GHz, 16GB RAM, Windows 7 / Ubuntu 16.04, Cubase Artist, Reaktor 6, Superior Drummer 3, M-Audio Audiophile 2496, Akai MPK-249, Roland TD-11KV+

Post

Anyone tried to port it to SE ?? Would be very interested in having such an OSC at my disposal

Post

Nice work dude - I just tried this in reaper - sounds smoooooth!!

Love reaperJS for dsp R&D - awesome!

Post

neotec wrote:And to get the sum over our step width we just need to do a:

Code: Select all

sum(phase + step) - sum(phase)
That's basically the whole trick about this ... it's just a pretty high oversampled, naive, digital oscillator decimated by an averaging/box filter, only that you're doing the oversampling by using formulas instead of loops.
This is the same as DPW right?

Richard
Synapse Audio Software - www.synapse-audio.com

Post

Isn't there a easy fix for the flaw in DPW to handle the sum crossing and changing
DT? something like this would maybe bring it up to the polyblep quality?

Code: Select all

float DPW(float t,float dt)
{
static float psum;
t=frac(t);
sum=(t*2.)-1.; // naive saw
sum=sum*sum;
float res=(sum-psum)/(dt*4.);
// Sum crossing fix: 1. Or whatever the integral 
// for the entire naive waveform is
if ((t-dt)<0.) res+=1./(dt*4);  
                                
psum=sum;
return res;
}

Edit - The only flaw was my misunderstanding the DPW algo :P

The correct integral of a naive saw is t*t-t
no need to handle sum's across the 0-1 wrap since the signed integral at 1 is 0!

it can be done slightly quicker tho with the proper integral (1 multiply less)

Code: Select all

float DPW(float t,float dt) // assuming t is frac wrapped
{
static float psum;
sum=t*t-t; // Correct Signed naive integral
float res=(sum-psum)/dt;
psum=sum;
return res;
}

Post

neotec wrote:... my latest experiments also showed that you can expand this technique to give you PM without aliasing.
And hard clipping. Here's 20Hz - 20kHz sine sweep clipped with hard clipper using this technique (trapezoidal integration for the non-clipped part), the bottom image is naive hard clipping for comparison.

Image

Post

Just for completeness - EPTR Saw (efficient polynomial transition region).

Code: Select all

float SawOut;

phaseAccumulator = phaseAccumulator + 2.f * phaseIncrement;
if (phaseAccumulator > 1.f - phaseIncrement)
{
SawOut = phaseAccumulator - (phaseAccumulator/phaseIncrement) + (1.f / phaseIncrement) - 1.f;
phaseAccumulator = phaseAccumulator - 2.f;
}
else
{
SawOut = phaseAccumulator;
}
Haven't checked it against DPW but the CPU is in the same ballpark.

Andrew

Post

Thanks for sharing.
Ichad.c wrote:Haven't checked it against DPW but the CPU is in the same ballpark.
I have just checked, and the frequency response is also virtually the same.

Post

Tale wrote:Thanks for sharing.
Ichad.c wrote:Haven't checked it against DPW but the CPU is in the same ballpark.
I have just checked, and the frequency response is also virtually the same.
Yeah, I though as much, thanks for testing! On my machine it has *slightly* better CPU, this is definetly a winner in the 'cheap & chearful class', since it behaves better when modulating.

Andrew

Post

Just tested the "box filter" oscillator, its really cheap. I just don't get this line / idea:

out = (integral(newPhase) - integral(phase) + lastIntegral) / (step + lastStep)

Neotec: could you explain?
giq

Post Reply

Return to “DSP and Plugin Development”