Oversampling in Reaktor, Part II
This tutorial is the second in a series on techniques for oversampling in Reaktor. We’ll be expanding upon our work from last time to build a polyphase filter. First, we’ll cover the basics of oversampling.
This tutorial also assumes basic knowledge of filters. Our series on filter design can be found in it’s whole here, only the first few are relevant to this discussion.
Generally, the purpose of oversampling is to reduce aliasing in a small part of a structure (for more on aliasing, please check our tutorial on the subject here). A perfect example is in the use of saturation or distortion algorithms, which typically use non-linear functions to add harmonics to a signal.
A very simple function of this type is:
y(x) = x / (|x|+1)
If the input has high enough frequency content, this function will cause aliasing, which will add some unwanted frequency content to our signal. By oversampling the input to this function, we can substantially reduce this aliasing, however.
The general concept works like this: to oversample, we insert zeros between each known sample point. For example, let’s say we have a signal with following input:
0.1, 0.15, 0.2, 0.25
Now let’s assume we want to oversample by a factor of 4. To do so, we insert 3 zeros between each known sample point:
0.1, 0, 0, 0, 0.15, 0, 0, 0, 0.2, 0, 0, 0, 0.25
This signal is then sent through a low pass filter, after which any operations which require a higher sampling rater can be done. This is known as upsampling, or interpolation.
Next, we can do any audio processing that we wish.
Using our example of oversampling by 4, our Nyquist limit is now 4 times higher than our original sampling rate can represent. Therefore, we can again lowpass the signal to remove any frequency content above the original Nyquist.
Finally, we need to get back to our original sampling rate. We can do this by simply discarding 3/4 of our output values from the second low pass filter. This process is referred to as downsampling, or decimation.
Now let’s talk about how we can modify the filters designed in the previous tutorial to work as an oversampling, or polyphase, filter. The basic design of an FIR filter makes the insertion of zeros between each sample point very easy.
If we take a zero-stuffed signal like the one we were discussing above:
0.1, 0, 0, 0, 0.15, 0, 0, 0, 0.2, 0, 0, 0, 0.25
And feed it into an FIR filter with 4 taps:
y[n] = a0*x[n] + a1*x[n-1] + a2*x[n-2] + a3*x[n-3]
Where x[n] is the input, y[n] is the output, are a0-a3 are coefficients. At any given point in time, 3 of the 4 x[n] values will be equal to zero. To calculate the output, we can actually ignore all of the zero-valued inputs since they do not contribute to the output in any way.
Therefore, even though the filter has 4 taps, we only need to calculate one tap for each output. Let’s take a look at the output of such a structure:
x*a0, x*a1, x*a2, x*a3, x*a0, x*a1, x*a2, etc etc…
Hence, each input gets multiplied by each coefficient, in order, and sent to the output. Let’s get started on some building!
IMPLEMENTATION IN REAKTOR
Building a filter that works this way is not so hard in Reaktor, however, there is one problem – making a filter with a single output that sends out 4 values for every 1 value received is very inefficient in Reaktor. The only way I know of to accomplish this is to use an Iteration module running at audio rate and merging the outputs with an audio signal in core.
However, there is another, easier solution. It requires that the filter have as many outputs as the oversampling factor. Let’s make an example.
For the purposes of this tutorial I will create a filter with an oversampling factor of 8, and a total of 64 coefficients. So we need to create a filter with 1 input and 8 outputs. Each output is connected to it’s own sub-filter.
Since we are oversampling by a factor of 8, we know that each incoming value is followed by 7 zeros. In reality, the zeros do not need to be added to the incoming audio stream, they are simply implied, as we will see in a moment.
In the first sub-filter, the incoming value is multiplied by the first coefficient (a0). There are presumed to be 7 zeros between this input and the previous audio input we received. Therefore, the next 7 coefficients can be ignored. In our example case of an 8x polyphase filter with 64 taps, the bottom sub-filter ends up using coefficients a0, a8, a16, a24, a32, a40, a48 and a56.
For the next sub-filter, we add a theoretical zero into the audio stream and calculated again. Here, the incoming value ends up getting multiplied by a1 instead of a0, then a9, a17, etc. For each following sub-filter an extra theoretical zero is added, until all 64 coefficients are used. Each sub-filter, then, makes up 1/8 of the whole filter.
Here’s a macro I created that contains a single tap for 8 sub-filters simultaneously (this macro is an edit of the last structure shown in the last tutorial):
The incoming values of 1-8 contain the running total for each sub-filter. The -> input is the incoming audio, which is multiplied by a different coefficient for each sub-filter, and then gets added to the total for each filter. The Pos value contains the coefficient number to read out of the table.
By connecting several of these macros in series, you can create 8x oversampling filters with any number of coefficients! For example, here’s our finished 64-tap version:
The S input is, of course, a table containing the coefficient values.
An interesting aspect of this polyphase filter is that whereas you would normally need 64 read/write modules to make a filter with 64 taps, here we only 8 reads and writes per sample.
ON CREATING PROPER COEFFICIENTS
One thing that initially had me tearing my hear out trying to implement this is that I forgot that the filter coefficients are actually dependent on the sampling frequency. Therefore if you want to create a filter for upsampling, you need to calculate the coefficients with a higher sampling frequency. This is simple:
I simply edited the Event Core Cell feeding the Event Table to match the above picture.
This tutorial has covered the creation of a filter to upsample an audio stream by 8 times. Next time, I’ll cover a way to downsample and show how we can use this technique to substantially reduce aliasing in a saturator, with an included download.
Thanks for reading!