Oscillator Framework part 2: Variable Sample Frequency Wavetables

Oscillator Framework part 2: Variable Sample Frequency Wavetables

Who doesn’t love a wavetable synth? Who in their right mind doesn’t immediately think of a wavetable synth when cataloging the possibility tied up in an Arduino? Quite a few seem to be like minded as there are numerous blog posts and even a fairly in-depth article in Wired about implenting a wavetable synth on the Arduino.

So why aren’t there more running implementations out there? Why can’t you just download one and be running immediately?

There is one reason most wavetable tutorials go no further than a simple mathematical ramp (saw) function. Fractional interpolation.

Fractional interpolation is hard. The problem is that most DAC systems operate at a fixed frequency. Sure, they may have a few to choose from (44.1k, 88.2k, 96k, etc) but those values are fixed. When your wavetable is 1024 samples long and you need to generate an 880hz signal at 44.1khz sampling rate, you immediately have a problem. How do you fit 1024 samples into the space of 55.125 clock cycles? This is the crux. You have to convert the signal to frequency domain – it cannot be done (without sounding like crap) in the time domain. This is where DSP chips shine. The Arduino is not a DSP, it’s a general purpose microprocessor.

So what’s different about the Arduino that can make this easier? What are we missing?

The difference is that there is no fixed-frequency DAC. The Arduino’s DAC is super simple. If you want the value to be 1.5V two seconds from now, you wait the requisite number of clock cycles and set it to 1.5V. If you want it to be 0.9V 2 milliseconds from now, you do the same.

So what we’ll do is not try to fit 1024 samples into 55.125 clock cycles, but instead will adjust the clock frequency to always be an integer multiple of the desired output frequency! This is not a new concept by any means. As with most things related to the modular synth resurgence, all that is old is new again. The Fairlight CMI used the same technique in it’s sample playback engine. Each voice gets its own DAC and that DAC operates at a range of frequencies up to 100kHz.

Our anti-imaging (reconstruction) filter’s corner frequency is 10kHz. Anti-aliasing and reconstruction filters are typically designed to have corner frequencies at the Nyquist frequency which is half the sampling frequency (Fs/2). We’ll set our upper bound at 20kHz. We don’t really have a lower bound understanding that there will possibly be some aliasing that’s hopefully not too bad.

Let’s hear what it sounds like. These samples are based on three waveforms from Adventure Kid’s collection of 4300 single cycle waveforms – the sin, the saw, and a voice tone. The sin is a single harmonic and is useful when you want to hear what distortions are introduced through imaging. The saw is harmonic rich and is good for testing anti-aliasing filters, and the voice tone is somewhere in-between and is just good to see how something a little more useful will sound.

The sweeps are exponential from 24Hz to 6,129Hz and represents a linear sweep from -5V to 5V on the bipolar analog inputs of the nw2s::b.

The sounds were sampled in three locations:

  1. The first is directly off the Arduino’s DAC. This is completely unfiltered and highlights the signal if there were no processing whatsoever.
  2. The second is from the nw2s::b’s DAC output. This signal is filtered through a gentle 3dB/octave anti-imaging filter with a 10kHz corner frequency.
  3. The third is after the nw2s::b signal has been run through a Z2040 low-pass filter with resonance set to 0 and the cutoff frequency about 2/3 of the way open.

Here’s the sin directly off the DAC chip:

Around 0:04 and 0:10, you can hear some imaging. This is the zipper sound of the square edges of the DAC. This is what our anti-imaging filter should take care of. There is also some aliasing heard at 0:34, but I suspect that’s a bug I need to work out in my decimation filter. Still – interesting to hear the difference between the two.

Now let’s hear what you’ll actually get out of the nw2s::b after the imaging filter and output buffer:

The imaging is a little better and the aliasing is still there, but, they’re both better. Now let’s hear it after a more real-world modular patch. This is through the Z2040 LPF.

Yup, better, not perfect, but sin waves are boring. Let’s move to the saw. Here it is straight off the DAC chip:

Buzzy. Good news is that with something like this, there is really no discernable aliasing. Bad news is that you can clearly hear when the oscillator switches from one decimation level to the next. That was to be expected. Let’s see what that sounds like once the output filter of the ‘b is in place.

That’s better. You can only hear the bigger jumps in the decimation levels. Lets see how it sounds in a real patch. Again, through the Z2040:

Hey – not bad! But since sin waves are boring and saws are – well, you probably have more saw VCOs than you know what to do with, so let’s hear a more useful typically wavetable sound. This is a voice waveform straight off the DAC:

You can hear the imaging problems at the lower frequency, so lets see what the output filter cleans up:

Again, it’s better. If you’re not doing sweeps, you probably won’t notice, but let’s see about the in-patch sound:

Well, thats that done. The next steps down this path will be adding morphing wave tables – modulating among any of the 4300 waves available to us from Adventure Kid! That’s gonna really open up potential. Having 4300 waves to choose from out of the box plus being able to load any number of others onto the SD card. Thanks for reading and listening this far!

Submit a Comment

Your email address will not be published. Required fields are marked *