Bandlimited pulse width modulation

Hi JUCE,
I am trying to implement a band limited / anti aliasing pulse width modulated square wav.

I have been hacking into this code

The source is here:

I thought if I implemented this method I might be able to make a bandlimited PWM from 2 SAWs put together :

“Take an upramping sawtooth and its inverse, a downramping sawtooth. Adding these two waves
with a well defined delay between 0 and period (1/f)
results in a square wave with a duty cycle ranging from 0 to 100%.”

https://www.musicdsp.org/en/latest/Synthesis/8-pulsewidth-modulation.html

The SAW looks like this :
m_sharp is a sweepable value between zero and one acting as a sort of low pass filter from the sounds of it .

m_srOverFour = m_sampleRate / 4.f;

           // SAW 
            maxHarms = m_srOverFour / m_freq;
            numh = m_sharp * 46.f + 4.f;
            if (numh > maxHarms)
                numh = maxHarms;
            pos = m_pointer_pos + 0.5f;
            if (pos >= 1.f)
                pos -= 1.f;
            pos = pos * 2.f - 1.f;
            value = -(pos - tanhf(numh * pos) / tanhf(numh));

Naively I tried this with no luck :

        {maxHarms = m_srOverFour / m_freq;
            numh = m_sharp * 46.f + 4.f;
            if (numh > maxHarms)
                numh = maxHarms;
            pos = m_pointer_pos + 0.5f;
            if (pos >= 1.f)
                pos -= 1.f;
            pos = pos * 2.f - 1.f;
            double valueOne = -(pos - tanhf(numh * pos) / tanhf(numh));
            
            pos = pos + 0.5f;
            if (pos >= 1.f)
                pos -= 1.f;
            pos = pos * 2.f - 1.f;
            double valueTwo = 1/(-(pos - tanhf(numh * pos) / tanhf(numh)));
            value = valueOne + valueTwo;
            break;}

Can anyone help me here?
Where should I go for help ??
Sean

Hi seanwayland,

the formular should be like so:
pwmSquare = saw1(t) - saw2(t + phaseOffset)
t = normalized phase (0…1)
and for phaseOffset = 0.5 you get a perfect square

In your code I can spot 2 mistakes.

  1. pos at first is the normalized phase, later it is a naive saw and after that you use it again as a phase.
  2. you took the reciprocal of the saw2, that is wrong.

Maybe trying somthing like that:

            maxHarms = m_srOverFour / m_freq;
            numh = m_sharp * 46.f + 4.f;
            if (numh > maxHarms)
                numh = maxHarms;
            double pos1 = m_pointer_pos + 0.5f;
            if (pos1 >= 1.f)
                pos1 -= 1.f;
            double naiveSaw1 = pos1 * 2.f - 1.f;
            double saw1 = -(naiveSaw1 - tanhf(numh * naiveSaw1) / tanhf(numh));
            
            double pos2 = pos1 + phaseOffset;
            if (pos2 >= 1.f)
                pos2 -= 1.f;
            double naiveSaw2 = pos2 * 2.f - 1.f;
            double saw2 = -(naiveSaw2 - tanhf(numh * naiveSaw2) / tanhf(numh));
            value = saw1 - saw2;

be carefull, this code is unchecked.

Thanks so much !
I tried this last night. It made a noise that sounds like a pulse wav to me . I think I hear a bit of aliasing. I am going to try out your version !

            double pulseWidth = 0.75f;
             maxHarms = m_srOverFour / m_freq;
             numh = m_sharp * 46.f + 4.f;
             if (numh > maxHarms)
                 numh = maxHarms;
             pos = m_pointer_pos + 0.5f;
             if (pos >= 1.f)
                 pos -= 1.f;
             pos = pos * 2.f - 1.f;
             value = -(pos - tanhf(numh * pos) / tanhf(numh));
             double saw1 = value;
            
            // --- phase shift on second oscillator
            maxHarms = m_srOverFour / m_freq;
            numh = m_sharp * 46.f + 4.f;
            if (numh > maxHarms)
                numh = maxHarms;
            //pos = m_pointer_pos + 0.5f;
            pos = pos + pulseWidth;
            if (pos >= 1.f)
                pos -= 1.f;
            pos = pos * 2.f - 1.f;
            value = -(pos - tanhf(numh * pos) / tanhf(numh));
            double saw2 = value;
            
            double squareOut = 0.5*saw1- 0.5*saw2;
            
            // --- apply DC correction
            double dcCorrection = 1.0 / pulseWidth;

            // --- modfiy for less than 50%
            if (pulseWidth < 0.5)
                dcCorrection = 1.0 / (1.0 - pulseWidth);

            // --- apply correction
            squareOut *= dcCorrection;
            
            value = squareOut;