PolyBLEP oscillators

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

itoa wrote:Neotec: could you explain?
Imagine you are downsampling a signal using a box-filter. You would sum up a bunch of samples and divide by the number of samples (averaging).

This is exactly what I do here, only instead of summing up samples I use the mathematical equivalent, the integral and the number of samples is our time period 'step'.

Code: Select all

integral(newPhase) - integral(phase)
This gives us the summed samples from the current phase to the next. The integral function here always calculates the sum for the given waveform from '0' to the provided value (phase). So we can get the sum for a given range [a, b] by using 'integral(b) - integral(a)'.

Dividing this by 'step' would be enough to calculate an average, but doubling the averaging interval has benefits for hard sync.

It's a bit hard to explain without a white-board and some neat drawings^^ ... hope it still helped a bit, if not, feel free to ask more specific.
... 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

This method is nice, however aliasing is still too high for today's standards. I wonder how can it be improved.
giq

Post

shabby wrote:


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 res=(sum-psum)/dt;
psum=sum;
return res;
}
I think this is similar to:
http://www.kvraudio.com/forum/viewtopic ... 6#p1710116
which uses higher order polynomial.
See you here and there... Youtube, Google Play, SoundCloud...

Post

Here is the response for my pulse EPTR at 1khz, but the point of it is that is uses oversampling and changes sample window sizes at higher frequencies, and it does FM and sync and PWM, so this is only part of its picture.

http://www.yofiel.com/software/cycling- ... scillators

Image

Red is mine, and blue is a standard bleep. And as you can see, there is virtually no difference in the harmonic response curves, but Yofiel's EPTR oscillator is calculated in real time without using wavetables or BLEPs/BLITs, with virtually the same CPU as for a raw aliased square wave; and Yofiel's oscillator is capable of FM without aliasing artifacts--as well as FM feedback, which the MSP~ version cannot do, because FM feedback requires single-cycle frequency changes, and the MSP~ oscillator has to operate on blocks of signal data.

You may also notice that the EPTR waveform has slightly higher gain. But I didn't match the gain of the two oscillators for this graph, because the EPTR is actually more accurate in this respect. With conventional antialiasing, the wave cannot swing entirely between the rails, as it needs some extra space above and below the pulse wave for the antialiasing wavelets (in order to keep the conventional antialiased-wave's maximum output between -1 and +1). The EPTR oscillator, on the other hand, doesn't need that headroom for its antialiasing, and if its the gain is reduced below what it should be, to correspond to the older antialiased version, the signal/noise ratio for the EPTR is even better.

Post

This topic is some years old, but is there a way to use the PolyBLEP discontinuity function for arbitrary oscillator waveforms, instead of just classical waveforms?

In other words, instead of having separate oscillator functions like one that generates a saw, one for a square, one for a triangle, I have a wavetable where the waveform shape could be one of thousands of different (unorthodox) shapes, and no matter the waveform shape I would like to just calls the PolyBLEP function the same way?
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post

