Oversampling


#1

I plan to implement oversampling in a plugin I am working on, and need some guidance.

For upsampling, Juce’s LagrangeInterpolator class looks appealing so that is what I plan to use. I am less sure about the downsampling process.

My first question is: can LagrangeInterpolator be used to filter & downsample too, or is that not its purpose?

Assuming that LagrangeInterpolator is not appropriate to use for decimation, my research has led me to FIR filters. If I understand correctly, FIR filters are good for decimation since you only need to process the samples that will be used in the output (downsampled) buffer, and not bother to filter the samples that will be thrown away, which makes it faster than IIR?

However when it comes to implementing an FIR filter in Juce I am clueless. Thus far the extent of my experience with filters is using Juce’s IIR filter classes, so I would be very grateful for any tips for how I might go about implementing an FIR filter for the purpose of decimation?

If you think I am barking up the wrong tree and should be pursuing a different approach to oversampling, please set me straight!


#2

Depends on why you need oversampling and what you’re going to do. But yes, FIR does give the best results and can achieve linear phase (edit: at the cost of linear delay/latency).

The process you’re referring to is called polyphase filtering. Also, you want to match the quality of the upsampling and downsampling. Artifacts in the upsampling don’t “disappear” with a good downsampling filter.

Here you can literally design the filter you want, and it spits out C code: http://t-filter.engineerjs.com/


#3

Thanks Mayae! Once again you’ve been very helpful. The filter generator you linked to is great, and I have found a similar tool at http://www.arc.id.au/FilterDesign.html.

My challenge now is to understand how to actually implement those FIR taps in Juce, but I found a thread at KVR which I think will point me in the right direction: https://www.kvraudio.com/forum/viewtopic.php?f=33&t=457129 (you actually contributed to that thread).

From what I gather more taps means a narrower transition band and a more accurate filter. I assume that there is a tradeoff to be made between computation/memory and the number of taps in the filter. Are there any guidelines that can be used to determine the appropriate number of taps? I guess I am asking: how many taps is too many?

To give some context to how I intend to use oversampling, there are two areas where I plan to use it: in a saturation plugin and in a chorus plugin. e.g. upsample/interpolate -> saturation -> filter -> downsample.


#4

In very broad generality, the more taps you use:

  • the more computationally expensive the filter is
  • the steeper the features you can have in the frequency domain
  • the greater the latency

You’ll want to use the smallest number of taps possible to obtain your desired filter characteristics, which you can get by experimenting and looking at the resultant frequency responses.


#5

Yes, or you could search for polyphase implementations on Google, for instance the first thing that pops up:
https://christianfloisand.wordpress.com/tag/polyphase/

For chorus, the modulation speed will determine the amount of needed oversampling; assuming your delay line is perfectly resampling (for the sake of this discussion it is irrelevant). This can be approximated in terms of FM modulation (I believe?). There’s a current interesting thread here: https://www.kvraudio.com/forum/viewtopic.php?f=33&t=479166

For saturation, no amount of oversampling will ever be enough so I’ll say it’s a matter of testing out some things and basically determine the amount of aliasing you’ll be able to live with…

Another thing: There’s a certain breakpoint, as your # of taps increase, where it’s computationally more efficient to do the filtering (convolution) in the frequency domain using FFTs. It gets more complicated to compare when you use polyphase filtering and that also depends on your oversampling factor…

For the rest, what @t0m said. The number of taps also depends on your oversampling factor. It highly depends on your context, but I would say aim for a latency no more than 1-5 ms. That’s roughly in the area around 32-128 # of taps using low sample rates.


#6

Thanks Mayae. I have briefly looked into Polyphase filtering, though I think I need to wrap my head around regular FIR filters first. Baby steps…

The KVR thread you linked to is actually mine! I started this thread at the Juce forum rather than KVR since my original question was about LagrangeInterpolator, but the two threads are beginning to converge! Both threads are giving me lots to think about.

I hadn’t realised that the speed of the chorus’ modulation was tied to the oversampling ratio (though I guess it makes perfect sense!). I have noticed that some chorus effects (e.g. the chorus in Sylenth) are oversampled x2 so that was my plan.


#7

I did consider whether it was you, such a coincidence but hard to know you go under different names :wink: In all seriousness, if your chorus doesn’t exceed 5 Hz the aliasing will be so low it will be completely unnoticeable, so no oversampling needed - SO LONG as you don’t have any nonlinear effects in the chorus… I would rather focus my efforts on getting the original resampling of the delay line to be good.


#8

Good to know! I need oversampling for a saturation effect anyway, so I’ve gone ahead an designed a class that uses LagrangeInterpolator for upsampling, and then an FIR filter during the decimation phase.

When I have time I may tackle polyphase filters, but for now I am stoked just to have it working. Thanks for the help Mayae and t0m.


#9

I’ve got a JUCE wrapper for managing polyphase filters using Laurent De’Soras’ HIIR library. Haven’t made it a module, but am happy to share.


#10

Please do :slight_smile:


#11

Pm me with your email and I’ll send you the code…


#12

Hi Andrew, just tried to include HIIR into my code.
Looks like it does not compile on OSX out of the box, but maybe my issue. Is it required to rewrite some code? I’m a also interested in your code or some hints :slight_smile:

I was able to fix it. There was an issue with the header include paths. I had to change that in the library as a quick fix.


#13

Hey kunz, pm me with your email address and I’ll send my code


#14

Thanks for your help. I could fix the issues. It works now. What a great library :slight_smile: