-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
187 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,176 +1,231 @@ | ||
// Porres 2017 | ||
// Porres 2017-2025 | ||
|
||
#include <m_pd.h> | ||
#include "math.h" | ||
#include <magic.h> | ||
#include <math.h> | ||
#include <stdlib.h> | ||
#include "magic.h" | ||
|
||
#define TWOPI (3.14159265358979323846 * 2) | ||
#define MAXLEN 1024 | ||
|
||
typedef struct _tri{ | ||
t_object x_obj; | ||
double *x_phase; | ||
int x_nchans; | ||
t_int x_n; | ||
t_int x_sig1; | ||
t_int x_sig2; | ||
t_int x_ch2; | ||
t_int x_ch3; | ||
t_int x_midi; | ||
t_int x_soft; | ||
t_int *x_dir; | ||
float *x_freq_list; | ||
t_int x_list_size; | ||
t_symbol *x_ignore; | ||
t_inlet *x_inlet_phase; | ||
t_inlet *x_inlet_sync; | ||
t_outlet *x_outlet; | ||
double x_sr_rec; | ||
// MAGIC: | ||
t_glist *x_glist; // object list | ||
t_float *x_signalscalar; // right inlet's float field | ||
t_float x_phase_sync_float; // float from magic | ||
}t_tri; | ||
|
||
static t_class *tri_class; | ||
|
||
typedef struct _tri | ||
{ | ||
t_object x_obj; | ||
double x_phase; | ||
double x_last_phase_offset; | ||
t_float x_freq; | ||
t_inlet *x_inlet_phase; | ||
t_inlet *x_inlet_sync; | ||
t_outlet *x_outlet; | ||
t_float x_sr; | ||
// MAGIC: | ||
t_glist *x_glist; // object list | ||
t_float *x_signalscalar; // right inlet's float field | ||
int x_hasfeeders; // right inlet connection flag | ||
t_float x_phase_sync_float; // float from magic | ||
} t_tri; | ||
double tri_wrap_phase(double phase){ | ||
while(phase >= 1) | ||
phase -= 1.; | ||
while(phase < 0) | ||
phase += 1.; | ||
return(phase); | ||
} | ||
|
||
static t_int *tri_perform(t_int *w){ | ||
t_tri *x = (t_tri *)(w[1]); | ||
int nblock = (t_int)(w[2]); | ||
t_float *in1 = (t_float *)(w[3]); // freq | ||
t_float *in3 = (t_float *)(w[5]); // phase | ||
t_float *out = (t_float *)(w[6]); | ||
t_float *in1 = (t_float *)(w[2]); | ||
t_float *in2 = (t_float *)(w[3]); | ||
t_float *in3 = (t_float *)(w[4]); | ||
t_float *out = (t_float *)(w[5]); | ||
t_int *dir = x->x_dir; | ||
double *phase = x->x_phase; | ||
// Magic Start | ||
t_float *scalar = x->x_signalscalar; | ||
if (!else_magic_isnan(*x->x_signalscalar)){ | ||
t_float input_phase = fmod(*scalar, 1); | ||
if (input_phase < 0) | ||
input_phase += 1; | ||
x->x_phase = input_phase; | ||
else_magic_setnan(x->x_signalscalar); | ||
if(!x->x_sig2){ | ||
t_float *scalar = x->x_signalscalar; | ||
if(!else_magic_isnan(*x->x_signalscalar)){ | ||
t_float input_phase = fmod(*scalar, 1); | ||
if(input_phase < 0) | ||
input_phase += 1; | ||
for(int j = 0; j < x->x_nchans; j++) | ||
x->x_phase[j] = input_phase; | ||
else_magic_setnan(x->x_signalscalar); | ||
} | ||
} | ||
// Magic End | ||
double phase = x->x_phase; | ||
double last_phase_offset = x->x_last_phase_offset; | ||
double sr = x->x_sr; | ||
double output; | ||
while (nblock--){ | ||
double hz = *in1++; | ||
double phase_offset = *in3++; | ||
double phase_step = hz / sr; // phase_step | ||
phase_step = phase_step > 0.5 ? 0.5 : phase_step < -0.5 ? -0.5 : phase_step; // clipped to nyq | ||
double phase_dev = phase_offset - last_phase_offset; | ||
if (phase_dev >= 1 || phase_dev <= -1) | ||
phase_dev = fmod(phase_dev, 1); // fmod(phase_dev) | ||
phase = phase + phase_dev; | ||
if (phase <= 0) | ||
phase = phase + 1.; // wrap deviated phase | ||
if (phase >= 1) | ||
phase = phase - 1.; // wrap deviated phase | ||
output = phase * 4; | ||
if (output >= 1 && output < 3) | ||
output = 1 - (output - 1); | ||
else if (output >= 3 && output) | ||
output = (output - 4); | ||
*out++ = output; | ||
phase = phase + phase_step; // next phase | ||
last_phase_offset = phase_offset; // last phase offset | ||
for(int j = 0; j < x->x_nchans; j++){ | ||
for(int i = 0, n = x->x_n; i < n; i++){ | ||
double hz = x->x_sig1 ? in1[j*n + i] : x->x_freq_list[j]; | ||
if(x->x_midi){ | ||
if(hz > 127) | ||
hz = 127; | ||
hz = hz <= 0 ? 0 : pow(2, (hz - 69)/12) * 440; | ||
} | ||
double step = hz * x->x_sr_rec; // phase step | ||
step = step > 0.5 ? 0.5 : step < -0.5 ? -0.5 : step; | ||
if(x->x_sig2){ | ||
if(x->x_soft){ | ||
if(dir[j] == 0) | ||
dir[j] = 1; | ||
step *= (dir[j]); | ||
} | ||
t_float trig = x->x_ch2 == 1 ? in2[i] : in2[j*n + i]; | ||
if(trig > 0 && trig <= 1){ | ||
if(x->x_soft) | ||
dir[j] = dir[j] == 1 ? -1 : 1; | ||
else | ||
phase[j] = trig; | ||
} | ||
} | ||
double phase_offset = x->x_ch3 == 1 ? in3[i] : in3[j*n + i]; | ||
t_float wrap = tri_wrap_phase(phase[j] + phase_offset); | ||
out[j*n + i] = 2 * fabs(2*(wrap+0.25 - floor(wrap+0.75))) - 1; | ||
phase[j] = tri_wrap_phase(phase[j] + step); | ||
} | ||
} | ||
x->x_phase = phase; | ||
x->x_last_phase_offset = last_phase_offset; | ||
return (w + 7); | ||
x->x_dir = dir; | ||
return(w+6); | ||
} | ||
|
||
static t_int *tri_perform_sig(t_int *w) | ||
{ | ||
t_tri *x = (t_tri *)(w[1]); | ||
int nblock = (t_int)(w[2]); | ||
t_float *in1 = (t_float *)(w[3]); // freq | ||
t_float *in2 = (t_float *)(w[4]); // sync | ||
t_float *in3 = (t_float *)(w[5]); // phase | ||
t_float *out = (t_float *)(w[6]); | ||
double phase = x->x_phase; | ||
double last_phase_offset = x->x_last_phase_offset; | ||
double sr = x->x_sr; | ||
double output; | ||
while (nblock--){ | ||
double hz = *in1++; | ||
t_float trig = *in2++; | ||
double phase_offset = *in3++; | ||
double phase_step = hz / sr; // phase_step | ||
phase_step = phase_step > 0.5 ? 0.5 : phase_step < -0.5 ? -0.5 : phase_step; // clipped to nyq | ||
double phase_dev = phase_offset - last_phase_offset; | ||
if (phase_dev >= 1 || phase_dev <= -1) | ||
phase_dev = fmod(phase_dev, 1); // fmod(phase_dev) | ||
if (trig > 0 && trig <= 1) | ||
phase = trig; | ||
else{ | ||
phase = phase + phase_dev; | ||
if (phase <= 0) | ||
phase = phase + 1.; // wrap deviated phase | ||
if (phase >= 1) | ||
phase = phase - 1.; // wrap deviated phase | ||
} | ||
output = phase * 4; | ||
if (output >= 1 && output < 3) | ||
output = 1 - (output - 1); | ||
else if (output >= 3 && output) | ||
output = (output - 4); | ||
*out++ = output; | ||
phase = phase + phase_step; // next phase | ||
last_phase_offset = phase_offset; // last phase offset | ||
static void tri_dsp(t_tri *x, t_signal **sp){ | ||
x->x_n = sp[0]->s_n, x->x_sr_rec = 1.0 / (double)sp[0]->s_sr; | ||
x->x_ch2 = sp[1]->s_nchans, x->x_ch3 = sp[2]->s_nchans; | ||
x->x_sig1 = else_magic_inlet_connection((t_object *)x, x->x_glist, 0, &s_signal); | ||
x->x_sig2 = else_magic_inlet_connection((t_object *)x, x->x_glist, 1, &s_signal); | ||
int chs = x->x_sig1 ? sp[0]->s_nchans : x->x_list_size; | ||
if(x->x_nchans != chs){ | ||
x->x_phase = (double *)resizebytes(x->x_phase, | ||
x->x_nchans * sizeof(double), chs * sizeof(double)); | ||
x->x_dir = (t_int *)resizebytes(x->x_dir, | ||
x->x_nchans * sizeof(t_int), chs * sizeof(t_int)); | ||
x->x_nchans = chs; | ||
} | ||
x->x_phase = phase; | ||
x->x_last_phase_offset = last_phase_offset; | ||
return (w + 7); | ||
signal_setmultiout(&sp[3], x->x_nchans); | ||
if((x->x_ch2 > 1 && x->x_ch2 != x->x_nchans) | ||
|| (x->x_ch3 > 1 && x->x_ch3 != x->x_nchans)){ | ||
dsp_add_zero(sp[3]->s_vec, x->x_nchans*x->x_n); | ||
pd_error(x, "[tri~]: channel sizes mismatch"); | ||
return; | ||
} | ||
dsp_add(tri_perform, 5, x, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec); | ||
} | ||
|
||
static void tri_dsp(t_tri *x, t_signal **sp){ | ||
x->x_hasfeeders = else_magic_inlet_connection((t_object *)x, x->x_glist, 1, &s_signal); // magic feeder flag | ||
x->x_sr = sp[0]->s_sr; | ||
if (x->x_hasfeeders){ | ||
dsp_add(tri_perform_sig, 6, x, sp[0]->s_n, | ||
sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec); | ||
} | ||
else{ | ||
dsp_add(tri_perform, 6, x, sp[0]->s_n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec); | ||
static void tri_midi(t_tri *x, t_floatarg f){ | ||
x->x_midi = (int)(f != 0); | ||
} | ||
|
||
static void tri_set(t_tri *x, t_symbol *s, int ac, t_atom *av){ | ||
x->x_ignore = s; | ||
if(ac != 2) | ||
return; | ||
int i = atom_getint(av); | ||
float f = atom_getint(av+1); | ||
if(i >= x->x_list_size) | ||
i = x->x_list_size; | ||
if(i <= 0) | ||
i = 1; | ||
i--; | ||
x->x_freq_list[i] = f; | ||
} | ||
|
||
static void tri_list(t_tri *x, t_symbol *s, int ac, t_atom * av){ | ||
x->x_ignore = s; | ||
if(ac == 0) | ||
return; | ||
if(x->x_list_size != ac){ | ||
x->x_list_size = ac; | ||
canvas_update_dsp(); | ||
} | ||
for(int i = 0; i < ac; i++) | ||
x->x_freq_list[i] = atom_getfloat(av+i); | ||
} | ||
|
||
static void tri_soft(t_tri *x, t_floatarg f){ | ||
x->x_soft = (int)(f != 0); | ||
} | ||
|
||
static void *tri_free(t_tri *x){ | ||
inlet_free(x->x_inlet_sync); | ||
inlet_free(x->x_inlet_phase); | ||
outlet_free(x->x_outlet); | ||
return (void *)x; | ||
freebytes(x->x_phase, x->x_nchans * sizeof(*x->x_phase)); | ||
freebytes(x->x_dir, x->x_nchans * sizeof(*x->x_dir)); | ||
free(x->x_freq_list); | ||
return(void *)x; | ||
} | ||
|
||
static void *tri_new(t_symbol *s, int ac, t_atom *av){ | ||
s = NULL; | ||
t_tri *x = (t_tri *)pd_new(tri_class); | ||
t_float f1 = 0, f2 = 0; | ||
if (ac && av->a_type == A_FLOAT){ | ||
f1 = av->a_w.w_float; | ||
ac--; av++; | ||
if (ac && av->a_type == A_FLOAT){ | ||
f2 = av->a_w.w_float; | ||
ac--; av++; | ||
x->x_ignore = s; | ||
x->x_midi = x->x_soft = 0; | ||
x->x_dir = (t_int *)getbytes(sizeof(*x->x_dir)); | ||
x->x_phase = (double *)getbytes(sizeof(*x->x_phase)); | ||
x->x_freq_list = (float*)malloc(MAXLEN * sizeof(float)); | ||
x->x_freq_list[0] = x->x_phase[0] = 0; | ||
x->x_list_size = 1; | ||
while(ac && av->a_type == A_SYMBOL){ | ||
if(atom_getsymbol(av) == gensym("-midi")){ | ||
x->x_midi = 1; | ||
ac--, av++; | ||
} | ||
else if(atom_getsymbol(av) == gensym("-soft")){ | ||
x->x_soft = 1; | ||
ac--, av++; | ||
} | ||
else if(atom_getsymbol(av) == gensym("-mc")){ | ||
ac--, av++; | ||
if(!ac || av->a_type != A_FLOAT) | ||
goto errstate; | ||
int n = 0; | ||
while(ac && av->a_type == A_FLOAT){ | ||
x->x_freq_list[n] = atom_getfloat(av); | ||
ac--, av++, n++; | ||
} | ||
x->x_list_size = n; | ||
} | ||
else | ||
goto errstate; | ||
} | ||
if(ac && av->a_type == A_FLOAT){ | ||
x->x_freq_list[0] = av->a_w.w_float; | ||
ac--, av++; | ||
if(ac && av->a_type == A_FLOAT){ | ||
x->x_phase[0] = av->a_w.w_float; | ||
ac--, av++; | ||
} | ||
} | ||
t_float init_freq = f1; | ||
t_float init_phase = f2; | ||
init_phase = init_phase < 0 ? 0 : init_phase >= 1 ? 0 : init_phase; // clipping phase input | ||
if (init_phase == 0 && init_freq > 0) | ||
x->x_phase = 1.; | ||
else | ||
x->x_phase = init_phase; | ||
x->x_last_phase_offset = 0; | ||
x->x_freq = init_freq; | ||
x->x_inlet_sync = inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal); | ||
pd_float((t_pd *)x->x_inlet_sync, 0); | ||
x->x_inlet_phase = inlet_new((t_object *)x, (t_pd *)x, &s_signal, &s_signal); | ||
pd_float((t_pd *)x->x_inlet_phase, init_phase); | ||
pd_float((t_pd *)x->x_inlet_phase, x->x_phase[0]); | ||
x->x_outlet = outlet_new(&x->x_obj, &s_signal); | ||
// Magic | ||
x->x_glist = canvas_getcurrent(); | ||
x->x_signalscalar = obj_findsignalscalar((t_object *)x, 1); | ||
return (x); | ||
return(x); | ||
errstate: | ||
post("[tri~]: improper args"); | ||
return(NULL); | ||
} | ||
|
||
void tri_tilde_setup(void){ | ||
tri_class = class_new(gensym("tri~"), | ||
(t_newmethod)tri_new, (t_method)tri_free, | ||
sizeof(t_tri), CLASS_DEFAULT, A_GIMME, 0); | ||
CLASS_MAINSIGNALIN(tri_class, t_tri, x_freq); | ||
tri_class = class_new(gensym("tri~"), (t_newmethod)tri_new, (t_method)tri_free, | ||
sizeof(t_tri), CLASS_MULTICHANNEL, A_GIMME, 0); | ||
class_addmethod(tri_class, nullfn, gensym("signal"), 0); | ||
class_addmethod(tri_class, (t_method)tri_dsp, gensym("dsp"), A_CANT, 0); | ||
class_addlist(tri_class, tri_list); | ||
class_addmethod(tri_class, (t_method)tri_soft, gensym("soft"), A_DEFFLOAT, 0); | ||
class_addmethod(tri_class, (t_method)tri_midi, gensym("midi"), A_DEFFLOAT, 0); | ||
class_addmethod(tri_class, (t_method)tri_set, gensym("set"), A_GIMME, 0); | ||
} |