From f40d8c3d61e9ac6a7a4e06f6bfc7a2567c38bb33 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 29 Apr 2003 16:52:44 +0000 Subject: sources from motex_1_1_3.tar.gz svn path=/trunk/externals/motex/; revision=603 --- polygate~.c | 518 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 polygate~.c (limited to 'polygate~.c') diff --git a/polygate~.c b/polygate~.c new file mode 100644 index 0000000..eb5771b --- /dev/null +++ b/polygate~.c @@ -0,0 +1,518 @@ +/*************************************************************************** + * File: polygate~.c + * Auth: Iain Mott [iain.mott@bigpond.com] + * Maintainer: Iain Mott [iain.mott@bigpond.com] + * Version: Part of motex_1.1.2 + * Date: January 2001 + * + * Description: Pd signal external. Switches between multiple signal inlets + * with either linear or equal-power crossfade. Polyphonic and with variable fade rate. + * See supporting Pd patch: polygate~.pd + * + * Copyright (C) 2001 by Iain Mott [iain.mott@bigpond.com] + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License, which should be included with this + * program, for more details. + * + ****************************************************************************/ + +#include "m_pd.h" +#include +#include + +#define HALFPI 1.570796327 + +static t_class *polygate_class; + +typedef struct _polygateelement // float outlets - signifying activity status of corresponding signal inlet +{ + t_outlet *e_outlet; +} t_polygateelement; + +#define INPUTLIMIT 10 +#define LINEAR 0 +#define EPOWER 1 +#define EPMIN 0 // minimum crossfade time (msec) for epower crossfade (below this epower fades sound too crunchy) +// NB - now set to zero - as new 'equal power' now used doesn't have this problem + +#define TIMEUNITPERSEC (32.*441000.) + +typedef struct _ip // keeps track of each signal input +{ + int active[INPUTLIMIT]; + int counter[INPUTLIMIT]; + double timeoff[INPUTLIMIT]; + float fade[INPUTLIMIT]; + float *in[INPUTLIMIT]; +} t_ip; + +typedef struct _polygate +{ + t_object x_obj; + float x_f; + int choice; + int lastchoice; + int actuallastchoice; + int ninlets; + int fadetime; + double changetime; + int fadecount; + int fadeticks; + int firsttick; + int fadetype; + int lastfadetype; + int fadealert; + float srate; + t_polygateelement *x_vec; + t_ip ip; +} t_polygate; + +static void *polygate_new(t_symbol *s, int argc, t_atom *argv) +{ + int usedefault = 0, i; + t_polygateelement *e, *b; + t_polygate *x = (t_polygate *)pd_new(polygate_class); + x->srate = sys_getsr(); + if(argc == 0 || argc > 3) + usedefault = 1; + else if(argc == 1 && argv[0].a_type != A_FLOAT) + usedefault = 1; + else if(argc >= 2) + if(argv[0].a_type != A_FLOAT || argv[1].a_type != A_FLOAT) + usedefault = 1; + if(argc == 3) + { + if(argv[2].a_type == A_SYMBOL && !strcmp(argv[2].a_w.w_symbol->s_name, "linear")) + x->fadetype = x->lastfadetype = LINEAR; + else + if(argv[1].a_w.w_float >= EPMIN) + { + post("polygate~: 3rd optional argument should be \"linear\". Reverting to equal power default"); + x->fadetype = x->lastfadetype = EPOWER; + } + else + { + post("polygate~: 3rd optional argument should be \"linear\". \nFade rate less than %d msec - using linear fading", EPMIN); + x->fadetype = x->lastfadetype = LINEAR; + } + } + else + { + if(argv[1].a_w.w_float >= EPMIN) + x->fadetype = x->lastfadetype = EPOWER; + else + { + post("polygate~: fade rate less than %d msec - using linear fading", EPMIN); + x->fadetype = x->lastfadetype = LINEAR; + } + } + if(usedefault) + { + post("polygate~: Incompatible arguments. Using base defaults"); + x->fadetype = x->lastfadetype = LINEAR; + x->ninlets = 1; + x->fadetime = 1; + } + else + { + x->ninlets = argv[0].a_w.w_float < 1 ? 1 : argv[0].a_w.w_float; + if(x->ninlets > INPUTLIMIT) + { + x->ninlets = INPUTLIMIT; + post("polygate~: maximum of %d inlets", INPUTLIMIT); + } + x->fadetime = argv[1].a_w.w_float > 0 ? argv[1].a_w.w_float : 1; + } + x->x_vec = (t_polygateelement *)getbytes(x->ninlets * sizeof(*x->x_vec)); + for(i = 0; i < x->ninlets - 1; i++) + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + for (i = 0, e = x->x_vec; i < x->ninlets; i++, e++) + e->e_outlet = outlet_new(&x->x_obj, &s_float); + x->choice = 0; x->lastchoice = x->actuallastchoice = 0; + x->fadecount = 0; + x->fadeticks = (int)(x->srate / 1000 * x->fadetime); // no. of ticks to reach specified fade 'rate' + x->firsttick = 1; + x->fadealert = 0; + x->x_f = 0; + for(i = 0; i < INPUTLIMIT; i++) + { + x->ip.active[i] = 0; + x->ip.counter[i] = 0; + x->ip.timeoff[i] = 0; + x->ip.fade[i] = 0; + } + return (x); +} + +static void adjustcounters2epower(t_polygate *x); +static void adjustcounters2linear(t_polygate *x); + +void polygate_f(t_polygate *x, t_floatarg f) +{ + f = (int)f; + f = f > x->ninlets ? x->ninlets : f; + f = f < 0 ? 0 : f; + if(f != x->lastchoice) + { + t_polygateelement *e; + if(f == x->actuallastchoice) + x->fadecount = x->fadeticks - x->fadecount; + else + x->fadecount = 0; + x->choice = f; + if(x->choice) + { + e = x->x_vec; + e += x->choice - 1; + outlet_float(e->e_outlet, 1); + x->ip.active[x->choice - 1] = 1; +/* if(x->fadealert) */ +/* { */ +/* x->fadealert = 0; */ +/* adjustcounters2epower(x); */ +/* } */ + } + if(x->lastchoice) + { + x->ip.active[x->lastchoice - 1] = 0; + x->ip.timeoff[x->lastchoice - 1] = clock_getlogicaltime(); + } +/* else if(x->fadetype) // changing from zero as equal power */ +/* { */ +/* // x->fadealert = 1; */ +/* adjustcounters2epower(x); */ +/* } */ +/* if(!x->choice && x->fadetype && x->actuallastchoice) */ +/* adjustcounters2linear(x); */ + x->actuallastchoice = x->lastchoice; + x->lastchoice = x->choice; + } +} + + +static void checkswitchstatus(t_polygate *x) // checks to see which input feeds ought to be "switch~"ed off +{ + int i; + t_polygateelement *e; + for(i = 0; i < x->ninlets; i++) + { + if(!x->ip.active[i]) + if(clock_gettimesince(x->ip.timeoff[i]) > x->fadetime + && x->ip.timeoff[i]) + { + e = x->x_vec; + e += i; + x->ip.timeoff[i] = 0; + outlet_float(e->e_outlet, 0); + x->ip.fade[i] = 0; + } + } +} + +static void updatefades(t_polygate *x) +{ + int i; + for(i = 0; i < x->ninlets; i++) + { + if(!x->ip.counter[i]) + x->ip.fade[i] = 0; + if(x->ip.active[i] && x->ip.counter[i] < x->fadeticks) + { + if(x->ip.counter[i]) + x->ip.fade[i] = x->ip.counter[i] / (float)x->fadeticks; + x->ip.counter[i]++; + } + else if (!x->ip.active[i] && x->ip.counter[i] > 0) + { + x->ip.fade[i] = x->ip.counter[i] / (float)x->fadeticks; + x->ip.counter[i]--; + } + } +} + +static double epower(double rate) +{ + double tmp; + if(rate < 0) + rate = 0; + if(rate > 0.999) + rate = 0.999; +/* tmp = (tan(1.5866 * rate - 0.785398) + 1) / 2; */ + +/* tmp = pow(rate, 0.5); */ + rate *= HALFPI; +/* tmp = sin(HALFPI - rate); */ + tmp = cos(rate - HALFPI); + tmp = tmp < 0 ? 0 : tmp; + tmp = tmp > 1 ? 1 : tmp; +/* return sqrt(tmp); */ + return tmp; +} + +static double aepower(double ep) // convert from equal power to linear rate +{ +/* double answer = (atan(2*ep*ep - 1) + 0.785398) / 1.5866; */ + double answer = (acos(ep) + HALFPI) / HALFPI; + + answer = 2 - answer; // ??? - but does the trick + + answer = answer < 0 ? 0 : answer; + answer = answer > 1 ? 1 : answer; + return answer; +} + +static void adjustcounters2epower(t_polygate *x) // no longer used +{ + // called when shifting from a linear fade-in (from zero) to an equal power crossfade + // adjusts each input counter to smoothly match subsequent equal power scalings + int i; + double ep; + for(i = 0; i < x->ninlets; i++) + { + ep = x->ip.counter[i] / (double)x->fadeticks; + x->ip.counter[i] = aepower(ep) * (double)x->fadeticks; + } +} + +static void adjustcounters2linear(t_polygate *x) // no longer used +{ + // opposite of above + int i; + double rate; + for(i = 0; i < x->ninlets; i++) + { + rate = x->ip.counter[i] / (double)x->fadeticks; + x->ip.counter[i] = epower(rate) * (double)x->fadeticks; + } +} + +static void outputfades(t_int *w, int flag) +{ + t_polygate *x = (t_polygate *)(w[1]); + float *out = (t_float *)(w[3+x->ninlets]); + int n = (int)(w[2]); + int i; + for(i = 0; i < x->ninlets; i++) + x->ip.in[i] = (t_float *)(w[3+i]); + while (n--) + { + float sum = 0; + updatefades(x); + for(i = 0; i < x->ninlets; i++) + if(x->ip.fade[i]) + { + if(flag && x->fadetype == EPOWER) + sum += *x->ip.in[i]++ * epower(x->ip.fade[i]); + else + sum += *x->ip.in[i]++ * x->ip.fade[i]; + } + *out++ = sum; + } +} + +static t_int *polygate_perform(t_int *w) +{ + t_polygate *x = (t_polygate *)(w[1]); + int n = (int)(w[2]); + t_polygateelement *e; + float *out = (t_float *)(w[3+x->ninlets]); + if (x->actuallastchoice == 0 && x->choice == 0 && x->lastchoice == 0) // initial state + { + if(x->firsttick) + { + int i; + for (i = 0, e = x->x_vec; i < x->ninlets; i++, e++) + outlet_float(e->e_outlet, 0); + x->firsttick = 0; + } + while (n--) + *out++ = 0; + } + else if (x->actuallastchoice == 0 && x->choice != 0) // change from zero state to non-zero +/* outputfades(w, LINEAR); */ + outputfades(w, x->fadetype); + else if(x->choice != 0) // change from non-zero to a different non-zero + outputfades(w, EPOWER); + else if (x->actuallastchoice != 0 && x->choice == 0) // change to zero state from non-zero +/* outputfades(w, LINEAR); */ + outputfades(w, x->fadetype); + checkswitchstatus(x); + return (w+4+x->ninlets); +} + +static void polygate_dsp(t_polygate *x, t_signal **sp) +{ + int n = sp[0]->s_n, i; + // must be a smarter way.... + switch (x->ninlets) + { + case 1: dsp_add(polygate_perform, 4, x, n, sp[0]->s_vec, sp[1]->s_vec); + break; + case 2: dsp_add(polygate_perform, 5, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec); + break; + case 3: dsp_add(polygate_perform, 6, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec); + break; + case 4: dsp_add(polygate_perform, 7, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec); + break; + case 5: dsp_add(polygate_perform, 8, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec); + break; + case 6: dsp_add(polygate_perform, 9, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec); + break; + case 7: dsp_add(polygate_perform, 10, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec); + break; + case 8: dsp_add(polygate_perform, 11, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, + sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec, sp[8]->s_vec); + break; + case 9: dsp_add(polygate_perform, 12, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, + sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec, sp[8]->s_vec, sp[9]->s_vec); + break; + case 10: dsp_add(polygate_perform, 13, x, n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, + sp[3]->s_vec, sp[4]->s_vec, sp[5]->s_vec, sp[6]->s_vec, sp[7]->s_vec, sp[8]->s_vec, sp[9]->s_vec, sp[10]->s_vec); + break; + } +} + +static void polygate_free(t_polygate *x) +{ + freebytes(x->x_vec, x->ninlets * sizeof(*x->x_vec)); +} + +static void shortcheck(t_polygate *x, int newticks, int shorter) +{ + int i; + for(i = 0; i < x->ninlets; i++) + { + if(shorter && x->ip.timeoff[i]) // correct active timeoffs for new x->fadeticks (newticks) + x->ip.timeoff[i] = clock_getlogicaltime() - ((newticks - x->ip.counter[i]) / (x->srate / 1000.) - 1) * (TIMEUNITPERSEC / 1000.); + } +} +static void adjustcounters_ftimechange(t_polygate *x, int newticks, int shorter) +{ + int i; + shortcheck(x, newticks, shorter); + for(i = 0; i < x->ninlets; i++) + { +/* if(shorter && x->ip.timeoff[i]) // correct active timeoffs for new x->fadeticks (newticks) */ +/* x->ip.timeoff[i] = clock_getlogicaltime() - (newticks - x->ip.counter[i]) / (x->srate / 1000.) * (TIMEUNITPERSEC / 1000.); */ + if(x->ip.counter[i]) + x->ip.counter[i] = x->ip.fade[i] * (float)newticks; + + +/* post("x->ip.fade[i] = %f", x->ip.fade[i]); */ +/* updatefades(x); */ +/* x->ip.fade[i] = x->ip.counter[i] / (float)x->fadeticks; */ + } +} + +void polygate_ftimeepower(t_polygate *x, t_floatarg ftime) +{ + int newticks, i, shorter; + ftime = ftime < 1 ? 1 : ftime; + shorter = ftime < x->fadetime ? 1 : 0; + x->fadetime = (int)ftime; + newticks = (int)(x->srate / 1000 * x->fadetime); // no. of ticks to reach specified fade time + x->fadeticks = newticks; + if(ftime < EPMIN) + { + // NB - if we change to linear as a tone is fading out ----> click + if(x->lastfadetype != LINEAR) // change to linear + { + shortcheck(x, x->fadeticks, shorter); + for(i = 0; i < x->ninlets; i++) + { + if(x->ip.counter[i]) + { + float fade = x->ip.fade[i]; + int oldcounter = x->ip.counter[i]; + x->ip.counter[i] = epower(fade) * x->fadeticks; +/* post("%d fade = %f : epower = %f : oldcounter = %d : new = %d : newfade = %f", */ +/* i, fade, epower(fade), oldcounter, x->ip.counter[i], ( x->ip.counter[i]/ (float)x->fadeticks)); */ + x->ip.fade[i] = x->ip.counter[i]/ (float)x->fadeticks; // ??? + } + } + } + else // plain fade-time change - linear + adjustcounters_ftimechange(x, x->fadeticks, shorter); + x->lastfadetype = x->fadetype = LINEAR; + } + else + { + if(x->lastfadetype != EPOWER) // change to equal power + for(i = 0; i < x->ninlets; i++) + { + if(x->ip.counter[i]) + { + float fade = x->ip.fade[i]; + int oldcounter = x->ip.counter[i]; + x->ip.counter[i] = aepower(fade) * x->fadeticks; +/* post("%d fade = %f : oldcounter = %d : new = %d : epower = %f", */ +/* i, fade, oldcounter, x->ip.counter[i], epower( x->ip.counter[i]/ (float)x->fadeticks)); */ + x->ip.fade[i] = epower(x->ip.counter[i]/ (float)x->fadeticks); // ??? + } + } + else // plain fade-time change - equal power + adjustcounters_ftimechange(x, x->fadeticks, shorter); + x->lastfadetype = x->fadetype = EPOWER; + } +} + +static void polygate_ftimelinear(t_polygate *x, t_floatarg ftime) +{ + int newticks, i, shorter; + ftime = ftime < 1 ? 1 : ftime; + shorter = ftime < x->fadetime ? 1 : 0; + x->fadetime = (int)ftime; + newticks = (int)(x->srate / 1000 * x->fadetime); + x->fadeticks = newticks; + if(x->lastfadetype != LINEAR) + { + shortcheck(x, x->fadeticks, shorter); + for(i = 0; i < x->ninlets; i++) + { +/* float fade = x->ip.fade[i]; */ +/* x->ip.counter[i] = aepower(fade) * (float)newticks; */ + float fade = x->ip.fade[i]; + int oldcounter = x->ip.counter[i]; + x->ip.counter[i] = epower(fade) * x->fadeticks; + // post("%d fade = %f : epower = %f : oldcounter = %d : new = %d : newfade = %f", + // i, fade, epower(fade), oldcounter, x->ip.counter[i], ( x->ip.counter[i]/ (float)x->fadeticks)); + x->ip.fade[i] = x->ip.counter[i]/ (float)x->fadeticks; // ??? + + } + } + else + adjustcounters_ftimechange(x, newticks, shorter); + x->lastfadetype = x->fadetype = LINEAR; +} + +void polygate_tilde_setup(void) +{ + polygate_class = class_new(gensym("polygate~"), (t_newmethod)polygate_new, (t_method)polygate_free, + sizeof(t_polygate), 0, A_GIMME, 0); + class_addmethod(polygate_class, nullfn, gensym("signal"), 0); + class_addmethod(polygate_class, (t_method)polygate_dsp, gensym("dsp"), 0); + CLASS_MAINSIGNALIN(polygate_class, t_polygate, x_f); + class_addmethod(polygate_class, (t_method)polygate_f, gensym("choice"), A_FLOAT, 0); + class_addmethod(polygate_class, (t_method)polygate_ftimeepower, gensym("ftime-epower"), A_FLOAT, (t_atomtype) 0); + class_addmethod(polygate_class, (t_method)polygate_ftimelinear, gensym("ftime-linear"), A_FLOAT, (t_atomtype) 0); +} + + + + + + + + + + + -- cgit v1.2.1