Old 07-16-2009, 12:55 AM   #1
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default 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
onqel is offline   Reply With Quote
Old 07-16-2009, 02:03 AM   #2
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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
liteon is offline   Reply With Quote
Old 07-16-2009, 06:19 AM   #3
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

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 View Post
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
onqel is offline   Reply With Quote
Old 07-18-2009, 04:40 AM   #4
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

Quote:
Originally Posted by onqel View Post
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.
liteon is offline   Reply With Quote
Old 07-24-2009, 04:53 PM   #5
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

Quote:
Originally Posted by liteon View Post
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..
onqel is offline   Reply With Quote
Old 07-24-2009, 08:59 PM   #6
dub3000
Human being with feelings
 
dub3000's Avatar
 
Join Date: Mar 2008
Location: Sydney, Australia
Posts: 3,813
Default

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...
dub3000 is offline   Reply With Quote
Old 07-25-2009, 03:06 AM   #7
Mich
Human being with feelings
 
Join Date: May 2009
Posts: 1,265
Default

Quote:
Originally Posted by onqel View Post
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 ).
Mich is offline   Reply With Quote
Old 07-27-2009, 10:09 PM   #8
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

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.
onqel is offline   Reply With Quote
Old 07-28-2009, 04:52 AM   #9
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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.
liteon is offline   Reply With Quote
Old 07-28-2009, 06:11 AM   #10
Mich
Human being with feelings
 
Join Date: May 2009
Posts: 1,265
Default

Quote:
Originally Posted by liteon View Post
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).
Mich is offline   Reply With Quote
Old 07-28-2009, 06:49 AM   #11
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

a big "FAIL!" for me then

thanks alot for your tutorial! Very useful!
Maybe I'll understand it correct now
onqel is offline   Reply With Quote
Old 07-28-2009, 09:50 AM   #12
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

Quote:
Originally Posted by Mich View Post
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
onqel is offline   Reply With Quote
Old 07-28-2009, 11:45 AM   #13
Mich
Human being with feelings
 
Join Date: May 2009
Posts: 1,265
Default

Quote:
Originally Posted by onqel View Post
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 View Post
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 :(
Mich is offline   Reply With Quote
Old 07-28-2009, 03:08 PM   #14
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

Quote:
Originally Posted by Mich View Post
... 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.
onqel is offline   Reply With Quote
Old 07-29-2009, 06:05 AM   #15
liteon
Human being with feelings
 
liteon's Avatar
 
Join Date: Apr 2008
Posts: 510
Default

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
liteon is offline   Reply With Quote
Old 07-29-2009, 12:47 PM   #16
Mich
Human being with feelings
 
Join Date: May 2009
Posts: 1,265
Default

Quote:
Originally Posted by onqel View Post
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 View Post
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 View Post
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.
Mich is offline   Reply With Quote
Old 03-04-2010, 09:29 AM   #17
Jonas_Eriksson_Swe
Human being with feelings
 
Jonas_Eriksson_Swe's Avatar
 
Join Date: Jan 2007
Location: Umeň, Sweden
Posts: 947
Default

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
Jonas_Eriksson_Swe is offline   Reply With Quote
Old 03-07-2010, 06:29 AM   #18
onqel
Human being with feelings
 
onqel's Avatar
 
Join Date: Jun 2007
Location: Troms°, Norway
Posts: 223
Default

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
onqel is offline   Reply With Quote
Old 08-06-2010, 01:49 PM   #19
pylorca
Human being with feelings
 
Join Date: Apr 2009
Posts: 191
Default

Quote:
Originally Posted by liteon View Post
.....
i have posted more info here:
http://forum.cockos.com/showpost.php...6&postcount=16

---
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
pylorca is offline   Reply With Quote
Old 08-07-2010, 04:05 PM   #20
pylorca
Human being with feelings
 
Join Date: Apr 2009
Posts: 191
Default

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
Attached Images
File Type: jpg fs_1x.jpg (67.0 KB, 1185 views)
File Type: jpg fs_2x.jpg (65.6 KB, 1227 views)
File Type: jpg js_2x.jpg (66.5 KB, 1390 views)
pylorca is offline   Reply With Quote
Old 08-08-2010, 02:11 AM   #21
Mich
Human being with feelings
 
Join Date: May 2009
Posts: 1,265
Default

Quote:
Originally Posted by pylorca View Post
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.
Mich is offline   Reply With Quote
Old 08-08-2010, 03:55 PM   #22
pylorca
Human being with feelings
 
Join Date: Apr 2009
Posts: 191
Default

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;
pylorca is offline   Reply With Quote
Old 08-08-2010, 08:21 PM   #23
pylorca
Human being with feelings
 
Join Date: Apr 2009
Posts: 191
Default

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.
pylorca is offline   Reply With Quote
Old 02-03-2017, 09:24 AM   #24
FredAnton
Human being with feelings
 
Join Date: Feb 2017
Posts: 5
Default

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
FredAnton is offline   Reply With Quote
Old 02-03-2017, 04:54 PM   #25
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 750
Default

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.
SaulT is offline   Reply With Quote
Old 02-05-2017, 12:16 PM   #26
FredAnton
Human being with feelings
 
Join Date: Feb 2017
Posts: 5
Default

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++

Attached Images
File Type: png Screen Shot 2017-02-05 at 18.18.59 copy-2-2-2-2.png (57.5 KB, 313 views)
FredAnton is offline   Reply With Quote
Old 02-05-2017, 03:38 PM   #27
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 750
Default

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.
SaulT is offline   Reply With Quote
Old 02-06-2017, 11:52 AM   #28
FredAnton
Human being with feelings
 
Join Date: Feb 2017
Posts: 5
Default

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) ?

Attached Images
File Type: png Screen Shot 2017-02-06 at 19.47.39.png (54.3 KB, 338 views)
FredAnton is offline   Reply With Quote
Old 02-06-2017, 07:03 PM   #29
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 750
Default

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.
SaulT is offline   Reply With Quote
Old 02-06-2017, 09:14 PM   #30
SaulT
Human being with feelings
 
Join Date: Oct 2013
Location: Seattle, WA
Posts: 750
Default

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
SaulT is offline   Reply With Quote
Old 02-07-2017, 02:04 PM   #31
FredAnton
Human being with feelings
 
Join Date: Feb 2017
Posts: 5
Default

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!
FredAnton is offline   Reply With Quote
Old 02-07-2017, 03:51 PM   #32
TryingToMakeMusic
Banned
 
Join Date: Nov 2016
Posts: 416
Default

Quote:
Originally Posted by FredAnton View Post
SaulT thank you for the quality of your explanation!
+1
.......
TryingToMakeMusic 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:08 PM.


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