Wavetable artifacts

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

I'm developing what is basically a wavetable oscillator for my Android phone. Of course I have encountered and comprehended the problem of overtones which alias when crossing the Nyquist frequency. To remedy this I have made one wavetable for each octav, and keep the phase increment lower than 1. But then, when it approaches higher frequencies and the resolution of the wavetables decreases, there is of course artifacts due to truncation of the phase increment. To remedy that I've used a quintic spline interpolator to approximate each sample value, which made the results vastly better.

But there is still noticeable artifacts, especially at higher frequencies. It seems to me, from googling around (a lot), that few discusses this problem of truncation errors at higher frequencies, and are happy with the results obtained from having f.ex. one wavetable for each octave, seemingly without interpolation.

Is there something I have overlooked with my approach? Is it not necessary for the wavetables at higher octaves to have lower resolution?

I also do not quite comprehend why (apart from simplifying algorithms) wavetables should have constant sizes. For example, a wave of frequency 440Hz requires only ~100,23 samples for one cycle, wich is far from 1024 as suggested by many. Is there any benefit from having multiple cycles of a wave in a wavetable?

Thanks for any thoughts.

Post

I can answer the part that I know something about:
I also do not quite comprehend why (apart from simplifying algorithms) wavetables should have constant sizes. For example, a wave of frequency 440Hz requires only ~100,23 samples for one cycle, wich is far from 1024 as suggested by many. Is there any benefit from having multiple cycles of a wave in a wavetable?
You want a wavetable as a buffer for your waveshape. This reduces computation but it introduces two kinds of error:

1) time quantization (if you want the value for something that should be at sample 4.5 in your wavetable than you have no value at that time)
2) sample quantization (you only know your wave at N points you've never seen some other values in between)

Both problems can be reduced by interpolation but, as you've heard, it gives noticeable artifacts. This is because interpolation is just a approximation of the real wave. So what can we do?

We can oversample the wavetable! This means instead of only taking enough samples for our samplingrate, we use, say, twice the samples. Instead of 100 samples for ONE 440Hz sine period we use 200 samples for ONE period.

This means that since we use twice the samples we would have a sine-value at sample 4.5. (in practice that would be sample 9 though).

Remember that the wave still hast to be bandlimited to the original samplerate but if you achieve that (for sine it's always true) than you gain accuracy on top of your interpolation gains.

About multiple cycles in one wavetable: This would complicate the lookup but as long as the samplerate is not a integer multiple of the wave period it still gives you new "real" sine values and might work. I don't know.

- eth

Post

there's a very well-referenced paper on band limited interpolated tables around somewhere.

the table should be generated for the highest frequency it is to be applied to, bcs each table contains the highest partial that can be rendered w/o aliasing.

and oc you know that the thing you modulate is the speed which you progress through the table..

if you have an octave between your tables, the highest partial will form a 'peak' which is going to be very discernible.. the top of the octave will have freqs to sr/2, the lowest note is going to be almost sr/4. if you did a pitchbend down playing the highest note, you'd hear a nasally 'peak' that would drop an octave too..

now - imagine applying an lfo to a note so that the pitch will overlap two table.. the lfo will suddenly transition between having highest frequencies at sr/2 and highest freqs at sr/4. it will sound really crap :)

you'll get the same kind of crappy 'clickyness' with a higher density of tables. depending on what you want to get away with, you may be able to do every 2nd note (and remember, the table is created for the highest note in the range..) but you'll still hear some weird transitions with lfos.

you also talk about 'artifacts..' there are artifacts from being imperfect (eg. -40 dB and lower) and there are more discrete artifacts from not reading the table correctly.

edit - not knowing what kind of quality, application or waveform you're implementing, or the environmental limitations, without having tried it myself, perhaps a single table (with linear interpolation? it's cheap!) and a chebyshev filter with fixed coefficients would be alright, since the cheby coeffs are proportionate to the samplerate and wouldn't have to be computed (i don't know if there's a more efficient AA lowpass..)
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

First and foremost: Thanks to both of you for insightful replies!
ethrandil wrote:We can oversample the wavetable! This means instead of only taking enough samples for our samplingrate, we use, say, twice the samples. Instead of 100 samples for ONE 440Hz sine period we use 200 samples for ONE period.
I have tested this now, and I must unhappily report that the artifacts actually got worse. What I did was just to stretch the wavetable to n times its original length, so that each sample in the original wavetable would appear n times in the stretched wavetable. Of course I also multipled the phase increments by n.

Here's what I did to stretch the wave:

Code: Select all

