PolyBLEP oscillators

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

I mentioned polyBLEP oscillators in another thread, where I was aksed what they are. I must admit I only understand them up to a certain point, so I may not be able to fully explain how they work, but I can show you that they work.

Just like (min)BLEPs, the basic idea of polyBLEPs is that you calculate a naive (non-bandlimited) waveform, and you then correct it at the transients. However, unlike minBLEPs you don't need an extra large precomputed table for this, you just caclulate a trivial polynomial, and you only do this for two(!) samples per transient. Check this out:

Code: Select all

double t = 0.; // 0 <= t < 1
double dt = freq / sample_rate;

...

double poly_blep(double t, double dt)
{
  // 0 <= t < 1
  if (t < dt)
  {
    t /= dt;
    // 2 * (t - t^2/2 - 0.5)
    return t+t - t*t - 1.;
  }

  // -1 < t < 0
  else if (t > 1. - dt)
  {
    t = (t - 1.) / dt;
    // 2 * (t^2/2 + t + 0.5)
    return t*t + t+t + 1.;
  }

  // 0 otherwise
  else
  {
    return 0.;
  }
}

double poly_saw(double t, double dt)
{
  // Correct phase, so it would be in line with sin(2.*M_PI * t)
  t += 0.5;
  if (t >= 1.) t -= 1.;

  double naive_saw = 2.*t - 1.;
  return naive_saw - poly_blep(t, dt);
}

...

for (int i = 0; i < nFrames; ++i)
{
  output[i] = poly_saw(t, dt);
  t += dt;
}
Code originally adapted from http://www.acoustics.hut.fi/publication ... seshaping/

Using the code above you can also create other waveforms, either directly (square, pulse), or by integrating other waveforms (square/pulse for triangle, saw for full-wave rectified sine).

Because PolyBLEPs are quasi-bandlimited, there will be some audible aliasing, especially when the fundamental approaches the Nyquist frequency. This is because the polynomial doesn't produce a clean sine. At 44.1 or 48 kHz this may be a problem, but this could be fixed by crossfading to a actual sine wave or to a brute-force Fourier series waveform. At higher sample rates this problem should fix itself, because the fundamental frequency will be way out of audible range before it approaches Nyquist.

PolyBLEPs attenuate higher harmonics slightly too much, so they might sound slightly dull, but again, this will probably only be audible at lower sample rates. (That being said, I have rejected polyBLEPs for Combo Model V for this very reason. That, and because in my case it was only marginally faster than BLITs.)

For those running REAPER (or ReaJS): I have a demo synth in JS that uses polyBLEPs:

Discussion/download:
http://forum.cockos.com/showthread.php?t=108288

Lame audio demo:
http://www.taletn.com/reaper/mono_synth.mp3

Slighty less lame audio demo:
http://www.taletn.com/reaper/poly_blep.mp3
Last edited by Tale on Tue Mar 05, 2013 11:23 pm, edited 1 time in total.

Post

Oh wow. I may already be doing something like this. (So it means poly[nomial]BandLimitedstEP?)

Though off the top of my head I think my implementation is 3 or 4 samples, not just 2. Maybe that's due to the shape of my function?

The only problem I'm having is what happens when they overlap in time, as happens at very high frequencies and/or short pulse widths. If I can get that fixed I'm done.

Post

AdmiralQuality wrote:(So it means poly[nomial]BandLimitedstEP?)
Yes.
AdmiralQuality wrote:Though off the top of my head I think my implementation is 3 or 4 samples, not just 2. Maybe that's due to the shape of my function?
More samples will further reduce aliasing, but it will also sound slightly duller. Another way to supposedly improve quality would be to use other (higher-order?) polynomials (see here). However, I couldn't detect much improvement, so for me two samples using this polynomial would seem the sweet spot, but YMMV.
AdmiralQuality wrote:The only problem I'm having is what happens when they overlap in time, as happens at very high frequencies and/or short pulse widths. If I can get that fixed I'm done.
If you would use two samples this wouldn't be a problem, because your fundamental can't be higher than Nyquist (thus shorter than two samples) ayway. ;)

