aboutsummaryrefslogtreecommitdiff
path: root/cyclone/sickle/onepole.c
blob: f06b581eaa805235c0bb1fe34c4c6e7b6e03a39f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* Copyright (c) 2003 krzYszcz and others.
 * For information on usage and redistribution, and for a DISCLAIMER OF ALL
 * WARRANTIES, see the file, "LICENSE.txt," in this distribution.  */

/* This is Pd's lop~ with signal-controlled cutoff. */

/* CHECKED scalar case: input preserved (not coefs) after changing mode */
/* CHECKME if creation arg (or a default) restored after signal disconnection */

#include <math.h>
#include "m_pd.h"
#include "shared.h"
#include "sickle/sic.h"

#if defined(NT) || defined(MACOSX)
/* cf pd/src/x_arithmetic.c */
#define sinf  sin
#endif

#define ONEPOLE_HZ        0
#define ONEPOLE_LINEAR    1
#define ONEPOLE_RADIANS   2
#define ONEPOLE_MINB0     .0001  /* CHECKED 1st term of ir for b0=0 */
#define ONEPOLE_MAXB0     .99    /* CHECKED 1st term of ir for b0=1 */
#define ONEPOLE_MINOMEGA  0.     /* CHECKME */
#define ONEPOLE_MAXOMEGA  (SHARED_PI * .5)  /* CHECKME */

typedef struct _onepole
{
    t_sic  x_sic;
    int    x_mode;
    float  x_srcoef;
    float  x_ynm1;
} t_onepole;

static t_class *onepole_class;

static t_symbol *ps_hz;
static t_symbol *ps_linear;
static t_symbol *ps_radians;

static void onepole_clear(t_onepole *x)
{
    x->x_ynm1 = 0.;
}

static void onepole_hz(t_onepole *x)
{
    x->x_mode = ONEPOLE_HZ;
}

static void onepole_linear(t_onepole *x)
{
    x->x_mode = ONEPOLE_LINEAR;
}

static void onepole_radians(t_onepole *x)
{
    x->x_mode = ONEPOLE_RADIANS;
}

/* LATER make ready for optional audio-rate modulation
   (separate scalar case routine, use sic_makecostable(), etc.) */
static t_int *onepole_perform(t_int *w)
{
    t_onepole *x = (t_onepole *)(w[1]);
    int nblock = (int)(w[2]);
    t_float *xin = (t_float *)(w[3]);
    t_float fin0 = *(t_float *)(w[4]);
    t_float *out = (t_float *)(w[5]);
    int mode = x->x_mode;
    float ynm1 = x->x_ynm1;
    /* CHECKME sampled once per block */
    float b0;
    if (mode == ONEPOLE_HZ)
    {
	float omega = fin0 * x->x_srcoef;
	if (omega < ONEPOLE_MINOMEGA)
	    omega = ONEPOLE_MINOMEGA;
	else if (omega > ONEPOLE_MAXOMEGA)
	    omega = ONEPOLE_MAXOMEGA;
	/* The actual solution for a half-power cutoff is:
	   b0 = sqrt(sqr(2-cos(omega))-1) + cos(omega) - 1.
	   The sin(omega) below is only slightly better approximation than
	   Miller's b0=omega, except for the two highest octaves (or so),
	   where it is much better (but far from good). */
	b0 = sinf(omega);
    }
    else if (mode == ONEPOLE_LINEAR)
	b0 = sinf(fin0 * (SHARED_PI * .5));  /* CHECKME actual range of fin0 */
    else
	b0 = fin0;
    if (b0 < ONEPOLE_MINB0)
	b0 = ONEPOLE_MINB0;
    else if (b0 > ONEPOLE_MAXB0)
	b0 = ONEPOLE_MAXB0;
    /* b0 is the standard 1-|a1| (where |a1| is pole's radius),
       specifically: a1=b0-1 => a1 in [-.9999 .. -.01] => lowpass (stable) */
    while (nblock--)
	*out++ = ynm1 = b0 * (*xin++ - ynm1) + ynm1;
    x->x_ynm1 = (PD_BADFLOAT(ynm1) ? 0. : ynm1);
    return (w + 6);
}

static void onepole_dsp(t_onepole *x, t_signal **sp)
{
    x->x_srcoef = SHARED_2PI / sp[0]->s_sr;
    onepole_clear(x);
    dsp_add(onepole_perform, 5, x, sp[0]->s_n,
	    sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec);
}

static void *onepole_new(t_symbol *s, t_floatarg f)
{
    t_onepole *x = (t_onepole *)pd_new(onepole_class);
    x->x_srcoef = SHARED_2PI / sys_getsr();
    /* CHECKED no int-to-float conversion (any int bashed to 0.) */
    sic_newinlet((t_sic *)x, f);
    outlet_new((t_object *)x, &s_signal);
    onepole_clear(x);
    if (s == ps_linear)
	x->x_mode = ONEPOLE_LINEAR;
    else if (s == ps_radians)
	x->x_mode = ONEPOLE_RADIANS;
    else
    {
	x->x_mode = ONEPOLE_HZ;
	if (s && s != &s_ && s != ps_hz && s != gensym("Hz"))
	{
	    /* CHECKED no warning */
	}
    }
    return (x);
}

void onepole_tilde_setup(void)
{
    ps_hz = gensym("hz");
    ps_linear = gensym("linear");
    ps_radians = gensym("radians");
    onepole_class = class_new(gensym("onepole~"),
			      (t_newmethod)onepole_new, 0,
			      sizeof(t_onepole), 0,
			      A_DEFFLOAT, A_DEFSYM, 0);
    sic_setup(onepole_class, onepole_dsp, SIC_FLOATTOSIGNAL);
    class_addmethod(onepole_class, (t_method)onepole_clear, gensym("clear"), 0);
    class_addmethod(onepole_class, (t_method)onepole_hz, ps_hz, 0);
    class_addmethod(onepole_class, (t_method)onepole_hz, gensym("Hz"), 0);
    class_addmethod(onepole_class, (t_method)onepole_linear, ps_linear, 0);
    class_addmethod(onepole_class, (t_method)onepole_radians, ps_radians, 0);
}