triangle & sawtooth oscillators : how to deal with aliasing ?

Status
Not open for further replies.

emmanuel63

Well-known member
Hello,

I noticed a lot of aliasing with the triangle & sawtooth oscillators, especially with high pitch notes. Is there a way to improve or smooth this waveforms ?

Thanks,
Emmanuel
 
The simplest way would be to construct a soundfont using a free program called Polyphone: https://www.polyphone-soundfonts.com/ for each c note provide a sample of a triangle wave which is not aliased. This can be achieved by sampling any number of free software synthesizers available online. Generally you would need a free DAW like Reaper https://www.reaper.fm/ as well. Export the wavs for each sampled C and import these into Polyphone to create a soundfont. Then you would use the pjrc provided soundfont decoder and voila! You may also be able to find alias free triangle wavs sampled at each C on the internet to save yourself some work but I don't have any ready links... You may find this link helpful as well: https://www.music.mcgill.ca/~gary/307/week5/bandlimited.html Cheers!
 
There are very many ways to do anti-aliased oscillator synthesis.
If the wavetable approach isn't your thing, Mutable Instruments provides some excellent code used in their highly regarded Plaits eurorack module that utilizes the PolyBLEP method for anti-aliased oscillators. The aliasing isn't removed completely, but greatly reduced, and inaudible if you keep your oscillator frequency below 4-5 kHz or so.
https://github.com/pichenettes/eurorack/blob/master/plaits/dsp/oscillator/variable_saw_oscillator.h
 
Finally, I used Faust language to add band-limited oscillators.
Faust can run on Teensy. It is really worth giving a try...
Emmanuel
 
Hello,

I noticed a lot of aliasing with the triangle & sawtooth oscillators, especially with high pitch notes. Is there a way to improve or smooth this waveforms ?

Thanks,
Emmanuel

You can process the triangle or sawtooth with an IIR filter (look at the CMSIS biquads supported by teensy for accelerated filtering) or use the waveform approach. The latter is is certainly more computationally efficient. Which one is easier depends on whether you find it easier to run a couple IIR biquads on it or compute and populate waveform tables.
 
I've been looking into this and have a scheme (currently emulated in Python with scipy.signal) which may be promising, and I think
I can get this to work with phase-modulation too. Its using a band-limited step-function table, and currently I'm seeing waveforms/
spectra like this:
square_sawtooth.png

I'm not sure if its worthwhile for triangle waves, the bottom traces are without any bandwidth limiting. The spectra are
scaled in dB and go down to -90dB on the y-axis.

The bad news is that processing load increases with frequency as more and more instances of the step-function table
have to be in use simultaneously.
 
Right, I've made progress on this, converting Python code to C++ and using integer only arithmetic.
By adding interpolation to the step-function table I've got a good 80 to 90dB S/N ratio now, across
the board I think. Here's a couple of representative frequencies, 5.111kHz and 511Hz:

band_limited_waves.JPG

Next step to look at integrating this into the Audio library framework.
 
And I've patched in a 32 bit version of FFT to lower the noise floor and see the full benefit of the band-limited
performance, this is sawtooth at 971Hz:
audiolib_sawtooth_imp.JPG

The noise floor is down to around the theoretical limit for 16 bits, -98dB...

The higher noise at low frequencies is due to having the microphone input active and forgetting to turn
the gain down!
 
I don''t claim its ready yet, comments are welcome, I would like to try to integrate it with AudioSynthWaveform
and AudioSynthWaveformModulated for one thing.
 
Thanks Mark, this is a major improvement and sounds quite clean. Yes if it could be integrated into the existing synth_waveform files, everyone would benefit without making any changes themselves. I can't see any problem with this? Things would sound slightly different but overall, better. Plus having two different objects for AudioSynthWaveform isn't great if your synth uses more than just square and sawtooth. If there's any way to do the same with the variable pulse and triangle waves, that would be outstanding.

Testing with the PlaySynthMusic example on a T4.1: with current square and sawtooth CPU maxs at 1.22, with band limited, maxes at 10.71. Sounds considerably better.
 
Last edited:
Right, that was hard work - changing from 64 bit to 32 bit phase representation and figuring out how to integrate with modulation...

And I've a pull-request for this version: https://github.com/PaulStoffregen/Audio/pull/360
This version of band limited square and sawtooth waveforms is integrated into AudioSynthWaveform and AudioSynthWaveformModulated.

The new tone types WAVEFORM_BANDLIMIT_SQUARE, WAVEFORM_BANDLIMIT_SAWTOOTH and WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE are used so side-by-side comparisons with originals can be done.
 
I'm testing the code at the moment and it sounds fine until I change the frequency while notes play, using waveform.frequency(XXX); The sound becomes distorted and I also get complete lock-ups with the sound continuing but no other response.
 
Interesting, I'd assumed handling frequency modulation would have been enough to be frequency agile with discrete calls...

Can't seem to reproduce this, despite changing frequency randomly every few ms - could you post your code?
band_limit_waveform2.png
 
Last edited:
Try the PlaySynthMusic example (Audio-->Synthesis-->PlaySynthMusic) with the waveforms all set to WAVEFORM_BANDLIMIT_SQUARE:

Code:
// allocate a wave type to each channel.
// The types used and their order is purely arbitrary.
short wave_type[16] = {
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE,
  WAVEFORM_BANDLIMIT_SQUARE
};

Similarly with WAVEFORM_BANDLIMIT_SAWTOOTH

Some of the oscillators are distorted, some are ok. I tested on a T4.1 and T3.6 with Audio Boards attached.
 
Try the PlaySynthMusic example (Audio-->Synthesis-->PlaySynthMusic) with the waveforms all set to WAVEFORM_BANDLIMIT_SQUARE:
...

Some of the oscillators are distorted, some are ok. I tested on a T4.1 and T3.6 with Audio Boards attached.

Ah, yes, it wasn't reseting all the state on subsequent begin()'s. Pushed.

BTW I noticed the Audio shield (rev D) outputs lineout's inverted, headphone correct polarity (sawtooths make this
obvious!)
 
Ok! That's solved the distortion. The problem with the locking-up is I think, because of me over-stretching the T3.6 (CPU maxes at 141:confused:), T4.1 seems fine. I'll carry on testing, I'm modifying my TSynth code for T4.1. Many thanks. Were you planning on band-limiting the pulse and variable triangle waves?
 
Status
Not open for further replies.
Back
Top