Basic anti-aliasing for non-linear functions

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Mayae wrote:Yes, don't bother with that stuff (especially during the prototyping stage). Just to clarify something I forgot: The vector should of course be a member of your processor class, not a local variable
Yup, I've moved the declaration out of the processor already, it was really horrible :D
Chris-S wrote:FIR interpolation filters have to be very steep, so they are using typically 64 or more taps (using the recent and the 63 previous samples). This gives some delay.
Alright, I think I get it, but I still need to read more to understand everything perfectly :D Thank you.

---

I'll add a C++ question.

I am using Vinnie's DSPFilter library to do the filtering.

Code: Select all

f1->process(buffer.getNumSamples(), buffer.getArrayOfWritePointers());
f1->process(buffer.getNumSamples(), oversampleArray);
If we compare both lines : the first one is working correctly, but of course I am processing the buffer using my oversampleArray (which is currently using the JUCE Array type). So the 2nd line does not work, since the function is requiring an array of pointers instead of just the array.
How can I solve that ?

I've tried many things, especially by replacing the JUCE Array by a vector<float> and using &myvector[0], but it's not working - I guess my understanding of the pointers is not perfect :D

Thanks a lot by advance,

A.

Post

Ivan_C wrote:
And: avoid IIR filters for downsampling (e.g. biquads or the like), FIR filters are the way to go because they have way better phase responses and a much narrower pass-band with less calculations.
Doing that might increase a lot the latency doesn't it ? For a guitar amp simulation for example, I might avoid like hell FIR filters in the up/downsampling
If you consider most guitar amps like to top out somewhere around 5000hz this makes sense. For signals with appreciable content al the way up to nyquist, always linear phase.

Post

Hi everyone,

I'm bumping my own thread, with some new questions.

Since last posts, I studied in particular the "DSPGuide" (http://www.dspguide.com/), including the part related to filters. My understanding of it has improved drastically, but at the same time I can't seem to implement them correctly. I saw a lot of code for doing filters, but haven't found yet a clear/simple/straight-forward example of FIRs in the case of real-time audio processing, i.e. with a buffer.

I tried a simple IIR to filter out the aliasing of a distortion with 4X over-sampling, but it was not enough it seems (I implemented a simple one using JUCE, which has a gentle slope).

So I am now willing to implement a simple FIR to get started with this kind of things. I am not (yet) trying to improve the speed or whatever (I read about polyphase half-band things, still a bit too complicated for me, not sure how to implement it as I am not sure my simple FIR implementation is correct).

Let's get to the point : here is my code (for one channel) :

Code: Select all

//in the header :
	float circularBufferFIRL[25];
	int indexCircular;

Code: Select all

			float filterTaps2[] = { 0.000000,
				0.000584,
				0.002130,
				0.003161,
				-0.000000,
				-0.010089,
				-0.023142,
				-0.025600,
				0.000000,
				0.061571,
				0.145875,
				0.220253,
				0.250000,
				0.220253,
				0.145875,
				0.061571,
				0.000000,
				-0.025600,
				-0.023142,
				-0.010089,
				-0.000000,
				0.003161,
				0.002130,
				0.000584,
				0.000000 };

int indexC = 24;

				for (int sample = 0; sample < oversampledBuffer.getNumSamples(); sample++) { // for each sample

					circularBufferFIRL[indexC] = readerback[sample]; // store the value in the circular buffer			
					
					float result = 0; // initialisation to 0 of the result

					for (int i = 0; i < 25; i++) { // for each tap
						result += circularBufferFIRL[indexC] * filterTaps2[i];	// multiply		
						if(i < 24) indexC--; // move the index except if that the furthest sample, since it's the point which will be replaced by the new sample
						if (indexC <0) indexC = 24; // ensure the buffer is circular
					}
					writer[sample] = result; // output
				}
I got the taps from this website : http://www.arc.id.au/FilterDesign.html
taps.PNG
My oversampling rate is 4x. I am interpolating the samples using JUCE's Lagrange interpolation :

Code: Select all

myInterpolatorL = new LagrangeInterpolator();
myInterpolatorL->process(0.25, reader, writer, buffer.getNumSamples() * 4);
The results I get are the following for a given sine wave with distortion :

NO Filtering
no-filtering.PNG
WITH Filtering
with-filtering.PNG
As you can see it's adding a lot of unwanted frequencies in the middle of the peaks.... something is going wrong but I can't figure out what.

Thanks a lot by advance if you can enlighten me a bit.

At the same time, and although there seems to be a "lot" of resources about half-band filters and optimised FIR filtering for downsampling, if anyone has some simple pointers regarding which would be my next steps after successfully implementing this simple FIR, it would be very helpful.
You do not have the required permissions to view the files attached to this post.

Post

I didn't answer previously, but as I have such a plugin open sourced, you can inspire yourself with it (it's ATK SD1).
So the first question I have to you is where is your IIR or FIR filter in the processing chain? Is it the downsampler?
It seems you are using JUCE to get the x4, so I suppose the IIR filter is for downsampling. To ensure that this process is sound, try your pipeline without any distortion filter. Just do oversampling (with the interpolator), filtering, downsampling. Even at first, to make sure that everything is fine, just do oversampling and downsampling. You should get in both cases the same spectrum response.
Then, depending on your distortion, perhaps the x4 oversampling and IIR filter (which one did you use and what order), is not enough, but this will depend on the amount of HF generated by the algorithm.
First things first! And that's ensuring that without distortion, you have more or less a identity transform.