float[] stretchIt (final float[] wave){
	float[] stretchedWave = new float[wave.length*2];
	for(int i=0; i<wave.length; i++){
		for(int j=0; j<2; j++){
			stretchedWave[(i*2)+j] = wave[i];
		}
	}
	return stretchedWave;
}
Have you (or anyone else) actually done this, and achieved improved results?
xoxos wrote:not knowing what kind of quality, application or waveform you're implementing, or the environmental limitations, without having tried it myself, perhaps a single table (with linear interpolation? it's cheap!) and a chebyshev filter with fixed coefficients would be alright, since the cheby coeffs are proportionate to the samplerate and wouldn't have to be computed (i don't know if there's a more efficient AA lowpass..)
The environmental limitation is that it will be running on Android phones. This of course means very limited CPU power. Merely calling a Math.round or a Math.sin in the audio thread will cause dramatic dropouts and sluggish UI, at least on my low-end HTC Hero. However, the amount of RAM is not that bad, so precalculating stuff is a good way to go.

Regarding filtering: Would I not have to oversample the wave to a higher sampling rate first, so that the above-nyquist frequencies does not alias, then filter out the frequencies which would potentionally alias, and finally downsample it? Cause if I just apply a lowpass to what I have, the above-nyquist frequencies must already have caused aliasing. Is this correct?

So, as you guys probably already have realized, I'm after a highly optimized way to produce results which are not perfect, but sufficient for consumers not to perceive any "errors" in the sound. A wavetable osc would be preferrable, since it would make it possible to have almost any sound, but if there are other methods of achieving a highly efficient oscillator which does not sound too bad I would be most interested to know about it.

Post

mrjohs wrote: I have tested this now, and I must unhappily report that the artifacts actually got worse. What I did was just to stretch the wavetable to n times its original length, so that each sample in the original wavetable would appear n times in the stretched wavetable. Of course I also multipled the phase increments by n..
That is not surprising since you don't interpolate when you stretch. Use interpolation and you will get better sound.
http://yehar.com/blog/wp-content/upload ... 8/deip.pdf
Now you're actually adding harmonics so that's why you get more aliasing.

Since you only do that particular interpolation you can probably afford a very good one.It will save you CPU cycles on playback. You are effectively trading RAM for CPU cycles.

If you use several tables for different pitches don't forget to filter the higher tables otherwise it's pointless.

Post

mrjohs wrote:

Code: Select all

float[] stretchIt (final float[] wave){
	float[] stretchedWave = new float[wave.length*2];
	for(int i=0; i<wave.length; i++){
		for(int j=0; j<2; j++){
			stretchedWave[(i*2)+j] = wave[i];
		}
	}
	return stretchedWave;
}
Yes, by "oversampling" i mean not to just duplicate the old wave values but to generate new values. If you have a analytical waveform this is quite easy: instead of taking say 100 samples of sine (sin(2*PI*i/N), N=100) you now take more values of sin by evaluating it at more points. For example by setting N to twice it's value.

If this is not possible because the source of your wave is not analytical than you can start with a lower samplerate and use advanced interpolation schemes (say - a sinc approach*) to precompute a larger wavetable. This does not improve your sound quality per se, but since you can calculate this interpolation offline it does not take so many cpu-cycles.

Again: If your wavetable has enough samples, than linear interpolation should do fine. But beware of higher harmonics if you subsample your wavetable, you have to remove harmonics above the nyquist frequency.

- Eth

*) a fast way do do this is to start with a 2^M size wavetable, FFT it, add zeros at the end of the spectrum and transform back!

Post

xoxos wrote:now - imagine applying an lfo to a note so that the pitch will overlap two table.. the lfo will suddenly transition between having highest frequencies at sr/2 and highest freqs at sr/4. it will sound really crap :)
Couldn't you interpolate between the two tables? :-D You would have some aliasing but not too much and you might be able to use fewer tables ^^ (you would have four points to interpolate though - the sample before and after the phase-pointer in the lower and higher table)

Post

i happened across someone at mcgill or ccrma mentioning using band limited interpolated tables an octave apart yesterday, so apparently someone likes it :) the way i remember it, aliasing was not unheard of with early vst. sometimes "it's just neat that a thing does something".

ftr i've been mucking about with naive waveforms today (eg. nonaliased, "pure" shapes) trying to find a filter that will make them usable. i didn't have much luck.

the best result i had was remarkably efficient. this seemed at least as effective as a 6th order chebyshev at .47 with significant ripple.. (read: more intensive iir filter, didn't do much good..)
y[n] = ((x[n] + x[n-2]) * .5f) + x[n-1]) * .5f;

using my crappy laptop speakers (which alias due to the driver anyway) aliasing wasn't overt lower than say A5, and it didn't dent the high end. i think the vst world would balk but it may work for some applications.

sometimes very mechanistic, simple things work, like "rounding saws off a bit".. or say, using a single sine table with phase modulation to bend it into a saw.

i've just uploaded an .exe for generating windowed sinc coefficients to see what that can get away with.. i haven't tried it on naive waveforms yet.
http://www.xoxos.net/software/software.html down the bottom
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

