top of page
Writer's picturephelankane

All About Digital Oscillators Part 2 – BLITS & BLEPS



In our last article, entitled ‘All About Digital Oscillators Part 1 – Aliasing & Foldover’, we discussed aspects of digital oscillator design within modern synthesis. We made the grand statement that the most important and deployed approach in digital synthesizer design within the modern landscape is the use of quasi band-limited oscillators to reduce aliasing. We learnt that this techniques is really a compromise – we allow a little bit of aliasing as long as the aliased components are far away from the fundamental frequency and low in amplitude. As promised, in this article we will explore how these techniques work. If none of this make sense to you I suggest going back and reading the first article here – don’t worry we can wait for you. Ready? Strap in. Let’s go:


Limit Your Bands


Many contemporary algorithms have been proposed to reduce the audible level of aliasing, mostly focusing on the discontinuities in the edges of the synthesized waveforms. This include:

  • Band-limited Impulse Trains (BLIT) (Stilson & Smith 1996)

  • Band-limited Step Functions (BLEP) (Brandt 2001, Leary & Bright 2009)

  • MiniBLEP (minimum phase lowpass filtered prior to BLEP integration) (Brandt 2001)

  • Differentiated Parabolic Waveform Oscillators (DPW) (Valimaki 2005)

  • Polynomial Band-limited Step Functions (Poly BLEP) (Valimaki et. al 2010)

If you’ve ever played with a software synthesiser that uses algorithmic techniques to generate its oscillator waveforms (i.e. most software synths) then it is highly likely that it was using one of the techniques listed above. For example, it is rumored that the oscillators in Cycling 74’s Max use either BLIT or BLEP techniques. This all began in the late 90’s, when Stilson and Smith used a Band-limited Impulse Train method to create a softened discontinuity. The BLIT technique creates a stream of lowpass filtered impulses, a stream of sinc() functions that are integrated together over time. The impulse response of an ideal lowpass filter, otherwise known as the function, is band-limited by its very nature. Polarity plays a big role here – integrating the unipolar BLIT results in a sawtooth wave with a rounded discontinuity (see figure 1), integrating a bipolar BLIT resulting in a square wave and doubly integrating a bipolar BLIT results in a triangle wave. BLIT oscillators feature a ringing on the band edges of the waveforms and aliasing is reduced but not totally eliminated. BLITs can be computationally expensive to calculate in real time and often the calculation is stored in a table, as the period of the desired waveform may not be an integer multiple of the sample rate. The table is interpolated when the algorithm is run.



Figure 1: Band-limited Impulse Trains Here a band-limited sawtooth waveshape is created by integrating a band-limited Impulse Train of sinc() functions


BLEPS


Brandt developed the BLIT idea further to create the Band-limited Step Function technique. Much like the BLIT technique, it begins with a lowpass filter impulse response. An ideal lowpass filter with a cutoff frequency at Nyquist has an impulse response described as the function:






Integrating this signal and transforming it to a bipolar form produces the step function (see figure 2). The ideal unit step function is then subtracted from the calculated step function to form a residual. This residual is stored in a table, then merged with a ‘naïve’ (i.e. aliased) waveshape generated mathematically to form the final BLEP output, offsetting the samples around the discontinuities by shifting them up or down to produce a smoothed waveform. The BLEP technique is flexible, with a myriad of options and design choices available (i.e. using a windowed sinc() function, two/four/eight point discontinuity correction, linear interpolation of BLEP table etc).



Figure 2: Band-limited Step Function The sinc() function (left) is integrated and converted to bipolar to produce the step function (right)


Poly BLEPs


The Poly BLEP methodology involves approximating the sinc() function with a polynomial, therefore using a different residual than BLEP techniques. The most basic polynomial to choose is a unipolar triangular pulse as it is the linear approximation of the windowed sinc() pulse. Integrating it produces the bipolar integrated triangle (see figure 3):



Figure 3: Polynomial Band-limited Step Function Unipolar triangular pulse (left) & bipolar integrated triangle (right)


