Wavetable Synthesis producing artifacts at High Frequencies, How to fix this? - Aliasing

Zeroing out bins which will fall above nyquist works well

How do you create “bins above Nyquist”? Is there an oversampling step here?

1 Like

I think @cesare meant to emphase the will in the sentence, or better: Zeroing bins which would fall above nyquist, when creating the band-limited wave-tables.
As @pizzafilms posted:
https://www.earlevel.com/main/2012/05/09/a-wavetable-oscillator—part-3/ this basically says all. You can create those band-limited wavetables by filtering in the frequency domain using FFT and removing everything which would fall above nyquist.


Ahh ok I got it. So it’s just additive synthesis via IFFT.

But here’s a quesiton, to get an M-sample wavetable from an N size FFT you zero bins (M:N) and take an N-size IFFT? Or would you just take the M-size FFT and just disregard the bins you would have zero-d anyway?

1 Like

In case you did an FFT and not an RFFT, you’ll have to get rid of the bins in the middle, as those represent frequencies (positive and negative) above your desired band limited cut-off frequency. It’s better to do an FFT of real-valued signals → RFFT, and just get rid of the end.

Example: original wavetable has 128 samples, the RFFT (order 7 as 2^7 == 128) will yield 65 complex-valued bins (from DC to Nyquist). In case you want to get 64 samples out of it (64 less than 128), you’ll have to get rid of 32 bins. So you basically ignore the last 32 bins, leaving you 65-32=33 bins. Inverse FFT (RFFT actually, order 6) will give you 64 samples of the band-limited signal.

Depending on the FFT implementation, you might have to scale the signal afterwards by a factor of 0.5.

1 Like

That’s an interesting approach, but I see another problem, that is, wavetables already have a limited bandwidth, because they can’t do anything in frequencies below that of 1/sample length as it is. Most of their sonic quality is in higher frequencies whose waves can fit in the sample range, and if you reduce the number of samples like that, they are going to end up sounding dull. I do sympathize with the problem, please forgive me for double posting on it today, but I did share something on optimal resampling which you might enjoy:


there is another problem with resampling I could mention here, it does not work very well with sharp transitions such as pulses and saws, so if you’re wavetable has those in them, your approach is probably better.

@rarrar In my experience by far the most course of the aliasing when using wavetables, is because of the wrap sync as it totally distorts the waveform, and differently pretty much every time it wraps, as it wrap a different place on the table! Just use hard sync and it eliminates most aliasing, even with a saw - See below where I play a 3520Hz saw note in the synth I am programming. First picture is with wrap sync, second is with hard sync and note I am only using one table for all frequencies!

What do you mean by wrap sync/hard sync? Hard sync usually implies resetting the phase of one oscillator based on another one’s cycle (usually higher frequency) - generating a lot of harmonics since the synced oscillator doesn’t do a full cycle.

Sorry for the confusion, how about “Hard Wrap” then. The place where you advance the angle with the delta, you test if angle + delta exceeds your table size, and most people wrap (angle -= tableSize). What I am saying, in my experience, wrap is bad because what it really does is make a new waveform with a new slightly different frequency virtually every time it wraps, as angle each time starts in a new place in the table.

So instead I do;
if (angle + delta > tableSize) angle = 0;

Isn’t then your frequency off all the time, at least just a little bit?

1 Like

Good question! I actually thought off that earlier this week, to see if I needed a tiny adjustment. So I recorded audio in Audacity while playing various notes. Then in Audacity I zoomed in and counted wave crests and they did line up, sum up, to what frequency they are supposed to be. However it was only up to 3520Hz, so I am going to do another test with much higher frequencies.

That does not seem like a good idea - providing you increase the phase by the same amount every sample read out (and add the difference in phase past the end of buffer to the read out location) you are not going to be changing the frequency - use a index method that can give you a float sample position so you can do weighted interpolation between samples.

You might want to try out my method. But think about it, for a saw for example. When you press the note it starts at position zero in the wavetable, say 4096 in size like mine, and then delta is 37.5467. That means for 3520Hz my wavetable will get sampled 110 times staying with table size. At sample # 111, where previous angle was 4092.5903, I am at the end of the table. If you wrap to next position is becomes 4130.137 - 4096 = 34.167. See now instead of starting at a sample value of 1, with a saw going from left to right, you now start at a sample value of 0.983331543, not much of a difference from 1, but keep in mind that the starting position will virtually be different every single time you wrap, and those small changes is what I think amount to most of the aliasing. It will take a very long time before that note finally comes around and starts exactly at wave table position zero again, if ever!

NOTE: The below recording have just been replaced, May 6th, 2020 10:45am, as Audacity aliased it when exporting to wav 16-bit PCM. It is now 32-bit PCM and sounds like I hear it in my synth.

Here is a straight left to right ramp style saw, from 1 to -1, meaning nothing fancy in the beginning or end, at 3520Hz using “hard wrap”.

I think that your method is changing the resulting waveform and timbre depending on the frequency. That would explain that you don’t get aliasing artifacts with only one table and without any anti-aliasing filtering.

I think I’d better let somebody else chime in on this one… but I’d suggest try with a sine wave at a low frequency… what will it look like when the phase increases past the end of the buffer and you just reset the buffer read out position to 0. If you want to ensure the start phase of the waveform on ‘Note-on’ events… then you can reset the phase to 0 when a note is played.

Sure it is, however I argue that so does wrap. Again see my previous post arguing that the wrap starts at a different place every time, and if that is agreed upon, then the waveform is also different every time. My method is consistent every time, wrap is not.

I have a sine, as well as literally thousands, well actually an understatement, as I made a waveform creator that can create millions of waveforms. Anyways the sine sounds ok to me. I’ll upload a sine and provide a link in a moment.

Here is a sine at 43.6535Hz (F1) using “hard wrap”.

And for comparison, here is the same sine in a wavetable, but with “regular” wrap.

My ears or my equipment may be horrible, because I cannot hear any difference.

Let me know if anyone wants to hear any type of waveform at any frequency, using my “Hard Wrap” method, and I’ll record and upload it.

I am not so awake as was coding late last night… in fact I just came back from a nap :slight_smile: - well anyway… I meant a high freq rather than low (less sample points - you’d be more likely to see the reset visually/hear it since the phase shift would be bigger) - but I guess the effect depends on the size of the table… I would assume you would see different tonal qualities on sweeping a frequency range with this hard reset to the start of the table - assuming you are using single cycle of data/single table - maybe the glitch is too small to notice though - or are you anyway also using bandlimited tables?.

End of the programming day for me. I’ll upload high frequency sine’s in the AM. I am only using one wavetable for all frequencies. In other words I do not use different wavetables for different say octave’s.

Anyways my synth sounds has until now sounded to my ears pretty good, but @danielrudrich made me think to test further with high frequencies to see if pitch stayed true to the note played, and actually I now do see that I may need an adjustment factor (multiplier), with the higher the frequency (note) played, the higher the adjustment needs to be, so the frequency stays as it’s supposed to. I will announce my findings about that tomorrow along with the sine wave files.