Aliasing, Sampling and Interpolation in High-Pitched Stored-Waveform Oscillators

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

I'm finding, at a 44.1kHz synthesis clock, that the sweet spot for band-limited pre-generated stored waveforms (BLPGSW) seems to be 2x oversampling with linear interpolation between samples.

Is that the consensus opinion or does it depend on too many things to even be a meaningful data point?

-------------

A bit more specifically about the results:

With interpolation, I hear NO IMPROVEMENT raising oversampling from 2x to 16x. Without interpolation, even 16x sounds worse than 2x interpolated.

In short, no reason to go ABOVE 2x, and you've got to have interpolation with any reasonable storage size.

However, without oversampling, the sound is absolute crap even with interpolation. Even worse is a "naive math" oscillator doing its best to make a ruler-flat sawtooth, calculating phase directly from amplitude. The VERY worst, surprisingly, is the BLPGSW oscillator without oversampling OR interpolation.

In short, you've GOT to be at least 2x oversampled. And there's no gain to be had above that point.

Slightly unrelated but my "Naive Math" oscillator, with its ruler-flat sawtooth whereby the numerical sample is calculated directly from phase, has quite detectable dirt down to G4. By C4 (=261Hz) however it sounds fine. So a side result of the BLPGSW testing is that I've realized that the "simple math" oscillator REALLY sucks. However I suppose in the fast majority of cases you're filtering out high frequencies or not bending, so perhaps the suckiness needn't always lead to problems.

------------

A bit more specifically about the testing:

I'm playing a sawtooth in the octave A8 to G9 (7040 to 13290 Hz), and pitch-bending manually over a whole tone, listening for aharmonic crap. In the "top class" (2x or more oversampled, interpolated) all I hear is the high frequency. Maybe not 100% pure but any dirt is below the threshold of identification. (And 16x oversampling doesn't improve it, leading me to believe its actually held back by the 44.1kHz synthesis rate--if the dirt isn't a figment of my imagination.)

In anything less than "top class" the aharmonic stuff goes from inaudible to overwhelming the actual signal.

-------------

A bit more specifically about the band-limited oscillator:

My stored-waveform oscillator builds up samples from sine waves according to a harmonic content specification, and a filter specification.

The harmonic content specification can be "sawtooth", "square" etc. (inc. PW square waves from the out-of-phase sawtooth trick), specification of individual harmonics one by one, or use of a harmonic "generator" function that in one (albeit messy) equation lets you specify every harmonic strength based on fundamental frequency, harmonic number, etc. For the purposes of discussion, the important thing is: tell it to generate a sawtooth and it does so from sine waves.

The filter is kind of like a 4-segment envelope, that adjusts the gain of each individual harmonic. Working from the high end: above 18kHz, no harmonics. At 18kHz, harmonics are at -24 dB, linearly rising to +3dB at 14kHz, down to a standard 0dB at 8kHz. The intent of subtly increasing harmonic strength 8k-14k is to offset the missing harmonics over 18k and weak harmonics 14-18k.

The user can specify their own filter parameters (indeed, to simulate a traditional resonant lowpass filter if you want). However the defaults were manually chosen manually such that 1) the output sounds as close as possible to a raw sawtooth; 2) the change between stored waveforms (default every 3 semitones) is inaudible. This was tested by playing unfiltered sawtooths in every octave and manually pitch-bending them slowly across the transition in waveforms, which was logged for this purpose by the software.

Post

In fact, I ended up making the default Oversampling an equation.

Although I can't hear a difference in oversampling 2x and 16x even around A8 (=7kHz fundamental sawtooth), its so cheap (memory-wise) to oversample the high frequencies I can't resist. Meanwhile, the bottom few octaves already have so many samples there's no reason to oversample at all. Very non-scientifically I tried switching to 1x below F2 (about 1.5 octaves below middle C) and to 8x above F7.

Oversample = IF( FreqTo < 100, 1, IF( FreqTo < 3000, 2, 8 ) )

Here's the debug trace showing the frequency from which a sample is used from, and the number of samples.

