6:05 am

August 27, 2013

I am trying to understand the trivial square wave implementation using polyBLEP. Here is the function call from Will's code, and the description Will gives for the polyBLEP function:

-----------------------------------------------------------------------------------------------

polyBLEP()

returns the PolyBLEP residual for a given sample

dModulo = modulo counter value (0->1)

dInc = phase increment

dHeight = height of discontinuity (0->1)

bRisingEdge = true for rising edge, false for falling

bDone = pass by reference flag telling caller PolyBLEP did the second sample

and is done for this discont.

dEdgeLocation = location of the discontinuity between 0 and 1;

for Sawtooth the edge is at 1.0, falling

for Square the rising edge is at 1.0 and the falling edge is at 0.5

------------------------------------------------------------------------------------------------------------------------

double dPW = m_dPulseWidth / 100.0;

double dTrivialSquare = dModulo >= dPW ? -1.0 : +1.0;

bool bDone = false;

double dEdgeLocation = *pRisingEdge ? 0.0 : dPW;

double dOut = dTrivialSquare + polyBLEP(dModulo, dInc, 1.0, *pRisingEdge, bDone, dEdgeLocation);

if(bDone)

*pRisingEdge = !*pRisingEdge;

-----------------------------------------------------------------------------------------------

For the variable dHeight in the square wave implementation, the value 1.0 is always used. I cannot reason why this would not alternate between one and zero. my guess is that this is a ratio of the change between -1 and 1. At each edge we jump the entire distance between -1 and 1.

With this logic if we were jumping from -1 to 0, dHeight would be 0.5. If we were jumping from -0.5 to 0.5 dHeight would be 0.5. If we were jumping from -1.0 to 0.5 dHeight would be 0.75.

Is this the correct way to think about it?

12:55 pm

September 12, 2013

Hi mgill404,

I also use the polyBLEP function from Will. This is what I found out: dHeight is 1.0 if you use the maximum digital values of your definition - and this is for RackAFX the distance between +1.0 -> -1.0 (due to the simple handling you have with a maximum of +/- 1.0).

If you define your maximum 0 -> 1 and your square wave is also 0->1 (and 1-> 0) dHeight is also 1. But if you use the same square (0-> 1, 1->0) in a maximum value range of -1-> +1 dHeight is 0.5. So your way to think about is is correct within the RackAFX value range (+/-1.0 Volt).

regards

Peter

10:09 pm

January 29, 2017

Yes, that's correct Marko - the PBLEP height is the unipolar version of the thing due to the way the original python code worked. I (or you) could change function to accept the height as bipolar so (+/-1 edge would be 2) by changing the polynomial by dividing each term by 2, as in the original document.

- Will

10:33 pm

January 29, 2017

For the record, here's polyBLEP modified to accept a bipolar edge height (0 to 2.0, and always a positive number). The changes are commented out.

/*

Will Pirkle

6/20/2013

Software is free to use as-is but comes with no warranties.

Use at your own risk.

*/

/* polyBLEP()

returns the PolyBLEP residual for a given sample

dModulo = modulo counter value (0->1)

dInc = phase increment

dHeight = height of discontinuity (0->2)

bRisingEdge = true for rising edge, false for falling

bDone = pass by reference flag telling caller PolyBLEP did the second sample and is done for this discont.

dEdgeLocation = location of the discontinuity between 0 and 1;

for Sawtooth the edge is at 0.0, falling

for Square the rising edge is at 0.0 and the falling edge is at 0.5 (for 50% pulse width)

NOTE: this does not match the original Python Source code at http://www.acoustics.hut.fi/pu.....s/spl-ptr/

because of the way I allow the edge location to move around, which is needed for PWM of a square wave.

If processing the falling edge of a sawtooth, you would call it like this:

residual = polyBLEP(dModulo, dInc, 1.0, false, bDone, 0.0);

For processing a square wave edge at the Pulse Width point (eg 50% here) it would be:

residual = polyBLEP(dModulo, dInc, 1.0, false, bDone, 0.5);

*/

inline double polyBLEP(double dModulo, double dInc, double dHeight, bool bRisingEdge, bool& bDone, double dEdgeLocation)

