Tutorial: BLEPs (using PolyBLEPs but extensible)

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

signalsmith wrote: Sat Feb 07, 2026 8:17 pm
Tone2 Synthesizers wrote: Thu Feb 05, 2026 9:15 am Something that looks similar like
Sin(x * a) * exp(-x * b)
What @mystran suggests is pretty close, since you can compute it as the sum of a few exp(-x*b[k]).
The procedure is as follows: take your transfer function and perform partial fractional expansion into a sum of 1st and 2nd order sections. Then each 1st order section (real pole) transforms into a decaying exponential and each 2nd order section transforms into a decaying exponential multiplied by a sinusoid. If there are duplicate poles then slightly more complex terms are necessary, but that's not really a concern for the standard steep lowpass designs where all the poles are normally distinct.

ps. Just keep in mind that unlike linear-phase filters, windowing these might not work that great.

Post

Tone2 Synthesizers wrote: Thu Feb 05, 2026 9:15 am With 'closed form' i am meaning somthing that is easy and fast to compute in realtime.
Something that looks similar like
Sin(x * a) * exp(-x * b)
Well, Integral sine is known to be non-elementary.

Also, it can't be used without windowing, and windowing the the residual (i.e. Si(x)-step(x)) doesn't seem to be working correctly from my observation. So in either case the windowing function must be back-ported and embedded into approximation, which will most likely be a rational fuction with periodic component (i.e sin*(x)*P(x)/Q(x)).

But even if it's possible to compute the function in reasonable time, lookup table will still be faster. Especially since interpolation and accumulation are trivially vectorizable; the cache misses are minimized by reordering.

There is really no point in bothering with anything more complicated when the whole BLEP discountinity handling is like 30 instructions per 32 sample blep.

minBLEP approach can only get away with a low degree polynomial, because it approximates only 2 samples and is horrible in aliasing rejection. The approach doesn't sacle well for bigger kernels.

Post

2DaT wrote: Sat Feb 07, 2026 10:55 pm Also, it can't be used without windowing, and windowing the the residual (i.e. Si(x)-step(x)) doesn't seem to be working correctly from my observation.
It does not work correctly either in theory or practice.

ps. The reason it doesn't work in practice is fairly easy to understand intuitively: windowing in time-domain is convolution in spectral domain and if the spectrum is piecewise constant, then this mostly amounts to some blur of the transition with a bit of decaying ripple around it ... but if the spectrum has a 1/w slope, then "blurring" over it doesn't converge to the right value even far away from the transition... and the "spiky" transition point results in excessive ripple in the stop band too.

Post

The good old "Gaussian is the optimal time/frequency midpoint" law where slope is traded for with ringing. A consequence of sampling emergent from sampling itself rather than reflecting upon foundational properties. You have to pick one to buy and pay with the other.

Having poor slope and rejection is entirely acceptable when the trade-off is inf samples to process or any sizable finite fraction thereof. Obviously switching to additive synthesis at some fraction of nyquist is likely to be far more optimal than fine-tuning these filters. Obviously, they get proportionately more expensive the more often they're inserted at higher frequencies.

One thing I noted at a younger age when I could still hear 20k+ is the relative sensitivity of hearing is near ~42 decibels. So if you have "aliasing partials" and a pure additive "control spectrum" you must raise the mean magnitude of the aliasing to near 42 decibels below the mean magnitude of the control to "hear" it. That makes the point at which aliasing really matters very dependent upon the particulars of both spectra however. The sensitivity is also non-linear and I would suspect hypothetically has a basis in the neurological circuits which resolve both partials and relationships between partials. Suffice it to say, in most cases we don't really need -500 decibel noise floors because the fractal "hallucinatory" noise in our inner ears and brains is much higher prior to resolution.

I've always aimed to attempt procedural or method purity rather than absolute purity. Human language and modes of thinking and communicating seem to be primarily derived from an aspect of concreteness (symbols) rather than abstractions. Therefore it's innately difficult to focus on optimizing methods or processes without being distracted by sampled measures.

My advice would be (however lackluster) to try focus on what you want to achieve without measuring it if possible. That can help to avoid needless fussing over irrelevant numbers.

For example: "does it sound great except for the aliasing?"
yes: "think about how to reduce the negatives."
no: "think about something important."
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

odibo wrote: Wed Feb 04, 2026 9:23 pm
Marvinh wrote:For delayless polybleps say span 4 we would do

if (phase + 3*phaseIncrement >= 1) then (add first equation) and so on
Well, basically yes, but actually no. Here is some code for demonstration:

Code: Select all

// phase in [-1,1], phaseInc in ]0,1], cutoff 1.0 nominal

float getSawtoothSample (float phase, float phaseInc, float cutoff)
{
   float m = max (0.5f, (cutoff * (2.f/NSL)) / phaseInc);
   float y = phase - getResidue ( max (0.f, ((phase - 1.f) *  m + 1.f)) );
   return y        + getResidue ( max (0.f, ((phase + 1.f) * -m + 1.f)) );
}

Code: Select all

#define NSL   6.87f	// nominal step length for the polynomial below

// input x in [0,1], output in [0,1]

float getResidue (float x)
{
   float y = x * (0.446f + x * (1.234312f - 0.680312f * x));
   return y*y*y;
}
Some notes:

The BLEP polynomial is suited for 88.2/96 kHz sample rates and needs some simple post-filtering (a boost at Nyquist/2). The cutoff parameter controls the BLEP cutoff, i.e. oscillator bandwidth.
Why is there a need for eq is it to compensate for the kernel or this is required since we use no delays.

Post

The EQ is not related to the algorithm. It's only for compensation. Here it is:
y(n) = x(n) - 0.5 * y(n-2)

Post Reply

Return to “DSP and Plugin Development”