Post

Thanks for your answer Miles1981 :)
Miles1981 wrote:I have such a plugin open sourced, you can inspire yourself with it (it's ATK SD1).
I had a look at it from a link I found in another thread, but it seemed that you were, ehh, processing the signal differently depending on whether the sample number was even or odd. I will have another look, maybe it was not the file you're mentioning, but for now I was trying to make the most basic possible FIR implementation, and you code seemed more complex to me ^^
Miles1981 wrote:where is your IIR or FIR filter in the processing chain? Is it the downsampler?
Yeah, I am simply doing a 4X Oversampling with Lagrange interpolation, applying a atan(x)-style distortion, filtering (the code I posted in my previous post) and the decimating by taking 1 sample out of 4.
Miles1981 wrote:To ensure that this process is sound, try your pipeline without any distortion filter. Just do oversampling (with the interpolator), filtering, downsampling. Even at first, to make sure that everything is fine, just do oversampling and downsampling. You should get in both cases the same spectrum response.
Tried that.
Oversampling/Downsampling only works fine apparently, it seems transparent with no effect on the signal.
But when I add the filtering, it's not working anymore.
By the way, I've just double-checked that the interpolation works good, by selecting the 2nd sample out of 4 instead of the 1st one when decimating, and it seems all good to me.

The result with oversampling+filtering+downsampling only (no distortion) :
no-distortion-with-filtering.PNG
Miles1981 wrote:Then, depending on your distortion, perhaps the x4 oversampling and IIR filter (which one did you use and what order), is not enough, but this will depend on the amount of HF generated by the algorithm.
Well, now with my new tests without the distortion it proves that the filter is the problem. There must be something wrong in my code, but I don't see what. I've tried to double-check how values behave when the loops are reaching their end, and from my understanding it seems "sound". Wrote the code alone though, so it's probably where the problem lies hehe :)
Miles1981 wrote:First things first! And that's ensuring that without distortion, you have more or less a identity transform.
Once again, thank you very much for helping me debugging this. Really difficult for 'beginners' in DSP to do :)
You do not have the required permissions to view the files attached to this post.

Post

Valenten wrote:I had a look at it from a link I found in another thread, but it seemed that you were, ehh, processing the signal differently depending on whether the sample number was even or odd. I will have another look, maybe it was not the file you're mentioning, but for now I was trying to make the most basic possible FIR implementation, and you code seemed more complex to me ^^
It's actually a FIR behind for the oversampled, it's just coded in an efficient way, as I know that some coeffs are symmetric. Then, if you want the FIR version for the filter, you can have a look at EQ/FIRFilter and the IIRFilter version (as FIR is based on the IIR implementation).
So, still on this, does it work if you bypass the filter (so just oversampler + downsampler)? You should almost have the same as the input. The noise floor seems indeed quite high (around 60dB headroom).

Also, try first without a circular buffer. This will separate the debugging. First make sure that your base filter is OK before optimizing it (premature optimization is the root of evil). And make sure that the circular buffer really helps (because with the two ifs inside the loop, you will have pipeline thrashing that may well cost you more than copying the data in a temporary array with the previous values you need, and this is why none of my implementations have any circular buffers).

