guest

Register | Lost password?

— Forum Scope —

— Match —

— Forum Options —

Minimum search word length is 3 characters - maximum search word length is 84 characters

Trivial Square Wave PolyBLEP
mgill404
Member
Members
October 7, 2013 - 6:05 am
Member Since: August 27, 2013
Forum Posts: 13
Offline

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?

Peter
Member
Members
October 7, 2013 - 12:55 pm
Member Since: September 12, 2013
Forum Posts: 43
Offline

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

W Pirkle
October 8, 2013 - 10:09 pm
Member Since: January 29, 2017
Forum Posts: 444
Offline

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

W Pirkle
October 8, 2013 - 10:33 pm
Member Since: January 29, 2017
Forum Posts: 444
Offline

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

Zoabis
Estonia
Member
Members
April 10, 2015 - 3:44 pm
Member Since: July 5, 2014
Forum Posts: 4
Offline

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?

W Pirkle
April 13, 2015 - 12:20 am
Member Since: January 29, 2017
Forum Posts: 444
Offline

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

Zoabis
Estonia
Member
Members
April 15, 2015 - 12:11 am
Member Since: July 5, 2014
Forum Posts: 4
Offline

Hi Will,

Thanks very much for the detailed explanation. That makes sense, I think!

If you do have any C++ for the sum-of-saws method kicking about that would be great.

All the best

Dave

W Pirkle
April 16, 2015 - 12:05 am
Member Since: January 29, 2017
Forum Posts: 444
Offline

The sum-of-saws method is in my Synth Book. You can get the code for it in the MiniSynth project, from the synth project download page.

- Will

Forum Timezone: America/New_York

Most Users Ever Online: 152

Currently Online:
5 Guest(s)

1 Guest(s)

Top Posters:

Skyler: 48

Derek: 46

Frodson: 45

Peter: 43

TheSmile: 43

clau_ste: 39

JimmyM: 33

Gwen: 32

EZB: 24

lppier: 23

Member Stats:

Guest Posters: 1

Members: 617

Moderators: 1

Forum Stats:

Groups: 12

Forums: 38

Topics: 651

Posts: 2558