COCKOS
CONFEDERATED FORUMS
Cockos : REAPER : NINJAM : Forums
Forum Home : Register : FAQ : Members List : Search :
Old 03-21-2011, 02:51 AM   #1
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default Bessel filter implementation

Hi,

I have added a low-pass Bessel filter (besselfilter.h) implementation using matched Z-transform to my WDL repository. I have extracted this implementation from from the source code of mkfilter.

I have divided the implementation into three classes: one for calculating coefficients (WDL_BesselFilterCoeffs), one for the actual filter (WDL_BesselFilterStage), and a third class combining both (WDL_BesselFilter).

Here are a couple of (pseudo) code examples of its use:

Example #1:
Code:
// 8th order anti-alias filter
#define WDL_BESSEL_FILTER_ORDER 8
#include "besselfilter.h"

int oversampling = 8;

WDL_BesselFilterCoeffs bessel;
WDL_BesselFilterStage filter;

bessel.Calc(0.5 / (double)oversampling);
filter.Reset();

for (int i = 0; i < nFrames; ++i)
{
	filter.Process(inputs[0][i], bessel.Coeffs());
	outputs[0][i] = filter.Output();
}
Example #2:
Code:
#include "besselfilter.h"

int order = 4;
int oversampling = 8;

// 2 cascaded filters
WDL_BesselFilterStage filter[2];
filter[0].Reset();
filter[1].Reset();

WDL_BesselFilterCoeffs coeffs;
coeffs.Calc(0.5 / (double)oversampling, order);

for (int i = 0; i < nFrames; ++i)
{
	filter[0].Process(inputs[0][i], &coeffs);
	filter[1].Process(filter[0].Output(), &coeffs);
	outputs[0][i] = filter[1].Output();
}
Example #3:
Code:
#include "besselfilter.h"

int order = 8;
int oversampling = 8;

WDL_BesselFilter bessel;
bessel.Calc(0.5 / (double)oversampling, order);
bessel.Reset();

for (int i = 0; i < nFrames; ++i)
{
	bessel.Process(inputs[0][i]);
	outputs[0][i] = bessel.Output();
}
Tale is offline   Reply With Quote
Old 03-21-2011, 03:01 AM   #2
junioreq
Human being with feelings
 
junioreq's Avatar
 
Join Date: Aug 2008
Location: Buffalo NY
Posts: 1,091
Default

Tale this is Great! But the first thing that popped out was the oversampling.. Could this be "modded" to work on its own oversampling without the filter? Seems oversampling for distortions etc is just way over my head at the moment. Thanks for the class though, its so nice to have these things built in and ready for use.

~Rob.
junioreq is offline   Reply With Quote
Old 03-21-2011, 04:55 AM   #3
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

wow, thanks for this tale!

rob - the filter is part of the oversampling - you need to filter out frequencies above the nyquist when up or down sampling
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 03-26-2011, 03:22 PM   #4
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

I tried to include besselfilter.h in an Xcode project today, but my MacBook wouldn't build it because _hypot is supposed to be called hypot (although Microsoft seems to disagree). Anyway, this has now been fixed (see my Git repository).
Tale is offline   Reply With Quote
Old 09-18-2011, 06:38 AM   #5
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default Added denormal fix

I have added the option to zero denormals in the output of the Bessel filter. I have also added an example using this filter for oversampling:

Code:
mAntiAlias.Calc(0.5 / (double)mOversampling);

...

double sample = <input sample>;
double output;

for (int j = 0; j < mOversampling; ++j)
{
	// Upsample
	if (j > 0) sample = 0.;
	mUpsample.Process(sample, mAntiAlias.Coeffs());
	sample = (double)mOversampling * mUpsample.Output();

	...

	// Downsample
	mDownsample.Process(sample, mAntiAlias.Coeffs());
	if (j == 0) output = mDownsample.Output();
}

<output sample> = output;
The complete (working) example can be found in WDL/IPlug/Example/Distortion in my WDL Git repository.
Tale is offline   Reply With Quote
Old 10-20-2011, 01:42 AM   #6
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