But seriously (and assuming standard waveforms): I haven't tried, but wouldn't my suggestion to crossfade to brute-force Fourier series fix this? You would only have to do this when the fundamental frequency is very high, so the Fourier series should be very short.

Post

Tale wrote:
AdmiralQuality wrote:(So it means poly[nomial]BandLimitedstEP?)
Yes.
AdmiralQuality wrote:Though off the top of my head I think my implementation is 3 or 4 samples, not just 2. Maybe that's due to the shape of my function?
More samples will further reduce aliasing, but it will also sound slightly duller. Another way to supposedly improve quality would be to use other (higher-order?) polynomials (see here). However, I couldn't detect much improvement, so for me two samples using this polynomial would seem the sweet spot, but YMMV.
Interesting. In my tests the aliases just drop to nothing on the spectroscope as soon as I activate it. It does "round off" the top end some, but if I run it also in the oversampled domain, then it becomes WONDERFUL!
AdmiralQuality wrote:The only problem I'm having is what happens when they overlap in time, as happens at very high frequencies and/or short pulse widths. If I can get that fixed I'm done.
If you would use two samples this wouldn't be a problem, because your fundamental can't be higher than Nyquist (thus shorter than two samples) ayway. ;)
Right. However a pulse width can conceptually be shorter than 2 samples.

But seriously (and assuming standard waveforms): I haven't tried, but wouldn't my suggestion to crossfade to brute-force Fourier series fix this? You would only have to do this when the fundamental frequency is very high, so the Fourier series should be very short.
That's an interesting angle I hadn't thought of. I was hoping to keep it "graceful". I'm pretty sure there's some way to apply one on top of the other (I think this happens in minBLEP too), I'm just not quite grokking it yet.

Post

AdmiralQuality wrote:Interesting. In my tests the aliases just drop to nothing on the spectroscope as soon as I activate it. It does "round off" the top end some, but if I run it also in the oversampled domain, then it becomes WONDERFUL!
I haven't tried oversampling a polyBLEP yet, but it sounds promising. Thanks for the hint. :)

Post

Tale wrote:
AdmiralQuality wrote:Interesting. In my tests the aliases just drop to nothing on the spectroscope as soon as I activate it. It does "round off" the top end some, but if I run it also in the oversampled domain, then it becomes WONDERFUL!
I haven't tried oversampling a polyBLEP yet, but it sounds promising. Thanks for the hint. :)
No problem! (I guess I can come out and say this, that I'm working on it for Poly-Ana. And it'll be an either/or/both approach, so you'll be able to use the existing oversampling capability PLUS this "polyBLEP" technique together, if you want. It's also incredibly CPU efficient, even at oversampled rates.)

I love your electronic organ clones, by the way. Didn't realize that was you. They're on my list of plug-ins to buy next time I'm liquid.

Post

AdmiralQuality wrote:I love your electronic organ clones, by the way. Didn't realize that was you. They're on my list of plug-ins to buy next time I'm liquid.
No need to go all liquid on me; both Comb Model V and F are freeware (although donations are appreciated, of course). :D

Post

Tale wrote:
AdmiralQuality wrote:I love your electronic organ clones, by the way. Didn't realize that was you. They're on my list of plug-ins to buy next time I'm liquid.
No need to go all liquid on me; both Comb Model V and F are freeware (although donations are appreciated, of course). :D
They ARE!?! Why did I think they were for pay? That's great news. (And I can't donate $$$, but I can set you up with a Poly-Ana license. Contact me at aq AT admiralquality.com )

Post

Thanks for the info on polyBLEP. That's great!

Post

Thanks Tale - that looks interisting! Has anybody tried this against DPW?

Post

It's basically a polynominal that follows the curve of a windowed sinc. Saves ram and divisions...

Tip: Check out bitmap resize algorithms such as Catmull and Mitchell... they've been doing this forever :)

