From Part 1, we have an oscillator. But we need to broaden it to allow scaling of harmonic content based on pitch so that we have all the harmonic content we need at the low frequency end, and, as we move up, eliminate those harmonics that would be above the the range of hearing and mirror (alias) back into the audible range.
How many harmonics do we need?
To start, we pick a lowest wavetable frequency. Let’s say 40 Hz (it could be 80 Hz, it could be 20 Hz—it could be 1 Hz). The highest harmonic that fits under half our sample rate is 551 (22050 / 40 is 551.25). So, we could make a sawtooth table of 551 harmonics. The top harmonic would be only 10 Hz from the aliasing threshold—Nyquist. If we shift that wavetable up one octave to 80 Hz, the top harmonic would double—which means it would alias back down to 20 Hz. And the top two hundred harmonics would be below 16 kHz…Ouch! That means as we glide the wavetable frequency from 40 Hz to 80 Hz, we’ll be inundated with harmonics aliasing downward throughout the audio range.
Of course, we could go the other direction, using that table from 40 Hz downward (and each higher table handling one octave downward similarly). But that means that as we shift down an octave, all of those harmonics at the top of the audio range will shift down to half the audio range—our highest harmonics would now top out at about 11 kHz before the next table takes over and restores harmonics to the top of the audio range. We’ll hear this shortcoming.
OK, we can cheat some and go for the middle ground. Most people don’t really hear to 20 kHz—adults are lucky if they can hear 14 kHz. If we go for 368 harmonics, that puts the top harmonic at 14.72 kHz for 40 Hz, and the alias would fold back to 14.66 kHz (44100 – 14720 x 2). I think this will work fine for me—I did a quick and dirty check a while back and could hear a 14 kHz tone, but not 15 kHz. The rest of you are on your own. Just kidding (and it ends up my hearing’s not that bad anyway—more later, in the “end notes” installment). Instead of requiring each subtable to cover an octave, we could use twice as many and have them cover half of an octave. Or an even smaller increment. Or we could just oversample the oscillator and expand our shallow frequency headroom greatly, from a few kHz to tens of kHz.
I’ll continue to develop this oscillator in octaves in order to keep the explanation simple (and because I’m going for “good” quality, to leave you to decide if you can sacrifice some quality for performance or memory, or whether you want to improve quality), but the extension to use more closely-spaces subtables is trivial. Oversampling 2x is also easy, but will complicate the explanation—you’re on your own if you want to go that way, but it’s a good learning exercise.
First, let’s back up and figure out how long our tables need to be. Recalling that we need to sample a signal at greater than twice the highest frequency component, that means that for 368 harmonics, we need at least 368 x 2 + 1 samples, or a table length of at least 737 samples. But also remember that we’ll be using linear interpolation, so we need to oversample for good results. That means at least 2x, for 1474 samples.
We have good reasons to choose a table length that’s a power of two, however. There are optimizations we can do that take advantage of binary arithmetic, such as zero-cost wrapping of the table index for fixed-point indices, for instance. Plus, we get a huge boost in building these tables if we use the FFT, compared to summing sine waves individually. Also, we’ll definitely want to use the FFT if we let users define their own waves—and the FFT is best suited for tables whose lengths are a power of two. From 1474, our next highest power of two is 2048, which gives us a bit more oversampling as well. Memory’s cheap, so no problem going bigger, but let’s see where this gets us.
The bottom subtable will be used for that first octave and all pitches below. The second subtable takes over from 80 Hz to 160 Hz, and so on, as frequency doubles (and pitch goes up one octave). We use a suitable, progressively smaller range of harmonics for each table as we go up—dropping the upper half of our harmonics for each higher octave. The top table will be a sine wave, always, so we can just add tables until the last one has one harmonic.
Next: let’s hear the results in Part 3
A probably better method is MinBLEPs, where the wavetable contains just the transition edge, anti-aliased, and then you sum it with the naively-generated square or sawtooth waves to effectively cancel out the aliasing. http://www.experimentalscene.com/articles/minbleps.php
I’m very familiar with MinBLEPs, and implemented them years ago. But “better”…they become more expensive at higher frequencies, and you can’t generate arbitrary waveforms. In part 1 I showed a sine wave from wavetable, and in part 3, a triangle—can you get those from a MinBLEP? No. And capturing (or computing) an arbitrary single cycle and translating that into an anti-aliased oscillator across the audio range…not with MinBLEPs. Sure, I realize you’re talking about saws and rectangles, but a MinBLEP oscillator is not “better”—just another way of doing a small subset of what a wavetable oscillator can do. But maybe it’s the only subset you care about, and it’s a valid choice.
However, for adding hard sync to our wavetable oscillator…that’s where MinBLEPs become interesting for me. (And I had already planned to mention this before the oscillator series ends.)
Thanks for reading!
Actually that is not quite true. I have a MinBLEP based saw oscillator that is done with about 10 integer ALU operations that can generate totally aliasing free sawtooth, double that and you have a PWM capable square wave or a triangle wave, all hardsyncable, and the CPU demand is not related to generated frequency, plus is extremely low. Nonetheless, a wavetable oscillator is an extremely important part of a synth, so thanks for sharing the article.
It’s not clear which part you’re saying is “not quite true”. The higher the frequency, the more often you need to generate minBLEPs, to the point that they overlap at high frequencies (in the general case). No?
Indeed, hard sync was one of my favorite tools on the old modular analog synth, and naive (aliasing) sync on a digital oscillator doesn’t cut it for me. I do plan to write an article on the topic (but I have a couple already in the queue ahead of it).
Thanks for the comments—and feel free to post links to audio or code of your oscillators if you’d like.
You’re correct of course, with minBlep there is an added CPU cost with higher frequency, thus “minBlep” is a wrong term for what I’m doing. I’ve implemented the saw and triangle oscillators from this paper (see section 1.5), and I can generate arbitrary frequencies without any change in CPU cost, and with branchless code. For the time being, I’ll keep the actual implementation to myself, but it is (almost) self evident from the linked paper.
Thanks Robert, that sounds very interesting (and efficient). I came across that paper just recently, but haven’t yet read it yet. I might give it a try after try I finish my own thoughts on hard sync.
How close to publishing your article on Hard Sync are you? I’d quite like to read that sometime…
I started down that path several months ago, but jumped off on different topics, including an article and code on envelope generators (mostly written late December!) that I need to finish editing and publish. But I’ll try to keep hard sync high on the list—thanks for the prod.
“In part 1 I showed a sine wave from wavetable, and in part 3, a triangle—can you get those from a MinBLEP?”
yes actually. you need minblamp – a ramp, and higher orders. 3,4,5 are good for sines although the usual implementation is just to use the squared parabolic approximation and only apply the higher order filter to sync which yields very close to perfect results.
yes, any waveform can be generated with better results than wavetables. that doesn’t mean tables are useless. it only means they’re obsolete in most cases. the concern about expense at high frequency is justified in some cases. you have to also consider the lack of expense at low frequencies and question how often high frequencies occur.
with a table of reasonable length you’ll tend to need at least eight samples, so that gives sr/8 as the threshold where you’ll start to expend equal and then more than one sample of filter per sample of output.
by the way, it’s 5512.5hz for a ramp or other single transient waveshape at 44.1k. been playing many high frequency whistles lately? again it’s entirely justified but you must question exactly how reasonable it is to make the assumption a majority of content will be above that level. for the pulse we have two transients so our limit is 2756.25, a little more reasonable.
yet still it’s quite clear that in general we’re dealing with frequencies in the range of 500hz, not 2500hz. at such frequencies the filter samples will occur with large spaces between and efficiency will quickly close to the naive case.
OK, but you’re changing the rules now when you answer “yes” (moving to relatives of MinBLEPs). I presented a wavetable oscillator for its generality. You can’t say “bah” into a mic, extract a cycle of it, and use that as your oscillator with MinBLEPs. Like I said, they are nice, and I especially like them for hard sync, but this is a tutorial on wavetable oscillators. And I disagree that “any waveform can be generated with better results than wavetables” and “they’re obsolete in most cases”—unless you are restricting yourself to “virtual analog” (with the classic waves).
But even more importantly, I think that learning about how wavetable oscillators work, and why naive approaches fail, helps with the understanding of digital audio in general. And that’s the purpose of this blog.
i should have also mentioned that it’s also possible to generate wavetables for blep oscillators. as you render your waveform at higher frequencies some information will be lost. if a specific transient does not contribute to the anti-aliased result at a particular frequency it can be replaced. for
example let’s say we have a pulse wave with a +2octave pulse overlay on it. sort of like the alpha-juno waveforms, same idea. once we get to a frequency high enough that +2 octave is beyond nyquist we can completely drop these pulses. they’ll be replaced by a dc offset and ripple which can be made inconsequential.
that is just a rare case, simply exceptional at best, it’s extremely rare! in the cases where it is needed the implementation is also like the rest of minblep definitely non-trivial. there are a lot of things to work out to have it perform as desired and many trade-offs to be made.
it is possible though and it exists in a working state.
Some variant of DPW is quite efficient for antialiasing of wavetables see http://forum.cockos.com/showthread.php?t=179974 for an implementation in Reaper jsfx.
Thanks a lot for your codes, I have been using them a lot to make some patches for Rebel Technology. Some of them are fully operational on their Owl module.
I am now trying to load external single cycle wav files in order to make a morphing synth. I am having troubles filtering those external waveforms. I tried biquads, FIR but I still have aliasing and gain difference when WTs cross.
I am now working without fft function but I am wondering: What is this 3.0 factor for in the maxharm formula?
Patches I published: https://www.rebeltech.org/patch-library/patch/
All my patches: https://github.com/alexniger/OLWpatches-RebelTech
The 3.0 factor is related to the “How many harmonics do we need?” section of Part 2. The code has each wavetable handling one octave of range. It’s easier to explain graphically, but I’ll give it a try in words: Let’s say you have a sawtooth (all harmonics) wavetable that handles the octave starting at 5 kHz, with a sample rate of 48 kHz. You’d have four harmonics maximum, at 5, 10, 15, and 20 kHz, because any higher harmonics would alias from the start. Shifted up one octave, that would become 10, 20, 30, and 40 kHz. Obviously, the third and fourth harmonics would alias (to 18 and 8 kHz). So, one solution would be to limit the table to two harmonics (5 and 10k). That’s a depressing choice, because it means that while our oscillator would be alias free, it would also be slightly dull.
Instead, what if we used three harmonics (5, 10, 15k), and lived with the 18 kHz alias when the table is shift up a full octave? For musical sounds, higher harmonics are weaker (one-third the level of the fundamental, for sawtooth), people don’t heard 18 kHz well, especially in the presence of another signal (the non-aliased harmonics), and a synthesizer usually runs such an oscillator through a lowpass filter anyway.
The 3.0 factor in the denominator is because the optimal point for this tradeoff is a third of the sample rate. A harmonic at a third of the sample rate, shifted up and octave, becomes two-thirds of the sample rate, which aliases to…a third of the sample rate. In order words, if you pick 16 kHz (for 48 kHz sample rate) as the highest harmonic you will allow, the lowest possible aliasing, when shifted up an octave, will also be 16 kHz.
It’s tradeoff—we accept a little aliasing that’s nearly impossible to hear for simplicity. We could make a completely alias-free oscillator by running at double the sample rate and down-sampling, but the oscillator wouldn’t be nearly as efficient.
thanks you for this article, that is great! I finding that ((2048 length * 10 sub-tables) + linear interpolation) is fine for most cases. but if you wanna go with no aliasing at all it will probably be better to use 20 sub-tables with 4points interpolation
I was trying to follow your instruction and implement it in Reaktor at first I toke a “perfect” saw and removed its harmonics with “brick-wall EQ” and created 10 sub-tables that way. following your instruction, one thing I forgot to do is to start with 368 harmonies right after the first “perfect” saw, and start resampleing the perfect saw that is why i post my first comment about aliasing..
but now I have fond this tiny gem called WCreate & WMorph with is cmd program that create tables for you here is a link
after using this program I now have successfully created anti-aliased sawtooth oscillator with linear interpolation that only clicks at high frequency slow sweep
Reaktor6 WTable AA Saw + LinInterp
Hello, a very nice article. I have developed a synthesis engine based on band-limited wavetables. I am facing the following problem, for the higher pitch, the sine wave generates artifacts which I think is aliasing. If my understanding of the approach is correct, I should not have alias since the sine wave contains only one harmonic by definition. FYI, my wavetables are 128 samples 8 bits. Is there any other possible cause for this?
I think your problem is the 8-bit quantization. If you listen to an 8-bit sine wave, it sounds more harmonically rich than a sine wave, due to the quantization error.
Question: since memory is cheap, why not use n-table, with n=number of harmonics?
So instead of “double the freq, half the harmonics”, just remove a single harmonics (the last) on each tablet.
What could be the disadvantage of this approch?
There’s no real advantage to using more tables than you need, and if you have hundred of tables, it could take more cycles to select the appropriate table. There’s greater initialization time, which may or may not matter. There’s a lower change of a cache hit—there’s no point in making that a prime concern, but a more efficient implementation will end dup with more hits.
To be clear, the “one table per octave” is just an efficient and minimal implementation. Check the more recent wavetable articles for more info. Wave table articles
I think it depends about the playing waveform, isn’t?
For a saw wave, of course… but what for custom waves?
Example (which is why I opened this question): I have one custom wave with 3 harmonics: 1°, 16° and 31°; first table play all 31 harmonics, the second the first 15 (31 / 2 = 15, not 16 due to floor), so actually swap table at 1896.77419hz will truncate the 16° harmonic, which can be hear as single harmonics on top on first table at its end, and I can clearly get a drop/click switching between those tables.
if “one harmonic per table” doesn’t have many advantages, how would you cover this kind of “custom” wave?
I’m not sure that I understand everything you’re asking (not sure about ‘if “one harmonic per table” doesn’t have many advantages’). But as far as clicks, in some cases cross fading will be acceptable, or you can oversample. If you do nothing, you’ll have clicks in cases where there are only a few harmonics, and one that has a substantial amplitude drops out.
It exactly what I’m talking about, your last statement. Having few harmonics, dropping half the harmonics can introduce huge drop outs.
In my case, switching table 1 near its topfreq (with 3 harmonics, or better 2, the 1° and 16°, since the 31° will be already inaudible at that higher freq) to table 2 drop out the 16° harmonics, which will so perfectly audible at table 1.
So my question, reducing the number of harmonics that I remove on each table. Instead of half… since for my config I won’t have more than 512 harmonics available…
Like I said in my first reply, the newer articles go into more detail about tradeoffs in the number of tables and where the switching happens. Did you go through those articles? You’re still not completely free of clicks when the harmonics are few, if they are a problem you need to cross fade, of go to a higher sample rate and downsample the result.