xoxos wrote:i happened across someone at mcgill or ccrma mentioning using band limited interpolated tables an octave apart yesterday, so apparently someone likes it :) the way i remember it, aliasing was not unheard of with early vst. sometimes "it's just neat that a thing does something".
Actually if you can afford to oversample by 2x then tables spaced octave apart are perfectly fine, since by the time the band-limitation point reaches your output Nyquist, you'll add another octave of harmonics. Obviously takes more CPU though, especially as you need a half-band filter then, though simple 2nd order all-pass based half-band isn't too bad.

Post

mystran wrote:
xoxos wrote:i happened across someone at mcgill or ccrma mentioning using band limited interpolated tables an octave apart yesterday, so apparently someone likes it :) the way i remember it, aliasing was not unheard of with early vst. sometimes "it's just neat that a thing does something".
Actually if you can afford to oversample by 2x then tables spaced octave apart are perfectly fine, since by the time the band-limitation point reaches your output Nyquist, you'll add another octave of harmonics. Obviously takes more CPU though, especially as you need a half-band filter then, though simple 2nd order all-pass based half-band isn't too bad.
That's exactly what I did, and it sounds perfect. Because of the filter specs, I had to 2x-sample anyway. And on a side note - if you spend some time doing your bit manipulation well, you can get away with 32 bit ints and still have great precision (I mean the table lookups and interpolations etc).

/Daniel

Post

ethrandil wrote: Yes, by "oversampling" i mean not to just duplicate the old wave values but to generate new values. If you have a analytical waveform this is quite easy: instead of taking say 100 samples of sine (sin(2*PI*i/N), N=100) you now take more values of sin by evaluating it at more points. For example by setting N to twice it's value.

If this is not possible because the source of your wave is not analytical than you can start with a lower samplerate and use advanced interpolation schemes (say - a sinc approach*) to precompute a larger wavetable. This does not improve your sound quality per se, but since you can calculate this interpolation offline it does not take so many cpu-cycles.

Again: If your wavetable has enough samples, than linear interpolation should do fine. But beware of higher harmonics if you subsample your wavetable, you have to remove harmonics above the nyquist frequency.
Well, how can I thank you! What you have written here seems to have led to the solution!

I think I misunderstood the whole approach of bandlimited oscillators. What I did was to generate waves at different octaves (55Hz, 110Hz etc) from a softsynth, so that they would not contain overtones which would alias. This worked fine for lower octaves, but of course for the higher ones table-lookup noise would kick in, and I couldn't figure out how to increase wavetable sizes without keeping the frequencies which would at some point alias.

What I tried now was to take the wavetable at 1760Hz, swap it that with the one for 880Hz, and applying the lp filter in Renoise at about 5500Hz. This doubled the wavetable size, but got rid of the unwanted frequencies. Et voila! My oscillator seems now to be working perfect for the purpose!

I will now try with just one wave generated at a very low note and do a brutal FFT/iFFT lowpass filtering at an adequate frequency for each octave upon initialization of the program. As far as I've comprehended that should take care of aliasing problems, as well as keeping the table sizes equally sized and big enough to not generate (too much) table-lookup noise. As xoxos points out I'll probably encounter some kind of click as the frequency goes from one octave to the other, but I'm pretty damned sure that a simple crossfading ought to get rid of that.

Thanks again for all your replies, which led to the solution! Hope this will be useful for others googling by also.

Post

mystran wrote:simple 2nd order all-pass based half-band isn't too bad.
uuh.. got any eggs for fats? :p is there one in particular you had in mind? :D

*edit* - this is dave@muon's polyphase fiters at musicdsp..?
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

Code: Select all

y[n] = a * a * (x[n] + y[n-2]) - x[n-2];
where a = 0.23647102099689224 for the first pole and 0.7145421497126001 for the second pole??

that worked better when i had it wrong :?




btw:
mrjohs wrote: I think I misunderstood the whole approach of bandlimited oscillators.

I will now try with just one wave generated at a very low note and do a brutal FFT/iFFT lowpass filtering at an adequate frequency for each octave upon initialization of the program.
if you're doing saws, squares and that sort of thing, you know the idea is to build the tables additively with sines so that they don't require any filtering.
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

xoxos wrote:
mystran wrote:simple 2nd order all-pass based half-band isn't too bad.
uuh.. got any eggs for fats? :p is there one in particular you had in mind? :D

*edit* - this is dave@muon's polyphase fiters at musicdsp..?
http://www.musicdsp.org/archive.php?classid=3#39

or see HIIR by Laurent or if you want to design your own, there's papers on the stuff, though right now my Google search seems to fail on the subject and I'm feeling lazy.

Post

xoxos wrote:if you're doing saws, squares and that sort of thing, you know the idea is to build the tables additively with sines so that they don't require any filtering.
Isn't this in essence what happens in an iFFT transform?

I could do it as you outline, but I would like to have the possibility of arbitrary (repeatable) waveforms which I can generate any way I'd like.

Post Reply

Return to “DSP and Plugin Development”