Post

Urs wrote: Tip: Check out bitmap resize algorithms such as Catmull and Mitchell... they've been doing this forever :)
My first job was programming binary load-lifters, very similar to your vaporators in most respects! ;)

Post

Ichad.c wrote:Thanks Tale - that looks interisting! Has anybody tried this against DPW?
I have read an article that compared different oscillator algorithms, and it concluded that DPW gives good sound quality and very low computational complexity, while DPW2X and polyBLEP both give very good quality and low CPU computational complexity. But I haven't tried DPW or DPW2X myself (perhaps I should).

Post

Urs wrote:It's basically a polynominal that follows the curve of a windowed sinc. Saves ram and divisions...

Tip: Check out bitmap resize algorithms such as Catmull and Mitchell... they've been doing this forever :)
More precisely, the curve of Si(x) (integral sine) (with some windowing of the difference against a non-bandlimited step). In this regard I've been always wondering, isn't it more efficient to have a piecewise polynomial approximation of such "windowed" Si(x) (or si(x), for that matter, if we use the residual approach), where the pieces would have a one-sample length. Because then you can use polynomials of much lower orders.

Post

Tale wrote:But I haven't tried DPW or DPW2X myself (perhaps I should).
Alright... I have now compared polyBLEP and DPW myself, and here are the results (of a 220 Hz saw, graphs from REAPER's JS: Analysis/gfxanalyzer):

Image
Naive

Image
DPW (order 2)

Image
DPW2X (order 2, 2x oversampled)

Image
DPW (order 5?, from this old thread)

Image
PolyBLEP from Tutorial: BLEPs (using PolyBLEPs but extensible)

Image
PolyBLEP (Välimäki & Huovilainen, source code in OP)

Image
PolyBLEP (Välimäki & Huovilainen), 2x oversampled

Image
Higher order polyBLEP (Pekonen, as found here)

Image
Alternate higher order polyBLEP (Pekonen #2, as also found here)

Image
7th order 4 point PolyBLEP from Better polybleps polynomials

Image
Fourier series

And here is the DPW source code (as adapted from FAUST's oscillator.lib):

Code: Select all

// DPW (order 2)

double x1 = 0.;

double dpw(double t, double dt)
{
  // Correct phase, so it would be in line with sin(2.*M_PI * t)
  t += 0.5 + 0.5*dt;
  if (t >= 1.) t -= 1.;

  // Naive saw
  double x = 2.*t - 1.;

  double x0 = x*x;
  double y = (x0 - x1) / (4.*dt);
  x1 = x0;
  return y;
}

// DPW (order 3)

double x1 = 0., x2 = 0.;

double dpw3(double t, double dt)
{
  t += 0.5 + dt;
  if (t >= 1.) t -= 1.;

  double x = 2.*t - 1.;

  double x0 = x*x*x - x;
  double y = ((x0 - x1) - (x1 - x2)) / (24.*dt*dt);
  x2 = x1;
  x1 = x0;
  return y;
}
Note that there is no graph for the 3rd order DPW, because its spectrum is exactly the same as for polyBLEP (i.e. it nulls). However, DPW seems a bit whobbly when you quickly change its frequency, a side-effect of differentiating I guess, so I would certainly prefer polyBLEP over 3rd order DPW.

2nd order DPW 2x oversampled, using a 6th order FIR filter (from "Antialiasing Oscillators in Subtractive Synthesis" by Välimäki & Huovilainen), seems to give slightly better quality than the non-oversampled polyBLEP, but of course at higher CPU usage.

EDIT: Added graphs for higher order DPW, Fourier series, higher order polyBLEPs, polyBLEP from Tutorial: BLEPs (using PolyBLEPs but extensible), and 7th order 4 point polyBLEP from Better polybleps polynomials, and updated all other graphs with lower floor.
Last edited by Tale on Sun Nov 02, 2014 10:20 pm, edited 6 times in total.

Post Reply

Return to “DSP and Plugin Development”