Do you get a dc offset/clicks when you change the drive amount in your example?
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Old 10-20-2011, 04:55 AM   #7
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by olilarkin View Post
Do you get a dc offset/clicks when you change the drive amount in your example?
Yes. Adding a small DC offset and then removing it again is part of the distortion algorithm. I remove the DC offset again by sending the downsampled signal through a high-pass filter, which causes the clicks. I guess it would be better to simply substract the distorted DC offset:

Code:
// Distortion
if (WDL_DENORMAL_OR_ZERO_DOUBLE_AGGRESSIVE(&sample))
	sample = mSilence;
else
	sample = mGain * fast_tanh(mDC + mDrive * sample);

// Add this line:
sample -= mSilence;
Of course this can be further optimized, and the high-pass filter can then be removed altogether. It will still use a tiny bit more CPU than the original version, but there will be no more clicks.

EDIT: I have updated the distortion example in my Git repository (currently on the "next" branch).

Last edited by Tale; 10-20-2011 at 05:28 AM. Reason: Updated Git repository
Tale is offline   Reply With Quote
Old 11-06-2011, 04:30 AM   #8
Mich
Human being with feelings
 
Join Date: May 2009
Posts: 1,265
Default

Quote:
Originally Posted by Tale View Post
I tried to include besselfilter.h in an Xcode project today, but my MacBook wouldn't build it because _hypot is supposed to be called hypot (although Microsoft seems to disagree).
It is supposed to be called hypot() according to the ANSI C99 standard.
But Microsoft is somewhat confused about it.
__________________
Quote:
Originally Posted by vBulletin Message
Sorry pipelineaudio is a moderator/admin and you are not allowed to ignore him or her.
Mich is offline   Reply With Quote
Old 07-24-2012, 02:55 AM   #9
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
Default

I tried this on one of my high gain ampsims, it can't compete with my original polyphase approach unfortunately (for 2X/4X oversampling), aliasing all over the place.. I tried order up to 10

Last edited by onqel; 07-24-2012 at 03:06 AM.
onqel is offline   Reply With Quote
Old 07-24-2012, 06:48 AM   #10
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Yeah, polyphase should surely give you better quality, but the Bessel filter is probably less CPU costly (I hope so anyway). I use 6 consecutive 4th order Bessel filters with 64x oversampling to suppress aliases on naive waveforms, with virtually no audible aliasing. Then again, polyphase might also work just as well in my specific case, but I haven't been able to really wrap my head around it yet.
Tale is offline   Reply With Quote
Old 11-30-2012, 07:44 AM   #11
random_id
Human being with feelings
 
random_id's Avatar
 
Join Date: May 2012
Location: PA, USA
Posts: 356
Default

Thank you for your oversampling Distortion sample. I am trying to implement oversampling in my own plugin and I have two questions (due to my extremely small math/dsp brain).

1. When changing oversampling from 4X, 8X, 16X, etc., should you also change the WDL_BESSEL_FILTER_ORDER? In your example, you have this set to 8, and oversampling set to 8X. I think this determines the filter order and would not be related to the oversampling.

2. You mention DC offset. Is this inherent to the oversampling process? I know that you solved it in your example by subtracting out a similar value.

Thanks
__________________
Website: LVC-Audio
random_id is offline   Reply With Quote
Old 11-30-2012, 08:18 AM   #12
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by random_id View Post
1. When changing oversampling from 4X, 8X, 16X, etc., should you also change the WDL_BESSEL_FILTER_ORDER? In your example, you have this set to 8, and oversampling set to 8X. I think this determines the filter order and would not be related to the oversampling.
The filter order and oversampling are not related. In my example both the oversampling and the filter order happen to be 8, but you could just as well use a filter order of 8 with 16x oversampling (or whatever other combination suits your purpose).

Quote:
Originally Posted by random_id View Post
2. You mention DC offset. Is this inherent to the oversampling process? I know that you solved it in your example by subtracting out a similar value.
The DC offset is part of the distortion algorithm, not the oversampling process.
Tale is offline   Reply With Quote
Old 11-30-2012, 08:40 AM   #13
random_id
Human being with feelings
 