Wave: from 0, 1960 samples
Wave: from 26.76, 1649 samples
Wave: from 31.82, 1386 samples
Wave: from 37.84, 1166 samples
Wave: from 45.00, 980 samples
Wave: from 53.51, 825 samples
Wave: from 63.64, 693 samples
Wave: from 75.68, 583 samples <-- bending slowly between these
Wave: from 90.00, 980 samples <-- two samples has no audible step
Wave: from 107.03, 825 samples
Wave: from 127.28, 693 samples
Wave: from 151.36, 583 samples
Wave: from 180.00, 490 samples
Wave: from 214.06, 413 samples
Wave: from 254.56, 347 samples
Wave: from 302.72, 292 samples
Wave: from 360.00, 245 samples
Wave: from 428.11, 207 samples
Wave: from 509.12, 174 samples
Wave: from 605.45, 146 samples
Wave: from 720.00, 123 samples
Wave: from 856.23, 104 samples
Wave: from 1018.23, 87 samples
Wave: from 1210.89, 73 samples
Wave: from 1440.00, 62 samples
Wave: from 1712.46, 52 samples
Wave: from 2036.47, 44 samples
Wave: from 2421.78, 37 samples <-- bending slowly between these
Wave: from 2880.00, 123 samples <-- two samples has no audible step
Wave: from 3424.92, 104 samples
Wave: from 4072.94, 87 samples
Wave: from 4843.56, 73 samples
Wave: from 5760.00, 62 samples
Wave: from 6849.83, 52 samples
Wave: from 8145.87, 44 samples
Wave: from 9687.13, 37 samples

Post

ACTUALLY I hate to say it but I've now found cases where the difference between 8x an 16x oversampling (both interpolated) can be heard.

I used my Stored Waveform Oscillator to make a wave with two harmonics: fundamental and 100th. At even 8x there's audible aliasing on that high harmonic, when the harmonic is 5kHz-10kHz.

If I just make a sine wave, no audible aliasing at even 4x.

Does this jive with what anyone would expect?

Code: Select all

[SWO1]
Waveform      = None
Harm1         = 1
Harm100       = 1
Oversample    = 4

Post

Are you doing the standard mip-mapping thing, where you pre-generate various forms of the base waveform with increasingly fewer harmonics? I've found this to work fine with no oversampling but it does make some of the high-frequencies pop in and out when bent/modulated.

Post

kuniklo wrote:Are you doing the standard mip-mapping thing, where you pre-generate various forms of the base waveform with increasingly fewer harmonics?
Hi Kuniklo,

I think the answer is "yes." :)

Honestly I have not studied or even discussed much, and am developing far too much based on the meagre amount I can figure out myself on-the-fly. I don't know what mip-mapping is.

By Stored Waveform Oscillator, what I mean is this:

For first waveform (nominally 22.5Hz) allocate an array of 1960 doubles. Then for each sample, for each harmonic, add in a sine of whatever strength the user wants (eg, 1/n for sawtooth, etc.) Repeat this every (default) 3 semitones up the keyboard. The harmonics only go up to a certain cutoff (default 18kHz) so that even playing that sample 3 semitones faster will still keep the harmonic reflecting off the 44.1kHz Nyquist from re-entering the audible range.
kuniklo wrote:I've found this to work fine with no oversampling...
Very interesting, as I've found something different. Two possibilties are either 1) I've screwed up my implementation somehow, or 2) when you tested you didn't happen to find a test-case that shows up the aliasing at its worst. I'd be very appreciative if you could repeat the test described below and tell me if you really get no aliasing even without oversampling?

In my testing this morning it does NOT work fine without oversampling: try a sawtooth at 7kHz, and slowly pitchbend it up and down a little. Mine, at least, wihtout oversampling, sounds like crap. 2x oversample and it sounds perfect though. And 16x oversample doesn't yield any further apparent improvement.
kuniklo wrote:... but it does make some of the high-frequencies pop in and out when bent/modulated.
This is always going to be case by case: for instance if a 100Hz note only has a fundamental and a 180th harmonic (=18kHz) you will notice when that harmonic disappears, no matter what you do!

But I made a general observation that typically there will be fair amounts of high frequency harmonics... or not. If not, then there are no harmonics to pop out! And if you have a lot of harmonics, then that will serve to mask the harmonics popping in/out.

