aboutsummaryrefslogtreecommitdiff
path: root/valverect~.c
blob: 246878c08822630efba90b82a8f5fcd620df8751 (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
// sigpack
// for
// pure-data
// by weiss
// www.weiss-archive.de

#include "m_pd.h"
#include <math.h>
#include <string.h>
#ifdef _MSC_VER
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif

// ------------------------ valverect~ ----------------------------- 
// valve rectifier
// code from swh_plugins by steve harris www.plugin.org.uk 

// 1.0 / ln(2)
#define LN2R 1.442695041f
/* Fast exponentiation function, y = e^x */
#define f_exp(x) f_pow2(x * LN2R) 

static t_class *valverect_tilde_class;

typedef struct _valverect_tilde
{
    t_object x_obj;
	t_sample x_sag;//sag level[0-1]
	t_sample x_dist_p;//distortion[0-1]
	unsigned int x_apos;
	float *x_avg;
	int x_avg_size;
	float x_avg_sizer;
	float x_avgs;
	float x_lp1tm1;
	float x_lp2tm1;
	float x_s_rate;
	float x_f;
} t_valverect_tilde;

static void *valverect_tilde_new(t_floatarg sag, t_floatarg dist)
{
    t_valverect_tilde *x = (t_valverect_tilde *)pd_new(valverect_tilde_class);
	x->x_sag = sag;
	x->x_dist_p = dist;
    outlet_new(&x->x_obj, gensym("signal"));
	floatinlet_new(&x->x_obj, &x->x_sag);
	floatinlet_new(&x->x_obj, &x->x_dist_p);
	x->x_s_rate = sys_getsr();
	x->x_avg_size = x->x_s_rate / 9;//number of samples in averaging buffer
	x->x_avg_sizer = 9.0f / x->x_s_rate;//reciprocal of above
	x->x_avg = (float *)getbytes(x->x_avg_size * sizeof(float));//averaging buffer
	memset(x->x_avg, 0, x->x_avg_size * sizeof(float));
	x->x_avgs = 0.0f;//sum of samples in averaging buffer
	x->x_apos = 0;//position in averaging buffer
	x->x_lp1tm1 = 0.0f;//last value in lowpass 1
	x->x_lp2tm1 = 0.0f;//last value in lowpass 2
	x->x_f = 0;
	if (sag) x->x_sag = sag;
	else x->x_sag = 0.0;
	if (dist) x->x_dist_p = dist;
	else x->x_dist_p = 0.0;
    return (x);
}

/* Andrew Simper's pow(2, x) aproximation from the music-dsp list */
static float f_pow2(float x)
{
	long *px = (long*)(&x); // store address of float as long pointer
	const float tx = (x-0.5f) + (3<<22); // temporary value for truncation
	const long  lx = *((long*)&tx) - 0x4b400000; // integer power of 2
	const float dx = x-(float)(lx); // float remainder of power of 2

	x = 1.0f + dx*(0.6960656421638072f + // cubic apporoximation of 2^x
                   dx*(0.224494337302845f +  // for x in the range [0, 1]
                   dx*(0.07944023841053369f)));
	*px += (lx<<23); // add integer power of 2 to exponent

	return x;
}

static t_int *valverect_tilde_perform(t_int *w)
{
	t_valverect_tilde *x = (t_valverect_tilde *)(w[1]);
    t_float *in = (t_float *)(w[2]);
    t_float *out = (t_float *)(w[3]);
    int n = (int)(w[4]);
	float abs, q, fx;
	const float dist = x->x_dist_p * 40.0f + 0.1f;
	float f;
    while (n--)
    {
		f = *in++;
		abs = fabs(f);
		if(abs > x->x_lp1tm1) {
			x->x_lp1tm1 = abs;
		}
		else {
			x->x_lp1tm1 = 0.9999f * x->x_lp1tm1 + 0.0001f * abs;
		}
		x->x_avgs -= x->x_avg[x->x_apos];
		x->x_avgs += x->x_lp1tm1;
		x->x_avg[x->x_apos] = x->x_lp1tm1;
		x->x_apos %= x->x_avg_size;

		x->x_lp2tm1 = 0.999f * x->x_lp2tm1 + x->x_avgs * x->x_avg_sizer * 0.001f;
		q = x->x_lp1tm1 * x->x_sag - x->x_lp2tm1 * 1.02f - 1.0f;
		if(q > -0.01f) {
			q = -0.01f;
		}
		else if(q < -1.0f) {
			q = -1.0f;
		}

		if(f == q) {
			fx = 1.0f / dist + q / (1.0f - f_exp(dist * q));
		}
		else {
			fx = (f - q) / (1.0f - f_exp(-dist * (f - q))) + q / (1.0f - f_exp(dist * q));
		}
		*out++ = fx;
    }
    return (w+5);
}

static void valverect_tilde_dsp(t_valverect_tilde *x, t_signal **sp)
{
    dsp_add(valverect_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
}

static void valverect_tilde_free(t_valverect_tilde *x)
{
	freebytes(x->x_avg, x->x_avg_size * sizeof(float));
}

void valverect_tilde_setup(void)
{
    valverect_tilde_class = class_new(gensym("valverect~"), (t_newmethod)valverect_tilde_new, (t_method)valverect_tilde_free,
    	sizeof(t_valverect_tilde), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
    CLASS_MAINSIGNALIN(valverect_tilde_class, t_valverect_tilde, x_f);
    class_addmethod(valverect_tilde_class, (t_method)valverect_tilde_dsp, gensym("dsp"), 0);
}