Post

Miles1981 wrote:does it work if you bypass the filter (so just oversampler + downsampler)?
Yeah, sorry if I wasn't clear before. It does work with only the oversampler and downsampler.
Miles1981 wrote:Also, try first without a circular buffer. This will separate the debugging.
Okay so I tried that. I even removed the oversampling/downsampling just to implement a simple FIR with a casual cut-off (say 1000Hz), but I must be doing something wrong (which is crazy for such a simple algorithm) :

Code: Select all

			float filterTaps2[] = { 0.000154,
				0.000262,
				0.000407,
				0.000596,
				0.000835,
				0.001132,
				0.001491,
				0.001919,
				0.002420,
				0.002997,
				0.003652,
				0.004387,
				0.005199,
				0.006087,
				0.007046,
				0.008069,
				0.009148,
				0.010272,
				0.011430,
				0.012607,
				0.013790,
				0.014962,
				0.016108,
				0.017209,
				0.018249,
				0.019212,
				0.020083,
				0.020846,
				0.021489,
				0.022001,
				0.022374,
				0.022600,
				0.022676,
				0.022600,
				0.022374,
				0.022001,
				0.021489,
				0.020846,
				0.020083,
				0.019212,
				0.018249,
				0.017209,
				0.016108,
				0.014962,
				0.013790,
				0.012607,
				0.011430,
				0.010272,
				0.009148,
				0.008069,
				0.007046,
				0.006087,
				0.005199,
				0.004387,
				0.003652,
				0.002997,
				0.002420,
				0.001919,
				0.001491,
				0.001132,
				0.000835,
				0.000596,
				0.000407,
				0.000262,
				0.000154 };
			
			// FIR
			if (channel == 0) { // only left channel

				for (int sample = 0; sample < buffer.getNumSamples(); sample++) { // for each sample

					// moving the samples in the delay line
					for (int i = 0; i < 64; i++) { // delayline being 25 values long
						delayLine[i + 1] = delayLine[i];
					}
					delayLine[0] = reader[sample]; // reader[sample]

					float result = 0; // initialisation to 0 of the result

					for (int i = 0; i < 65; i++) { // for each tap

						result += delayLine[i] * filterTaps2[i];	// multiply		
					}
					writeback[sample] = result; // output

				}
			}
With these coefficients I am seeing absolutely no filtering at all, although it is supposed to be a LPF at 0.5kHz... I'm sure my mistake must be very obvious now but several hours of work later I can't find it :D

Note : again I used this website http://www.arc.id.au/FilterDesign.html to generate the coefficients.
Miles1981 wrote:The noise floor seems indeed quite high (around 60dB headroom).
Yeah, I'm also generating the sine wave through Ableton Live's operator, so maybe the algorithm in it isn't as good as it could be ?

Post

Indeed, nothing seems definitely wrong :/
Same result with your IIR filter?

Post

Here are the coeffs I'm using in a distortion plugin.

Code: Select all

// FIR coefficients
// 40 Tap 
// 4500 Hz Lowpass @44.1

  c[0]  = -0.0014165954080014;
  c[1]  = -0.0025521236876232;
  c[2]  = -0.0035276472089329;
  c[3]  = -0.0032173273346744;
  c[4]  = -0.0008256542718032; 
  c[5]  =  0.0036663648370821;   
  c[6]  =  0.0090342435245324;
  c[7]  =  0.0128732889187857;
  c[8]  =  0.0123577391755932;
  c[9]  =  0.0055770573630402;
  c[10] = -0.0070483731815419;
  c[11] = -0.0219662178339273;
  c[12] = -0.0328812206937348;
  c[13] = -0.0324941250461002;
  c[14] = -0.0151543066805541;
  c[15] =  0.0205261865884486;
  c[16] =  0.0702034973614205;
  c[17] =  0.1242588718201388;
  c[18] =  0.1702631673904925;
  c[19] =  0.1967109228679927;
  c[20] =  0.1967109228679927;
  c[21] =  0.1702631673904925;
  c[22] =  0.1242588718201388;
  c[23] =  0.0702034973614205;
  c[24] =  0.0205261865884486;
  c[25] = -0.0151543066805540;
  c[26] = -0.0324941250461002;
  c[27] = -0.0328812206937348;
  c[28] = -0.0219662178339273;
  c[29] = -0.0070483731815419;
  c[30] =  0.0055770573630402;
  c[31] =  0.0123577391755932;
  c[32] =  0.0128732889187858;
  c[33] =  0.0090342435245325;
  c[34] =  0.0036663648370821;
  c[35] = -0.0008256542718032;
  c[36] = -0.0032173273346744;
  c[37] = -0.0035276472089329;
  c[38] = -0.0025521236876232;
  c[39] = -0.0014165954080014;
