rockbox/apps/plugins/pdbox/PDa/extra/hlshelf.c

260 lines
5.5 KiB
C
Raw Normal View History

/* (C) Guenter Geiger <geiger@epy.co.at> */
#ifdef ROCKBOX
#include "plugin.h"
#include "../../pdbox.h"
#include "../src/m_pd.h"
#include "../../math.h"
#else /* ROCKBOX */
#include "../src/m_pd.h"
#include <math.h>
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
#endif /* ROCKBOX */
/* ------------------------ hlshelf ----------------------------- */
#ifndef M_PI
#define M_PI 3.141593f
#endif
#define SRATE 44100.0
#define MAX_GAIN 120.0f
static t_class *hlshelf_class;
typedef struct _hlshelf
{
t_object x_obj;
float s_rate;
float s_gain0;
float s_gain1;
float s_gain2;
float s_ltransfq;
float s_htransfq;
float s_lradians;
float s_hradians;
} t_hlshelf;
int hlshelf_check_stability(t_float fb1,
t_float fb2,
t_float ff1,
t_float ff2,
t_float ff3)
{
#ifdef ROCKBOX
(void) ff1;
(void) ff2;
(void) ff3;
#endif
float discriminant = fb1 * fb1 + 4 * fb2;
if (discriminant < 0) /* imaginary roots -- resonant filter */
{
/* they're conjugates so we just check that the product
is less than one */
if (fb2 >= -1.0f) goto stable;
}
else /* real roots */
{
/* check that the parabola 1 - fb1 x - fb2 x^2 has a
vertex between -1 and 1, and that it's nonnegative
at both ends, which implies both roots are in [1-,1]. */
if (fb1 <= 2.0f && fb1 >= -2.0f &&
1.0f - fb1 -fb2 >= 0 && 1.0f + fb1 - fb2 >= 0)
goto stable;
}
return 0;
stable:
return 1;
}
void hlshelf_check(t_hlshelf *x)
{
if(x->s_gain0 - x->s_gain1 > MAX_GAIN) {
x->s_gain0 = x->s_gain1 + MAX_GAIN;
post("setting gain0 to %f",x->s_gain0);
}
if(x->s_gain1 > MAX_GAIN) {
x->s_gain1 = MAX_GAIN;
post("setting gain1 to %f",x->s_gain1);
}
if(x->s_gain2 - x->s_gain1 > MAX_GAIN) {
x->s_gain2 = x->s_gain1 + MAX_GAIN;
post("setting gain2 to %f",x->s_gain2);
}
/* constrain: 0 <= x->s_ltransfq < x->s_htransfq. */
x->s_ltransfq = (x->s_ltransfq < x->s_htransfq) ? x->s_ltransfq : x->s_htransfq - 0.5f;
if (x->s_ltransfq < 0) x->s_ltransfq = 0.0f;
x->s_lradians = M_PI * x->s_ltransfq / x->s_rate;
x->s_hradians= M_PI * (0.5f - (x->s_htransfq / x->s_rate));
}
void hlshelf_bang(t_hlshelf *x)
{
t_atom at[6];
#ifndef ROCKBOX
float c0;
float g1, g2;
#endif
float c1, c2, d0, d1, d2; /* output coefs */
float a1, a2, b1, b2; /* temp coefs */
double xf;
hlshelf_check(x);
/* low shelf */
xf = 0.5 * 0.115129255 * (double)(x->s_gain0 - x->s_gain1); /* ln(10) / 20 = 0.115129255 */
if(xf < -200.) /* exp(x) -> 0 */
{
a1 = 1.0f;
b1 = -1.0f;
#ifndef ROCKBOX
g1 = 0.0f;
#endif
}
else
{
double t = tan(x->s_lradians);
double e = exp(xf);
double r = t / e;
double kr = t * e;
a1 = (r - 1) / (r + 1);
b1 = (kr - 1) / (kr + 1);
#ifndef ROCKBOX
g1 = (kr + 1) / (r + 1);
#endif
}
/* high shelf */
xf = 0.5 * 0.115129255 * (double)(x->s_gain2 - x->s_gain1); /* ln(10) / 20 = 0.115129255 */
if(xf < -200.) /* exp(x) -> 0 */
{
a2 = -1.0f;
b2 = 1.0f;
#ifndef ROCKBOX
g2 = 0.0f;
#endif
}
else
{
double t = tan(x->s_hradians);
double e = exp(xf);
double r = t / e;
double kr = t * e;
a2 = (1 - r) / (1 + r);
b2 = (1 - kr) / (1 + kr);
#ifndef ROCKBOX
g2 = (1 + kr) / (1 + r);
#endif
}
/* form product */
#ifndef ROCKBOX
c0 = g1 * g2 * (float)(exp((double)(x->s_gain1) * 0.05f * 2.302585093f));
#endif
c1 = a1 + a2;
c2 = a1 * a2;
d0 = 1.0f;
d1 = b1 + b2;
d2 = b1 * b2;
if (!hlshelf_check_stability(-c1/d0,-c2/d0,d0/d0,d1/d0,d2/d0)) {
post("hlshelf: filter unstable -> resetting");
#ifndef ROCKBOX
c0=1.;
#endif
c1=0.;c2=0.;
d0=1.;d1=0.;d2=0.;
}
SETFLOAT(at,-c1/d0);
SETFLOAT(at+1,-c2/d0);
SETFLOAT(at+2,d0/d0);
SETFLOAT(at+3,d1/d0);
SETFLOAT(at+4,d2/d0);
outlet_list(x->x_obj.ob_outlet,&s_list,5,at);
}
void hlshelf_float(t_hlshelf *x,t_floatarg f)
{
x->s_gain0 = f;
hlshelf_bang(x);
}
static void *hlshelf_new(t_symbol* s,t_int argc, t_atom* at)
{
#ifdef ROCKBOX
(void) s;
(void) argc;
#endif
t_hlshelf *x = (t_hlshelf *)pd_new(hlshelf_class);
t_float k0 = atom_getfloat(at);
t_float k1 = atom_getfloat(at+1);
t_float k2 = atom_getfloat(at+2);
t_float f1 = atom_getfloat(at+3);
t_float f2 = atom_getfloat(at+4);
f1 = atom_getfloat(at);
f2 = atom_getfloat(at);
if ((f1 == 0.0f && f2 == 0.0f) || f1 > f2){ /* all gains = 0db */
f1 = 150.0f;
f2 = 5000.0f;
}
if (f1 < 0) f1 = 0.0f;
if (f2 > SRATE) f2 = .5f*SRATE;
x->s_rate = SRATE; /* srate default */
x->s_gain0 = k0;
x->s_gain1 = k1;
x->s_gain2 = k2;
x->s_ltransfq = 0.0f;
x->s_htransfq = SRATE/2;
x->s_lradians = M_PI * x->s_ltransfq / x->s_rate;
x->s_hradians= M_PI * (0.5f - (x->s_htransfq / x->s_rate));
floatinlet_new(&x->x_obj, &x->s_gain1);
floatinlet_new(&x->x_obj, &x->s_gain2);
floatinlet_new(&x->x_obj, &x->s_ltransfq);
floatinlet_new(&x->x_obj, &x->s_htransfq);
outlet_new(&x->x_obj, &s_list);
return (x);
}
void hlshelf_setup(void)
{
hlshelf_class = class_new(gensym("hlshelf"), (t_newmethod)hlshelf_new, 0,
sizeof(t_hlshelf), 0, A_GIMME, 0);
class_addbang(hlshelf_class,hlshelf_bang);
class_addfloat(hlshelf_class,hlshelf_float);
}