{

// bDone is a flag letting the caller know we processed

// the point on the right side of the discontinuity

bDone = false;

// return value

double dPolyBLEP = 0.0;

// t = the distance from the discontinuity

double t = 0.0;

// LEFT side of edge

// -1 < t 1.0 - dInc) ||

((dModulo > dEdgeLocation - dInc) && dModulo 1

// dPolyBLEP = dHeight*(t*t + 2.0*t + 1.0);

// for edge height 0->2

dPolyBLEP = dHeight*(t*t/2.0 + t + 0.5;

// subtract for falling, add for rising edge

if(!bRisingEdge)

dPolyBLEP *= -1.0;

return dPolyBLEP;

}

// RIGHT side of discontinuity

// 0 <= t < 1

if(dModulo dEdgeLocation)

{

// set done flag

bDone = true;

// calculate distance

t = (dModulo - dEdgeLocation)/dInc;

// calculate residual for edge = 0->1

// dPolyBLEP = dHeight*(2.0*t - t*t - 1.0);

// for edge height 0->2

dPolyBLEP = dHeight*(t - t*t/2.0 - 0.5);

// subtract for falling, add for rising edge

if(!bRisingEdge)

dPolyBLEP *= -1.0;

return dPolyBLEP;

}

return 0.0;

}

Will

3:44 pm

July 5, 2014

Sorry to bother you Will but I'm having some trouble with the above code. I dropped the following into Xcode but it's not properly formatted. It's missing some ifs and I'm not clear how it should be exactly. I'm trying to make a polyBlep residual that can be added to a trivial pulse wave, with variable pulse width:

///////above code for pulse width square polyBlep:

inline double polyBLEP(double dModulo, double dInc, double dHeight, bool bRisingEdge, bool& bDone, double dEdgeLocation)

{

// bDone is a flag letting the caller know we processed

// the point on the right side of the discontinuity

bDone = false;

// return value

double dPolyBLEP = 0.0;

// t = the distance from the discontinuity

double t = 0.0;

// LEFT side of edge

// -1 dEdgeLocation - dInc) && dModulo 1

// dPolyBLEP = dHeight*(t*t + 2.0*t + 1.0);

// for edge height 0->2

dPolyBLEP = dHeight*(t*t/2.0 + t + 0.5;

// subtract for falling, add for rising edge

if(!bRisingEdge)

dPolyBLEP *= -1.0;

return dPolyBLEP;

}

// RIGHT side of discontinuity

// 0 <= t 1

// dPolyBLEP = dHeight*(2.0*t - t*t - 1.0);

// for edge height 0->2

dPolyBLEP = dHeight*(t - t*t/2.0 - 0.5);

// subtract for falling, add for rising edge

if(!bRisingEdge)

dPolyBLEP *= -1.0;

return dPolyBLEP;

}

return 0.0;

}

//////

code from your book:

inline double bookPolyBLEP(double dModulo, double dInc, double dHeight, bool bRisingEdge)

{

// return value

double dPolyBLEP = 0.0;

if (dModulo > 1.0 - dInc) {

double t = (dModulo - 1.0)/dInc;

dPolyBLEP = dHeight*(t*t + 2.0*t +1.0);

}else if(dModulo < dInc){

double t = dModulo/dInc;

dPolyBLEP = dHeight*(2.0*t -t*t -1.0);

}

if(!bRisingEdge)

dPolyBLEP *= -1.0;

return dPolyBLEP;

}

Would you mind sharing the code for the pulse width square or at least pointing me to it online?

12:20 am

January 29, 2017

Hi Zoabis

I don't have the square wave version in front of me right now, but the way that I did it involved a boolean flag that kept track of the edge direction, up/down/up/down, etc... then I just called the BLEP function with the bRisingEdge flag set accordingly.

Ultimately, my former colleague from Korg R&D named Andy Leary (he's in the Forward of the book) convinced me not to use BLEP with square waves with modulate-able pulse width (in the book). The reason is that when the pulse width hits extremes, the pulse edge may require multiple BLEP correction. In other words, you have to correct it once for being on right side of the discontinuity, then again for being on the left side of the discontinuity when the edges are within the BLEP transition band width. This gets worse if you use 4-points-per-side BLEP correction where it falls apart even earlier. This massively complicates the code and issue. This is the same reason I don't include the hard-sync-saw -- it will also require multiple BLEP corrections-per-point when the slave oscillator's frequency is much higher than the master.

After checking my own aliasing with the RackAFX analyzer in high-resolution FFT mode, I noticed that my square waves were failing when the pulse widths were near the extremes (90% and 10%), and if the fundamental frequency became too high, it failed at even wider pulse widths.

Andy convinced me to use the sum-of-saws method instead and stated that this is in use in many analog-modeling synth designs, past and current.

If you are still interested in the code, let me know and I will dig it up.

- Will

Most Users Ever Online: 36

Currently Online:

3 Guest(s)

Currently Browsing this Page:

1 Guest(s)

Top Posters:

Skyler: 48

Derek: 41

Peter: 41

Frodson: 40

clau_ste: 39

Gwen: 32

EZB: 24

lppier: 23

Msaldaña: 18

JimmyM: 17

Member Stats:

Guest Posters: 1

Members: 495

Moderators: 1

Admins: 4

Forum Stats:

Groups: 11

Forums: 31

Topics: 511

Posts: 1966

Newest Members:

CoraDias, Edoardo, knooierd, daniel, Merril Bradshaw, BillPlunkett, Pajczur, michaelwayneharwood, RickM, rainbow windModerators: W Pirkle: 187

Administrators: Tom: 66, JD Young: 80, Will Pirkle: 0, W Pirkle: 187