Response:
Image

With your 65 coefs I get a LP at 800 hz, so something is wrong with your algo.

Post

Alright, thanks for checking out my coeffs. At least now I am confident that these numbers have an effect.

I will look again at the algorithm, can't understand what's wrong haha but maybe it's somewhere else ?

As a side question, how do you guys debug your plugins in such cases ? At the begininng I was simply trying them in my DAW, which is not exactly what debugging is supposed to be. Recently I started having Visual Studio open the DLL inside SAVIHost after compiling, and it kinda allows me to debug the program, add break points and track values using VS's "spies", but it seems to me a bit of a wonky process.

Edit : @Miles well for IIRs I was using the IIRs from JUCE, so I haven't coded them myself.

I've stripped down my code to the simplest possible algorithm, and seriously I'm not getting what's wrong. It's even similar to that one : http://shulgadim.blogspot.fr/2012/01/fi ... ation.html

All I'm getting is a lower volume in general. For instance if I'm feeding it with white noise, then the volume will be lower but I will see no filtering on the frequencies.

Code: Select all

			if (channel == 0) {

				float result = 0; // initialisation to 0 of the result

				for (int sample = 0; sample < buffer.getNumSamples(); sample++) { // for each sample

					// moving the samples in the delay line
					for (int i = 0; i < 8; i++) { // delayLine being 9 values long
						delayLine[i + 1] = delayLine[i];
					}
					delayLine[0] = reader[sample];

					result = 0;
					for (int i = 0; i < 9; i++) { // for each tap
						result = result + delayLine[i] * filterTaps[i];	// multiply		
					}
					writeback[sample] = result; // output

				}
			} 

Post

Valenten wrote:As a side question, how do you guys debug your plugins in such cases ? At the begininng I was simply trying them in my DAW, which is not exactly what debugging is supposed to be. Recently I started having Visual Studio open the DLL inside SAVIHost after compiling, and it kinda allows me to debug the program, add break points and track values using VS's "spies", but it seems to me a bit of a wonky process.
I usually have a test project created with REAPER that contains the plugin under development with some fitting input, e.g. a white noise generator if I test a filter. I then start a debugging session from the development environment that's configured to start the DAW with the test project. Works quite well, i.e. I can set break points, etc.

Other than that you could also write unit tests to check whether your code does what you expect.
Valenten wrote: I've stripped down my code to the simplest possible algorithm, and seriously I'm not getting what's wrong. It's even similar to that one : http://shulgadim.blogspot.fr/2012/01/fi ... ation.html

All I'm getting is a lower volume in general. For instance if I'm feeding it with white noise, then the volume will be lower but I will see no filtering on the frequencies.
To test your algorithm I propose to use a simple unit impulse for the coefficients, i.e. if you have 65 coefficients the first one is set to 1 and all the other 64 remaining ones are set to zero ([1, 0, 0, ...]). If your algorithm works you should get a perfect reproduction of the input that's fed into the filter. If it produces something else then your algorithm is broken.

Such a test is also well suited for a unit test because in the test you can control the input that you send into the filter and check for the expected result.

Last but not least, if you are looking for oversampling filters have a look at half band filters. Because they are symmetric and most of their coefficients are zero they can be implemented quite efficiently. Also please keep in mind that if you downsample you don't need to actually evaluate the samples that you will be throwing away anyway. You only have to push them into the delay line of the filter, so that the pattern looks as follows: push, evaluate, store, push, push, evaluate, store, push, push, evaluate, store, etc. Using these properties you can save a lot of computations.