The bipolar integrated triangle can expressed as:



We will soon see an example in code of how the bipolar integrated equation is used to smooth the waveshapes discontinuities. The Poly BLEP two part residual is then obtained by subtracting the perfect step from the bipolar integrated triangle and converting again to bipolar (see figure 4). Much like the BLEP technique, in the Poly BLEP method the residual is then applied to the naïve waveshape to offset the samples around the discontinuities, shifting them up or down to producing a smoothed waveform.



Figure 4: Polynomial Band-limited Step Function The poly BLEP residual applied to the naïve waveshape


The Poly BLEP algorithm is known for is good sound quality, is implemented without lookup tables as it utilises a fairly simple second order equation (one of the left hand side of the waveshape, one for the right hand side) and maintains low computational overheads. This makes it ideal for embedded devices such as tablets and mobile phones. If you’ve ever used a software synthesizer on any of these devices, chances are it uses the Poly BLEP technique.


Code


‘Ok – I think I’ve got it, but what does the code look like?’ I hear you say. Below is an example of two Poly BLEP functions the author coded in C++. The first function, nextSample, renders a single sample based on the instantaneous phase of the required frequency (since frequency is the first derivative of phase with respect to time). This function is usually called by the ProcessBlock function within the plug-in that renders each required block of samples (at the buffer size set by the host application). A naïve waveshapes based on user choice (i.e. OSCILLATOR_MODE_SAW) is rendered and a second function, poly_blep, is called which creates the residual. Remember the bipolar integrated triangle equation – well the algorithm then checks for discontinuities (first between 0 and 1 then between -1 and 0) and smooths them based on the equation. The smoothed value is passed back to the nextSample function, which then layers the smoothed residual value on top of the naïve waveshape. This all happens for every sample in your buffer size, at the speed of your sample rate. Phew.


// This class provides a band-limited oscillator
double BandlimitedOscillator::nextSample()
{
    double value = 0.0; // Init output to avoid nasty surprises
    double t = mPhase / twoPI; // Define half phase
    
    if (mOscillatorMode == OSCILLATOR_MODE_SINE)
    {
      value = sin(mPhase); // No harmonics in sine so no aliasing!! No Poly BLEPs needed!
    } 
    
    else if (mOscillatorMode == OSCILLATOR_MODE_SAW)
    {
      value = (2.0 * mPhase / twoPI) - 1.0; // Render naive waveshape
      value -= poly_blep(t); // Layer output of Poly BLEP on top
    }
    
    else if (mOscillatorMode == OSCILLATOR_MODE_SQUARE) 
    {
      if (mPhase < mPI)
      {     
        value = 1.0; // Flip 
      } else { 
        value = -1.0; // Flop
      }
      value += poly_blep(t); // Layer output of Poly BLEP on top (flip)
      value -= poly_blep(fmod(t + 0.5, 1.0)); // Layer output of Poly BLEP on top (flop)
    }
    
    return value; // Output
}

// This function calculates the PolyBLEPs
double BandlimitedOscillator::poly_blep(double t)
{
  double dt = mPhaseIncrement / twoPI;

  // t-t^2/2 +1/2
  // 0 < t <= 1
  // discontinuities between 0 & 1
  if (t < dt) 
  {
   t /= dt;
   return t + t - t * t - 1.0;
  }

  // t^2/2 +t +1/2
  // -1 <= t <= 0
  // discontinuities between -1 & 0
  else if (t > 1.0 - dt) 
  {
   t = (t - 1.0) / dt;
   return t * t + t + t + 1.0;
  }

  // no discontinuities 
  // 0 otherwise
  else return 0.0;
}

What next?


So now we have a better idea how quasi band-limited oscillators are constructed in digital synthesizer design to reduce aliasing. If you want to know more then I recommend checking out Will Pirkle’s book ‘Designing Software Synthesizer Plug-ins in C++’ or Beat Frei’s online book ‘Digital Sound Generation’. Both are excellent resources. So for now, happy wiggling.

Comments


bottom of page