I created a wavetable synthesizer and it works perfectly in most frequencies but when you go to extremely high frequencies around the C8 - C9 range it creates artifacts that makes the sound unusable. Here’s me playing the C scale at high frequencies using a saw wavetable: https://clyp.it/0ppn3e33

The wavetable size is 256 but I tried to increase it to 2048 and it still won’t fix the problem. I’m using linear interpolation on the wavetable, and one thing I noticed is a sinewave wavetable actually does not produce artifacts at higher frequencies compared to a saw/square wave.

How do mainstream synthesizers like sylenth1 fix this?

Your wavetable oscillator is aliasing. It’s trying to play harmonics that are higher than Nyquist and therefore they wrap around in the audio spectrum.

There’s a good series of articles on a wavetable oscillator and how to deal with aliasing at www.earlevel.com. Good stuff.

AFAIK they usually antialias with oversampling -> filtering the unwanted frequencies -> downsampling. When you work with 44,1khz the frequencies higher than 22,05Khz bounce back creating inharmonics, and putting a filter in general won’t do much. So what they do is oversample, displacing those frequencies higher hence being able to filter them so they won’t bounce back. Once filtered, downsample back to your original sampling rate.

@johngalt91 Wow, that’s smart! Thanks for the info. But just to clarify(in case I’m understanding it completely wrong): what they do is set the synthesizer’s rate to let’s say 2 times the sample rate; which interpolates the wavetable & plays it at a much higher sample rate, take that output and apply a lowpass filter(like JUCE’s dsp::FIR filter), then downsample back to the original sample rate.

And I’m guessing the over-sample process is needed because everything beyond the nyquist frequency will be reflected over. So to combat the “reflection” problem, increase the sample rate so that “reflection line” moves much higher, so if you set it to 2*44100hz then your synthesizer can produce frequencies up to 44100hz, and now you can filter everything from 22500hz - 44100hz. Then you downsample again to match your original sample rate. I’m understanding this correctly right?

Also, how do you know what factor to over-sample by? Would multiplying the original sampleRate by 2 optimal?

Yes that’s basically what they do. I’m no expert on this process tho since I haven’t tried myself, but I think they don’t change synth sample rate itself (since if it’s a plugin you depend on host SR hence on user). I recommend you taking a look at KVR audio, that’s been discussed a lot there if I remember correctly. Take a look at Oversampling and Lagrange Interpolator classes aswell, they may be useful to you.

About the oversampling rate it depends on the quality you wanna achieve. AFAIK, oversampling is a bit expensive so in most synths there are options to choose the oversampling rate depending on the quality/CPU usage user wants.

Seriously…this explains it very well.

https://www.earlevel.com/main/category/digital-audio/oscillators/wavetable-oscillators/

Yeah you’re looking for mip maps.

If you octave space them you’ll need to oversample and decimate. You can put them closer together but it’ll use more memory.

Logically the simplest way to think about how to do this is to produce a series of wavetables, each decimated from the previous one. This will give you, say, tables of size 1024, 512, 256, 128 etc

The total space taken by these tables is 2 * the original table length.

Now when playing a note, you need to choose the right table to use. If you are stepping through the wavetable at a rate > 1 sample per sample, then by definition you’ll be aliasing, and you’ll want to use the next table in the list, with half the step rate (since it has half the number of samples for a single sample of the original waveform).

Now with this strategy, you’ll produce output that has step changes in the harmonic content as you move between wavetables (essentially if you were to sweep the pitch up, you’d expect the harmonics above 1/2 nyquist to be removed as you move to the next wavetable).

So, you’ve removed the aliasing, but instead introduced changes in the highest harmonics. You can remove this problem by oversampling the entire wavetable oscillator, and decimating it’s output - basically you shift the missing harmonics to be out of band when downsampled, so these don’t have a material effect. You might want to use a larger base wavetable length to ensure you have enough data for lower frequencies.

