Aliasing issues - [phasor~] driving [wave~] exhibits unwanted aliasing
hey all! i could use the collective wisdom of the C74 forum.
i am experimenting with building a wavetable synth, where a single cycle of a waveform (sampled from an old analog synth at 192 kHz 24bit) is loaded into [wave~], which is driven by [phasor~]. however, this adds some aliasing to the mix; i can see frequency bands moving downwards on the spectrogram when i increase the frequency. if i put the oscillator in a [poly~] with upsampling, and @resampling set to 1, i get even worse aliasing somehow.
what am i missing? can't figure it out, even though it seems super simple.
attached is an image comparing the spectra of [cycle~], a single cycle of a sine wave loaded into [wave~], and various combinations of the [poly~] upsampling.
many thanks!
update: it is probably something with my original waveform sample, as addig a phase-inverted sine reveals some "grit" (which i'd like to keep in there, that's why i sampled the old synth in the first place). any ideas how to prevent the aliasing from happening?

upsampling alone does not cause bandlimiting, the upsampling only makes sense in conjunction with a lowpassfilter.
i dont use it myself, but the built-in poly~ anti-aliasing filters are only related to the downsampling at the output of poly~, it can not know at what frequency you run wave~ at and it can not remove the HF content in the buffer.
to prevent the wave~ object from folding frequencies in the sample at nyquist you would have to bandlimit it before you play it pitched up.
or in othe words: this is only possible in discrete time.
a labourios but effective method is to bandlimit the sample to nyquist, and one time to nyquist/2, and one time to nyquist/4 and so on... and then read from these different buffers for each octave.
alternatively you could already record an individual sample for each octave, dont filter them at all, and only pitch them downwards. (this will also capture a few other things more from the original instrument compard to using only one sample)
try all this using a bunch of proper cos~ running at different frequencies (2.5k, 5k, 10k, 20k), then you can hear the effect of aliasing - and your attempts to supress it - best.
Thanks for your help and the detailed answers, Roman!
I have checked out some more forum discussions regarding the topic and gathered some more information. (I have seen your name a lot in those threads, haha)
I have tried bandlimiting the sample with three methods; filtering frequencies above Nyquist using a brick-wall filter on the sample file itself, reducing the sample rate of the (already filtered) sample, and by "treating the symptom, not the cause": low-pass filtering the output inside of poly~ containing both phasor~ and wave~. The first two seem to be ineffective, the third option seems to help somewhat if I set the cutoff to 3 kHz-ish, which is uncomfortably low. Am I missing something here?
I have attached a patcher (MAIN_antialiasing_tester.maxpat) where you can try my wavetables and the methods I've tried so far, so you can see my results. Maybe you can spot what I'm not getting right. :)
Thanks, all the best!
As Roman says, upsampling using poly~ won't be very helpful here. If you are playing wavetable data back at different pitches, then you are moving through the wavetable's sample data at different speeds. This is a resampling problem, and the aliasing comes (in part) from the discrepancies between the current samplerate, and the rate at which you are moving through the wavetable data.
There's a few ways to address this. One is to bake a set of waveforms, typically one per octave, each of which is already bandlimited for a certain playback rate, and blend between these according to the current desired pitch. This is like mipmapping in 3D graphics. You also need the math to identify which tables to blend between according to the current pitch, the table resolution, and the sample rate.
The output can also be affected by how you sample from the wavetable data itself -- i.e. what kind of interpolation you use. With no interpolation you will get another kind of aliasing. With other basic interpolations (e.g. linear, cubic) you may get an undesirable spectral footprint shaping to the waveform; using a better interpolator (e.g. sinc) with more data points (more samples from the wavetable) will result in a cleaner and more consistent waveform as pitch changes.
There's a section in the gen~ book (https://cycling74.com/books/go) all about getting antialiased waveforms for arbitrary wavetable data (in 1D, 2D, and 3D), using both of the above methods. The final example shows using a 16 point sinc interpolation for the interpolation filter, and a mipmap method blending between octaves that is dynamic, meaning that you don't have to pre-bake the octave tables at all.
Thanks for your input, Graham!
moving through the wavetable's sample data at different speeds. This is a resampling problem, and the aliasing comes (in part) from the discrepancies between the current samplerate, and the rate at which you are moving through the wavetable data.
Isn't the @resampling 1 attribute supposed to remedy this? I thought that's what it was designed for.
I have tried baking a bandlimited waveform to test with, however, this seems to also exhibit some aliasing issues. Someone in the Max/MSP Facebook group pointed out that I might be missing a proper zero-crossing from one of the samples I have issues with (thanks Reaper), so I'll try padding it, to see if that helps.
(edit: the lack of a proper zero-crossing was not the issue)
I'll try playing around with interpolation! Thanks for that idea!
I'll have to get that book soon! There seems to be a lot of good stuff in there.
Edit: If I do something crazy, like upsample the poly~ 32 times, put a low-pass filter in the poly~ on the output at SR*0.4, and use @resampling 1, the aliasing is a lot less noticeable. I know that this is a band-aid "solution" to the problem, just figured I'd try it for fun.
(i know 64x upsampling is cursed)

poly~ @resampling 1 will apply some antialiasing filters to suppress frequencies generated above the filtering frequency, in a kind of brute force way. It should work but if you have to upsample 64x then you have also effectively increased the CPU activity by 64x as well.
What Roman & I are suggesting is different -- adjusting the bandlimiting & filtering to match the pitch at which you are playing the wavetable. It is more complicated than just oversampling via poly~, but may be more accurate and efficient in the end.
Also one last note on bandlimiting -- remember to use your ears more than your eyes! Even if you are seeing some aliasing visible in a spectrogram/sonogram, it doesn't mean it is audible -- and a little bit low amplitude aliasing up near 20kHz is not going to be a problem.
in either cases consider what onepole~ does: it is only -6db per octave. so if you want to put it at 20kHz, you would proabably want 20 instances of it, not 1. :)
(4-6 at 11kHz should be fine in practice.)