In principle, yes. However
1. You also need to antialias discontinuities in the derivaties (1st and higher orders). And it can get more difficult as the order of derivative is growning. The 1st derivative residual is commonly referred to as BLAMP. Although you may ignore these higher orders, the result might still be better than a non-antialiased waveform.
2. The waveform might be non-bandlimited even without discontinuities (I have a strong hunch it might be related to PWS theorem, but I'm not sure).

Post

In other words the below code works great for a saw, but with pretty much any other shape I get distortion, even on low frequencies (notes).

Code: Select all

float getSample (float currentIndex)
{
  float t, dt, correction;

  t = currentIndex  / tableSize;
  dt = phaseDelta / tableSize;

  if (t < dt)
  {
  	t /= dt;
	correction = t + t - t * t - 1;
  }
  else if (t > 1 - dt)
  {
	t = (t - 1) / dt;
	correction = t * t + t + t + 1;
  }
  else
	correction = 0;

  return tableWaveform[currentIndex] + correction;
}
So what I really need is for the PolyBLEP (correction) code above, to work with any wave shape.
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post

DKDiveDude wrote: Mon Jul 06, 2020 1:47 pm This topic is some years old, but is there a way to use the PolyBLEP discontinuity function for arbitrary oscillator waveforms, instead of just classical waveforms?

In other words, instead of having separate oscillator functions like one that generates a saw, one for a square, one for a triangle, I have a wavetable where the waveform shape could be one of thousands of different (unorthodox) shapes, and no matter the waveform shape I would like to just calls the PolyBLEP function the same way?
If your waveform can be described (or approximated well enough for your purposes) as a (low-order) piece-wise polynomial function, then BLEP anti-aliasing (polynomial or not) is fairly straight-forward: at each transition from one segment to the next, you compute all the non-zero derivatives at the transition point for both of the polynomial segments involved, then subtract the values for the previous segments from those of the next and use the results to scale matching BLEPs for each of the derivatives (and the straight discontinuity, if any).

In principle this works for any piece-wise polynomial, since the number of non-zero derivatives is always finite. In practice, there are various numerical challenges as the order of the segments gets higher (and the processing cost obviously goes up with both the number of derivatives you have to correct and the number of segments your waveform has), though I have some "arbitrary" piece-wise cubic oscillators (with shape morphing) that work just fine. Note that you might need slightly longer kernels to get decent results for higher orders though.

While it is fairly straight-forward to prove that this works for polynomials (ignoring the numerical issues), once you try it with anything else, things get complicated to the point where it's really hard to even definitely say that it doesn't work, even though it probably doesn't. Depending on the waveform, you might still have some success in reducing the amount of aliasing by correcting a finite number of derivatives, but other than that we currently don't have very good theoretical basis to say very much one way or another.

Even with polynomials though, if you're hoping for a simple function you can copy-paste that "just does the magic" then that's just not going to happen.

Post

DKDiveDude wrote: Mon Jul 06, 2020 1:47 pm This topic is some years old, but is there a way to use the PolyBLEP discontinuity function for arbitrary oscillator waveforms, instead of just classical waveforms?
I'm assuming here that you already know that PolyBLEP doesn't sharply cut aliasing but rather removes some of it.

If you ignore the derivatives. PolyBLEP can be considered a special case of a cubic interpolater/crossfader (EDIT: actually PolyBLEP is quadratic, read next post):

Code: Select all

f(x) =  3*x^2 - 2*x^3
Where x is from 0 to 1. You basically crossfade between a few samples around the discontinuity. PolyBLEP uses 2 samples. (So x will equal 0 at the start of the left sample and x will equal 1 at the end of the right sample, fractions in between). Then you'd crossfade simply by:

Code: Select all

output = (1-f(x))*left_sample + f(x)*right_sample
Now you could generalize this and use more samples around the discontinuity which will remove more aliasing but will also filter out more high frequencies from your good input signal. At some point, as you increase samples, a sinc filter will start to become more attractive in terms of CPU and quality.

ps. Sorry I don't have any references for this. This is straight out of my code which I know works, but forgot how I derived it.

EDIT: Also assuming here that the waveform it self doesn't have a discontinuity within it. ie. it's pre-anti-aliased. Speaking of which, if your just looping one waveform, you could pre-anti-alias it in a looping fashion.
Last edited by S0lo on Tue Jul 07, 2020 7:15 pm, edited 2 times in total.
www.solostuff.net
Advice is heavy. So don’t send it like a mountain.

Post

S0lo wrote: Tue Jul 07, 2020 6:32 pm
DKDiveDude wrote: Mon Jul 06, 2020 1:47 pm This topic is some years old, but is there a way to use the PolyBLEP discontinuity function for arbitrary oscillator waveforms, instead of just classical waveforms?
I'm assuming here that you already know that PolyBLEP doesn't sharply cut aliasing but rather removes some of it.

If you ignore the derivatives. PolyBLEP can be considered a special case of a cubic interpolater/crossfader:

Code: Select all

f(x) =  3*x^2 - 2*x^3
Where x is from 0 to 1. You basically crossfade between a few samples around the discontinuity. PolyBLEP uses 2 samples. (So x will equal 0 at the start of the left sample and x will equal 1 at the end of the right sample, fractions in between). Then you'd crossfade simply by:

Code: Select all

output = (1-f(x))*left_sample + f(x)*right_sample
Now you could generalize this and use more samples around the discontinuity which will remove more aliasing but will also filter out more high frequencies from your good input signal. At some point, as you increase samples, a sinc filter will start to become more attractive in terms of CPU and quality.

ps. Sorry I don't have any references for this. This is straight out of my code which I know works, but forgot how I derived it.
Thank you very much for taking the time to chime in!

I seem to be missing something, as I am unsure what and where x is coming from here; 3*x^2 - 2*x^3. Is that my current index sample from my wave table, valued between -1 and 1?
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post

Correction: I've said that PolyBLEP is Cubic. I was wrong it's actually quadratic. The above cubic formula is my own just to get rid of the if statement. the original form is like this:

Code: Select all

if (x<0.5) f(x) = 2*x^2;
else  f(x) = 4*x -2*x^2 -1;
Both formulas behave almost the same. You could use either.
Last edited by S0lo on Tue Jul 07, 2020 7:48 pm, edited 2 times in total.
www.solostuff.net
Advice is heavy. So don’t send it like a mountain.

Post

DKDiveDude wrote: Tue Jul 07, 2020 6:46 pmI seem to be missing something, as I am unsure what and where x is coming from here; 3*x^2 - 2*x^3. Is that my current index sample from my wave table, valued between -1 and 1?
Looking at your code. If I'm not mistaken. x should be equal to (t+1)/2

Assuming t is from -1 to 1.
www.solostuff.net
Advice is heavy. So don’t send it like a mountain.

Post

S0lo wrote: Tue Jul 07, 2020 7:08 pm Correction: I've said that PolyBLEP is Cubic. I was wrong it's actually quadratic.
You can make PolyBLEPs (ie. polynomial BLEPs) of any order that you choose.

Post

apologise for aslo chiming in,
as i don't have teh attn to follow teh progress exacrtly, but yes generalised transition point aa can handle any amount of transitions in a wavecycle, arbitrary waveforms, eg. ones you could draw with point to point graphics instructions. i always tire of teh endles sacronyms and such. there was a method in dafx17 or related, i appropriated, and similar to mystrans source of something i don't know where you are. but my simple first order appropriation drops bands 36dB if my memory isn't gorifying. teh transition only needs ot be scaled from whatever is in teh saw transition.
viewtopic.php?f=33&t=522701&p=7362993
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

S0lo wrote: Tue Jul 07, 2020 7:24 pm
DKDiveDude wrote: Tue Jul 07, 2020 6:46 pmI seem to be missing something, as I am unsure what and where x is coming from here; 3*x^2 - 2*x^3. Is that my current index sample from my wave table, valued between -1 and 1?
Looking at your code. If I'm not mistaken. x should be equal to (t+1)/2

Assuming t is from -1 to 1.
Ok thanks for that clairifcation.

One more thing if you have the time, in your code; output = (1-f(x))*left_sample + f(x)*right_sample

left_sample is the same as the current sample right?

And if so, is the right_sample, as is it coming from my wave table, the one immediately next in the table as in current index + 1, or is it the next in line as in current index + delta?
"Scuba divers work best under pressure!"

https://www.youtube.com/user/DKDiveDude (Original music and hardware videos)

Post Reply

Return to “DSP and Plugin Development”