Hey people,
While I'm not an expert with digital filters, I did manage to piece together a 
decent-sounding ladder emulation instead of vcf~ a while ago. It only does 
resonant lowpass though. It's called mvcf~ and is found in the ekext externals 
library.
I've since been reading about analogue filter design and I reckon there may be 
high pass and band pass filters available from the source code, with the 
correct adjustment to the algorithm. I'm trying to work out how to adjust the 
coefficients to accurately model the alternative functions (i.e. highpass, and 
hence bandpass through arithmetic processes) according to the resistance 
factors outlined in this paper. Any help would be appreciated :)
So, currently, at line 78 in the code I have translations for highpass and 
bandpass (the current implementation is lowpass only with a gain factor) but I 
may be stupid in not trying this myself (or I've just been super-busy with 
other stuff - I'm marking this week). The idea of implementing the other two 
modes comes from an Electronotes paper from Bernie Hutchins in the 1970s. 
http://electronotes.netfirms.com/EN85VCF.PDF

Take a look.Cheers,Ed
PS have yet to try bob~ but it sounds interesting...will check it out. 
 

   

 On Sunday, 16 October 2016, 13:10, Alexandre Torres Porres <por...@gmail.com> 
wrote:
 
 

 > But [bp~] and [vcf~] are almost unusable IMHO and should probably be 
 >replaced> by better filters in the future (while keeping the old ones for 
 >compatibility reasons).

how about bob~?
2016-10-14 21:34 GMT-03:00 Christof Ressi <christof.re...@gmx.at>:

There are a number of big problems with all build-in filters in Pd (expect for 
the raw filters).

Problem number 1:
[lop~] and [hip~] both use a weird (you could also say: wrong) formula for the 
cutoff frequency which makes them gradually converge to a fixed output state 
(reached by about 7000 Hz). The same is true for [vcf~] and [bp~] with Q <= 1. 
Therefore the actual cutoff frequency is only correct for very low frequencies 
and approximately gets more and more off until it doesn't move at all.

Problem number 2:
[bp~] and [vcf~] don't have zeros at DC and Nyquist. For low Q values, the 
slope is different for each side and changes with frequency.

Problem number 3:
the gain at the center frequency is not 1 for both [bp~] and [vcf~]. It rather 
depends on frequency and Q. [bp~] even has has a gain of 2 for Q <= 1!

I did some FFT plots, see the attachment.

I remember Miller saying somewhere that these filters are not designed for high 
cutoff frequencies - but even for low frequencies, the behaviour of [bp~] and 
[vcf~] is horrible. I can see these filters are mere approximations to reduce 
CPU usage.
[hip~] is indeed much more efficient than iemlib's [hp1~], so it's well suited 
for DC removal (but not much else).
[bp~] only is a little bit more CPU friendly than iemlib's [bp2~] - but the 
latter one has a correct and stable frequency response.
[vcf~], however, is a real CPU sucker!!! 100 [vcf~] objects need 3,40% on my 
laptop whereas 100 of iemlib's [vcf_bp2~] only need 1,80%! But you have to 
consider that [vcf_bp2~] not only acts correctly but lets you set the Q at 
audio rate. The high CPU usage of [vcf~] seems like a bug to me...

I only use the vanilla filters for the most basic stuff like DC removal and 
smoothing. I guess these are the use cases which Miller had in mind and that 
way [lop~] and [hip~] have their justification (although there should be some 
more warning about the 'wrong' frequency response in the help file).
But [bp~] and [vcf~] are almost unusable IMHO and should probably be replaced 
by better filters in the future (while keeping the old ones for compatibility 
reasons).

Christof


> Gesendet: Freitag, 14. Oktober 2016 um 23:51 Uhr
> Von: katja <katjavet...@gmail.com>
> An: pd-list <pd-l...@iem.at>
> Betreff: [PD] could vanilla borrow iemlib's hi pass filter recipe?
>
> In pd 0.47.1 [hip~] is still not perfect. Attenuation at cutoff is not
> constant over the frequency range: -6 dB with cutoff=SR/8, -3 dB with
> cutoff=SR/4, 0 DB with cutoff=SR/2. In contrast, iemlib's [hp1~] has
> -3 dB at cutoff consistently.
>
> Could vanilla pd implement iemlib's hipass filter recipe? I don't know
> if the license also covers the math. Documentation in
> https://git.iem.at/pd/iemlib/ tree/master points to external literature
> for part of the math (bilinear transform). I've implemented the recipe
> with vanilla objects for comparison, see attached.
>
> Katja
> ______________________________ _________________
> Pd-list@lists.iem.at mailing list
> UNSUBSCRIBE and account-management -> https://lists.puredata.info/ 
> listinfo/pd-list
> 
______________________________ _________________
Pd-list@lists.iem.at mailing list
UNSUBSCRIBE and account-management -> https://lists.puredata.info/ 
listinfo/pd-list




_______________________________________________
Pd-list@lists.iem.at mailing list
UNSUBSCRIBE and account-management -> 
https://lists.puredata.info/listinfo/pd-list


 
   
/*
 * moog vcf, 4-pole lowpass resonant filter
 *
 * (c) Edward Kelly 2012
 * BSD License
 */

#include "m_pd.h"
#include <math.h>
#define _limit 0.95

static t_class *mvcf_tilde_class;

typedef struct _mvcf_tilde {
  t_object x_obj;
  t_float b0, b1, b2, b3, b4;  //filter buffers to keep (beware denormals!)
  t_float token, debug, safety;
  t_outlet *lp;
} t_mvcf_tilde;

/* We could have a mode where the fc and res are only registered at the start 
of the block (quick) or are registered in signal mode (slow) - i.e. a flag */

static inline float saturate( float input ) { //clamp without branching
  float x1 = fabsf( input + _limit );
  float x2 = fabsf( input - _limit );
  return 0.5 * (x1 - x2);
}


t_int *mvcf_tilde_perform(t_int *w) {
  t_mvcf_tilde   *x =   (t_mvcf_tilde *)(w[1]);
  t_sample      *in =       (t_sample *)(w[2]);
  t_sample      *fc =       (t_sample *)(w[3]);
  t_sample     *res =       (t_sample *)(w[4]);
  t_sample     *out =       (t_sample *)(w[5]);
  int             n =              (int)(w[6]);

  float t1 = 0;
  float t2 = 0;
  float xb0 = x->b0;
  float xb1 = x->b1;
  float xb2 = x->b2;
  float xb3 = x->b3;
  float xb4 = x->b4;
  float i1 = 0;
  float fc1 = 0;
  float res1 = 0;
  float q = 0;
  float p = 0;
  float fcoeff = 0;

  //  while (n-=4) {
  while (n--) {
    i1=(*in++);
    fc1 = (*fc++);
    /* This failsafe line stops the filter bursting
     * ...but it is expensive! */
//    if(x->safety) {
//      fc1 = fc1 <= 1 ? fc1 >= 0 ? fc1 : 0 : 1;
//    }
    res1 = (*res++);
    q = 1.0f - fc1;
    p = fc1 + 0.8f * fc1 * q;
    fcoeff = p + p - 1.0f;
    q = res1 * (1.0f + 0.5f * q * (1.0f - q + 5.6f * q * q));
    i1 -= q * xb4;                          //feedback
    t1 = xb1;
    xb1 = (i1 + xb0) * p - xb1 * fcoeff;
    t2 = xb2;
    xb2 = (xb1 + t1) * p - xb2 * fcoeff;
    t1 = xb3;
    xb3 = (xb2 + t2) * p - xb3 * fcoeff;
    xb4 = (xb3 + t1) * p - xb4 * fcoeff;
    xb4 = saturate(xb4);
    xb4 = xb4 - xb4 * xb4 * xb4 * 0.01f;
    xb0 = i1;
    *out++ = xb4*1.414; // lowpass mode
// Lowpass  output:  xb4
// Highpass output:  i1 * 0.166667 + xb1 * -0.66667 + xb2 * 1 + xb3 * -0.66667 
+ xb4 * 0.166667;
// Bandpass output:  3.0f * (b3 - xb4);

  }
  x->b0 = xb0;
  x->b1 = xb1;
  x->b2 = xb2;
  x->b3 = xb3;
  x->b4 = xb4;
//  if(x->debug != 0) {
//    x->token +=1;
//    if(x->token == 15) {
//      post("q = %f, p=%f, fcoeff=%f, b0=%f, b1=%f, b2=%f, b3=%f, 
b4=%f",q,p,fcoeff,xb0,xb1,xb2,xb3,xb4);
//      x->token = 0;
//    }
//  }
  return (w+7);
}

void mvcf_tilde_dsp(t_mvcf_tilde *x, t_signal **sp) {
  dsp_add(mvcf_tilde_perform, 6, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, 
sp[3]->s_vec, sp[0]->s_n);
}

void mvcf_tilde_safety(t_mvcf_tilde *x, t_floatarg f) {
  x->safety = f != 0 ? 1 : 0;
}

/*void mvcf_tilde_mode(t_mvcf_tilde *x, t_floatarg f) {
x->mode = f < 1 ? 0 : f > 1 ? 2 : 1;
}
 */

void mvcf_tilde_clear(t_mvcf_tilde *x) {
  x->b0 = 0;
  x->b1 = 0;
  x->b2 = 0;
  x->b3 = 0;
  x->b4 = 0;
}

void mvcf_tilde_debug(t_mvcf_tilde *x, t_floatarg f) {
  x->debug = f != 0 ? 1 : 0;
}

void *mvcf_tilde_new(t_floatarg f) {
  t_mvcf_tilde *x = (t_mvcf_tilde *)pd_new(mvcf_tilde_class);

  x->b0 = 0;
  x->b1 = 0;
  x->b2 = 0;
  x->b3 = 0;
  x->b4 = 0;
  x->token = 0;
  x->safety = 0;

  inlet_new (&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
  inlet_new (&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
  outlet_new(&x->x_obj, &s_signal);
  return (void *)x;
}

void mvcf_tilde_setup(void) {
  mvcf_tilde_class = class_new(gensym("mvcf~"), 
  (t_newmethod)mvcf_tilde_new, 
  0, sizeof(t_mvcf_tilde),
  CLASS_DEFAULT, A_DEFFLOAT, 0);

//  post("~~~~~~~~~~~~~~~>mvcf~");
//  post("~~~>by Ed Kelly, 2012");

  class_addmethod(mvcf_tilde_class,
  (t_method)mvcf_tilde_dsp, gensym("dsp"), 0);
  CLASS_MAINSIGNALIN(mvcf_tilde_class, t_mvcf_tilde, token);
  class_addmethod(mvcf_tilde_class, (t_method)mvcf_tilde_clear, 
gensym("clear"), 0);
  class_addmethod(mvcf_tilde_class, (t_method)mvcf_tilde_debug, 
gensym("debug"), 0);
  class_addmethod(mvcf_tilde_class, (t_method)mvcf_tilde_safety, 
gensym("safe"), 0);
  //  class_addmethod(mvcf_tilde_class, (t_method)mvcf_tilde_mode, 
gensym("mode"), A_DEFFLOAT, 0);
}
_______________________________________________
Pd-list@lists.iem.at mailing list
UNSUBSCRIBE and account-management -> 
https://lists.puredata.info/listinfo/pd-list

Reply via email to