random_id's Avatar
 
Join Date: May 2012
Location: PA, USA
Posts: 356
Default

Thanks for responding to my ignorance.
__________________
Website: LVC-Audio
random_id is offline   Reply With Quote
Old 10-25-2018, 09:45 AM   #14
hannesmenzel
Human being with feelings
 
Join Date: Jul 2018
Posts: 24
Default

Taking into account that this is a rather old thread, how to implement this oversampling algorithm for multichannel plugins?

Looking into the Saturation Example in WDL-OL I see the plugins class testing for mono input. If I'd like to up- and downsample, let's say, stereo in and out, do I need two filters declared in the header? Am I supposed to process the incoming audio twice, declaring
Code:
WDL_BesselFilterCoeffs mAntiAlias1, mAntiAlias2;
WDL_BesselFilterStage mUpsample1, mDownsample1, mUpsample2, mDownsample2;
and then
Code:
mUpsample1.Process(sample1, mAntiAlias1.Coeffs());
sample1 = (double)mOversampling * mUpsample1.Output();
mUpsample2.Process(sample2, mAntiAlias2.Coeffs());
sample2 = (double)mOversampling * mUpsample2.Output();
Thanks in advance!

Last edited by hannesmenzel; 10-25-2018 at 09:48 AM. Reason: Found out how to tag code
hannesmenzel is offline   Reply With Quote
Old 10-28-2018, 02:51 AM   #15
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by hannesmenzel View Post
Looking into the Saturation Example in WDL-OL I see the plugins class testing for mono input. If I'd like to up- and downsample, let's say, stereo in and out, do I need two filters declared in the header?
Yeah, you would need two pairs of up and downsamplers, one pair for each channel.

BTW, for oversampling distortion perhaps a cascaded short FIR filter might me more suited. But you would also need two of those.
Tale is offline   Reply With Quote
Old 10-29-2018, 04:24 AM   #16
hannesmenzel
Human being with feelings
 
Join Date: Jul 2018
Posts: 24
Default

Quote:
Originally Posted by Tale View Post
Yeah, you would need two pairs of up and downsamplers, one pair for each channel.

BTW, for oversampling distortion perhaps a cascaded short FIR filter might me more suited. But you would also need two of those.
Thanks a lot for the answer, will try to implement it in my test plugin.

For the second part of the answer: I'm learning C++ at the moment, my skills are on that level, that I'm totally glad to evaluate pre-written classes and their implementation. But I'll keep your comment in mind until I'll understand more of it.

And thanks for everyone for helping comments out of this community.
hannesmenzel is offline   Reply With Quote
Old 10-29-2018, 03:18 PM   #17
hannesmenzel
Human being with feelings
 
Join Date: Jul 2018
Posts: 24
Default

Would you maybe have a look on the code? I see different output levels depending on the oversampling multiplier.

First the header:


Code:
#define WDL_BESSEL_FILTER_ORDER 8
#define WDL_BESSEL_DENORMAL_AGGRESSIVE
#include "../../WDL/besselfilter.h"

//...

int mOversampling;
WDL_BesselFilterCoeffs mAntiAliasL, mAntiAliasR;
WDL_BesselFilterStage mUpsampleL, mDownsampleL, mUpsampleR, mDownsampleR;
Then in the plugins class I have this function which is called from the constructor, Reset() and the OnParamChange(kOversampling):

Code:
void SRChannel::InitOversampling() {
	mAntiAliasL.Calc(0.5 / (double)mOversampling);
	mAntiAliasR.Calc(0.5 / (double)mOversampling);
	mUpsampleL.Reset();
	mUpsampleR.Reset();
	mDownsampleL.Reset();
	mDownsampleR.Reset();
}
Then in DoubleReplacing I have this:

Code:
for (int j = 0; j < mOversampling; ++j)
{
// Upsample
if (j > 0) {
  *out1 = 0.;
  *out2 = 0.;
}
  mUpsampleL.Process(*out1, mAntiAliasL.Coeffs());
  mUpsampleR.Process(*out2, mAntiAliasR.Coeffs());
  *out1 = (double)mOversampling * mUpsampleL.Output();
  *out2 = (double)mOversampling * mUpsampleR.Output();

// here are my distortion functions, then...

  mDownsampleL.Process(*out1, mAntiAliasL.Coeffs());
  mDownsampleR.Process(*out2, mAntiAliasR.Coeffs());
  if (j == 0) {
    *out1 = mDownsampleL.Output();
    *out2 = mDownsampleR.Output();
  }
}
Finally, OnParamChange in case of my integer kOversampling parameter:

Code:
case kOversampling:
mOversampling = int(pow(2, GetParam(paramIdx)->Value() - 1)); // results in 1x, 2x, 4x ...
GetParam(paramIdx)->SetDisplayText(1, "1");
GetParam(paramIdx)->SetDisplayText(2, "2");
GetParam(paramIdx)->SetDisplayText(3, "4");
GetParam(paramIdx)->SetDisplayText(4, "8");
GetParam(paramIdx)->SetDisplayText(5, "16");
InitOversampling(); // calls the function again, see above
break;
By the way, on compiling I get an assert error concerning this one in besselfilter.h:
Code:
assert(alpha >= 1e-37 && alpha < 0.5);
Can you or someone who knows tell me, what I am doing wrong?

Cheers and thanks in advance!

Last edited by hannesmenzel; 10-29-2018 at 03:49 PM.
hannesmenzel is offline   Reply With Quote
Old 10-30-2018, 02:25 AM   #18
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by hannesmenzel View Post
By the way, on compiling I get an assert error concerning this one in besselfilter.h:
Code:
assert(alpha >= 1e-37 && alpha < 0.5);
That is because you can't do mAntiAliasL.Calc(0.5 / (double)mOversampling) when mOversampling <= 1, so you should handle mOversampling == 1 differently.
Tale is offline   Reply With Quote
Old 10-30-2018, 04:16 AM   #19
hannesmenzel
Human being with feelings
 
Join Date: Jul 2018
Posts: 24
Default

Quote:
Originally Posted by Tale View Post
That is because you can't do mAntiAliasL.Calc(0.5 / (double)mOversampling) when mOversampling <= 1, so you should handle mOversampling == 1 differently.
Thanks, yes, meanwhile I figured that out. Any ideas what causes the loudness differences in the code? I see no system in it, it's somewhat +3dB when 2x oversampled, but then around -20dB for 4x, then again +2dB for 8x...

Likely that I didn't get the concept of the process. I tried to understand the oversampling implementation of this distortion example. Do I have to do additional interpolation on it?

Last edited by hannesmenzel; 10-30-2018 at 04:22 AM.
hannesmenzel is offline   Reply With Quote
Old 11-03-2018, 03:25 AM   #20
Tale
Human being with feelings
 
Tale's Avatar
 
Join Date: Jul 2008
Location: The Netherlands
Posts: 3,645
Default

Quote:
Originally Posted by hannesmenzel View Post
Any ideas what causes the loudness differences in the code? I see no system in it, it's somewhat +3dB when 2x oversampled, but then around -20dB for 4x, then again +2dB for 8x...
Hmm, you're right... I don't really have a solution ATM. Sorry!
Tale is offline   Reply With Quote
Old 11-04-2018, 12:26 PM   #21
olilarkin
Human being with feelings
 
Join Date: Apr 2009
Location: Berlin, Germany
Posts: 1,248
Default

I never had much luck with the bessel filter oversampler.

Lately i've been using the HIIR library and the iPlug2 i've been working on comes with a helper class and slightly modified HIIR...

here is a zip with all you need:

https://www.dropbox.com/s/5yebb3md2e...mpler.zip?dl=0

blog post about it:

https://www.patreon.com/posts/oversampling-in-17662230

oli
__________________
VirtualCZ | Endless Series | iPlug2 | Linkedin | Facebook
olilarkin is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 06:58 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.