But to make the pop-out gradual, I implemented a harmonic "envelope." Harmonics requested by the user up until (these are all default values editable per-patch) 8kHz are created as ordered. Then they are boosted slightly, up to a maximum boost of 3dB at 14kHz. Then they are progressively cut, down to a point of being cut 24dB by 18kHz, at which point they disappear.

Therefore, harmonics don't suddenly "pop" out of existence, instead they fade away. And the wide boost from 8kHz to 14kHz should generally make up for the harmonics that are reduced or eliminated.

Post

I've looked pretty carefully at the output of my sawtooth in a spectrum analyzer and I can hear and see next to no aliasing with basic linear interpolation. Maybe you should double-check that you're selecting the right waveform with the right number of harmonics for the base note you're playing?

If you want it to remain alias-free when the user is modulating or pitch bending you have to re-calculate which table exactly you should be using every time you re-calculate the base pitch.

Post

Maybe I'm misunderstanding what you mean by oversampling. I'm actually generating all my wave tables as 10 bits, or 1024 samples, so maybe we're actually doing something similar?

Post

kuniklo wrote:Maybe I'm misunderstanding what you mean by oversampling. I'm actually generating all my wave tables as 10 bits, or 1024 samples, so maybe we're actually doing something similar?
To explain what I mean by oversampling, let me first explain my normal sample.

Say I'm at 44.1kHz synthesis rate. I am making a stored waveform to use from A0 (22.5Hz) through B0 (total three semitones). My "normal" sampling would be 44100/22.5=1960 samples. Oversampling by 2x would mean twice that, 3920 samples.

Post

Swiss Frank wrote:
kuniklo wrote:Maybe I'm misunderstanding what you mean by oversampling. I'm actually generating all my wave tables as 10 bits, or 1024 samples, so maybe we're actually doing something similar?
To explain what I mean by oversampling, let me first explain my normal sample.

Say I'm at 44.1kHz synthesis rate. I am making a stored waveform to use from A0 (22.5Hz) through B0 (total three semitones). My "normal" sampling would be 44100/22.5=1960 samples. Oversampling by 2x would mean twice that, 3920 samples.
That's not what is generally meant by oversampling.

http://www.oblique-audio.com/technique/oversampling
An idiot on Set Theory:
"In some cases there is an object called red that contains everything that is red. In much the same way a pot is a plate."

Post

whyterabbyt wrote: That's not what is generally meant by oversampling.

http://www.oblique-audio.com/technique/oversampling
Thanks for the detailed reference.

I'm familiar with this meaning: running some portion of the audio chain at a multiple of the frequency of other parts. You could do all your calculation at 44.1k, but just when outputting up it by 4x and apply some smoothing algo. Alternately you could produce 44.1kHz output, even though your internal signal processing is at a more accurate 88.2kHz.

In the context of stored waveforms, I feel like I'm doing about the same thing. I'm creating the stored waveform at 2x or 8x the frequency I plan on using it at. I think the main difference from how I read your citation is that I'm doing that calculation when the patch is opened, and buffering the result. I then down-sample it back to my synthesis frequency as I'm playing notes.

The result is that I've got 44.1kHz CPU use, but closer to 88.1kHz anti-aliasing.

This has got to be an obvious way to synthesize with reams of prior art, so it must have a name... and if the name's not oversampling, what is it?

Post

Oversampling is the correct term.

You are sampling the waveform at a wider bandwidth than the analog / continuous waveform. However the waveform is defined, whether it comes in via an ADC or whether its generated by a mathematical sum of sines.

The Oblique Audio page is all well and good, but that is more Joe public oversampling, not really DSP definition oversampling.

IMHO.

That said it can be useful to be maybe say "oversampled wavetables" instead of just "oversampling", in case of confusion. But it was clear to me what you meant FWIW.
Chris Jones
www.sonigen.com

Post

sonigen wrote:That said it can be useful to be maybe say "oversampled wavetables" instead of just "oversampling", in case of confusion. But it was clear to me what you meant FWIW.
Makes sense Chris and thanks.

I took a quick look at Sonigen. Wow, I feel we're working on very similar systems. Mine just has formulae instead of patch cords. (And mine is a CPU hog.)

Post Reply

Return to “DSP and Plugin Development”