Hey thanks for the information but I’m really confused on how that’s gonna work. Why can’t the multiple wave table part be skipped and go straight to oversampling the entire synthesizer output, applying a low pass filter with cutoff 20khz, then downsampling? Shouldn’t that remove all the frequencies that was going to be reflected back? And do mainstream wavetable synthesizers like serum generate multiple wavetables per wave?

Aliasing happens before the low-pass/downsample.

Say your sample rate is 44100 and you’re 2x oversampling. When you sample your wavetable the signal aliases and frequencies are mirrored back at 44100 (the oversampling nyquist). Some of the frequencies after mirroring fall below 22050 (which is your non-oversampled nyquist). So when you downsample you remove the aliases from 22050 to 44100 but are still left with all the aliasing that is mirrored below that.

Yes many do, including serum.

The others I know of do a fourier transform to generate a band limited waveform in real-time. I think the tell-tail sign is that these synths tend to have ‘spectral’ filters that work on the frequency content.

Thanks for the explanation! I’ll implement that but also I’d like to know more about band limiting a signal in real time. I originally thought I can just do a Fourier transform and zero out all the bins above 20khz and then do an inverse FFT(which I guess counts as bandlimiting?), but after doing some research I learned that wouldn’t work. Are there any articles on how real time band limiting is implemented into code correctly? Any pointers would be nice

I do the mip-map method myself so I can’t say for sure.

What led you to believe that wouldn’t work? That method sounds right to me (with the addition of smoothing from one waveform to the next).

They’re talking about filtering a non-repeating signal. Filtering with Fourier transforms is perfect for your use case.

thanks for the correction! but waittt, I feel like I’m understanding this wrong… So just to clarify, basically all I have to do now to get rid of aliasing is to just take my wavetable, apply a FFT and zero out all bins above 20khz and no oversampling or mipmaps is needed? It sounds too good to be true lol but I’ll try it out tomorrow!

Prepare for some very complex signals… okay Fourier jokes aside…

A thing to remember about aliasing: once you have aliasing, you can’t get rid of it. Once you create signals above Nyquist, you’ll get signals below Nyquist.

So cutting of everything above Nyquist is not possible, you’ll simply zero out the negative frequency counterparts of your spectrum, making your signal complex valued.

Also a thing to remember: applying hard masks in the frequency domain (like zero-ing out), comes down to filtering in the time domain with a very cruel filter, we are talking zero-phase filter with non-causal impulse response leading to a post echo, destroying your transients. That’s the case for applying this to regular signals. In your case, you have signals you’ll loop, so this circular convolution which is happening here, doesn’t do any harm, as the non-causal parts will be put to the right place So as @mtytel already said, that’s what you need, but not to remove aliasing, to **prevent** aliasing.

Zeroing out bins which will fall above nyquist works well. I made a wavetable synth years ago that didn’t use oversampling where it built a lookup table. Basically it FFT’d then zeroed bins, and iFFT’d to produce a set of wavetables with less and less harmonics. It built a lookup table based on midi note to quickly find the appropriate table to use which wouldn’t alias badly. So as the note pitch changed, it would re-calculate which wavetable to use.

The wavetables were all the same length to allow the logic to interpolate into the wavetables to be simpler (since it didn’t need to adjust the index position). I seem to remember linear interpolation didn’t really help much if you have large enough tables, and the noise introduced by nearest lookup is kind of interesting too.

Thanks for the detailed explanation

but not to remove aliasing, to

preventaliasing.

So is the method of preventing aliasing by applying a FFT and iFFT to the wavetable, enough to stop all aliasing from occurring in the first place? Or is there something that I don’t know of which requires the extra removing step with oversampling?

I haven’t tried it out yet bc I need to read more about FFTs(I’m new to dsp ) but so far this is my plan:

On wavetable creation(256 resolution), apply FFT to the wave, zero out bins above 20khz, then iFFT back and store new version in wavetable.