Another advantage is that the higher the current oversampling factor the more relaxed the half band filters can be specified. So if you for example design the filter that's used to downsample from 8x to 4x the transition region can be quite wide (which means lesser coefficients and lesser computations) because the frequencies that will alias back into the audible range will be filtered out by the filters that follow (4x to 2x, 2x to 1x).
Passed 303 posts. Next stop: 808.

Post

BlitBit wrote:I usually have a test project created with REAPER that contains the plugin under development with some fitting input, e.g. a white noise generator if I test a filter. I then start a debugging session from the development environment that's configured to start the DAW with the test project. Works quite well, i.e. I can set break points, etc.
Thank you for your answer BlitBit.

Actually, I had in mind (naively!) that it would not work to start my DAW from VisualStudio in debugging mode, but maybe it will - I will try !

Just to share a possible technique for fast testing, working at least in Ableton Live :

With Live, the VST folder can be a folder of shortcuts pointing to the actual DLLs. So I put a shortcut to the dll I am compiling. I can keep my DAW open, recompile, then load the VST again, and test it right out. Useful, at least for GUI testing for instance, because it's faster than re-opening the DAW.
BlitBit wrote:Other than that you could also write unit tests to check whether your code does what you expect.
Thanks for the suggestion. I had a quick-look at your wikipedia link, and while I know nothing about it, it seems it will be useful !
BlitBit wrote:To test your algorithm I propose to use a simple unit impulse for the coefficients, i.e. if you have 65 coefficients the first one is set to 1 and all the other 64 remaining ones are set to zero ([1, 0, 0, ...]). If your algorithm works you should get a perfect reproduction of the input that's fed into the filter. If it produces something else then your algorithm is broken.
Yes, I tried that and other similar variants. My problem in that case was mostly : how to compare the input and the output ? I realized I could have exported the audio coming out of the filter to an audio track, but is there are more convenient way ?
BlitBit wrote:Last but not least, if you are looking for oversampling filters have a look at half band filters. Because they are symmetric and most of their coefficients are zero they can be implemented quite efficiently. Also please keep in mind that if you downsample you don't need to actually evaluate the samples that you will be throwing away anyway. You only have to push them into the delay line of the filter, so that the pattern looks as follows: push, evaluate, store, push, push, evaluate, store, push, push, evaluate, store, etc. Using these properties you can save a lot of computations.
Yes, thanks a lot for the details. I am planning to look at half band filters, but since FIRs seemed like a simpler go-to solution, I thought I would learn how to program them first.

Correct note about only processing the samples I will keep, I wanted to do that but ended up stripping down my algorithm because of the problem :D
BlitBit wrote: Another advantage is that the higher the current oversampling factor the more relaxed the half band filters can be specified. So if you for example design the filter that's used to downsample from 8x to 4x the transition region can be quite wide (which means lesser coefficients and lesser computations) because the frequencies that will alias back into the audible range will be filtered out by the filters that follow (4x to 2x, 2x to 1x).
Thanks for the clarification, I knew I had to look at them but I haven't yet gathered much information about this kind of filters. This is going to get me started in the right direction :) !

-------

PROBLEM SOLUTION

Okay so I asked a simplified question on the DSP board of StackExchange, as I had noticed "similar" code requests.

The mistake was at the same time obvious and simple, can't believe I missed it !!

The error was linked to the delay line sample move, instead of :

Code: Select all

for (int i = 0; i < 8; i++) {
delayLine[i + 1] = delayLine[i];
} 
it should be :

Code: Select all

for (int i = 7; i >=0; i--) { // delayLine being 9 values long
delayLine[i + 1] = delayLine[i];
}
Because, of course, in my original code I was simply shifting the same value in the array...

Post

In my case, I first draft all my filters in a Python pipeline before adding the plugin layer. It is quite powerful, as I can ensure that everything works in a non DAW environment and then trust wdl-ol to be compatible with all DAWs.

Post

Probably I should try that too. Why Python and not straight console-style C++ ? (sorry if it's a stupid question ;) )

Post

Valenten wrote:Probably I should try that too. Why Python and not straight console-style C++ ? (sorry if it's a stupid question ;) )
You should use a "scratch" system to test ideas, it helps discover errors in new ideas. C/C++ can be a lot of typing for testing stuff, setting up the output formatting, usually there is something that's faster.

Post Reply

Return to “DSP and Plugin Development”