|
|
|
07-16-2009, 12:55 AM
|
#1
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
up/downsampling
is it possible to do 4x, 8x oversampling in JS? I`m having trouble finding a way to do this.. I`m making a guitaramp and but the aliasing is awful! i only need to to upsample a small section in the code and then decimate again.. any thoughts/examples on this?
i`ve searched, but its hard surfing on my pda..
-John
|
|
|
07-16-2009, 02:03 AM
|
#2
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
you can find a nx oversampling example in:
waveshapers/graphdist
waveshapers/graphdist-dyn
i've also made another simple 2x oversampler (in 'waveshapermulti') via fast power series and fir bandlimit, however i has to be modified if your fx processor has recursive topology.
note that with 8x os an algorithm may end up taking a lot from a modern cpu. e.x.: up to 20%.
aliasing is best to be avoided in the algorithm, instead of dealt with afterward, because the up and down sampling introduces aliasing of its own. :-)
lubomir
|
|
|
07-16-2009, 06:19 AM
|
#3
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
Thanks alot, I will look into it
I wish people commented more in their code though :P
Do you have any documentation/links on the technique you are using in the graphdist plugin? it's a little hard separating the multirate-filters from the rest hehe, but I'll try to see if I understand what you did there.
Quote:
Originally Posted by liteon
you can find a nx oversampling example in:
waveshapers/graphdist
waveshapers/graphdist-dyn
i've also made another simple 2x oversampler (in 'waveshapermulti') via fast power series and fir bandlimit, however i has to be modified if your fx processor has recursive topology.
note that with 8x os an algorithm may end up taking a lot from a modern cpu. e.x.: up to 20%.
aliasing is best to be avoided in the algorithm, instead of dealt with afterward, because the up and down sampling introduces aliasing of its own. :-)
lubomir
|
|
|
|
07-18-2009, 04:40 AM
|
#4
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
Quote:
Originally Posted by onqel
Thanks alot, I will look into it
I wish people commented more in their code though :P
Do you have any documentation/links on the technique you are using in the graphdist plugin? it's a little hard separating the multirate-filters from the rest hehe, but I'll try to see if I understand what you did there.
|
i'm not the author of the graphdist fx and tbh i haven't checked it out
but basically in the "zero stuffing" method:
you have an up-sample filter which has 1 input and N outputs. N streams are processed in the case of a basic ws effect in parallel. then a down-sample filter processes N inputs to N outputs, where only one output is significant and N-1 are discarded.
you may want to disable the possibility of N times oversampling and simply use the unrolled code for 8x, which may end up faster.
|
|
|
07-24-2009, 04:53 PM
|
#5
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
Quote:
Originally Posted by liteon
i'm not the author of the graphdist fx and tbh i haven't checked it out
but basically in the "zero stuffing" method:
you have an up-sample filter which has 1 input and N outputs. N streams are processed in the case of a basic ws effect in parallel. then a down-sample filter processes N inputs to N outputs, where only one output is significant and N-1 are discarded.
you may want to disable the possibility of N times oversampling and simply use the unrolled code for 8x, which may end up faster.
|
I'm a noob, so have i understand this right?
sample -> zero-stuffing -> lowpass @ original samplerate -> process -> lowpass@original samplerate -> decimate ...
- john..
|
|
|
07-24-2009, 08:59 PM
|
#6
|
Human being with feelings
Join Date: Mar 2008
Location: Sydney, Australia
Posts: 3,955
|
afaik yes, but your lowpass filter characteristics will change the character of that a lot. hence why a lot of plugs that use oversampling also introduce significant delay...
|
|
|
07-25-2009, 03:06 AM
|
#7
|
Human being with feelings
Join Date: May 2009
Posts: 1,265
|
Quote:
Originally Posted by onqel
I'm a noob, so have i understand this right?
sample -> zero-stuffing -> lowpass @ original samplerate -> process -> lowpass@original samplerate -> decimate ...
- john..
|
Almost. You must lowpass at the nyquist frequency (i.e. half the sampling frequency)( http://en.wikipedia.org/wiki/Nyquist...mpling_theorem ).
|
|
|
07-27-2009, 10:09 PM
|
#8
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
I've started making the plugin in c++/vst-sdk so I will finish it there, before I make port to JS.
I'm getting a small high end roll-off, I'm not sure if I'm doing this right though.. I'm having a hard time understanding it all :\
this is a simplified version of my tube gainstage.. (and it might be a big "FAIL!")
float process(float input)
{
int OVERSAMPLE = 4;
bool zerostuff = false;
float a;
for (int i = 0; i <= OVERSAMPLE; ++i)
{
if (zerostuff) { input = 0; }
a = kernel.upsample(input); //input filter
a = guitar_gainstage.process(a);
a = guitar_poweramp.process(a);
a = kernel.downsample(a); //output filter
zerostuff = true;
}
kernel.reset();
return a;
}
is there a better (but still simple) way to do this?
Last edited by onqel; 07-27-2009 at 10:19 PM.
|
|
|
07-28-2009, 04:52 AM
|
#9
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
an upsample filter should produce two outputs. "float input" as an input of the filter is correct, but the function "upsample()" in the "kernel" class should return two floats. example:
Code:
function upsample(float input) {
float out0,out1;
//usf here
//...
return out0,out1;
}
edit: and accordingly 2 inputs and one output for the down-sample function.
i have posted more info here:
http://forum.cockos.com/showpost.php...6&postcount=16
---
Last edited by liteon; 07-28-2009 at 05:03 AM.
|
|
|
07-28-2009, 06:11 AM
|
#10
|
Human being with feelings
Join Date: May 2009
Posts: 1,265
|
Quote:
Originally Posted by liteon
an upsample filter should produce two outputs. "float input" as an input of the filter is correct, but the function "upsample()" in the "kernel" class should return two floats. example:
Code:
function upsample(float input) {
float out0,out1;
//usf here
//...
return out0,out1;
}
edit: and accordingly 2 inputs and one output for the down-sample function.
i have posted more info here:
http://forum.cockos.com/showpost.php...6&postcount=16
---
|
True, but onqel's code is upsampling, processing and downsampling in _one_ step, so this is the upsample, processing and downsample function all in _one_ and thus it is correct (as long as the kernel.upsample() and kernel.downsample() do a lowpass at the _original_ nyquist).
The only thing (possibly) wrong I can see is the kernel.reset(). What does this call do? Does it reset the state of the filters? Then it shouldn't be called, because the filters should run continuously over the whole signal that is retain their state from sample to sample and _not_ just process each sample frame separately on its own.
Also I don't think that the "zero-stuffing" method will yield a reasonable quality oversampled signal. In my opinion "true" interpolation should be used.
Tip: Christian Budde's VST Analyzer ( http://www.savioursofsoul.de/Christian/?page_id=106 ) is excellent to prove VST processing code (Tip2: use the ReaJS VST to also analyze JesuSonic script files).
|
|
|
07-28-2009, 06:49 AM
|
#11
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
a big "FAIL!" for me then
thanks alot for your tutorial! Very useful!
Maybe I'll understand it correct now
|
|
|
07-28-2009, 09:50 AM
|
#12
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
Quote:
Originally Posted by Mich
True, but onqel's code is upsampling, processing and downsampling in _one_ step, so this is the upsample, processing and downsample function all in _one_ and thus it is correct (as long as the kernel.upsample() and kernel.downsample() do a lowpass at the _original_ nyquist).
The only thing (possibly) wrong I can see is the kernel.reset(). What does this call do? Does it reset the state of the filters? Then it shouldn't be called, because the filters should run continuously over the whole signal that is retain their state from sample to sample and _not_ just process each sample frame separately on its own.
Also I don't think that the "zero-stuffing" method will yield a reasonable quality oversampled signal. In my opinion "true" interpolation should be used.
Tip: Christian Budde's VST Analyzer ( http://www.savioursofsoul.de/Christian/?page_id=106 ) is excellent to prove VST processing code (Tip2: use the ReaJS VST to also analyze JesuSonic script files).
|
Thank you for the tip on the analyzers and my process-algo!
if I use a filter like this: .. can I remove the loop-part of my process, or just replace the standard lowpass with this example?
Code:
// Optimal 4x (2-point, 3rd-order) (z-form)
float z = input - 1/2.0;
float even1 = y[1]+y[0], odd1 = y[1]-y[0];
float c0 = even1*0.50013034073688023;
float c1 = odd1*1.09617817497678520;
float c2 = even1*-0.001564088842561871;
float c3 = odd1*-1.32598918957298410;
return ((c3*z+c2)*z+c1)*z+c0;
(http://www.student.oulu.fi/~oniemita/dsp/deip.pdf)
from the Multirate FAQ I read "As a linear process, the DSP sense of interpolation is somewhat different from the "math" sense of interpolation, but the result is conceptually similar: to create "in-between" samples from the original samples. The result is as if you had just originally sampled your signal at the higher rate."
My mind tells me I have to this:
Code:
Input -> interpolation-filter -> process-fx -> bandpass -> output
or do I have to loop the process N times?
It's a little confusing .. I see that interpolation filter has only one output, but uses the two previous outputs.. I will read a little more
|
|
|
07-28-2009, 11:45 AM
|
#13
|
Human being with feelings
Join Date: May 2009
Posts: 1,265
|
Quote:
Originally Posted by onqel
Thank you for the tip on the analyzers and my process-algo!
if I use a filter like this: .. can I remove the loop-part of my process, or just replace the standard lowpass with this example?
Code:
// Optimal 4x (2-point, 3rd-order) (z-form)
float z = input - 1/2.0;
float even1 = y[1]+y[0], odd1 = y[1]-y[0];
float c0 = even1*0.50013034073688023;
float c1 = odd1*1.09617817497678520;
float c2 = even1*-0.001564088842561871;
float c3 = odd1*-1.32598918957298410;
return ((c3*z+c2)*z+c1)*z+c0;
(http://www.student.oulu.fi/~oniemita/dsp/deip.pdf)
|
This code is interpolating ... seems to take input in range [0,1] and based on the discrete samples y[0] and y[1] (presumable input gives the position between those 2 samples) interpolates the signal. So if you want the sample right in between y[0] and y[1] you'd call this function with input 0.5, etc ... at least this is how I read it, haven't looked at the paper though.
Quote:
Originally Posted by onqel
from the Multirate FAQ I read "As a linear process, the DSP sense of interpolation is somewhat different from the "math" sense of interpolation, but the result is conceptually similar: to create "in-between" samples from the original samples. The result is as if you had just originally sampled your signal at the higher rate."
My mind tells me I have to this:
Code:
Input -> interpolation-filter -> process-fx -> bandpass -> output
or do I have to loop the process N times?
It's a little confusing .. I see that interpolation filter has only one output, but uses the two previous outputs.. I will read a little more
|
My advice would be to start with something simple like a simple samplehold based upsampler (nearest neighbor interpolation) followed by the processing and a downsample that just discards the samples.
Actually wait ... lets look at this:
Code:
desc:n times oversampler
slider1:1<1,64,1>oversample (x)
slider2:1<0,1,1{OFF,ON}>Antialiasing filter
@slider
factor = slider1;
/* simple (i.e. crappy) one pole antialiasing filter at nyquist of original srate .. is definitively not flat enough for higher upsample factors and generally no steep enough etc ... but should suffice for demo purpose */
filter_coef = exp(-2*$pi* (0.5/factor) );
filter = slider2;
@sample
spl0_ = spl0_tmp; /* store last input sample */
spl0_tmp = spl0;
x = 0;
loop(factor,
/* linear interp eq between to points P0(x0,y0) and P1(x1,y1)
wanted is Px(x,y) on line P0 to P1
y1-y0
y = y0 + (x-x0) * -----
x1-x0
*/
y = spl0_tmp + (x - 0)*(spl0-spl0_tmp)/(1-0); /* interpolation ! */
/* do processing here
note: this will be at rate = srate*factor!!!!
*/
y = y*0.8;
/* eof proc */
filter ? tmp = y + filter_coef*(tmp-y) : tmp = y; /* AA filter ! should discared all info above the nyquist freq of the orig srate to which we will decimate back again*/
x==0 ? spl0 = tmp; /* decimator! ... discard additional samples */
x += 1/factor; /* increment our position x between P0 and P1
);
spl1 = spl0;
... hope it works and helps you to understand what needs to be done for oversampling. Basically:
for N times oversampling a signal of M samples
Upsample: turn each 1 sample of the M samples into N samples
Process: M*N samples
Downsample: output 1 samples then discard N-1 samples and repeat this M times
Done.
Hope this helps.
Note: the code posted is only for demo purposes and, while it should work, doesn't work any good (needs better AA filter).
Last edited by Mich; 07-29-2009 at 12:58 PM.
Reason: me stupid :(
|
|
|
07-28-2009, 03:08 PM
|
#14
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
Quote:
Originally Posted by Mich
... hope it works and helps you to understand what needs to be done for oversampling. Basically:
for N times oversampling a signal of M samples
Upsample: turn each 1 sample of the M samples into N samples
Process: M*N samples
Downsample: output 1 samples then discard N-1 samples and repeat this M times
Done.
Hope this helps.
Note: the code posted is only for demo purposes and, while it should work, doesn't work any good (needs better AA filter).
|
Thank you! With a little modification I got it to work properly.
the first line in the interpolation example I gave was actually float z = x - 1/2.0 .. but I thought it's input was "input, output_0, output_1" ..
but after reading your democode, I figured it out, and that interpolation works like a charm at 4x(almost) when I replaced the linear interpolation with that one..
the line y = y * 0.8 .. is that a part of the code or just something to put in the /* process here */ section?
btw, your antialias filter is cutting at 10hz or something :P
Last edited by onqel; 07-28-2009 at 06:09 PM.
|
|
|
07-29-2009, 06:05 AM
|
#15
|
Human being with feelings
Join Date: Apr 2008
Posts: 510
|
a perfect aa filter would be one that has Fc=Fs/2 and has 90 degrees rolloff . those that are considered very good, are only for offline processing, found in sox, r8b and others.
a simple aa fir:
Code:
m1=m0;
spl0=0.5*(m1+m0=spl0);
or the same but executed 3 times:
Code:
m1=m0;m3=m2;m5=m4;
spl0=0.5*(m5+m4=0.5*(m3+m2=0.5*(m1+m0=spl0)));
after the interpolation and using the above, there will be a "dip" in the audible range (below Fs/2). you can try compensating for that with a "baxandall curve", or the similar effect from a peak filter at Fs/2, with boost and q related to the oversampling factor. an example peak filter can be found in "js:filters/parametric_eq". just examine the dip after oversampling and place the peak filter after the loop(...) code that michael has posted.
btw some effects like a guitar amp can do ok with the "dip" i.e. keeping it.
if you wish to investigate further into "brickwall-like" filter design, check the second post here (by rbj):
http://www.dsprelated.com/showmessage/17213/1.php
there is a zip file containing a remez exchange algorithm.
i believe same can be found in c.budde's delphi-asio-vst -> search for file containing "remez".
or here for some windowed-sinc examples:
http://www.dspguide.com/ch16/4.htm
these could be quite expensive for higher number of points.
---
don't forget to share your guitar-amp js when its done.
lubomir
|
|
|
07-29-2009, 12:47 PM
|
#16
|
Human being with feelings
Join Date: May 2009
Posts: 1,265
|
Quote:
Originally Posted by onqel
With a little modification I got it to work properly.
|
Ah, right it's $pi (not pi) ... sorry didn't have time to test it, but you already figured it out ...
Quote:
Originally Posted by onqel
the line y = y * 0.8 .. is that a part of the code or just something to put in the /* process here */ section?
|
That's the processing that is done (in this case volume scaling).
Quote:
Originally Posted by onqel
btw, your antialias filter is cutting at 10hz or something :P
|
Well now that I tried it I was also surprised myself at just _how_ suboptimal a simple RC filter is (it really is rolling of at nyquist though (or at least is supposed to ... but I might have messed that up) but due to the slow roll off it is affecting a huge portion of the signal (especially at higher rates, where the lowpass frequency must be lower and thus bandwidth of the filter roll off narrower, etc ...)). But like liteon already suggested you can look into a better filter like a sinc filter, etc ... it was just to demonstrate how it is done.
Last edited by Mich; 07-29-2009 at 12:56 PM.
|
|
|
03-04-2010, 09:29 AM
|
#17
|
Human being with feelings
Join Date: Jan 2007
Location: Umeå, Sweden
Posts: 947
|
Sorry to revive such an old thread but I got intrigued by the idea of making 16x oversampling clipper. So I tried to make one.
However, I reaized that I don't know crap about up/downsampling so I might have got it all wrong. Anyway here it is:
https://stash.reaper.fm/oldsb/812373/...ng_clipper.txt
The difference is quite audiable when switching between oversampled and non-oversampled output. However, since I pretty much made this up I'm not sure I got it right at all - does it look correct to you?
(I'm working on a JS amp sim as well that's why I'm bothering to figure this out)
Any help is much appreciated! Regards,
- Jonas
PS: The reason it doesn't use loops instead of this looong code is that it seems to use way less CPU this way, about 6% on my MBP : DS
|
|
|
03-07-2010, 06:29 AM
|
#18
|
Human being with feelings
Join Date: Jun 2007
Location: Tromsø, Norway
Posts: 223
|
I can't say much about your oversampling routine.. but you will see a decrease in cpu usage if you replace dividing by multiplication.. "X / 16" can be replaced with "X * 0.0625" (1/16) .. dividing is a slow routine
|
|
|
08-06-2010, 01:49 PM
|
#19
|
Human being with feelings
Join Date: Apr 2009
Posts: 191
|
Quote:
Originally Posted by liteon
|
I tried it, but I think that not works, first I tried a allpass filter and the phase decay to 0º at nyquist of the original srate. Then I' tried waveshaper, the aliasing is the same that without oversampling (some less volume of the highs harmonics due to the decay at higher freqs)
Code:
@sample
input = spl0;
usout0=(usout1+input)*0.5;
usout1=input;
fxout0 = atan(usout0) * 0.5*$pi;
fxout1 = atan(usout1) * 0.5*$pi;
output=0.25*(dsmem0+2*dsmem1+fxout1);
dsmem0=fxout0;
dsmem1=fxout1;
spl0=output;
spl1 = spl0;
Am I missing something?
P.S. sorry for my bad English
|
|
|
08-07-2010, 04:05 PM
|
#20
|
Human being with feelings
Join Date: Apr 2009
Posts: 191
|
here the screenshot of a waveshaper:
sine wave at 10k aprox.
funcshaper no oversampling:
funcshaper 2x oversampling (The aliasing is out):
JS oversample 2x, same waveshaper function (no bandlimit):
I've test the Mich algorithm and it doesnt work... maybe I'm doing something wrong, that is the most likely
|
|
|
08-08-2010, 02:11 AM
|
#21
|
Human being with feelings
Join Date: May 2009
Posts: 1,265
|
Quote:
Originally Posted by pylorca
I've test the Mich algorithm and it doesnt work... maybe I'm doing something wrong, that is the most likely
|
I said "Note: the code posted is only for demo purposes and, while it should work, doesn't work any good ( needs better AA filter)."
So you should take a look at what liteon said about better anti aliasing filters.
Or look at:
http://en.wikipedia.org/wiki/Anti-aliasing_filter
http://www.dspguide.com/ch16.htm
https://ccrma.stanford.edu/~jos/resample/
those should give you an idea of what to do to improve the AA filter.
|
|
|
08-08-2010, 03:55 PM
|
#22
|
Human being with feelings
Join Date: Apr 2009
Posts: 191
|
Thanks!
with the LP filter the same aliasing, i think that "usout0=(usout1+input)*0.5;usout1=input;" doesn't do oversample. I check the swell code and test some scripts and I think that the code doesn't do oversampling.
with func shaper 2x oversampling the aliasing is removed.
Here is the code with 2x oversampling and AA posted by liteon. The same aliasing than the previous code.
I'm confused :S
Code:
@sample
input = spl0;
usout0=(usout1+input)*0.5;
usout1=input;
bm1=bm0;bm3=bm2;bm5=bm4;
usout0=0.5*(bm5+bm4=0.5*(bm3+bm2=0.5*(bm1+bm0=usout0)));
cm1=cm0;cm3=cm2;cm5=cm4;
usout0=0.5*(cm5+cm4=0.5*(cm3+cm2=0.5*(cm1+cm0=usout1)));
fxout0 = atan(usout0) * 0.5*$pi;
fxout1 = atan(usout1) * 0.5*$pi;
m1=m0;m3=m2;m5=m4;
fxout0=0.5*(m5+m4=0.5*(m3+m2=0.5*(m1+m0=fxout0)));
am1=am0;am3=am2;am5=am4;
fxout1=0.5*(am5+am4=0.5*(am3+am2=0.5*(am1+am0=fxout1)));
//dsf
output=0.25*(dsmem0+2*dsmem1+fxout1);
dsmem0=fxout0;
dsmem1=fxout1;
spl0=output;
spl1 = output;
|
|
|
08-08-2010, 08:21 PM
|
#23
|
Human being with feelings
Join Date: Apr 2009
Posts: 191
|
I was doing more tests, and I' think that the upsample works fine, but the filter is not so good...
it is the code:
Code:
slider1:0<0,1,1{OFF, ON}>Oversample
@init
out_vol = exp(4/8.65617025);
@slider
os = slider1;
@sample
os == 1 ? (
in = spl0;
in1 = (in_prev + in) * 0.5;
fxout0 = atan(in) * 0.5*$pi;
fxout1 = atan(in1) * 0.5*$pi;
am1=am0;am3=am2;am5=am4;
fxout0=0.5*(am5+am4=0.5*(am3+am2=0.5*(am1+am0=fxout0)));
am1=am0;am3=am2;am5=am4;
fxout1=0.5*(am5+am4=0.5*(am3+am2=0.5*(am1+am0=fxout1)));
out = (fxout0+fxout1)*0.5*out_vol;
in_prev = in;
):(
out = atan(spl0) * 0.5*$pi;
);
spl0=out;
spl1=spl0;
Test with a sine at 3226hz, host=48k, the alias at 390hz reduces at osx2, the lpf is not 90º...
Mich tanks for the links!!
Last edited by pylorca; 08-08-2010 at 11:38 PM.
|
|
|
02-03-2017, 09:24 AM
|
#24
|
Human being with feelings
Join Date: Feb 2017
Posts: 5
|
I've you figure out what's the issue with your filter ? I'm having similar behaviour while trying to implement the oversample method described here
I've written a post on kvr here
Help will be appreciated
|
|
|
02-03-2017, 04:54 PM
|
#25
|
Human being with feelings
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
|
Both Tale and I have oversamplers coded. What Tale did (and I followed) was unroll the polyphase out a little and generate all samples at once by hard coding the coefficients and multiplies instead of looping through an array. Tale has a very compact and simple 7 tap 2x oversampler and I have both a 19 pt 2x and 31 pt 4x. Tale's file is oversampler.jsfx-inc, mine is st-oversampler.jsfx-inc. I don't think either one of us is in Reapack yet, but you can search for Tale's code by searching for "Tale mono synth" and mine can be found in a recent post on a 4x oversampler limiter. I believe that Smashed Transistor also has an implementation built into many of the effects he publishes, it's pretty beefy too. I actually have an older oversamplibg library, but it isnt as good.
Two different approaches, each with implementations, check em out.
|
|
|
02-05-2017, 12:16 PM
|
#26
|
Human being with feelings
Join Date: Feb 2017
Posts: 5
|
Thanks! I've checked your last implementation (4x), set the test scenario and compare the outputs. I've finally change the way I manage the gain compensation for each sub filters of the polyphase in the interpolate method. Seems to fix the issue I was encountering... By fx I mean: atan(input) * .5 * M_PI
code updated
port of your oversampler in C/C++
|
|
|
02-05-2017, 03:38 PM
|
#27
|
Human being with feelings
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
|
So we need to think about two factors - both size of the spectrum and how steep a filter we need to avoid audible aliasing.
With this filter I tried to strike a balance between latency, passband, and maximum attenuation. For instance, if the samplerate is 48 kHz the 31 tap filter has a passband from approximately 17 kHz to 34 kHz, with a maximum attenuation of 55 dB. I chose 34 khz because if most people can't hear aliasing above 15 khz that's where the foldover would happen. 24 + (24 - 15) = 33 kHz, and that's where the max attenuation hits. That's not bad for a linear phase filter with less than 1 ms of latency!
If you need more attenuation, a narrower passband, or a greater oversampling factor of course you can cascade filters, or you'll need to design a filter with more taps. It's all trade-offs... if you want less latency you have either reduce the number of taps, which reduces the filter's effectiveness, or turn it into a minimum phase, which increases CPU and negates the advantage of it being an Lth-band filter.
I've toyed around with the idea of adding a minimum phase downsampling function. Would cut down on the latency, but obviously the trade off is loss of linear phase. I dunno. Maybe at some point.
|
|
|
02-06-2017, 11:52 AM
|
#28
|
Human being with feelings
Join Date: Feb 2017
Posts: 5
|
Why 17 hz ? Does it make a difference ? I've tried to design your filter with t-filter. Here is what I got for the following settings:
sampling frequency: 96000 Hz
* 17 Hz - 24000 Hz
gain = 1
desired ripple = 0.1 dB
actual ripple = 0.06289750484831265 dB
* 33000 Hz - 48000 Hz
gain = 0
desired attenuation = -55 dB
actual attenuation = -56.38048034902137 dB
The coefficients are not the same, so I guess I'm wrong. By the way, don't you think that increasing the float precision of your coefficients will increase the quality of the filtering (you used 4 digits) ?
|
|
|
02-06-2017, 07:03 PM
|
#29
|
Human being with feelings
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
|
Quote:
Why 17 hz ? Does it make a difference ?
|
It's all a trade-off. My design goals were a quarterband filter with < 1 ms latency and all aliasing and passband should happen above 15 khz given a samplerate of 44.1 khz.
So the "quarterband" part of that signifies that this is a Nyquist filter, sometimes called an L-th or M-th band filter. Every L coefficient counting from the center coefficient is zero, which gives us a computational advantage.
There are a few ways to design a Nyquist filter, but T-filter is not one of them. I used Matlab for the 31-tap filter above, but I did find one online utility that can generate them.
http://www.arc.id.au/FilterDesign.html
The following settings will generate a 31 pt quarterband (Nyquist, L=4) filter. (note that 4 * 44.1 kHz = 176.4 kHz, and that 22.050/88.2 = Fc of 0.25)
Srate 176400
Fb 22050
M Length 31 tap
Att 50 dB
Again, we know it is a Nyquist quarterband filter because when we set the Fc at 0.25 (1/4th) and count outwards from the center coefficient, every 4th coefficient is zero.
Quote:
By the way, don't you think that increasing the float precision of your coefficients will increase the quality of the filtering (you used 4 digits) ?
|
I think I was copy/pasting using Matlab's default formatting. Probably could go back and update that. Does it matter? Probably not audibly. Wouldn't hurt. I'll start poking around for them.
|
|
|
02-06-2017, 09:14 PM
|
#30
|
Human being with feelings
Join Date: Oct 2013
Location: Seattle, WA
Posts: 876
|
The full coefficients are
Code:
-0.002469076374678
-0.004548665973301
-0.004334792589110
0.000000000000000
0.007919578847318
0.014707851640244
0.013514259906394
0.000000000000000
-0.022350687252104
-0.040721602089144
-0.037513557645167
0.000000000000000
0.070316075718208
0.154646454517718
0.223473433718352
0.250000000000000
|
|
|
02-07-2017, 02:04 PM
|
#31
|
Human being with feelings
Join Date: Feb 2017
Posts: 5
|
As you predicted increasing the precision does not help much, I've tested in my c/c++ version.
SaulT thank you for the quality of your explanation! Your oversampler works great!
|
|
|
02-07-2017, 03:51 PM
|
#32
|
Banned
Join Date: Nov 2016
Posts: 416
|
Quote:
Originally Posted by FredAnton
SaulT thank you for the quality of your explanation!
|
+1
.......
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 04:39 PM.
|