From d41e58e6cd71fdacdca69ba78c29d42dc7d330d5 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 20 Nov 2002 17:46:33 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r224, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/maxlib/; revision=225 --- src/arbran.c | 178 ++++++ src/average.c | 192 ++++++ src/beat.c | 394 ++++++++++++ src/beta.c | 98 +++ src/bilex.c | 82 +++ src/borax.c | 228 +++++++ src/cauchy.c | 81 +++ src/chord.c | 1802 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/delta.c | 128 ++++ src/dist.c | 269 ++++++++ src/divide.c | 100 +++ src/divmod.c | 90 +++ src/edge.c | 77 +++ src/expo.c | 77 +++ src/fifo.c | 86 +++ src/gauss.c | 78 +++ src/gestalt.c | 109 ++++ src/history.c | 257 ++++++++ src/ignore.c | 112 ++++ src/iso.c | 178 ++++++ src/lifo.c | 95 +++ src/limit.c | 119 ++++ src/linear.c | 72 +++ src/listfunnel.c | 82 +++ src/match.c | 268 ++++++++ src/minus.c | 100 +++ src/mlife.c | 497 +++++++++++++++ src/multi.c | 99 +++ src/netclient.c | 348 +++++++++++ src/netdist.c | 304 +++++++++ src/netrec.c | 435 +++++++++++++ src/netserver.c | 563 +++++++++++++++++ src/nroute.c | 173 +++++ src/pitch.c | 106 ++++ src/plus.c | 100 +++ src/poisson.c | 82 +++ src/pong.c | 327 ++++++++++ src/pulse.c | 265 ++++++++ src/remote.c | 84 +++ src/rhythm.c | 329 ++++++++++ src/scale.c | 133 ++++ src/score.c | 293 +++++++++ src/speedlim.c | 227 +++++++ src/step.c | 167 +++++ src/subst.c | 408 ++++++++++++ src/temperature.c | 109 ++++ src/tilt.c | 176 ++++++ src/triang.c | 70 +++ src/velocity.c | 102 +++ src/weibull.c | 85 +++ 50 files changed, 10834 insertions(+) create mode 100644 src/arbran.c create mode 100644 src/average.c create mode 100644 src/beat.c create mode 100644 src/beta.c create mode 100644 src/bilex.c create mode 100644 src/borax.c create mode 100644 src/cauchy.c create mode 100644 src/chord.c create mode 100644 src/delta.c create mode 100644 src/dist.c create mode 100644 src/divide.c create mode 100644 src/divmod.c create mode 100644 src/edge.c create mode 100644 src/expo.c create mode 100644 src/fifo.c create mode 100644 src/gauss.c create mode 100644 src/gestalt.c create mode 100644 src/history.c create mode 100644 src/ignore.c create mode 100644 src/iso.c create mode 100644 src/lifo.c create mode 100644 src/limit.c create mode 100644 src/linear.c create mode 100644 src/listfunnel.c create mode 100644 src/match.c create mode 100644 src/minus.c create mode 100644 src/mlife.c create mode 100644 src/multi.c create mode 100644 src/netclient.c create mode 100644 src/netdist.c create mode 100644 src/netrec.c create mode 100644 src/netserver.c create mode 100644 src/nroute.c create mode 100644 src/pitch.c create mode 100644 src/plus.c create mode 100644 src/poisson.c create mode 100644 src/pong.c create mode 100644 src/pulse.c create mode 100644 src/remote.c create mode 100644 src/rhythm.c create mode 100644 src/scale.c create mode 100644 src/score.c create mode 100644 src/speedlim.c create mode 100644 src/step.c create mode 100644 src/subst.c create mode 100644 src/temperature.c create mode 100644 src/tilt.c create mode 100644 src/triang.c create mode 100644 src/velocity.c create mode 100644 src/weibull.c (limited to 'src') diff --git a/src/arbran.c b/src/arbran.c new file mode 100644 index 0000000..6fdcb24 --- /dev/null +++ b/src/arbran.c @@ -0,0 +1,178 @@ +/* ---------------------------- rand_arbran ----------------------------------- */ +/* */ +/* rand_arbran generates a random variable that conforms to the */ +/* piecewise probability density in two arrays */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX +#ifndef M_PI +#define M_PI 3.1415927 +#endif + +static char *version = "arbran v0.1b, generates a random variable that conforms to the\n" + " piecewise probability density in two arrays\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_arbran ------------------------------ */ + +static t_class *rand_arbran_class; + +typedef struct _rand_arbran +{ + t_object x_obj; + t_symbol *x_x; + t_symbol *x_p; + t_garray *x_bufx; + t_garray *x_bufp; +} t_rand_arbran; + +static void rand_arbran_pdfscale(t_rand_arbran *x) +{ + t_garray *bx = x->x_bufx, *bp = x->x_bufp; + t_float a = 0; + t_int k = 0; + t_float *tx, *tp; + t_int ix, ip; + if (!garray_getfloatarray(bx, &ix, &tx)) + { + post("arbran: couldn't read from array!"); + return; + } + if (!garray_getfloatarray(bp, &ip, &tp)) + { + post("arbran: couldn't read from array!"); + return; + } + + for(k = 1; k < ix; k++) + { + a += (tx[k]-tx[k-1])*(tp[k]+tp[k-1])/2.0; + } + for(k = 0; k < ix; k++) + { + tp[k] = tp[k]/a; + } + garray_redraw(x->x_bufp); +} + +static void rand_arbran_bang(t_rand_arbran *x) +{ + t_garray *bx = x->x_bufx, *bp = x->x_bufp; + t_float a, u, a0, slope, b, d, r; + t_int k = 0; + t_float *tx, *tp; + t_int ix, ip; + if (!garray_getfloatarray(bx, &ix, &tx)) + { + post("arbran: couldn't read from array!"); + return; + } + if (!garray_getfloatarray(bp, &ip, &tp)) + { + post("arbran: couldn't read from array!"); + return; + } + + a = 0; + u = fran(); + while(u > a) + { + a0 = (tx[k+1]-tx[k])*(tp[k+1]+tp[k])/2.0; + a += a0; + k++; + } + k--; + slope = (tp[k+1]-tp[k])/(tx[k+1]-tx[k]); + if(slope == 0) + { + r = (u-a+a0)/tp[k]+tx[k]; + } + else + { + b=tp[k]/slope-tx[k]; + d=b*b+tx[k]*tx[k]+2*b*tx[k]+2*(u-a+a0)/slope; + if(slope > 0) + r=-b+sqrt(d); + else + r=-b-sqrt(d); + } + outlet_float(x->x_obj.ob_outlet, r); +} + +static void rand_arbran_set(t_rand_arbran *x) +{ + t_garray *b, *b2; + + if ((b = (t_garray *)pd_findbyclass(x->x_x, garray_class))) + { + post("arbran: array set to \"%s\"", x->x_x->s_name); + x->x_bufx = b; + } else { + post("arbran: no array \"%s\" (error %d)", x->x_x->s_name, b); + x->x_bufx = 0; + } + if ((b2 = (t_garray *)pd_findbyclass(x->x_p, garray_class))) + { + post("arbran: array set to \"%s\"", x->x_p->s_name); + x->x_bufp = b2; + } else { + post("arbran: no array \"%s\" (error %d)", x->x_p->s_name, b); + x->x_bufp = 0; + } +} + +static void rand_arbran_setarrays(t_rand_arbran *x, t_symbol *s1, t_symbol *s2) +{ + x->x_x = s1; + x->x_p = s2; + rand_arbran_set(x); +} + +static void *rand_arbran_new(t_symbol *s1, t_symbol *s2) +{ + t_rand_arbran *x = (t_rand_arbran *)pd_new(rand_arbran_class); + srand( (unsigned)time( NULL ) ); + outlet_new(&x->x_obj, &s_float); + x->x_x = s1; + x->x_p = s2; + rand_arbran_set(x); + return (x); +} + +void arbran_setup(void) +{ + rand_arbran_class = class_new(gensym("arbran"), (t_newmethod)rand_arbran_new, 0, + sizeof(t_rand_arbran), 0, A_SYMBOL, A_SYMBOL, 0); + class_addbang(rand_arbran_class, rand_arbran_bang); + class_addmethod(rand_arbran_class, (t_method)rand_arbran_pdfscale, gensym("pdfscale"), 0); + class_addmethod(rand_arbran_class, (t_method)rand_arbran_setarrays, gensym("set"), A_SYMBOL, A_SYMBOL, 0); + class_sethelpsymbol(rand_arbran_class, gensym("maxlib/help-arbran.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/average.c b/src/average.c new file mode 100644 index 0000000..2c46ce3 --- /dev/null +++ b/src/average.c @@ -0,0 +1,192 @@ +/* -------------------------- average ----------------------------------------- */ +/* */ +/* Calculates the average value of the last N elements. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAX_ARG 128 /* maximum number of items to average */ + +static char *version = "average v0.1, written by Olaf Matthes "; + +typedef struct average +{ + t_object x_ob; + t_clock *x_clock; + t_inlet *x_inindex; + t_outlet *x_outfloat; /* output the average */ + t_outlet *x_outtendency; /* outputs the tendency of the average */ + t_int x_limit; /* indicates if input is 'blocked' (1) */ + t_int x_index; /* the number of elements to average */ + t_float x_input[MAX_ARG]; /* stores the input values we need for averaging */ + t_int x_inpointer; /* actual position in above array */ + t_float x_average; /* what do you guess ? */ + t_float x_lastaverage; + t_int x_mode; /* how to average: linear or geometric */ + +} t_average; + + /* there must be a function for this in math.h but how is the + german 'Fakultät' called in english ???? */ +static int normalise(int i) +{ + int ret = i; + while(i--) + { + if(i == 0)break; + ret += i; + } + return (ret); +} + +static void average_float(t_average *x, t_floatarg f) +{ + int i, j = 0; + t_float tendency; + t_float geo = 1.0; + + x->x_average = 0; + /* put value into array */ + x->x_input[x->x_inpointer] = f; + /* calulate average */ + for(i = 0; i < x->x_index; i++) + { + if(x->x_mode == 0) /* linear */ + { + x->x_average += x->x_input[i] * (1.0 / (float)x->x_index); + } + else if(x->x_mode == 1) /* geometric */ + { + if(x->x_input[i] == 0)x->x_input[i] = 0.001; /* need to cheat a bit... */ + geo *= x->x_input[i]; + if(i == x->x_index - 1) + x->x_average = pow(geo, (1.0/(float)x->x_index)); + } + else if(x->x_mode == 2) /* weighted */ + { + x->x_average += x->x_input[(j + x->x_inpointer + x->x_index) % x->x_index] * (float)(x->x_index - (i + 1)); + j--; /* go back in array */ + /* normalise output */ + if(i == x->x_index - 1) + x->x_average = x->x_average / (float)normalise(x->x_index - 1); + } else post("average: internal error!"); + } + if(++x->x_inpointer > x->x_index) + { + x->x_inpointer = 0; + if(x->x_lastaverage < x->x_average) + { + tendency = 1; /* getting more */ + } + else if(x->x_lastaverage > x->x_average) + { + tendency = -1; /* getting less */ + } + else tendency = 0; /* nothing has changed */ + outlet_float(x->x_outtendency, tendency); + x->x_lastaverage = x->x_average; + } + outlet_float(x->x_outfloat, x->x_average); +} + +static void average_index(t_average *x, t_floatarg f) +{ + x->x_index = (t_int)f; + if(x->x_index > MAX_ARG)x->x_index = MAX_ARG; +} + +static void average_reset(t_average *x) +{ + int i; + /* zeroe out the array */ + for(i = 0; i < MAX_ARG; i++)x->x_input[i] = 0.0; + x->x_inpointer = 0; + x->x_average = 0; + x->x_lastaverage = 0; + post("average: reset"); +} + +static void average_linear(t_average *x) +{ + x->x_mode = 0; + post("average: linear"); +} + +static void average_geometric(t_average *x) +{ + x->x_mode = 1; + post("average: geometric"); +} + +static void average_weight(t_average *x) +{ + x->x_mode = 2; + post("average: weighted"); +} + +static void average_free(t_average *x) +{ + /* nothing to do */ +} + +static t_class *average_class; + +static void *average_new(t_floatarg f) +{ + int i; + + t_average *x = (t_average *)pd_new(average_class); + x->x_inindex = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("index")); + x->x_outfloat = outlet_new(&x->x_ob, gensym("float")); + x->x_outtendency = outlet_new(&x->x_ob, gensym("float")); + + /* zeroe out the array */ + for(i = 0; i < MAX_ARG; i++)x->x_input[i] = 0.0; + x->x_index = (t_int)f; + if(x->x_index > MAX_ARG) + { + x->x_index = MAX_ARG; + post("average: set number of items to %d", x->x_index); + } + x->x_inpointer = 0; + x->x_average = 0; + x->x_mode = 0; +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void average_setup(void) +{ + average_class = class_new(gensym("average"), (t_newmethod)average_new, + (t_method)average_free, sizeof(t_average), 0, A_DEFFLOAT, 0); + class_addmethod(average_class, (t_method)average_reset, gensym("reset"), 0); + class_addmethod(average_class, (t_method)average_linear, gensym("linear"), 0); + class_addmethod(average_class, (t_method)average_geometric, gensym("geometric"), 0); + class_addmethod(average_class, (t_method)average_weight, gensym("weight"), 0); + class_addfloat(average_class, average_float); + class_addmethod(average_class, (t_method)average_index, gensym("index"), A_FLOAT, 0); + class_sethelpsymbol(average_class, gensym("maxlib/help-average.pd")); +} + diff --git a/src/beat.c b/src/beat.c new file mode 100644 index 0000000..f21c393 --- /dev/null +++ b/src/beat.c @@ -0,0 +1,394 @@ +/* --------------------------- beat ------------------------------------------ */ +/* */ +/* Detect the beats per minute of a MIDI stream. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code written by Robert Rowe. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define BEAT_LONG 1500 /* longest time we take into concideration (40 bpm) */ +#define BEAT_SHORT 300 /* shortest time we take into concideration (200 bpm) */ + +static char *version = "beat v0.1, written by Olaf Matthes "; + +typedef struct +{ + t_int points; /* number of points assigned to this theory */ + double expect; /* time of next expected hit */ + t_int onbeat; /* whether (1) or not (0) it was on the beat */ +} beat_theory; + +typedef struct /* used for sorting theories */ +{ + t_int points; + t_int theory; +} beat_sort_record; + + +typedef struct beat +{ + t_object x_ob; + t_clock *x_clock; + t_outlet *x_outbpm; /* beat as MIDI note number */ + t_outlet *x_outms; /* beat in milliseconds */ + t_outlet *x_outbeat; /* send a bang whenever beat is 'on beat' */ + t_int x_print; /* switch printing to console window on / off */ + + t_int x_num_beats; /* number of beats we've received */ + double x_beat_period; /* time in ms until next expected beat / beat pulse */ + beat_theory x_beats[BEAT_LONG]; + double x_beatexpect; /* expected time for next beat */ + t_int x_on_beat; /* indicate if last event was on beat */ + t_int x_band_percent; + + t_int x_pitch; + t_int x_velo; + /* helpers needed to do the time calculations */ + double x_this_input; + double x_last_input; + double x_lasttime; + double x_lastlasttime; +} t_beat; + +/* ---------------- mathematical functions to work with doubles -------------- */ +static double double_abs(double value) +{ + if(value < 0) + return (value * -1); + else + return (value); +} + +/* --------------- beat stuff ------------------------------------------------ */ + /* evaluate results: find theory that is the most likely one and + print out internal data to console window if print is enabled */ +static int beat_evaluate(t_beat *x) +{ + int i, j, K; + char string[256]; + char info[40]; + beat_sort_record theories[BEAT_LONG], *sortp, R; + int value; /* the result of the sorting */ + + for (i = 0; i < BEAT_LONG; i++) + { /* prepare sort records */ + sortp = &(theories[i]); + sortp->points = x->x_beats[i].points; + sortp->theory = i; + } + for (j = 2; j < BEAT_LONG; j++) + { /* sort */ + i = j - 1; + K = theories[j].points; + R = theories[j]; + while (i > 0) + { + if (K >= theories[i].points) + { + theories[i+1] = R; + break; + } + else + { + theories[i+1] = theories[i]; + i -= 1; + } + } + if (i==0) theories[i+1] = R; + } + /* get leading result */ + sortp = &(theories[BEAT_LONG - 1]); + value = sortp->theory; /* get our resulting theory */ + + if(x->x_print) + { + post(" 0 1 2 3 4 R E"); + *string = '\0'; /* print out five leading theories */ + sprintf(info, "%4g", x->x_this_input); + strcat(string, info); + for(i = 1; i < 6; i++) + { + sortp = &(theories[BEAT_LONG - i]); + sprintf(info, " %4d[%3d]", sortp->theory, sortp->points); + strcat(string, info); + } + sprintf(info, " %g %g", clock_getlogicaltime(), x->x_beatexpect); + strcat(string, info); + post(string); + } + + return value; +} + + /* reduce duration to fit into our processing window */ + /* some sort of 'double modulo'... */ +static double beat_reduce_offset(double duration) +{ + double temp = duration; + int divisor = 2; /* first try dividing by two */ + while (temp > BEAT_LONG) /* while duration is too long */ + temp = duration / divisor++; /* divide by progressively higher divisors */ + return temp; /* return a value in bounds */ +} + +/* + * beat_eligible: determine whether an event is eligible for consideration + * as a beat theory + */ +static int beat_eligible(double candidate, int* offsets, int num_offsets) +{ + double diff; + int i; + + if (candidate >= BEAT_LONG) /* if too long try subharmonics */ + candidate = beat_reduce_offset(candidate); + + /* if candidate is close to one already found */ + for(i = 0; i < num_offsets; i++) + { + diff = double_abs((candidate - offsets[i])); + if (diff < offsets[i]/20) { + if (candidate > offsets[i]) + ++offsets[i]; else /* pull existing one */ + if (candidate < offsets[i]) /* toward new candidate */ + --offsets[i]; + return 0; /* declare candidate ineligible */ + } + } + return candidate; /* otherwise return legal candidate */ +} + +static void beat_float(t_beat *x, t_floatarg f) +{ + t_int velo = x->x_velo; + int i, j, indx; + int num_offsets, candidate; + int low_limit, high_limit, width, deviation; + int points, band, center_offset, period; + beat_theory* t; + int offsets[7]; + static int factors[10] = + { 200, 50, 300, 150, 66, 400, 600, 133, 33, 75 }; + double now = clock_getlogicaltime(); + t_float outvalue; + + x->x_pitch = (t_int)f; + x->x_this_input = clock_gettimesince(x->x_last_input); + + if(velo != 0) /* note-on received */ + { + if(++x->x_num_beats == 1) + { + goto time; /* only one event, no beats yet */ + } + + num_offsets = 0; + candidate = beat_eligible(x->x_this_input, offsets, num_offsets); + if(candidate) + offsets[num_offsets++] = candidate; /* offset[0] set to incoming offset */ + + if(x->x_num_beats > 2) + { /* if three events */ + /* check previous for eligibility */ + candidate = beat_eligible(x->x_lasttime, offsets, num_offsets); + if (candidate) + offsets[num_offsets++] = candidate; + candidate = x->x_this_input + x->x_lasttime; /* add current and previous offsets */ + candidate = beat_eligible(candidate, offsets, num_offsets); + if (candidate) /* add to list if eligible */ + offsets[num_offsets++] = candidate; + } + + if(x->x_num_beats > 3) + { + candidate = beat_eligible(x->x_lastlasttime, offsets, num_offsets); + if (candidate) + offsets[num_offsets++] = candidate; + candidate += x->x_lasttime; + candidate = beat_eligible(candidate, offsets, num_offsets); + if (candidate) + offsets[num_offsets++] = candidate; + } + + indx = 0; + for(i = num_offsets; i < 7; i++) + { + offsets[i] = 0; + if (indx >= 10) break; + candidate = 0; + while ((indx < 10) && (!candidate)) + candidate = beat_eligible((x->x_this_input * factors[indx++])/100, offsets, num_offsets); + if (candidate) + offsets[num_offsets++] = candidate; + } + + for(i = 0; i < num_offsets; i++) + { + band = offsets[i] * x->x_band_percent / 100; + if ((low_limit = offsets[i] - band) < 0) /* add points in a critical band */ + low_limit = 0; /* around calculated offset */ + if ((high_limit = offsets[i] + band) > BEAT_LONG) + high_limit = BEAT_LONG; + center_offset = offsets[i]; /* midpoint of increment */ + points = 0; + for (j = low_limit; j < high_limit; j++) + { + if ((points = x->x_beats[j].points) > 0) + { /* if there is already activation */ + deviation = j - center_offset; /* record deviation from midpoint */ + x->x_beats[j].points = 0; + if (deviation < 0) { /* if there is activation below midpoint */ + t = &(x->x_beats[j+1]); /* take theory one above prior */ + } else + if (deviation > 0) { /* if there is activation above midpoint */ + t = &(x->x_beats[j-1]); /* take theory one below prior */ + } else + t = &(x->x_beats[j]); /* landed right on it */ + t->points = points + (num_offsets-i); + break; + } + } + if (!points) + x->x_beats[center_offset].points = num_offsets - i; + } + + /* boost hits, and suppress theories with missed beats */ + period = 0; + points = 0; + for (i = BEAT_SHORT; i < BEAT_LONG; i++) + { + t = &(x->x_beats[i]); + width = 5 > (t->expect / 7) ? 5 : (t->expect / 7); + t->expect -= x->x_this_input; + t->onbeat = 0; + if(double_abs(t->expect) <= width) /* lies within range */ + { + t->expect = i; + t->onbeat = 1; + if (t->points > 0) + t->points += 4; /* add 4 points */ + } + else if(t->expect < 0) + { + t->points -= 8; + t->expect = i; + } + if (t->points < 0) t->points = 0; else + if (t->points > 200) t->points = 200; + if (t->points > points) + { + points = t->points; + period = i; + } + } + + + + x->x_beat_period = (double)period; + t = &(x->x_beats[period]); + x->x_beatexpect = now + (double)t->expect; + x->x_on_beat = t->onbeat; + +time: + x->x_lastlasttime = x->x_lasttime; + x->x_lasttime = x->x_this_input; //now; + x->x_last_input = now; + + if(x->x_on_beat)outlet_bang(x->x_outbeat); + outvalue = (t_float)beat_evaluate(x); + outlet_float(x->x_outms, outvalue); + if(x->x_beat_period)outlet_float(x->x_outbpm, (t_float)(60000.0 / outvalue)); + } + return; +} + +static void beat_ft1(t_beat *x, t_floatarg f) +{ + x->x_velo = (t_int)f; +} + + /* toggle printing on/off */ +static void beat_print(t_beat *x) +{ + if(x->x_print)x->x_print = 0; + else x->x_print = 1; +} + +static void beat_reset(t_beat *x) +{ + int i; + + for(i = 0; i < BEAT_LONG; i++) + { + x->x_beats[i].points = 0; + x->x_beats[i].expect = i; + x->x_beats[i].onbeat = 0; + } + x->x_lastlasttime = 0; + x->x_lasttime = 0; + x->x_num_beats = 0; + x->x_beat_period = 0; + x->x_on_beat = 0; +} + +static t_class *beat_class; + +static void beat_free(t_beat *x) +{ + /* nothing to do */ +} + +static void *beat_new(t_floatarg f) +{ + t_beat *x = (t_beat *)pd_new(beat_class); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_outbpm = outlet_new(&x->x_ob, gensym("float")); + x->x_outms = outlet_new(&x->x_ob, gensym("float")); + x->x_outbeat = outlet_new(&x->x_ob, gensym("bang")); + + beat_reset(x); + x->x_band_percent = 4; /* allow 4% 'jitter' by default */ + if(f)x->x_band_percent = (t_int)f; + +#ifndef MAXLIB + post(version); +#endif + post("beat: band percentage set to %d", x->x_band_percent); + + return (void *)x; +} + +void beat_setup(void) +{ + beat_class = class_new(gensym("beat"), (t_newmethod)beat_new, + (t_method)beat_free, sizeof(t_beat), 0, A_DEFFLOAT, 0); + class_addcreator((t_newmethod)beat_new, gensym("max.beat"), A_DEFFLOAT, 0); + class_addfloat(beat_class, beat_float); + class_addmethod(beat_class, (t_method)beat_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(beat_class, (t_method)beat_reset, gensym("reset"), 0); + class_addmethod(beat_class, (t_method)beat_print, gensym("print"), 0); + class_sethelpsymbol(beat_class, gensym("maxlib/help-beat.pd")); +} + diff --git a/src/beta.c b/src/beta.c new file mode 100644 index 0000000..df554ec --- /dev/null +++ b/src/beta.c @@ -0,0 +1,98 @@ +/* ---------------------------- rand_beta ----------------------------------- */ +/* */ +/* rand_beta generates a beta distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX +#ifndef M_PI +#define M_PI 3.1415927 +#endif + +static char *version = "beta v0.1, generates a beta distributed random variable\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_beta ------------------------------ */ + +static t_class *rand_beta_class; + +typedef struct _rand_beta +{ + t_object x_obj; + t_float x_a; + t_float x_b; +} t_rand_beta; + +static void *rand_beta_new(t_floatarg a, t_floatarg b) +{ + t_rand_beta *x = (t_rand_beta *)pd_new(rand_beta_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_a); + floatinlet_new(&x->x_obj, &x->x_b); + outlet_new(&x->x_obj, &s_float); + x->x_a = a; + x->x_b = b; + return (x); +} + +static void rand_beta_bang(t_rand_beta *x) +{ + t_float u1, u2, y1, y2, sum, a, b, ainv, binv; + a = (x->x_a <= 0 ? 0.0001 : x->x_a); + b = (x->x_b <= 0 ? 0.0001 : x->x_b); + ainv = 1/a; + binv = 1/b; + do + { + do + { + u1 = fran(); + } + while(u1 == 0); + do + { + u2 = fran(); + } + while(u2 == 0); + y1 = pow(u1, ainv); + y2 = pow(u2, binv); + sum = y1 + y2; + } + while(sum > 1); + outlet_float(x->x_obj.ob_outlet, y1/sum); +} + +void beta_setup(void) +{ + rand_beta_class = class_new(gensym("beta"), (t_newmethod)rand_beta_new, 0, + sizeof(t_rand_beta), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addbang(rand_beta_class, rand_beta_bang); + class_sethelpsymbol(rand_beta_class, gensym("maxlib/help-beta.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/bilex.c b/src/bilex.c new file mode 100644 index 0000000..44bc045 --- /dev/null +++ b/src/bilex.c @@ -0,0 +1,82 @@ +/* ---------------------------- rand_bilex ------------------------------------ */ +/* */ +/* rand_bilex generates a bilinear exponentially distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX + +static char *version = "bilex v0.1, generates bilinear exponentially distributed random\n" + " variable, written by Olaf Matthes "; + +/* -------------------------- rand_bilex ------------------------------ */ + +static t_class *rand_bilex_class; + +typedef struct _rand_bilex +{ + t_object x_obj; + t_float x_lambda; +} t_rand_bilex; + +static void *rand_bilex_new(t_floatarg f) +{ + t_rand_bilex *x = (t_rand_bilex *)pd_new(rand_bilex_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_lambda); + outlet_new(&x->x_obj, &s_float); + x->x_lambda = f; + return (x); +} + +static void rand_bilex_bang(t_rand_bilex *x) +{ + t_float u, s = 1, l; + l = (x->x_lambda <= 0 ? 0.0001 : x->x_lambda); + do + { + u = 2*fran(); + } + while(u == 0 || u == 2); + if(u > 1) + { + u = 2-u; + s=-1; + } + outlet_float(x->x_obj.ob_outlet, s*log(u)/l); +} + +void bilex_setup(void) +{ + rand_bilex_class = class_new(gensym("bilex"), (t_newmethod)rand_bilex_new, 0, + sizeof(t_rand_bilex), 0, A_DEFFLOAT, 0); + class_addbang(rand_bilex_class, rand_bilex_bang); + class_sethelpsymbol(rand_bilex_class, gensym("maxlib/help-bilex.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/borax.c b/src/borax.c new file mode 100644 index 0000000..7031384 --- /dev/null +++ b/src/borax.c @@ -0,0 +1,228 @@ +/* ------------------------- borax ------------------------------------------ */ +/* */ +/* "swiss army knife" for music analysis. Inspired by 'borax' for Max. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +#define MAX_POLY 128 /* maximum number of notes played at a time */ + +static char *version = "borax v0.1, written by Olaf Matthes "; + +typedef struct borax +{ + t_object x_ob; + t_inlet *x_invelo; /* inlet for velocity */ + t_inlet *x_inreset; /* inlet to reset the object */ + t_outlet *x_outnotecount; /* counts notes */ + t_outlet *x_outvoicealloc; /* assigns every note a unique number */ + t_outlet *x_outpoly; /* number of notes playing (polyphony) */ + t_outlet *x_outpitch; /* pitch of current note */ + t_outlet *x_outvelo; /* velocity of current note */ + t_outlet *x_outdurcount; /* number assigned to duration value */ + t_outlet *x_outdurval; /* duration value */ + t_outlet *x_outtimecount; /* number assigned to delta time value */ + t_outlet *x_outtimeval; /* delta time value */ + + + t_float x_notecount; + t_int x_pitch; + t_int x_velo; + t_float x_voicecount; + t_int x_voicealloc; + t_int x_poly; + t_float x_durcount; + t_float x_durval; + t_float x_timecount; + t_float x_timeval; + /* helpers needed to do the calculations */ + double x_starttime[MAX_POLY]; + double x_laststarttime; + t_int x_alloctable[MAX_POLY]; + +} t_borax; + +static void borax_float(t_borax *x, t_floatarg f) +{ + t_int velo = x->x_velo; + t_int allloc = 0; + int i; + + x->x_pitch = (t_int)f; + + if(velo == 0) + { + /* note off received... */ + if(x->x_poly > 0)x->x_poly--; /* polyphony has decreased by one */ + x->x_durcount++; /* we can calculate the duration */ + for(i = 0; i < MAX_POLY; i++) /* search for voice allocation number */ + { + /* search for corresponding alloc number */ + if(x->x_alloctable[i] == x->x_pitch) + { + x->x_voicealloc = i; + x->x_alloctable[i] = 0; /* free the alloc number */ + break; + } + /* couldn't find it ? */ + if(i == MAX_POLY - 1) + { + post("borax: no corresponding note-on found (ignored)"); + return; + } + } + x->x_durval = clock_gettimesince(x->x_starttime[x->x_voicealloc]); + } + else if(velo != 0) + { + /* note on received... */ + x->x_poly++; /* number of currently playing notes has increased */ + x->x_notecount++; /* total number of notes has increased */ + /* assign a voice allocation number */ + for(i = 0; i < MAX_POLY; i++) + { + /* search for free alloc number */ + if(x->x_alloctable[i] == 0) + { + x->x_voicealloc = i; /* take the number */ + x->x_alloctable[i] = x->x_pitch; /* ... and store pitch */ + break; + } + /* couldn't find any ? */ + if(i == MAX_POLY - 1) + { + post("borax: too many note-on messages (ignored)"); + return; + } + } + /* calculate time in case it's not the first note */ + if(x->x_notecount > 1) + { + x->x_timecount++; + x->x_timeval = clock_gettimesince(x->x_laststarttime); + } + /* save the new start time */ + x->x_laststarttime = x->x_starttime[x->x_voicealloc] = clock_getlogicaltime(); + } + /* output values from right to left */ + outlet_float(x->x_outtimeval, x->x_timeval); + outlet_float(x->x_outtimecount, x->x_timecount); + outlet_float(x->x_outdurval, x->x_durval); + outlet_float(x->x_outdurcount, x->x_durcount); + outlet_float(x->x_outvelo, velo); + outlet_float(x->x_outpitch, x->x_pitch); + outlet_float(x->x_outpoly, x->x_poly); + outlet_float(x->x_outvoicealloc, x->x_voicealloc); + outlet_float(x->x_outnotecount, x->x_notecount); +} + +static void borax_ft1(t_borax *x, t_floatarg f) +{ + x->x_velo = (t_int)f; +} + +static void borax_reset(t_borax *x) +{ + int i; + post("borax: reset"); + x->x_notecount = 0; + x->x_pitch = 0; + x->x_velo = 0; + x->x_voicecount = 0; + x->x_voicealloc = 0; + x->x_poly = 0; + x->x_durcount = 0; + x->x_durval = 0; + x->x_timecount = 0; + x->x_timeval = 0; + outlet_float(x->x_outtimeval, x->x_timeval); + outlet_float(x->x_outtimecount, x->x_timecount); + outlet_float(x->x_outdurval, x->x_durval); + outlet_float(x->x_outdurcount, x->x_durcount); + for(i = 0; i < MAX_POLY; i++) + { + if(x->x_alloctable[i] != 0) + { + x->x_poly--; + /* send note-off */ + outlet_float(x->x_outvelo, 0); + outlet_float(x->x_outpitch, x->x_alloctable[i]); + outlet_float(x->x_outpoly, x->x_poly); + outlet_float(x->x_outvoicealloc, i); + } + x->x_alloctable[i] = 0; + } + outlet_float(x->x_outvelo, x->x_velo); + outlet_float(x->x_outpitch, x->x_pitch); + outlet_float(x->x_outpoly, x->x_poly); + outlet_float(x->x_outvoicealloc, x->x_voicealloc); + outlet_float(x->x_outnotecount, x->x_notecount); +} + +static t_class *borax_class; + +static void *borax_new(void) +{ + int i; + + t_borax *x = (t_borax *)pd_new(borax_class); + x->x_invelo = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_inreset = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("bang"), gensym("ft2")); + x->x_outnotecount = outlet_new(&x->x_ob, gensym("float")); + x->x_outvoicealloc = outlet_new(&x->x_ob, gensym("float")); + x->x_outpoly = outlet_new(&x->x_ob, gensym("float")); + x->x_outpitch = outlet_new(&x->x_ob, gensym("float")); + x->x_outvelo = outlet_new(&x->x_ob, gensym("float")); + x->x_outdurcount = outlet_new(&x->x_ob, gensym("float")); + x->x_outdurval = outlet_new(&x->x_ob, gensym("float")); + x->x_outtimecount = outlet_new(&x->x_ob, gensym("float")); + x->x_outtimeval = outlet_new(&x->x_ob, gensym("float")); + + for(i = 0; i < MAX_POLY; i++)x->x_alloctable[i] = 0; + x->x_notecount = 0; + x->x_pitch = 0; + x->x_velo = 0; + x->x_voicecount = 0; + x->x_voicealloc = 0; + x->x_poly = 0; + x->x_durcount = 0; + x->x_durval = 0; + x->x_timecount = 0; + x->x_timeval = 0; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void borax_setup(void) +{ + borax_class = class_new(gensym("borax"), (t_newmethod)borax_new, + 0, sizeof(t_borax), 0, 0); + class_addmethod(borax_class, (t_method)borax_reset, gensym("reset"), 0); + class_addmethod(borax_class, (t_method)borax_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(borax_class, (t_method)borax_reset, gensym("ft2"), A_GIMME, 0); + class_addfloat(borax_class, borax_float); + class_sethelpsymbol(borax_class, gensym("maxlib/help-borax.pd")); +} + diff --git a/src/cauchy.c b/src/cauchy.c new file mode 100644 index 0000000..8daf86f --- /dev/null +++ b/src/cauchy.c @@ -0,0 +1,81 @@ +/* ---------------------------- rand_cauchy ----------------------------------- */ +/* */ +/* rand_cauchy generates a Cauchy distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX +#ifndef M_PI +#define M_PI 3.1415927 +#endif + +static char *version = "cauchy v0.1, generates a Cauchy distributed random variable\n" + " with a spread governed by to parameter 'aplha',\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_cauchy ------------------------------ */ + +static t_class *rand_cauchy_class; + +typedef struct _rand_cauchy +{ + t_object x_obj; + t_float x_alpha; +} t_rand_cauchy; + +static void *rand_cauchy_new(t_floatarg f) +{ + t_rand_cauchy *x = (t_rand_cauchy *)pd_new(rand_cauchy_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_alpha); + outlet_new(&x->x_obj, &s_float); + x->x_alpha = f; + return (x); +} + +static void rand_cauchy_bang(t_rand_cauchy *x) +{ + t_float u; + do + { + u = fran(); + } + while(u == 0.5); + u *= M_PI; + outlet_float(x->x_obj.ob_outlet, x->x_alpha*tan(u)); +} + +void cauchy_setup(void) +{ + rand_cauchy_class = class_new(gensym("cauchy"), (t_newmethod)rand_cauchy_new, 0, + sizeof(t_rand_cauchy), 0, A_DEFFLOAT, 0); + class_addbang(rand_cauchy_class, rand_cauchy_bang); + class_sethelpsymbol(rand_cauchy_class, gensym("maxlib/help-cauchy.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/chord.c b/src/chord.c new file mode 100644 index 0000000..0d460fc --- /dev/null +++ b/src/chord.c @@ -0,0 +1,1802 @@ +/* ------------------------- chord ------------------------------------------ */ +/* */ +/* Tries to detect a chord (or any harmonic relations) of incoming notes. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +#define MAX_POLY 32 /* maximum number of notes played at a time */ + +#define kUnison 0 +#define kMaj 1 +#define kMin 2 +#define kDim 3 +#define kAug 4 +#define kMaj7 5 +#define kDom7 6 +#define kMin7 7 +#define kHalfDim7 8 +#define kDim7 9 +#define kMinMaj7 10 +#define kMaj7s5 11 +#define kMaj7b5 12 +#define kDom7s5 13 +#define kDom7b5 14 +#define kDomb9 15 +#define kMaj9 16 +#define kDom9 17 +#define kMin9 18 +#define kHalfDim9 19 +#define kMinMaj9 20 +#define kDimMaj9 21 +#define kMaj9b5 22 +#define kDom9b5 23 +#define kDom9b13 24 +#define kMin9s11 25 +#define kmM9b11 26 +#define kMaj7b9 27 +#define kMaj7s5b9 28 +#define kDom7b9 29 +#define kMin7b9 30 +#define kMinb9s11 31 +#define kHalfDimb9 32 +#define kDim7b9 33 +#define kMinMajb9 34 +#define kDimMajb9 35 +#define kMaj7s9 36 +#define kDom7s9 37 +#define kMaj7s11 38 +#define kMs9s11 39 +#define kHDimb11 40 +#define kMaj11 41 +#define kDom11 42 +#define kMin11 43 +#define kHalfDim11 44 +#define kDim11 45 +#define kMinMaj11 46 +#define kDimMaj11 47 +#define kMaj11b5 48 +#define kMaj11s5 49 +#define kMaj11b9 50 +#define kMaj11s9 51 +#define kMaj11b13 52 +#define kMaj11s13 53 +#define kM11b5b9 54 +#define kDom11b5 55 +#define kDom11b9 56 +#define kDom11s9 57 +#define kHalfDim11b9 58 +#define kDom7s11 59 +#define kMin7s11 60 +#define kDom13s11 61 +#define kM7b913 62 +#define kMaj7s13 63 +#define kMaj9s13 64 +#define kM7b9s13 65 +#define kDom7b13 66 +#define kChrom 67 +#define kNone 68 + +#define kXX -1 + + +static char *version = "chord v0.2, written by Olaf Matthes "; + +static char* pitch_class[13] = {"C ", "Db ", "D ", "Eb ", "E ", "F ", "Gb ", "G ", "Ab ", "A ", "Bb ", "B ", "no root "}; +static char name_class[7] = {'C', 'D', 'E', 'F', 'G', 'A', 'B'}; + +typedef struct { + int type; + int rootMember; +} t_type_root; + +typedef struct chord +{ + t_object x_ob; + t_outlet *x_outchordval; /* chord as MIDI note number of base note */ + t_outlet *x_outchordclass; /* class of chord's bass note */ + t_outlet *x_outchordname; /* chord name, e.g. "Cmajor7" */ + t_outlet *x_outchordinversion; /* inversion of the chord (root = 0, 1st = 1, 2nd = 2) */ + t_outlet *x_outchordnotes; /* list with note numbers belonging to the chord */ + + t_int x_pitch; + t_int x_pc[12]; /* pitch class array */ + t_int x_abs_pc[12]; /* pitch class array: absolute MIDI note numbers */ + t_int x_velo; + t_int x_alloctable[MAX_POLY]; /* a table used to store all playing notes */ + t_int x_poly; /* number of notes currently playing */ + t_atom x_chordlist[12]; /* list that stores the note numbers for output */ + t_int x_split; /* highes note number to process */ + + t_int x_chord_type; /* chord's type (number between 0 and 68) */ + t_int x_chord_root; /* chord's root (pitch class) */ + t_int x_chord_bass; /* chord's bass note (MIDI note number) */ + t_int x_chord_inversion; /* chord's state of inversion (root, 1st, 2nd) */ + +} t_chord; + +/* functions */ +static void chord_kick_out_member(t_chord *x, t_int number, t_int *members); +static void chord_chord_finder(t_chord *x, t_int num_pcs); +static void chord_draw_chord_type(t_chord *x, t_int num_pcs); + + +static void chord_unison(t_chord *x) +{ + int i; + int member = 0; + for(i = 0; i < 12; i++) + if(x->x_pc[i]) + { + member = i; // find pitch class + break; + } + x->x_chord_type = 0; + x->x_chord_root = member; + chord_draw_chord_type(x, 1); // output onto the screen +} + +static void chord_dyad(t_chord *x) +{ + static t_type_root dyads[11] = + {{ kMaj7, 1 }, { kDom7, 1 }, { kMin, 0 }, { kMaj, 0 }, { kMaj, 1 }, + { kDom7 , 0 }, { kMaj, 0 }, { kMaj, 1 }, { kMin, 1 }, { kDom7, 0 }, { kMaj7, 0 }}; + register t_type_root* t; + + int members[2]; + int i, j = 0; + int interval1; + + for(i = 0; i < 12; i++) + if(x->x_pc[i]) members[j++] = i; /* load members array with chord pitch classes */ + interval1 = members[1] - members[0]; /* calculate interval between first two members */ + interval1 = interval1 - 1; /* reduce interval1 to start at zero */ + t = &(dyads[interval1]); /* find TypeRoot struct for this interval */ + x->x_chord_type = t->type; + if (interval1 == 5) + x->x_chord_root = (members[0]+8)%12; + else + x->x_chord_root = members[t->rootMember]; + x->x_chord_inversion = t->rootMember; /* get state of inversion */ + chord_draw_chord_type(x, 2); /* output results */ +} + +static void chord_triad(t_chord *x) +{ + static t_type_root triads[10][10] = + {/* interval1 is a half step */ + {{ kMaj7b9, 1 }, { kMaj9, 1 }, { kMinMaj7, 1 }, { kMaj7, 1 }, { kDom7s11,2 }, + { kDomb9 , 0 }, { kMaj7, 1 }, { kMaj7s5, 1 }, { kMin9, 2 }, { kMaj7b9, 0 }}, + /* interval1 is a whole step */ + {{ kMin9, 0 }, { kDom9, 0 }, { kMin7, 1 }, { kDom7, 1 }, { kDom9, 0 }, + { kHalfDim7, 1 }, { kDom7, 1 }, { kDom9, 0 }, { kMaj9, 0 }}, + /* interval1 is a minor third */ + {{ kMaj7s5, 2 }, { kDom7, 2 }, { kDim, 0 }, { kMin, 0 }, { kMaj, 2 }, + { kDim, 2 }, { kMin7, 0 }, { kMinMaj7, 0 }}, + /* interval1 is a major third */ + {{ kMaj7, 2 }, { kHalfDim7, 2 }, { kMaj, 0 }, { kAug, 0 }, { kMin, 2 }, + { kDom7, 0 }, { kMaj7, 0 }}, + /* interval1 is a perfect fourth */ + {{ kDomb9, 1 }, { kDom9, 1 }, { kMin, 1 }, { kMaj, 1 }, { kDom9, 2 }, + { kDom7s11, 1 }}, + /* interval1 is an augmented fourth */ + {{ kDom7s11, 0 }, { kDom7, 2 }, { kDim, 1 }, { kHalfDim7, 0 }, { kDomb9, 2 }}, + /* interval1 is a perfect fifth */ + {{ kMaj7, 2 }, { kMin7, 2 }, { kDom7, 0 }, { kMaj7, 0 }}, + /* interval1 is a minor sixth */ + {{ kMinMaj7, 2 }, { kDom9, 1 }, { kMaj7s5, 0 }}, + /* interval1 is a major sixth */ + {{ kMaj9, 2 }, { kMin9, 1 }}, + /* interval1 is a minor seventh */ + {{ kMaj7b9, 2 }} + }; + register t_type_root* t; + + int members[3]; + int i, j = 0; + int interval1, interval2; + + for(i = 0; i < 12; i++) + if(x->x_pc[i]) members[j++] = i; /* load members array with chord pitch classes */ + interval1 = members[1] - members[0]; /* calculate interval between first two members */ + interval2 = members[2] - members[0]; /* calculate interval between first and third */ + interval2 = interval2 - interval1 - 1; /* reduce interval2 to start at zero */ + interval1 = interval1 - 1; /* reduce interval1 to start at zero */ + t = &(triads[interval1][interval2]); /* find TypeRoot struct for this interval vector */ + x->x_chord_type = t->type; + x->x_chord_root = members[t->rootMember]; + switch(t->rootMember) { /* get state of inversion */ + case 0: + x->x_chord_inversion = 0; + break; + case 1: + x->x_chord_inversion = 2; + break; + case 2: + x->x_chord_inversion = 1; + } + chord_draw_chord_type(x, 3); /* output onto the screen */ +} + +static void chord_quartad(t_chord *x) +{ + static t_type_root quartads[9][9][9] = + { + {/* interval1 is a half step */ + {/* interval2 is a whole step */ + { kM7b9s13, 2 }, { kMinMajb9,1 }, { kMaj7b9, 1 }, { kMaj7s13, 2 }, { kDimMajb9, 1 }, + { kMaj7b9, 1 }, { kMaj7s13, 2 }, { kM7b913, 1 }, { kM7b9s13, 1 }}, + {/* interval2 is a minor third */ + { kMinMaj9, 1 }, { kMaj9, 1 }, { kHalfDimb9,0 }, { kMin7b9, 0 }, { kMaj9, 1 }, + { kDim7b9, 0 }, { kMin7b9, 0 }, { kMinMajb9, 0 }}, + {/* interval2 is a major third */ + { kMaj7s9, 1 }, { kDom7s11, 3 }, { kDomb9, 0 }, { kMinMaj7, 1 }, { kDom7s9, 3 }, + { kDomb9, 0 }, { kMaj7b9, 0 }}, + {/* interval2 is a perfect fourth */ + { kMaj11, 1 }, { kMaj7b5, 1 }, { kMaj7, 1 }, { kMaj7s5, 1 }, { kMin9, 3 }, + { kMaj7s13, 1 }}, + {/* interval2 is a tritone */ + { kDimMaj9, 3 }, { kDom11, 3 }, { kDim7b9, 0 }, { kHalfDimb9,0 }, { kDimMajb9, 0 }}, + {/* interval2 is a perfect fifth */ + { kMaj11, 3 }, { kDom7s9, 3 }, { kDomb9, 0 }, { kMaj7b9, 0 }}, + {/* interval2 is a minor sixth */ + { kMaj7s9, 3 }, { kMin9, 3 }, { kMaj7s13, 1 }}, + {/* interval2 is a major sixth */ + { kMinMaj9, 3 }, { kM7b913, 0 }}, + {/* interval2 is a minor seventh */ + { kM7b9s13, 0 }} + }, + {/* interval1 is a whole step */ + {/* interval2 is a minor third */ + { kM7b913, 2 }, { kMin7b9, 1 }, { kDomb9, 1 }, { kMin9, 0 }, { kHalfDimb9,1 }, + { kDomb9, 1 }, { kMin9, 0 }, { kMinMaj9, 0 }}, + {/* interval2 is a major third */ + { kMin9, 1 }, { kDom9, 1 }, { kDom9, 0 }, { kDom7s5, 2 }, { kDom9, 1 }, + { kDom9, 0 }, { kMaj9, 0 }}, + {/* interval2 is a perfect fourth */ + { kDom7s9, 1 }, { kDom11, 3 }, { kHalfDim7, 1 }, { kMin7, 1 }, { kDom9, 3 }, + { kHalfDimb9,3 }}, + {/* interval2 is a tritone */ + { kDom11, 1 }, { kDom7b5, 3 }, { kDom7, 1 }, { kDom7s5, 1 }, { kMin7b9, 3 }}, + {/* interval2 is a perfect fifth */ + { kMaj7b5, 3 }, { kDom11, 1 }, { kDom9, 0 }, { kMaj9, 0 }}, + {/* interval2 is a minor sixth */ + { kDom7s11, 1 }, { kDom9, 3 }, { kDim7b9, 3 }}, + {/* interval2 is a major sixth */ + { kMaj9, 3 }, { kMin7b9, 3 }}, + {/* interval2 is a minor seventh */ + { kMinMajb9, 3 }} + }, + {/* interval1 is a minor third */ + {/* interval2 is a major third */ + { kMaj7s13, 3 }, { kDim7b9, 1 }, { kDom7s9, 0 }, { kMaj7s5, 2 }, { kDim7b9, 1 }, + { kDom7s9, 0 }, { kMaj7s9, 0 }}, + {/* interval2 is a perfect fourth */ + { kDomb9, 2 }, { kDom9, 2 }, { kMin7, 2 }, { kDom7, 2 }, { kDom11, 2 }, + { kDom7s11, 2 }}, + {/* interval2 is a tritone */ + { kDim7b9, 2 }, { kDom7, 3 }, { kDim7, 0 }, { kHalfDim7, 0 }, { kDomb9, 3 }}, + {/* interval2 is a perfect fifth */ + { kMaj7, 3 }, { kHalfDim7,3 }, { kMin7, 0 }, { kMinMaj7, 0 }}, + {/* interval2 is a minor sixth */ + { kDomb9, 2 }, { kDom9, 2 }, { kDom7s9, 2 }}, + {/* interval2 is a major sixth */ + { kHalfDimb9,2 }, { kDomb9, 3 }}, + {/* interval2 is a minor seventh */ + { kMaj7b9, 3 }} + }, + {/* interval1 is a major third */ + {/* interval2 is a perfect fourth */ + { kMaj7b9, 2 }, { kMaj9, 2 }, { kMinMaj7, 2 }, { kMaj7, 2 }, { kDom11, 0 }, + { kMaj11, 0 }}, + {/* interval2 is a tritone */ + { kHalfDimb9,2 }, { kDom7s5, 3 }, { kHalfDim7, 2 }, { kDom7b5, 0 }, { kMaj7b5, 0 }}, + {/* interval2 is a perfect fifth */ + { kMaj7s5, 3 }, { kMin7, 3 }, { kDom7, 0 }, { kMaj7, 0 }}, + {/* interval2 is a minor sixth */ + { kMinMaj7, 3 }, { kDom7s5, 0 }, { kMaj7s5, 0 }}, + {/* interval2 is a major sixth */ + { kMin7b9, 2 }, { kMin9, 2 }}, + {/* interval2 is a minor seventh */ + { kMaj7s13, 0 }} + }, + {/* interval1 is a perfect fourth */ + {/* interval2 is a tritone */ + { kDimMajb9, 2 }, { kMin7b9, 1 }, { kDomb9, 1 }, { kMaj7b5, 2 }, { kDimMaj9, 0 }}, + {/* interval2 is a perfect fifth */ + { kMin9, 1 }, { kDom9, 1 }, { kDom11, 0 }, { kDom11, 2 }}, + {/* interval2 is a minor sixth */ + { kDom7s9, 1 }, { kDom9, 3 }, { kDim7b9, 3 }}, + {/* interval2 is a major sixth */ + { kMaj9, 3 }, { kHalfDimb9,3 }}, + {/* interval2 is a minor seventh */ + { kDimMajb9, 3 }} + }, + {/* interval1 is a tritone */ + {/* interval2 is a perfect fifth */ + { kMaj7s13, 3 }, { kHalfDimb9,1 }, { kDom7s11, 0 }, { kMaj11, 2 }}, + {/* interval2 is a minor sixth */ + { kDomb9, 2 }, { kDom9, 2 }, { kDom7s9, 2 }}, + {/* interval2 is a major sixth */ + { kDim7b9, 2 }, { kDomb9, 3 }}, + {/* interval2 is a minor seventh */ + { kMaj7b9, 3 }} + }, + {/* interval1 is a perfect fifth */ + {/* interval2 is a minor sixth */ + { kMaj7b9, 2 }, { kMaj9, 2 }, { kMaj7s9, 2 }}, + {/* interval2 is a major sixth */ + { kMin7b9, 2 }, { kMin9, 2 }}, + {/* interval2 is a minor seventh */ + { kMaj7s13, 0 }} + }, + {/* interval1 is a minor sixth */ + {/* interval2 is a major sixth */ + { kMinMajb9, 2 }, { kMinMaj9, 2 }}, + {/* interval2 is a minor seventh */ + { kM7b913, 3 }} + }, + {/* interval1 is a major sixth */ + {/* interval2 is a minor seventh */ + { kM7b9s13, 2 }} + } + }; + + register t_type_root* t; + + int members[4]; + int interval1, interval2, interval3; + int i, j = 0; + for (i=0; i<12; i++) + if (x->x_pc[i]) members[j++] = i; /* load members array with chord pitch classes */ + interval1 = members[1] - members[0]; /* calculate interval between first two members */ + interval2 = members[2] - members[0]; /* calculate interval between first and third */ + interval3 = members[3] - members[0]; /* calculate interval between first and third */ + interval3 = interval3 - interval2 - 1; /* reduce interval3 to start at zero */ + interval2 = interval2 - interval1 - 1; /* reduce interval2 to start at zero */ + interval1 = interval1 - 1; /* reduce interval1 to start at zero */ + + /* find TypeRoot struct for this interval set */ + t = &(quartads[interval1][interval2][interval3]); + x->x_chord_type = t->type; + x->x_chord_root = members[t->rootMember]; + switch(t->rootMember) { /* get state of inversion */ + case 0: + x->x_chord_inversion = 0; + break; + case 1: + x->x_chord_inversion = 2; + break; + case 2: + x->x_chord_inversion = 2; + break; + case 3: + x->x_chord_inversion = 1; + } + chord_draw_chord_type(x, 4); /* output results */ +} + +static void chord_fatal_error(char* s1, char* s2) +{ + post("chord: error: %s : %s", s1, s2); +} + +static void chord_quintad(t_chord *x) +{ + static int initialized = 0; + static t_type_root quintads[8][8][8][8]; + register int i, j, k, l; + register t_type_root *t; + int members[5]; + int interval1, interval2, interval3, interval4; + int *st; + int maj9[5][4] = {{1,1,2,3}, {0,1,1,2}, {3,0,1,1}, {2,3,0,1}, {1,2,3,0}}; + int dom9[5][4] = {{1,1,2,2}, {1,1,1,2}, {2,1,1,1}, {2,2,1,1}, {1,2,2,1}}; + int min9[5][4] = {{1,0,3,2}, {1,1,0,3}, {2,1,1,0}, {3,2,1,1}, {0,3,2,1}}; + int had9[5][4] = {{1,0,2,3}, {1,1,0,2}, {3,1,1,0}, {2,3,1,1}, {0,2,3,1}}; + int miM9[5][4] = {{1,0,3,3}, {0,1,0,3}, {3,0,1,0}, {3,3,0,1}, {0,3,3,0}}; + int diM9[5][4] = {{1,0,2,4}, {0,1,0,2}, {4,0,1,0}, {2,4,0,1}, {0,2,4,0}}; + int M9b5[5][4] = {{1,1,1,4}, {0,1,1,1}, {4,0,1,1}, {1,4,0,1}, {1,1,4,0}}; + int D9b5[5][4] = {{1,1,1,3}, {1,1,1,1}, {3,1,1,1}, {1,3,1,1}, {1,1,3,1}}; + int mM91[5][4] = {{1,0,0,6}, {0,1,0,0}, {6,0,1,0}, {0,6,0,1}, {0,0,6,0}}; + int M7b9[5][4] = {{0,2,2,3}, {0,0,2,2}, {3,0,0,2}, {2,3,0,0}, {2,2,3,0}}; + int M5b9[5][4] = {{0,2,3,2}, {0,0,2,3}, {2,0,0,2}, {3,2,0,0}, {2,3,2,0}}; + int D7b9[5][4] = {{0,2,2,2}, {1,0,2,2}, {2,1,0,2}, {2,2,1,0}, {2,2,2,1}}; + int m7b9[5][4] = {{0,1,3,2}, {1,0,1,3}, {2,1,0,1}, {3,2,1,0}, {1,3,2,1}}; + int mb51[5][4] = {{0,1,2,0}, {4,0,1,2}, {0,4,0,1}, {2,0,4,0}, {1,2,0,4}}; + int d7b9[5][4] = {{0,1,2,3}, {1,0,1,2}, {3,1,0,1}, {2,3,1,0}, {1,2,3,1}}; + int mMb9[5][4] = {{0,1,3,3}, {0,0,1,3}, {3,0,0,1}, {3,3,0,0}, {1,3,3,0}}; + int dMb9[5][4] = {{0,1,2,4}, {0,0,1,2}, {4,0,0,1}, {2,4,0,0}, {1,2,4,0}}; + int dib9[5][4] = {{0,1,2,2}, {2,0,1,2}, {2,2,0,1}, {2,2,2,0}, {1,2,2,2}}; + int M7s9[5][4] = {{2,0,2,3}, {0,2,0,2}, {3,0,2,0}, {2,3,0,2}, {0,2,3,0}}; + int D7s9[5][4] = {{2,0,2,2}, {1,2,0,2}, {2,1,2,0}, {2,2,1,2}, {0,2,2,1}}; + int M7s1[5][4] = {{3,1,0,3}, {0,3,1,0}, {3,0,3,1}, {0,3,0,3}, {1,0,3,0}}; + int d9b3[5][4] = {{1,1,2,0}, {3,1,1,2}, {0,3,1,1}, {2,0,3,1}, {1,2,0,3}}; + int M9s3[5][4] = {{1,4,2,0}, {0,1,4,2}, {0,0,1,4}, {2,0,0,1}, {4,2,0,0}}; + int M9st[5][4] = {{1,1,5,0}, {0,1,1,5}, {0,0,1,1}, {5,0,0,1}, {1,5,0,0}}; + int s9s1[5][4] = {{2,0,1,0}, {4,2,0,1}, {0,4,2,0}, {1,0,4,2}, {0,1,0,4}}; + int h7b1[5][4] = {{2,0,1,3}, {1,2,0,1}, {3,1,2,0}, {1,3,1,2}, {0,1,3,1}}; + int M711[5][4] = {{3,0,1,3}, {0,3,0,1}, {3,0,3,0}, {1,3,0,3}, {0,1,3,0}}; + int M115[5][4] = {{1,1,0,5}, {0,1,1,0}, {5,0,1,1}, {0,5,0,1}, {1,0,5,0}}; + int d711[5][4] = {{3,0,1,2}, {1,3,0,1}, {2,1,3,0}, {1,2,1,3}, {0,1,2,1}}; + int d712[5][4] = {{1,1,0,1}, {4,1,1,0}, {1,4,1,1}, {0,1,4,1}, {1,0,1,4}}; + int d713[5][4] = {{1,1,0,4}, {1,1,1,0}, {4,1,1,1}, {0,4,1,1}, {1,0,4,1}}; + int m711[5][4] = {{2,1,1,2}, {1,2,1,1}, {2,1,2,1}, {1,2,1,2}, {1,1,2,1}}; + int m712[5][4] = {{1,0,1,1}, {4,1,0,1}, {1,4,1,0}, {1,1,4,1}, {0,1,1,4}}; + int di11[5][4] = {{1,0,1,0}, {5,1,0,1}, {0,5,1,0}, {1,0,5,1}, {0,1,0,5}}; + int mM11[5][4] = {{2,1,1,3}, {0,2,1,1}, {3,0,2,1}, {1,3,0,2}, {1,1,3,0}}; + int dM11[5][4] = {{2,1,0,4}, {0,2,1,0}, {4,0,2,1}, {0,4,0,2}, {1,0,4,0}}; + int Meb5[5][4] = {{3,0,0,4}, {0,3,0,0}, {4,0,3,0}, {0,4,0,3}, {0,0,4,0}}; + int Mes5[5][4] = {{3,0,2,2}, {0,3,0,2}, {2,0,3,0}, {2,2,0,3}, {0,2,2,0}}; + int Meb9[5][4] = {{0,2,0,5}, {0,0,2,0}, {5,0,0,2}, {0,5,0,0}, {2,0,5,0}}; + int Mes9[5][4] = {{2,0,0,5}, {0,2,0,0}, {5,0,2,0}, {0,5,0,2}, {0,0,5,0}}; + int Deb5[5][4] = {{3,0,0,3}, {1,3,0,0}, {3,1,3,0}, {0,3,1,3}, {0,0,3,1}}; + int Mes3[5][4] = {{3,0,4,0}, {0,3,0,4}, {0,0,3,0}, {4,0,0,3}, {0,4,0,0}}; + int Deb9[5][4] = {{0,2,0,4}, {1,0,2,0}, {4,1,0,2}, {0,4,1,0}, {2,0,4,1}}; + int De91[5][4] = {{0,2,0,1}, {4,0,2,0}, {1,4,0,2}, {0,1,4,0}, {2,0,1,4}}; + int Des9[5][4] = {{2,0,0,4}, {1,2,0,0}, {4,1,2,0}, {0,4,1,2}, {0,0,4,1}}; + int Ds11[5][4] = {{3,1,0,2}, {1,3,1,0}, {2,1,3,1}, {0,2,1,3}, {1,0,2,1}}; + int m7s1[5][4] = {{2,2,0,2}, {1,2,2,0}, {2,1,2,2}, {0,2,1,2}, {2,0,2,1}}; + int D3s1[5][4] = {{5,0,1,0}, {1,5,0,1}, {0,1,5,0}, {1,0,1,5}, {0,1,0,1}}; + int Mb9s[5][4] = {{0,2,5,0}, {0,0,2,5}, {0,0,0,2}, {5,0,0,0}, {2,5,0,0}}; + int D7b3[5][4] = {{3,2,0,1}, {1,3,2,0}, {1,1,3,2}, {0,1,1,3}, {2,0,1,1}}; + + if (!initialized) { + for (i=0; i<8; i++) + for (j=0; j<8; j++) + for (k=0; k<8; k++) + for (l=0; l<8; l++) { + quintads[i][j][k][l].type = kNone; + quintads[i][j][k][l].rootMember = kXX; + } + + + // major ninths + for (i=0; i<5; i++) { + st = maj9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + t->type = kMaj9; + t->rootMember = i; + } + + // dominant ninths + for (i=0; i<5; i++) { + st = dom9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "dom9"); + t->type = kDom9; + t->rootMember = i; + } + + // minor ninths + for (i=0; i<5; i++) { + st = min9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "min9"); + t->type = kMin9; + t->rootMember = i; + } + + // half diminished ninths + for (i=0; i<5; i++) { + st = had9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "had9"); + t->type = kHalfDim9; + t->rootMember = i; + } + + // minor/major ninths + for (i=0; i<5; i++) { + st = miM9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "miM9"); + t->type = kMinMaj9; + t->rootMember = i; + } + + // diminished/major ninths + for (i=0; i<5; i++) { + st = diM9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "diM9"); + t->type = kDimMaj9; + t->rootMember = i; + } + + // major ninth flat 5 + for (i=0; i<5; i++) { + st = M9b5[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M9b5"); + t->type = kMaj9b5; + t->rootMember = i; + } + + // dominant ninth flat 5 + for (i=0; i<5; i++) { + st = D9b5[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D9b5"); + t->type = kDom9b5; + t->rootMember = i; + } + + // minor/major ninth flat 11 + for (i=0; i<5; i++) { + st = mM91[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "mM91"); + t->type = kmM9b11; + t->rootMember = i; + } + + // major seventh flat nine + for (i=0; i<5; i++) { + st = M7b9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M7b9"); + t->type = kMaj7b9; + t->rootMember = i; + } + + // major seventh sharp five flat nine + for (i=0; i<5; i++) { + st = M5b9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M5b9"); + t->type = kMaj7s5b9; + t->rootMember = i; + } + + // dominant seventh flat nine + for (i=0; i<5; i++) { + st = D7b9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D7b9"); + t->type = kDom7b9; + t->rootMember = i; + } + + // minor seventh flat nine + for (i=0; i<5; i++) { + t = &(quintads[m7b9[i][0]][m7b9[i][1]][m7b9[i][2]][m7b9[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "m7b9"); + t->type = kMin7b9; + t->rootMember = i; + } + + // minor flat nine sharp eleventh + for (i=0; i<5; i++) { + st = mb51[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "mb51"); + t->type = kMinb9s11; + t->rootMember = i; + } + + // half diminished seventh flat nine + for (i=0; i<5; i++) { + st = d7b9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d7b9"); + t->type = kHalfDimb9; + t->rootMember = i; + } + + // minor/major seventh flat nine + for (i=0; i<5; i++) { + st = mMb9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "mMb9"); + t->type = kMinMajb9; + t->rootMember = i; + } + + // diminished major seventh flat nine + for (i=0; i<5; i++) { + st = dMb9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "dMb9"); + t->type = kDimMajb9; + t->rootMember = i; + } + + // diminished seventh flat nine + for (i=0; i<5; i++) { + t = &(quintads[dib9[i][0]][dib9[i][1]][dib9[i][2]][dib9[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "dib9"); + t->type = kDim7b9; + t->rootMember = i; + } + + // major seventh sharp nine + for (i=0; i<5; i++) { + t = &(quintads[M7s9[i][0]][M7s9[i][1]][M7s9[i][2]][M7s9[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M7s9"); + t->type = kMaj7s9; + t->rootMember = i; + } + + // dominant seventh sharp nine + for (i=0; i<5; i++) { + t = &(quintads[D7s9[i][0]][D7s9[i][1]][D7s9[i][2]][D7s9[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D7s9"); + t->type = kDom7s9; + t->rootMember = i; + } + + // major seventh sharp eleventh + for (i=0; i<5; i++) { + t = &(quintads[M7s1[i][0]][M7s1[i][1]][M7s1[i][2]][M7s1[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M7s1"); + t->type = kMaj7s11; + t->rootMember = i; + } + + // dominant ninth flat thirteenth + for (i=0; i<5; i++) { + st = d9b3[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d9b3"); + t->type = kDom9b13; + t->rootMember = i; + } + + // major ninth sharp thirteenth + for (i=0; i<5; i++) { + st = M9s3[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M9s3"); + t->type = kMaj9s13; + t->rootMember = i; + } + + // major ninth sharp thirteenth + for (i=0; i<5; i++) { + t = &(quintads[M9st[i][0]][M9st[i][1]][M9st[i][2]][M9st[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M9st"); + t->type = kMaj9s13; + t->rootMember = i; + } + + // major chord sharp ninth sharp eleventh + for (i=0; i<5; i++) { + st = s9s1[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "s9s1"); + t->type = kMs9s11; + t->rootMember = i; + } + + // half diminished seven flat 11 + for (i=0; i<5; i++) { + st = h7b1[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "h7b1"); + t->type = kHDimb11; + t->rootMember = i; + } + + // major eleventh + for (i=0; i<5; i++) { + t = &(quintads[M711[i][0]][M711[i][1]][M711[i][2]][M711[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M711"); + t->type = kMaj11; + t->rootMember = i; + } + + // major eleventh + for (i=0; i<5; i++) { + t = &(quintads[M115[i][0]][M115[i][1]][M115[i][2]][M115[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M711"); + t->type = kMaj11; + t->rootMember = i; + } + + // dominant eleventh + for (i=0; i<5; i++) { + t = &(quintads[d711[i][0]][d711[i][1]][d711[i][2]][d711[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d711"); + t->type = kDom11; + t->rootMember = i; + } + + // dominant eleventh + for (i=0; i<5; i++) { + t = &(quintads[d712[i][0]][d712[i][1]][d712[i][2]][d712[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d712"); + t->type = kDom11; + t->rootMember = i; + } + + // dominant eleventh + for (i=0; i<5; i++) { + st = d713[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d713"); + t->type = kDom11; + t->rootMember = i; + } + + // minor eleventh + for (i=0; i<5; i++) { + t = &(quintads[m711[i][0]][m711[i][1]][m711[i][2]][m711[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "m711"); + t->type = kMin11; + t->rootMember = i; + } + + // minor eleventh + for (i=0; i<5; i++) { + t = &(quintads[m712[i][0]][m712[i][1]][m712[i][2]][m712[i][3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "m712"); + t->type = kMin11; + t->rootMember = i; + } + + // diminished eleventh + for (i=0; i<5; i++) { + st = di11[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "di11"); + t->type = kDim11; + t->rootMember = i; + } + + // minor/major eleventh + for (i=0; i<5; i++) { + st = mM11[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "mM11"); + t->type = kMinMaj11; + t->rootMember = i; + } + + // diminished major eleventh + for (i=0; i<5; i++) { + st = dM11[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "dM11"); + t->type = kDimMaj11; + t->rootMember = i; + } + + // major eleventh flat fifth + for (i=0; i<5; i++) { + st = Meb5[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Meb5"); + t->type = kMaj11b5; + t->rootMember = i; + } + + // major eleventh sharp fifth + for (i=0; i<5; i++) { + st = Mes5[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Mes5"); + t->type = kMaj11s5; + t->rootMember = i; + } + + // major eleventh flat ninth + for (i=0; i<5; i++) { + st = Meb9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Meb9"); + t->type = kMaj11b9; + t->rootMember = i; + } + + // major eleventh sharp ninth + for (i=0; i<5; i++) { + st = Mes9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Mes9"); + t->type = kMaj11s9; + t->rootMember = i; + } + + // major eleventh sharp thirteenth + for (i=0; i<5; i++) { + st = Mes3[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Mes3"); + t->type = kMaj11s13; + t->rootMember = i; + } + + // dominant eleventh flat fifth + for (i=0; i<5; i++) { + st = Deb5[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Deb5"); + t->type = kDom11b5; + t->rootMember = i; + } + + // dominant eleventh flat ninth + for (i=0; i<5; i++) { + st = Deb9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Deb9"); + t->type = kDom11b9; + t->rootMember = i; + } + + // dominant eleventh flat ninth + for (i=0; i<5; i++) { + st = De91[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "De91"); + t->type = kDom11b9; + t->rootMember = i; + } + + // dominant eleventh sharp ninth + for (i=0; i<5; i++) { + st = Des9[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Des9"); + t->type = kDom11s9; + t->rootMember = i; + } + + // dominant seventh sharp eleventh + for (i=0; i<5; i++) { + st = Ds11[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Ds11"); + t->type = kDom7s11; + t->rootMember = i; + } + + // minor seventh sharp eleventh + for (i=0; i<5; i++) { + st = m7s1[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "m7s1"); + t->type = kMin7s11; + t->rootMember = i; + } + + // dominant thirteenth sharp eleventh + for (i=0; i<5; i++) { + st = D3s1[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D3s1"); + t->type = kDom13s11; + t->rootMember = i; + } + + // major seventh flat ninth sharp thirteenth + for (i=0; i<5; i++) { + st = Mb9s[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "Mb9s"); + t->type = kM7b9s13; + t->rootMember = i; + } + + // dominant seventh flat thirteenth + for (i=0; i<5; i++) { + st = D7b3[i]; + t = &(quintads[st[0]][st[1]][st[2]][st[3]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D7b3"); + t->type = kDom7b13; + t->rootMember = i; + } + + initialized = 1; + return; + } + + j = 0; + for (i=0; i<12; i++) + if (x->x_pc[i]) members[j++] = i; /* load members array with chord pitch classes */ + interval1 = members[1] - members[0]; /* calculate interval between first two members */ + interval2 = members[2] - members[0]; /* calculate interval between first and third */ + interval3 = members[3] - members[0]; /* calculate interval between first and third */ + interval4 = members[4] - members[0]; /* calculate interval between first and fourth */ + interval4 = interval4 - interval3 - 1; /* reduce interval4 to start at zero */ + interval3 = interval3 - interval2 - 1; /* reduce interval3 to start at zero */ + interval2 = interval2 - interval1 - 1; /* reduce interval2 to start at zero */ + interval1 = interval1 - 1; /* reduce interval1 to start at zero */ + + // find TypeRoot struct for this interval set + t = &(quintads[interval1][interval2][interval3][interval4]); + if (t->rootMember != kXX) + { + x->x_chord_type = t->type; + x->x_chord_root = members[t->rootMember]; + switch(t->rootMember) { /* get state of inversion */ + case 0: + x->x_chord_inversion = 0; + break; + case 1: + x->x_chord_inversion = 2; + break; + case 2: + x->x_chord_inversion = 2; + break; + case 3: + x->x_chord_inversion = 2; + break; + case 4: + x->x_chord_inversion = 1; + } + chord_draw_chord_type(x, 5); /* output result */ + } else + chord_kick_out_member(x, 5, members); +} + +static void chord_sextad(t_chord *x) +{ + static int initialized = 0; + static t_type_root sextads[7][7][7][7][7]; + register int i, j, k, l, m; + register t_type_root *t; + register int* st; + int members[6]; + int interval1, interval2, interval3, interval4, interval5; + + int D9b3[6][5] = + {{1,1,2,0,1}, {1,1,1,2,0}, {1,1,1,1,2}, {0,1,1,1,1}, {2,0,1,1,1}, {1,2,0,1,1}}; + int m9s1[6][5] = + {{1,0,2,0,2}, {1,1,0,2,0}, {2,1,1,0,2}, {0,2,1,1,0}, {2,0,2,1,1}, {0,2,0,2,1}}; + int M711[6][5] = + {{1,1,0,1,3}, {0,1,1,0,1}, {3,0,1,1,0}, {1,3,0,1,1}, {0,1,3,0,1}, {1,0,1,3,0}}; + int D711[6][5] = + {{1,1,0,1,2}, {1,1,1,0,1}, {2,1,1,1,0}, {1,2,1,1,1}, {0,1,2,1,1}, {1,0,1,2,1}}; + int hd11[6][5] = + {{1,0,1,0,3}, {1,1,0,1,0}, {3,1,1,0,1}, {0,3,1,1,0}, {1,0,3,1,1}, {0,1,0,3,1}}; + int M1b5[6][5] = + {{1,1,0,0,4}, {0,1,1,0,0}, {4,0,1,1,0}, {0,4,0,1,1}, {0,0,4,0,1}, {1,0,0,4,0}}; + int M159[6][5] = + {{0,2,0,0,4}, {0,0,2,0,0}, {4,0,0,2,0}, {0,4,0,0,2}, {0,0,4,0,0}, {2,0,0,4,0}}; + int M1s3[6][5] = + {{1,1,0,4,0}, {0,1,1,0,4}, {0,0,1,1,0}, {4,0,0,1,1}, {0,4,0,0,1}, {1,0,4,0,0}}; + int hd19[6][5] = + {{0,1,1,0,3}, {1,0,1,1,0}, {3,1,0,1,1}, {0,3,1,0,1}, {1,0,3,1,0}, {1,1,0,3,1}}; + int M1b3[6][5] = + {{3,0,1,0,2}, {0,3,0,1,0}, {2,0,3,0,1}, {0,2,0,3,0}, {1,0,2,0,3}, {0,1,0,2,0}}; + int D1b5[6][5] = + {{1,1,0,0,3}, {1,1,1,0,0}, {3,1,1,1,0}, {0,3,1,1,1}, {0,0,3,1,1}, {1,0,0,3,1}}; + int D1s9[6][5] = + {{2,0,0,1,2}, {1,2,0,0,1}, {2,1,2,0,0}, {1,2,1,2,0}, {0,1,2,1,2}, {0,0,1,2,1}}; + int m791[6][5] = + {{0,1,2,0,2}, {1,0,1,2,0}, {2,1,0,1,2}, {0,2,1,0,1}, {2,0,2,1,0}, {1,2,0,2,1}}; + int d7s1[6][5] = + {{1,1,1,0,2}, {1,1,1,1,0}, {2,1,1,1,1}, {0,2,1,1,1}, {1,0,2,1,1}, {1,1,0,2,1}}; + int d3s1[6][5] = + {{3,1,0,1,0}, {1,3,1,0,1}, {0,1,3,1,0}, {1,0,1,3,1}, {0,1,0,1,3}, {1,0,1,0,1}}; + + + if (!initialized) { + for (i=0; i<7; i++) + for (j=0; j<7; j++) + for (k=0; k<7; k++) + for (l=0; l<7; l++) + for (m=0; m<7; m++) { + sextads[i][j][k][l][m].type = kNone; + sextads[i][j][k][l][m].rootMember = kXX; + } + + // dominant ninth flat thirteen + for (i=0; i<6; i++) { + st = D9b3[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D9b3"); + t->type = kDom9b13; + t->rootMember = i; + } + + // minor ninth sharp eleventh + for (i=0; i<6; i++) { + st = m9s1[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "m9s1"); + t->type = kMin9s11; + t->rootMember = i; + } + + // major eleventh + for (i=0; i<6; i++) { + st = M711[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M711"); + t->type = kMaj11; + t->rootMember = i; + } + + // dominant eleventh + for (i=0; i<6; i++) { + st = D711[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D711"); + t->type = kDom11; + t->rootMember = i; + } + + // half diminished eleventh + for (i=0; i<6; i++) { + st = hd11[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "hd11"); + t->type = kHalfDim11; + t->rootMember = i; + } + + // major eleventh flat 5 + for (i=0; i<6; i++) { + st = M1b5[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M1b5"); + t->type = kMaj11b5; + t->rootMember = i; + } + + // major eleventh flat 5 flat 9 + for (i=0; i<6; i++) { + st = M159[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M159"); + t->type = kM11b5b9; + t->rootMember = i; + } + + // major eleventh sharp 13 + for (i=0; i<6; i++) { + st = M1s3[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M1s3"); + t->type = kMaj11s13; + t->rootMember = i; + } + + // half diminished eleventh flat 9 + for (i=0; i<6; i++) { + st = hd19[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "hd19"); + t->type = kHalfDim11b9; + t->rootMember = i; + } + + // major eleventh flat 13 + for (i=0; i<6; i++) { + st = M1b3[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "M1b3"); + t->type = kMaj11b13; + t->rootMember = i; + } + + // dominant eleventh flat five + for (i=0; i<6; i++) { + st = D1b5[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D1b5"); + t->type = kDom11b5; + t->rootMember = i; + } + + // dominant eleventh sharp nine + for (i=0; i<6; i++) { + st = D1s9[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "D1s9"); + t->type = kDom11s9; + t->rootMember = i; + } + + // minor seventh flat 9 sharp 11 + for (i=0; i<6; i++) { + st = m791[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "m791"); + t->type = kMinb9s11; + t->rootMember = i; + } + + // dominant seventh sharp 11 + for (i=0; i<6; i++) { + st = d7s1[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d7s1"); + t->type = kDom7s11; + t->rootMember = i; + } + + // dominant thirteenth sharp 11 + for (i=0; i<6; i++) { + st = d3s1[i]; + t = &(sextads[st[0]][st[1]][st[2]][st[3]][st[4]]); + if (t->type != kNone) chord_fatal_error("redefining chord", "d3s1"); + t->type = kDom13s11; + t->rootMember = i; + } + + initialized = 1; + return; + } + + j = 0; + for (i=0; i<12; i++) + if (x->x_pc[i]) members[j++] = i; // load members array with chord pitch classes + interval1 = members[1] - members[0]; // calculate interval between first two members + interval2 = members[2] - members[0]; // calculate interval between first and third + interval3 = members[3] - members[0]; // calculate interval between first and third + interval4 = members[4] - members[0]; // calculate interval between first and fourth + interval5 = members[5] - members[0]; // calculate interval between first and fifth + interval5 = interval5 - interval4 - 1; // reduce interval5 to start at zero + interval4 = interval4 - interval3 - 1; // reduce interval4 to start at zero + interval3 = interval3 - interval2 - 1; // reduce interval3 to start at zero + interval2 = interval2 - interval1 - 1; // reduce interval2 to start at zero + interval1 = interval1 - 1; // reduce interval1 to start at zero + + // find TypeRoot struct for this interval set + t = &(sextads[interval1][interval2][interval3][interval4][interval5]); + if (t->rootMember != kXX) { + x->x_chord_type = t->type; + x->x_chord_root = members[t->rootMember]; + switch(t->rootMember) { /* get state of inversion */ + case 0: + x->x_chord_inversion = 0; + break; + case 1: + x->x_chord_inversion = 2; + break; + case 2: + x->x_chord_inversion = 2; + break; + case 3: + x->x_chord_inversion = 2; + break; + case 4: + x->x_chord_inversion = 2; + break; + case 5: x->x_chord_inversion = 1; + } + chord_draw_chord_type(x, 6); // output onto the screen + } else + chord_kick_out_member(x, 6, members); +} + +static int chord_accidental(t_int pc) +{ + switch (pc) { + case 0: + case 2: + case 4: + case 5: + case 7: + case 9: + case 11: return 0; + case 1: + case 3: + case 6: + case 8: + case 10: + default: return 1; + } +} + +static int chord_name_third(t_chord *x, char* chord, int c, int rootName) +{ + int third = (x->x_chord_root+4)%12; // look for major third + if (x->x_pc[third]) { // if one is there + x->x_pc[third] = 0; // erase from pcs array + chord[c++] = name_class[(rootName+2)%7]; + if (chord_accidental(third)) // if it has an chord_accidental + // make it a flat if the root also has an chord_accidental + if (chord_accidental(x->x_chord_root)) chord[c++] = 'b'; + // otherwise make it a sharp + else chord[c++] = '#'; + chord[c++] = ' '; + return c; // return if major third found + } + + third = (x->x_chord_root+3)%12; // no major, look for minor third + if (x->x_pc[third]) { // if one is there + x->x_pc[third] = 0; // erase from pcs array + chord[c++] = name_class[(rootName+2)%7]; + if (chord_accidental(third)) // if it has an chord_accidental + chord[c++] = 'b'; else // make it a flat + if (chord_accidental(x->x_chord_root)) { // if the root has an chord_accidental + chord[c++] = 'b'; // make the third a flat + if (chord[0] == 'G') // if the root is Gb + chord[c++] = 'b'; // this must be Bbb + } + chord[c++] = ' '; + return c; + } + + return c; // if we get here there was no third +} + +static int chord_name_fifth(t_chord *x, char* chord, int c, int rootName) +{ + int fifth = (x->x_chord_root+7)%12; + if (x->x_pc[fifth]) { + x->x_pc[fifth] = 0; + chord[c++] = name_class[(rootName+4)%7]; + if (chord_accidental(fifth)) { + if (chord_accidental(x->x_chord_root)) chord[c++] = 'b'; + else chord[c++] = '#'; + } + chord[c++] = ' '; + return c; + } + + fifth = (x->x_chord_root+6)%12; + if (x->x_pc[fifth]) { + x->x_pc[fifth] = 0; + chord[c++] = name_class[(rootName+4)%7]; + if (chord[0] != 'B') chord[c++] = 'b'; + if (chord_accidental(x->x_chord_root)) chord[c++] = 'b'; + chord[c++] = ' '; + return c; + } + + fifth = (x->x_chord_root+8)%12; + if (x->x_pc[fifth]) { + x->x_pc[fifth] = 0; + chord[c++] = name_class[(rootName+4)%7]; + if (chord_accidental(fifth)) chord[c++] = '#'; else + if (!chord_accidental(x->x_chord_root)) { + chord[c++] = '#'; + if (chord[0] == 'B') + chord[c++] = '#'; + } + chord[c++] = ' '; + return c; + } + + return c; +} + +static int chord_name_seventh(t_chord *x, char* chord, int c, int rootName) +{ + int seventh = (x->x_chord_root+11)%12; + if (x->x_pc[seventh]) { + x->x_pc[seventh] = 0; + chord[c++] = name_class[(rootName+6)%7]; + if (chord_accidental(seventh)) chord[c++] = '#'; + chord[c++] = ' '; + return c; + } + seventh = (x->x_chord_root+10)%12; + if (x->x_pc[seventh]) { + x->x_pc[seventh] = 0; + chord[c++] = name_class[(rootName+6)%7]; + if (chord_accidental(seventh) || chord_accidental(x->x_chord_root)) + chord[c++] = 'b'; + chord[c++] = ' '; + return c; + } + seventh = (x->x_chord_root+9)%12; + if (x->x_pc[seventh]) { + x->x_pc[seventh] = 0; + chord[c++] = name_class[(rootName+6)%7]; + chord[c++] = 'b'; + if (chord_accidental(x->x_chord_root)) chord[c++] = 'b'; else + if (chord_accidental((seventh+1)%12)) chord[c++] = 'b'; + chord[c++] = ' '; + return c; + } + return c; +} + +static int chord_name_ninth(t_chord *x, char* chord, int c, int rootName) +{ + int ninth = (x->x_chord_root+2)%12; + if (x->x_pc[ninth]) { + x->x_pc[ninth] = 0; + chord[c++] = name_class[(rootName+1)%7]; + if (chord_accidental(ninth)) { + if (chord_accidental(x->x_chord_root)) chord[c++] = 'b'; + else chord[c++] = '#'; + } + chord[c++] = ' '; + return c; + } + + ninth = (x->x_chord_root+1)%12; + if (x->x_pc[ninth]) { + x->x_pc[ninth] = 0; + chord[c++] = name_class[(rootName+1)%7]; + if (chord_accidental(ninth)) chord[c++] = 'b'; + else { + if (chord_accidental(x->x_chord_root)) { + chord[c++] = 'b'; + if ((x->x_chord_root == 1) || (x->x_chord_root == 6) || (x->x_chord_root == 8)) + chord[c++] = 'b'; + } + } + chord[c++] = ' '; + return c; + } + + ninth = (x->x_chord_root+3)%12; + if (x->x_pc[ninth]) { + x->x_pc[ninth] = 0; + chord[c++] = name_class[(rootName+1)%7]; + if (chord_accidental(ninth)) chord[c++] = '#'; else + if (!chord_accidental(x->x_chord_root)) { + chord[c++] = '#'; + if (chord_accidental((x->x_chord_root+2)%12)) + chord[c++] = '#'; + } + chord[c++] = ' '; + return c; + } + + return c; +} + +static int chord_name_eleventh(t_chord *x, char* chord, int c, int rootName) +{ + int eleventh = (x->x_chord_root+5)%12; + if (x->x_pc[eleventh]) { + x->x_pc[eleventh] = 0; + chord[c++] = name_class[(rootName+3)%7]; + if (chord_accidental(eleventh)) chord[c++] = 'b'; else + if (chord_accidental(x->x_chord_root)) chord[c++] = 'b'; + chord[c++] = ' '; + return c; + } + + eleventh = (x->x_chord_root+6)%12; + if (x->x_pc[eleventh]) { + x->x_pc[eleventh] = 0; + chord[c++] = name_class[(rootName+3)%7]; + if (chord_accidental(eleventh)) chord[c++] = '#'; else + if ((!chord_accidental(x->x_chord_root)) && (x->x_chord_root == 11)) + chord[c++] = '#'; + chord[c++] = ' '; + return c; + } + + return c; +} + +static int chord_name_thirteenth(t_chord *x, char* chord, int c, int rootName) +{ + int thirteenth = (x->x_chord_root+9)%12; + if (x->x_pc[thirteenth]) { + x->x_pc[thirteenth] = 0; + chord[c++] = name_class[(rootName+5)%7]; + if (chord_accidental(thirteenth)) + if (chord_accidental(x->x_chord_root)) + chord[c++] = 'b'; else + chord[c++] = '#'; + chord[c++] = ' '; + return c; + } + + thirteenth = (x->x_chord_root+10)%12; + if (x->x_pc[thirteenth]) { + x->x_pc[thirteenth] = 0; + chord[c++] = name_class[(rootName+5)%7]; + if (chord_accidental(thirteenth)) chord[c++] = '#'; else + if (!chord_accidental(x->x_chord_root)) { + chord[c++] = '#'; + if (chord_accidental((x->x_chord_root+9)%12)) + chord[c++] = '#'; + } + chord[c++] = ' '; + return c; + } + + thirteenth = (x->x_chord_root+8)%12; + if (x->x_pc[thirteenth]) { + x->x_pc[thirteenth] = 0; + chord[c++] = name_class[(rootName+5)%7]; + if (chord_accidental(thirteenth)) chord[c++] = 'b'; else + if (chord_accidental(x->x_chord_root)) { + chord[c++] = 'b'; + if (chord_accidental(x->x_chord_root+9)%12) + chord[c++] = 'b'; + } + chord[c++] = ' '; + return c; + } + + return c; +} + + + +static void chord_spell_chord(t_chord *x, char *chord, t_int num_pcs) +{ + int rootName = 0; // keep index of root name class + int c = 0; // pointer to current character + int named = 0; // how many members have been named + int mark; + int i; + + // use chordRoot to set rootName index and store characters for name + switch (x->x_chord_root) + { + case 0: chord[c++] = name_class[rootName=0]; break; + case 1: chord[c++] = name_class[rootName=1]; + chord[c++] = 'b'; break; + case 2: chord[c++] = name_class[rootName=1]; break; + case 3: chord[c++] = name_class[rootName=2]; + chord[c++] = 'b'; break; + case 4: chord[c++] = name_class[rootName=2]; break; + case 5: chord[c++] = name_class[rootName=3]; break; + case 6: chord[c++] = name_class[rootName=4]; + chord[c++] = 'b'; break; + case 7: chord[c++] = name_class[rootName=4]; break; + case 8: chord[c++] = name_class[rootName=5]; + chord[c++] = 'b'; break; + case 9: chord[c++] = name_class[rootName=5]; break; + case 10: chord[c++] = name_class[rootName=6]; + chord[c++] = 'b'; break; + case 11: chord[c++] = name_class[rootName=6]; break; + default: break; + } + x->x_pc[x->x_chord_root] = 0; /* set this member to zero */ + + chord[c++] = ' '; // insert space + if (++named == num_pcs) { // if everything is named + chord[c] = '\0'; // terminate the string + return; // and return + } + + mark = c; // use mark to see if new names are added + for (i=0; i<6; i++) { + // advance search by thirds + switch (i) { + case 0: mark = chord_name_third (x, chord, c, rootName); break; + case 1: mark = chord_name_fifth (x, chord, c, rootName); break; + case 2: mark = chord_name_seventh (x, chord, c, rootName); break; + case 3: mark = chord_name_ninth (x, chord, c, rootName); break; + case 4: mark = chord_name_eleventh (x, chord, c, rootName); break; + case 5: mark = chord_name_thirteenth(x, chord, c, rootName); break; + } + if (mark != c) { // if new name is added + ++named; // increment count of named members + c = mark; // update character pointer + } + if (named == num_pcs) { // if everything is named + chord[c] = '\0'; // terminate the string + return; // and return + } + } + + chord[c] = '\0'; +} + + +static void chord_draw_chord_type(t_chord *x, t_int num_pcs) +{ + char chord[255]; /* output string */ + int i, j; + + /* get members of chord */ + j = 0; + for(i = 0; i < 12; i++) + { + if(x->x_pc[i]) + { + SETFLOAT(x->x_chordlist+j, x->x_abs_pc[i]); + j++; + } + } + + if (x->x_chord_type != kNone) + { + chord_spell_chord(x, chord, num_pcs); /* spell chord members */ + } + else + { + post("going..."); + chord[0] = '\0'; + for(i = 0; i < 12; i++) + if (x->x_pc[i]) + strcat(chord, pitch_class[i]); /* output single notes */ + post("did it"); + } + + strcat(chord, ": "); + strcat(chord, pitch_class[x->x_chord_root]); + + /* append name of chord type */ + switch (x->x_chord_type) { + case kUnison: strcat(chord, "unison"); break; + case kMaj: strcat(chord, "major"); break; + case kMin: strcat(chord, "minor"); break; + case kDim: strcat(chord, "diminished"); break; + case kAug: strcat(chord, "augmented"); break; + + case kMaj7: strcat(chord, "major 7th"); break; + case kDom7: strcat(chord, "dominant 7th"); break; + case kMin7: strcat(chord, "minor 7th"); break; + case kHalfDim7: strcat(chord, "half diminished 7th"); break; + case kDim7: strcat(chord, "diminished 7th"); break; + case kMinMaj7: strcat(chord, "minor/major 7th"); break; + + case kMaj7s5: strcat(chord, "major 7th #5"); break; + case kMaj7b5: strcat(chord, "major 7th b5"); break; + case kDom7s5: strcat(chord, "dominant 7th #5"); break; + case kDom7b5: strcat(chord, "dominant 7th b5"); break; + case kDomb9: strcat(chord, "dominant b9"); break; + + case kMaj9: strcat(chord, "major 9th"); break; + case kDom9: strcat(chord, "dominant 9th"); break; + case kMin9: strcat(chord, "minor 9th"); break; + case kHalfDim9: strcat(chord, "half diminished 9th"); break; + case kMinMaj9: strcat(chord, "minor major 9th"); break; + case kDimMaj9: strcat(chord, "diminished major 9th");break; + case kMaj9b5: strcat(chord, "major 9th b5"); break; + case kDom9b5: strcat(chord, "dominant 9th b5"); break; + case kDom9b13: strcat(chord, "dominant 9th b13"); break; + case kMin9s11: strcat(chord, "minor 9th #11"); break; + case kmM9b11: strcat(chord, "minor/maj 9th b11"); break; + + case kMaj7b9: strcat(chord, "major 7th b9"); break; + case kMaj7s5b9: strcat(chord, "major 7th #5 b9"); break; + case kDom7b9: strcat(chord, "dominant 7th b9"); break; + case kMin7b9: strcat(chord, "minor 7th b9"); break; + case kMinb9s11: strcat(chord, "minor b9 #11"); break; + case kHalfDimb9:strcat(chord, "half diminished b9"); break; + case kDim7b9: strcat(chord, "diminished b9"); break; + case kMinMajb9: strcat(chord, "minor/major b9"); break; + case kDimMajb9: strcat(chord, "diminished M7 b9"); break; + + case kMaj7s9: strcat(chord, "major 7th #9"); break; + case kDom7s9: strcat(chord, "dominant #9"); break; + case kMaj7s11: strcat(chord, "major 7th #11"); break; + case kMaj9s13: strcat(chord, "major 9th #13"); break; + case kMs9s11: strcat(chord, "major #9 #11"); break; + case kHDimb11: strcat(chord, "half diminished b11"); break; + + case kMaj11: strcat(chord, "major 11th"); break; + case kDom11: strcat(chord, "dominant 11th"); break; + case kMin11: strcat(chord, "minor 11th"); break; + case kHalfDim11:strcat(chord, "half diminished 11th");break; + case kDim11: strcat(chord, "diminished 11th"); break; + case kMinMaj11: strcat(chord, "minor/major 11th"); break; + case kDimMaj11: strcat(chord, "diminished maj 11th"); break; + + case kMaj11b5: strcat(chord, "major 11th b5"); break; + case kMaj11s5: strcat(chord, "major 11th #5"); break; + case kMaj11b9: strcat(chord, "major 11th b9"); break; + case kMaj11s9: strcat(chord, "major 11th #9"); break; + case kMaj11b13: strcat(chord, "major 11th b13"); break; + case kMaj11s13: strcat(chord, "major 11th #13"); break; + case kM11b5b9: strcat(chord, "major 11th b5 b9"); break; + case kDom11b5: strcat(chord, "dominant 11th b5"); break; + case kDom11b9: strcat(chord, "dominant 11th b9"); break; + case kDom11s9: strcat(chord, "dominant 11th #9"); break; + case kHalfDim11b9:strcat(chord, "half dim 11th b9"); break; + case kDom7s11: strcat(chord, "dominant #11"); break; + case kMin7s11: strcat(chord, "minor 7th #11"); break; + + case kDom13s11: strcat(chord, "dominant 13th #11"); break; + case kM7b913: strcat(chord, "major 7 b9 13"); break; + case kMaj7s13: strcat(chord, "major 7th #13"); break; + case kM7b9s13: strcat(chord, "major 7 b9 #13"); break; + case kDom7b13: strcat(chord, "dominant 7th b13"); break; + case kChrom: strcat(chord, "chromatic"); break; + case kNone: + default: strcat(chord, "unknown"); break; + } + + x->x_chord_bass = x->x_abs_pc[x->x_chord_root]; /* get MIDI note number of bass */ + + /* output results */ + outlet_list(x->x_outchordnotes, NULL, j, x->x_chordlist); + outlet_float(x->x_outchordinversion, x->x_chord_inversion); + outlet_symbol(x->x_outchordname, gensym(chord)); + outlet_float(x->x_outchordclass, x->x_chord_root); + outlet_float(x->x_outchordval, x->x_chord_bass); +} + +static void chord_kick_out_member(t_chord *x, t_int number, t_int *members) +{ + int *distances; + int minDistance = 1000; + int badMember = 0; + int i, j, interval; + + distances = getbytes(number*sizeof(int)); + + for (i=0; i 6) interval = 12 - interval; + // add absolute interval size to total + distances[i] += interval; + } + + // if this is the smallest total distance + if (distances[i] < minDistance) { + // remember it + minDistance = distances[i]; + badMember = i; + } + } + freebytes(distances, number * sizeof(int)); + x->x_pc[members[badMember]] = 0; // cancel out most dissonant member + chord_chord_finder(x, number-1); // call chord finder again without it + x->x_pc[members[badMember]] = 1; // replace most dissonant member +} + +static void chord_chord_finder(t_chord *x, t_int num_pcs) +{ + int i; + x->x_chord_type = kNone; + x->x_chord_root = kXX; /* none */ + switch (num_pcs) { + case 1: chord_unison(x); break; + case 2: chord_dyad(x); break; + case 3: chord_triad(x); break; + case 4: chord_quartad(x); break; + case 5: chord_quintad(x); break; + case 6: chord_sextad(x); break; + default: x->x_chord_type = kChrom; + for(i = 0; i < 12; i++) // 12 was num_pcs !? + { + if(x->x_pc[i]) + { + x->x_chord_root = i; + break; + } + } + } +} + +static void chord_float(t_chord *x, t_floatarg f) +{ + t_int velo = x->x_velo; + t_int allloc = 0; + t_int num_pc = 0; /* number of pitch classes present */ + int i, j, k, l; + + x->x_pitch = (t_int)f; + + if(x->x_pitch <= x->x_split) + { + /* first we need to put the note into the allocation table */ + if(velo == 0) /* got note-off: remove from allocation table */ + { + if(x->x_poly > 0)x->x_poly--; /* polyphony has decreased by one */ + for(i = 0; i < MAX_POLY; i++) /* search for voice allocation number */ + { + /* search for corresponding alloc number */ + if(x->x_alloctable[i] == x->x_pitch) + { + x->x_alloctable[i] = -1; /* free the alloc number */ + break; + } + /* couldn't find it ? */ + if(i == MAX_POLY - 1) + { + post("chord: no corresponding note-on found (ignored)"); + return; + } + } + return; /* no need to look for chord */ + } + else /* we got a note-on message */ + { + if(x->x_poly == MAX_POLY) + { + post("chord: too many note-on messages (ignored)"); + return; + } + + x->x_poly++; /* number of currently playing notes has increased */ + /* assign a voice allocation number */ + for(i = 0; i < MAX_POLY; i++) + { + /* search for free alloc number */ + if(x->x_alloctable[i] == -1) + { + x->x_alloctable[i] = x->x_pitch; /* ... and store pitch */ + break; + } + } + /* copy all notes into the pitch class array */ + for(i = 0; i < 12; i++) + { + x->x_pc[i] = 0; /* empty pitch class */ + x->x_abs_pc[i] = -1; /* empty absolute values */ + } + for(i = 0; i < MAX_POLY; i++) + { + /* check for presence of pitch class */ + if(x->x_alloctable[i] != -1) + { + if(!x->x_pc[x->x_alloctable[i]%12]) /* a new pitch class */ + { + x->x_abs_pc[x->x_alloctable[i]%12] = x->x_alloctable[i]; + } + else if(x->x_abs_pc[x->x_alloctable[i]%12] > x->x_alloctable[i]) /* remember lowest pitch */ + { + x->x_abs_pc[x->x_alloctable[i]%12] = x->x_alloctable[i]; + } + + x->x_pc[x->x_alloctable[i]%12] = 1; /* indicate presence of pc */ + } + } + /* count number of pitch classes */ + for(i = 0; i < 12; i++) + { + num_pc += x->x_pc[i]; + } + // post("%d pitch classes", num_pc); + } + } + + chord_chord_finder(x, num_pc); +} + +static void chord_ft1(t_chord *x, t_floatarg f) +{ + x->x_velo = (t_int)f; +} + +static t_class *chord_class; + +static void *chord_new(t_floatarg f) +{ + int i; + t_chord *x = (t_chord *)pd_new(chord_class); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_outchordval = outlet_new(&x->x_ob, gensym("float")); + x->x_outchordclass = outlet_new(&x->x_ob, gensym("float")); + x->x_outchordname = outlet_new(&x->x_ob, gensym("symbol")); + x->x_outchordinversion = outlet_new(&x->x_ob, gensym("float")); + x->x_outchordnotes = outlet_new(&x->x_ob, gensym("float")); + + x->x_split = (t_int)f; + if(x->x_split == 0)x->x_split = 128; + for(i = 0; i < MAX_POLY; i++)x->x_alloctable[i] = -1; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void chord_setup(void) +{ + chord_class = class_new(gensym("chord"), (t_newmethod)chord_new, + 0, sizeof(t_chord), 0, A_DEFFLOAT, 0); + class_addfloat(chord_class, chord_float); + class_addmethod(chord_class, (t_method)chord_ft1, gensym("ft1"), A_FLOAT, 0); + class_sethelpsymbol(chord_class, gensym("maxlib/help-chord.pd")); +} + diff --git a/src/delta.c b/src/delta.c new file mode 100644 index 0000000..e6c6a74 --- /dev/null +++ b/src/delta.c @@ -0,0 +1,128 @@ +/* ------------------------- delta ------------------------------------------ */ +/* */ +/* Claculate 1st or 2nd order difference. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Inspired by code written by Trond Lossius. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAXSIZE 32 + +static char *version = "delta v0.1, written by Olaf Matthes "; + +typedef struct delta +{ + t_object x_ob; + t_outlet *x_out; /* result */ + t_int x_order; /* 1st or second order */ + t_int x_clearflag; + t_float x_delta; /* the result */ + + t_float x_prev; /* previous value */ + t_float x_prev2; /* value previous to previous value */ +} t_delta; + +static void delta_clear(t_delta *x) +{ + if(x->x_order == 2) + x->x_clearflag = 2; + else + x->x_clearflag = 1; + x->x_delta = 0; +} + +static void delta_bang(t_delta *x) +{ + outlet_float(x->x_out, x->x_delta); +} + +static void delta_float(t_delta *x, t_floatarg f) +{ + if(x->x_order != 2) /* first order */ + { + if(x->x_clearflag) + { + x->x_prev = f; + x->x_delta = 0; + x->x_clearflag = 0; + } + else + { + x->x_delta = f - x->x_prev; + x->x_prev = f; + } + } + else + { + switch(x->x_clearflag) + { + case 0: + x->x_delta = f - 2*x->x_prev + x->x_prev2; + x->x_prev2 = x->x_prev; + x->x_prev = f; + break; + case 1: + x->x_prev = f; + x->x_clearflag--; + break; + case 2: + x->x_prev2 = f; + x->x_clearflag--; + break; + } + } + delta_bang(x); +} + +static t_class *delta_class; + +static void *delta_new(t_floatarg f) +{ + int i; + + t_delta *x = (t_delta *)pd_new(delta_class); + x->x_out = outlet_new(&x->x_ob, gensym("float")); + + x->x_order = (int)f; + if(x->x_order == 2) + x->x_clearflag = 2; + else + x->x_clearflag = 1; + x->x_delta = 0; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void delta_setup(void) +{ + delta_class = class_new(gensym("delta"), (t_newmethod)delta_new, + 0, sizeof(t_delta), 0, A_DEFFLOAT, 0); + class_addfloat(delta_class, delta_float); + class_addbang(delta_class, (t_method)delta_bang); + class_addmethod(delta_class, (t_method)delta_clear, gensym("clear"), 0); + class_sethelpsymbol(delta_class, gensym("maxlib/help-delta.pd")); +} + diff --git a/src/dist.c b/src/dist.c new file mode 100644 index 0000000..a150259 --- /dev/null +++ b/src/dist.c @@ -0,0 +1,269 @@ +/* -------------------------- dist ------------------------------------------ */ +/* */ +/* Distributes incoming data to a changeable list of receive objects. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ +/* + connect : store receive objects in list of receivers + disconnect : remove objects from list of receivers + clear: clear list of receivers + send : send anything to all receives named in the list of receivers */ + +#include "m_pd.h" + +#include +#include + +#define MAX_REC 64 /* maximum number of receive objects */ +#define MAX_ARG 32 /* maximum number of arguments to pass on */ + +static char *version = "dist v0.1, written by Olaf Matthes "; + +static t_class *dist_class; + +typedef struct _dist +{ + t_object x_obj; + t_symbol *x_sym[MAX_REC]; /* names of receiving objects */ + t_int x_rec; /* current number of receiving objects */ + t_int x_verbose; /* set to 0 to turn off detailed output in Pd window */ +} t_dist; + +static void dist_bang(t_dist *x) +{ + int i; + + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) pd_bang(x->x_sym[i]->s_thing); + } +} + +static void dist_float(t_dist *x, t_float f) +{ + int i; + + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) pd_float(x->x_sym[i]->s_thing, f); + } +} + +static void dist_symbol(t_dist *x, t_symbol *s) +{ + int i; + + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) pd_symbol(x->x_sym[i]->s_thing, s); + } +} + +static void dist_pointer(t_dist *x, t_gpointer *gp) +{ + int i; + + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) pd_pointer(x->x_sym[i]->s_thing, gp); + } +} + +static void dist_list(t_dist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) pd_list(x->x_sym[i]->s_thing, s, argc, argv); + } +} + +static void dist_anything(t_dist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) typedmess(x->x_sym[i]->s_thing, s, argc, argv); + } +} + + /* send 'anything' to receiver */ +static void dist_send(t_dist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + t_atom av[MAX_ARG]; /* the 'new' t_atom without first element */ + t_int ac = argc - 1; /* the 'new' number of arguments */ + + if(ac > MAX_ARG) + { + post("dist: too many arguments!"); + return; + } + + for(i = 1; i < argc; i++) + { + av[i - 1] = argv[i]; /* just copy, don't care about types */ + } + /* send only argument-part to receivers */ + for(i = 0; i <= x->x_rec; i++) + { + if (x->x_sym[i]->s_thing) pd_forwardmess(x->x_sym[i]->s_thing, argc, argv); + } +} + +static void dist_connect(t_dist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i, j; + int exist; + t_symbol *name; + /* just append every new receive-name to end of list */ + for(i = 0; i < argc; i++) + { + exist = 0; + if(x->x_rec == MAX_REC - 1) + { + post("dist: too many connections in use!"); + return; + } + name = atom_getsymbolarg(i, argc, argv); + for(j = 0; j <= x->x_rec; j++) + { + /* check if the name already exists */ + if(x->x_sym[j] == name) + { + post("dist: \"%s\" already exists in list of receivers", name->s_name); + exist = 1; /* indicate that it _does_ exist */ + } + } + /* add it in case it's a new one */ + if(!exist) + { + x->x_rec++; + x->x_sym[x->x_rec] = name; + if(x->x_verbose)post("dist: \"%s\" added to list of receivers", x->x_sym[x->x_rec]->s_name); + } + } +} + +static void dist_disconnect(t_dist *x, t_symbol *s, int argc, t_atom *argv) +{ + /* need to rearrange list in order to get rid of empty entries */ + int i, j, k; + int done; + t_symbol *name; + + for(i = 0; i < argc; i++) + { + name = atom_getsymbolarg(i, argc, argv); /* the one we're going to remove */ + done = 0; /* not yet removed */ + for(j = 0; j <= x->x_rec; j++) /* search for it... */ + { + if(x->x_sym[j] == name) + { + x->x_rec--; + if(x->x_verbose)post("dist: \"%s\" removed from list of receivers", x->x_sym[j]->s_name); + x->x_sym[j] = NULL; /* delete entry */ + /* rearrange list now: move entries to close the gap */ + for(k = j; k <= x->x_rec; k++) + { + x->x_sym[k] = x->x_sym[k + 1]; + } + done = 1; /* removed successfully */ + } + } + if(!done)post("dist: \"%s\" not in list of receivers, ignored", name->s_name); + } +} + +static void dist_clear(t_dist *x) +{ + int i; + + for(i = 0; i < MAX_REC; i++) + { + x->x_sym[i] = NULL; + } + x->x_rec = -1; +} + +static void dist_print(t_dist *x) +{ + int i; + + if(x->x_rec == 0) + { + post("dist: there is one object in receiver list:"); + } + else if(x->x_rec > 0) + { + post("dist: there are %d objects in receiver list:", x->x_rec + 1); + } + else + { + post("dist: there are no objects in receiver list"); + return; + } + + for(i = 0; i <= x->x_rec; i++) + { + post(" \"%s\"", x->x_sym[i]->s_name); + } +} + +static void *dist_new(t_symbol *s, int argc, t_atom *argv) +{ + int i; + + t_dist *x = (t_dist *)pd_new(dist_class); + + x->x_rec = -1; + x->x_verbose = 1; /* display info on connect/disconnect */ + for(i = 0; i < argc; i++) + { + x->x_sym[i] = atom_getsymbolarg(i, argc, argv); + x->x_rec++; + } +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +void dist_setup(void) +{ + dist_class = class_new(gensym("dist"), (t_newmethod)dist_new, 0, + sizeof(t_dist), 0, A_GIMME, 0); + class_addcreator((t_newmethod)dist_new, gensym("d"), A_GIMME, 0); + class_addbang(dist_class, dist_bang); + class_addfloat(dist_class, dist_float); + class_addsymbol(dist_class, dist_symbol); + class_addpointer(dist_class, dist_pointer); + class_addlist(dist_class, dist_list); + class_addmethod(dist_class, (t_method)dist_connect, gensym("connect"), A_GIMME, 0); + class_addmethod(dist_class, (t_method)dist_disconnect, gensym("disconnect"), A_GIMME, 0); + class_addmethod(dist_class, (t_method)dist_clear, gensym("clear"), 0); + class_addmethod(dist_class, (t_method)dist_print, gensym("print"), 0); + class_addmethod(dist_class, (t_method)dist_send, gensym("send"), A_GIMME, 0); + class_addanything(dist_class, dist_anything); + class_sethelpsymbol(dist_class, gensym("maxlib/help-dist.pd")); +} diff --git a/src/divide.c b/src/divide.c new file mode 100644 index 0000000..8b1bc2b --- /dev/null +++ b/src/divide.c @@ -0,0 +1,100 @@ +/* ------------------------- divide ------------------------------------------ */ +/* */ +/* Like '/', but calculates output whenever _any_ of the inlets changes. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAXSIZE 32 + +static char *version = "divide v0.2, written by Olaf Matthes "; + +typedef struct divide +{ + t_object x_ob; + t_inlet *x_inleft; /* leftmost inlet */ + t_inlet *x_inright; /* right inlet */ + t_outlet *x_outlet; /* result */ + t_int x_numvalues; /* number of values / inlets */ + + t_float x_dividevalue[MAXSIZE]; + +} t_divide; + +static void divide_bang(t_divide *x) +{ + int i; + t_float result = x->x_dividevalue[0]; + for(i = 1; i < x->x_numvalues; i++) + result /= x->x_dividevalue[i]; + outlet_float(x->x_outlet, result); +} + +static void divide_float(t_divide *x, t_floatarg f) +{ + x->x_dividevalue[0] = f; + divide_bang(x); /* calculate result */ +} + +static void divide_ft1(t_divide *x, t_floatarg f) +{ + x->x_dividevalue[1] = f; + divide_bang(x); /* calculate result */ +} + +static t_class *divide_class; + +static void *divide_new(t_symbol *s, t_int argc, t_atom* argv) +{ + int i; + + t_divide *x = (t_divide *)pd_new(divide_class); + x->x_inright = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + for(i = 2; i < argc; i++) /* create additional inlets, if any */ + { + floatinlet_new(&x->x_ob, &x->x_dividevalue[i]); + } + x->x_outlet = outlet_new(&x->x_ob, gensym("float")); + + for(i = 0; i < argc; i++) + { + x->x_dividevalue[i] = atom_getfloatarg(i, argc, argv); + } + x->x_numvalues = i; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void divide_setup(void) +{ + divide_class = class_new(gensym("divide"), (t_newmethod)divide_new, + 0, sizeof(t_divide), 0, A_GIMME, 0); + class_addfloat(divide_class, divide_float); + class_addmethod(divide_class, (t_method)divide_ft1, gensym("ft1"), A_FLOAT, 0); + class_addbang(divide_class, (t_method)divide_bang); + class_sethelpsymbol(divide_class, gensym("maxlib/help-divide.pd")); +} + diff --git a/src/divmod.c b/src/divmod.c new file mode 100644 index 0000000..5c0406b --- /dev/null +++ b/src/divmod.c @@ -0,0 +1,90 @@ +/* ------------------------- divmod ----------------------------------------- */ +/* */ +/* Calculates / and % together. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +static char *version = "divmod v0.1, written by Olaf Matthes "; + +typedef struct divmod +{ + t_object x_ob; + t_inlet *x_inleft; /* leftmost inlet */ + t_inlet *x_inright; /* right inlet */ + t_outlet *x_outlet1; /* result of division */ + t_outlet *x_outlet2; /* result of modulo */ + + t_int x_leftvalue; + t_int x_rightvalue; + +} t_divmod; + +static void divmod_float(t_divmod *x, t_floatarg f) +{ + x->x_leftvalue = (t_int)f; + outlet_float(x->x_outlet1, x->x_leftvalue / x->x_rightvalue); + outlet_float(x->x_outlet2, x->x_leftvalue % x->x_rightvalue); +} + +static void divmod_ft1(t_divmod *x, t_floatarg f) +{ + x->x_rightvalue = (t_int)f; + outlet_float(x->x_outlet1, x->x_leftvalue / x->x_rightvalue); + outlet_float(x->x_outlet2, x->x_leftvalue % x->x_rightvalue); +} + +static void divmod_bang(t_divmod *x) +{ + outlet_float(x->x_outlet1, x->x_leftvalue / x->x_rightvalue); + outlet_float(x->x_outlet2, x->x_leftvalue % x->x_rightvalue); +} + +static t_class *divmod_class; + +static void *divmod_new(t_floatarg fl, t_floatarg fr) +{ + t_divmod *x = (t_divmod *)pd_new(divmod_class); + x->x_inright = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_outlet1 = outlet_new(&x->x_ob, gensym("float")); + x->x_outlet2 = outlet_new(&x->x_ob, gensym("float")); + + x->x_rightvalue = fr; + x->x_leftvalue = fl; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void divmod_setup(void) +{ + divmod_class = class_new(gensym("divmod"), (t_newmethod)divmod_new, + 0, sizeof(t_divmod), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(divmod_class, divmod_float); + class_addmethod(divmod_class, (t_method)divmod_ft1, gensym("ft1"), A_FLOAT, 0); + class_addbang(divmod_class, (t_method)divmod_bang); + class_sethelpsymbol(divmod_class, gensym("maxlib/help-divmod.pd")); +} + diff --git a/src/edge.c b/src/edge.c new file mode 100644 index 0000000..db89f35 --- /dev/null +++ b/src/edge.c @@ -0,0 +1,77 @@ +/* --------------------------- edge ----------------------------------------- */ +/* */ +/* Detect rising or falling edge of float input. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +static char *version = "edge v0.1, written by Olaf Matthes "; + +typedef struct edge +{ + t_object x_ob; + t_outlet *x_out1; /* bang on rising edge */ + t_outlet *x_out2; /* bang on falling edge */ + t_float x_lastval; /* last input value */ +} t_edge; + +static void edge_float(t_edge *x, t_floatarg f) +{ + if((x->x_lastval <= 0) && (f >= 1)) /* rising edge */ + outlet_bang(x->x_out1); + else if((x->x_lastval >= 1) && (f <= 0)) /* falling edge */ + outlet_bang(x->x_out2); + + x->x_lastval = f; /* save last value */ +} + +static t_class *edge_class; + +static void *edge_new(t_floatarg f) +{ + int i; + + t_edge *x = (t_edge *)pd_new(edge_class); + x->x_out1 = outlet_new(&x->x_ob, gensym("bang")); + x->x_out2 = outlet_new(&x->x_ob, gensym("bang")); + + x->x_lastval = f; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void edge_setup(void) +{ + edge_class = class_new(gensym("edge"), (t_newmethod)edge_new, + 0, sizeof(t_edge), 0, A_DEFFLOAT, 0); + class_addfloat(edge_class, edge_float); +#ifndef MAXLIB + class_sethelpsymbol(edge_class, gensym("help-edge.pd")); +#else + class_sethelpsymbol(edge_class, gensym("maxlib/help-edge.pd")); +#endif +} + diff --git a/src/expo.c b/src/expo.c new file mode 100644 index 0000000..3af8629 --- /dev/null +++ b/src/expo.c @@ -0,0 +1,77 @@ +/* ---------------------------- rand_expo ------------------------------------- */ +/* */ +/* rand_expo generates a exponentially distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX + +static char *version = "expo v0.1, generates exponentially distributed random variable\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_expo ------------------------------ */ + +static t_class *rand_expo_class; + +typedef struct _rand_expo +{ + t_object x_obj; + t_float x_lambda; +} t_rand_expo; + +static void *rand_expo_new(t_floatarg f) +{ + t_rand_expo *x = (t_rand_expo *)pd_new(rand_expo_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_lambda); + outlet_new(&x->x_obj, &s_float); + x->x_lambda = f; + return (x); +} + +static void rand_expo_bang(t_rand_expo *x) +{ + t_float u, l; + l = (x->x_lambda <= 0 ? 0.0001 : x->x_lambda); + do + { + u = fran(); + } + while(u == 0); + outlet_float(x->x_obj.ob_outlet, -log(u)/l); +} + +void expo_setup(void) +{ + rand_expo_class = class_new(gensym("expo"), (t_newmethod)rand_expo_new, 0, + sizeof(t_rand_expo), 0, A_DEFFLOAT, 0); + class_addbang(rand_expo_class, rand_expo_bang); + class_sethelpsymbol(rand_expo_class, gensym("maxlib/help-expo.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/fifo.c b/src/fifo.c new file mode 100644 index 0000000..1e60550 --- /dev/null +++ b/src/fifo.c @@ -0,0 +1,86 @@ +/* ---------------------------- fifo ------------------------------------------ */ +/* */ +/* Fifo buffer of floats, empties itselfe on every bang (in order of coming in) */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* Fifi-code based St. Rainstick fifi.c for Max, */ +/* copyright St. Rainstick, Amsterdam 1995 */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +static char *version = "fifo v0.1, written by Olaf Matthes "; + +typedef struct fifo +{ + t_object d_ob; + t_float *getal; + t_int count, end, size; + t_outlet *out; + +}t_fifo; + +static t_class *fifo_class; + +static void fifo_int(t_fifo *x, t_floatarg n) +{ + x->getal[x->count] = n; + x->count = (x->count + 1) % x->size; +} + +static void fifo_bang(t_fifo *x) +{ + if (x->end != x->count){ + outlet_float(x->out,x->getal[x->end]); + x->end = (x->end + 1) % x->size; + } +} + +static void fifo_free(t_fifo *x) +{ + freebytes(x->getal, x->size * sizeof(t_float)); +} + +static void *fifo_new(t_floatarg n) +{ + + t_fifo *x = (t_fifo *)pd_new(fifo_class); + if (n<10) n = 10; + x->size = (t_int)n; + x->end = 0; + x->count = 0; + x->getal = (t_float *)getbytes(x->size * sizeof(t_float)); + x->out = outlet_new(&x->d_ob, gensym("float")); + +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +void fifo_setup(void) +{ + fifo_class = class_new(gensym("fifo"), (t_newmethod)fifo_new, + (t_method)fifo_free, sizeof(t_fifo), 0, A_DEFFLOAT, 0); + class_addfloat(fifo_class, fifo_int); + class_addbang(fifo_class, fifo_bang); + class_sethelpsymbol(fifo_class, gensym("maxlib/help-fifo.pd")); +} diff --git a/src/gauss.c b/src/gauss.c new file mode 100644 index 0000000..2584475 --- /dev/null +++ b/src/gauss.c @@ -0,0 +1,78 @@ +/* ---------------------------- rand_gauss ----------------------------------- */ +/* */ +/* rand_gauss generates a gauss distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX + +static char *version = "gauss v0.1, generates a Gaussian distributed random variable\n" + " with mean 'mu' and standard deviation 'sigma',\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_gauss ------------------------------ */ + +static t_class *rand_gauss_class; + +typedef struct _rand_gauss +{ + t_object x_obj; + t_float x_sigma; + t_float x_mu; +} t_rand_gauss; + +static void *rand_gauss_new(t_floatarg fs, t_floatarg fm) +{ + t_rand_gauss *x = (t_rand_gauss *)pd_new(rand_gauss_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_sigma); + floatinlet_new(&x->x_obj, &x->x_mu); + outlet_new(&x->x_obj, &s_float); + x->x_sigma = fs; + return (x); +} + +static void rand_gauss_bang(t_rand_gauss *x) +{ + t_float u, halfN = 6.0, sum = 0, scale; + t_int k, N = 12; + scale = 1/sqrt(N/12); + for(k = 1; k <= N; k++) + sum += fran(); + outlet_float(x->x_obj.ob_outlet, x->x_sigma*scale*(sum-halfN)+x->x_mu); +} + +void gauss_setup(void) +{ + rand_gauss_class = class_new(gensym("gauss"), (t_newmethod)rand_gauss_new, 0, + sizeof(t_rand_gauss), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addbang(rand_gauss_class, rand_gauss_bang); + class_sethelpsymbol(rand_gauss_class, gensym("maxlib/help-gauss.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/gestalt.c b/src/gestalt.c new file mode 100644 index 0000000..a0efc39 --- /dev/null +++ b/src/gestalt.c @@ -0,0 +1,109 @@ +/* ------------------------- gestalt ---------------------------------------- */ +/* */ +/* Find the 'gestalt' of the MIDI input. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +static char *version = "gestalt v0.1, written by Olaf Matthes "; + +typedef struct gestalt +{ + t_object x_ob; + t_inlet *x_invelocity; /* inlet for velocity */ + t_outlet *x_outgestalt; /* calculated 'gestalt'-value */ + + t_float x_lastpitch; + t_float x_velocity; + + t_float x_reftime; + + double x_lastontime; + +} t_gestalt; + +static void gestalt_ft1(t_gestalt *x, t_floatarg f) +{ + x->x_velocity = f; +} + +static void gestalt_ft2(t_gestalt *x, t_floatarg f) +{ + if(f > 0.0) x->x_reftime = f; +} + +static void gestalt_float(t_gestalt *x, t_floatarg f) +{ + + int interval, pitch, gestalt; + double ontime = clock_getlogicaltime(); + + if(x->x_velocity) /* only process note-ons */ + { + + pitch = (t_int)f; + if(pitch < 1) pitch = 0; + if(pitch > 127) pitch = 127; + + interval = abs(pitch - x->x_lastpitch); + gestalt = (clock_gettimesince(x->x_lastontime)/x->x_reftime) + interval; + + x->x_lastpitch = pitch; + x->x_lastontime = ontime; + + /* output values from right to left */ + outlet_float(x->x_outgestalt, gestalt); + } +} + +static t_class *gestalt_class; + +static void *gestalt_new(t_floatarg f) +{ + t_gestalt *x = (t_gestalt *)pd_new(gestalt_class); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft2")); + x->x_outgestalt = outlet_new(&x->x_ob, gensym("float")); + + x->x_lastontime = clock_getlogicaltime(); + + x->x_reftime = f; + if(x->x_reftime < 1) x->x_reftime = 1; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void gestalt_setup(void) +{ + gestalt_class = class_new(gensym("gestalt"), (t_newmethod)gestalt_new, + 0, sizeof(t_gestalt), 0, A_DEFFLOAT, 0); + class_addfloat(gestalt_class, gestalt_float); + class_addmethod(gestalt_class, (t_method)gestalt_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(gestalt_class, (t_method)gestalt_ft2, gensym("ft2"), A_FLOAT, 0); + class_sethelpsymbol(gestalt_class, gensym("maxlib/help-gestalt.pd")); +} + diff --git a/src/history.c b/src/history.c new file mode 100644 index 0000000..51119cf --- /dev/null +++ b/src/history.c @@ -0,0 +1,257 @@ +/* -------------------------- history ----------------------------------------- */ +/* */ +/* Calculates the average value of the elements within the last N seconds. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAX_ARG 1024 /* maximum number of items to average */ +#define MAX_TIME 60000 /* maximum time to look back */ + +static char *version = "history v0.1, written by Olaf Matthes "; + +typedef struct history +{ + t_object x_ob; + t_clock *x_clock; + t_inlet *x_inindex; + t_outlet *x_outfloat; /* output the history */ + t_outlet *x_outtendency; /* outputs the tendency of the average */ + t_int x_limit; /* indicates if input is 'blocked' (1) */ + t_int x_index; /* the number of elements to average */ + t_float x_input[MAX_ARG]; /* stores the input values we need for averaging */ + double x_intime[MAX_ARG]; /* stores the time of arrival of an element */ + t_int x_inpointer; /* actual position in above array */ + t_float x_average; /* what do you guess ? */ + t_float x_lastaverage; + t_int x_mode; /* how to history: linear or geometric */ + t_int x_time; + +} t_history; + + /* there must be a function for this in math.h but how is the + german 'Fakultät' called in english ???? */ +static int normalise(int i) +{ + int ret = i; + while(i--) + { + if(i == 0)break; + ret += i; + } + return (ret); +} + +static void history_tick(t_history *x) +{ + t_float tendency = 0.0; + if(x->x_lastaverage < x->x_average) + { + tendency = 1.0; /* getting more */ + } + else if(x->x_lastaverage > x->x_average) + { + tendency = -1.0; /* getting less */ + } + else tendency = 0.0; /* nothing has changed */ + outlet_float(x->x_outtendency, tendency); + x->x_lastaverage = x->x_average; + clock_delay(x->x_clock, x->x_time); +} + +static void history_float(t_history *x, t_floatarg f) +{ + int i, j = 0, k = 0, l; + t_float geo = 1.0; + + x->x_average = 0; + /* put value into array */ + x->x_input[x->x_inpointer] = f; + x->x_intime[x->x_inpointer] = clock_getlogicaltime(); + /* look for elements that are too old */ + x->x_index = 0; + for(i = 0; i < MAX_ARG; i++) /* check all valid elements */ + { + if(x->x_intime[i] != 0) + { + if(clock_gettimesince(x->x_intime[i]) <= x->x_time) /* it's in our time window */ + { + x->x_index++; /* count valid entries */ + } + else /* too old, delete entry */ + { + x->x_intime[i] = 0; + } + } + } + if(x->x_index > 1) + { + /* calulate history */ + for(i = 0; i < MAX_ARG; i++) /* check all valid elements */ + { + if(x->x_intime[i] != 0) /* it's a valid entry */ + { + k++; + l = MAX_ARG; + + if(x->x_mode == 0) /* linear */ + { + x->x_average += x->x_input[i] * (1.0 / (float)x->x_index); + } + else if(x->x_mode == 1) /* geometric */ + { + if(x->x_input[i] == 0)x->x_input[i] = 0.001; /* need to cheat a bit... */ + geo *= x->x_input[i]; + if(k == x->x_index) + x->x_average = pow(geo, (1.0/(float)x->x_index)); + } + else if(x->x_mode == 2) /* weighted */ + { + /* normalise output */ + if(k == x->x_index) + { + x->x_average += x->x_input[(j + x->x_inpointer + MAX_ARG) % MAX_ARG] * (float)(x->x_index - k); + x->x_average = x->x_average / (float)normalise(x->x_index - 1); + } + else + { + x->x_average += x->x_input[(j + x->x_inpointer + MAX_ARG) % MAX_ARG] * (float)(x->x_index - k); + j--; /* go back in array */ + while(l--) /* check if this will result in a valid value */ + { + if(x->x_intime[(j + x->x_inpointer + MAX_ARG) % MAX_ARG] == 0) + { + j--; /* go back more if necessary */ + } + else break; /* finished on first non-zero */ + } + } + } else post("history: internal error!"); + } + } + } + else x->x_average = x->x_input[x->x_inpointer]; + + if(++x->x_inpointer > MAX_ARG) + { + x->x_inpointer = 0; + } + outlet_float(x->x_outfloat, x->x_average); +} + +static void history_time(t_history *x, t_floatarg f) +{ + x->x_time = (t_int)f; + if(x->x_time < 1) x->x_time = 1; + if(x->x_time > MAX_TIME)x->x_time = MAX_TIME; + clock_unset(x->x_clock); + clock_delay(x->x_clock, 0); +} + +static void history_reset(t_history *x) +{ + int i; + /* zeroe out the array */ + for(i = 0; i < MAX_ARG; i++) + { + x->x_input[i] = 0.0; + x->x_intime[i] = 0.0; + } + x->x_index = 0; + x->x_inpointer = 0; + x->x_average = 0; + x->x_lastaverage = 0; + post("history: reset"); +} + +static void history_linear(t_history *x) +{ + x->x_mode = 0; + post("history: linear"); +} + +static void history_geometric(t_history *x) +{ + x->x_mode = 1; + post("history: geometric"); +} + +static void history_weight(t_history *x) +{ + x->x_mode = 2; + post("history: weighted"); +} + +static void history_free(t_history *x) +{ + clock_free(x->x_clock); +} + +static t_class *history_class; + +static void *history_new(t_floatarg f) +{ + int i; + + t_history *x = (t_history *)pd_new(history_class); + x->x_inindex = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("time")); + x->x_outfloat = outlet_new(&x->x_ob, gensym("float")); + x->x_outtendency = outlet_new(&x->x_ob, gensym("float")); + x->x_clock = clock_new(x, (t_method)history_tick); + /* zeroe out the array */ + for(i = 0; i < MAX_ARG; i++) + { + x->x_input[i] = 0.0; + x->x_intime[i] = 0.0; + } + x->x_time = (t_int)f; + if(x->x_time < 1) x->x_time = 1; + if(x->x_time > MAX_TIME) + { + x->x_time = MAX_TIME; + post("history: set number time to %d", x->x_time); + } + x->x_index = 0; + x->x_inpointer = 0; + x->x_average = 0; + x->x_mode = 0; + clock_delay(x->x_clock, 0); +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void history_setup(void) +{ + history_class = class_new(gensym("history"), (t_newmethod)history_new, + (t_method)history_free, sizeof(t_history), 0, A_DEFFLOAT, 0); + class_addmethod(history_class, (t_method)history_reset, gensym("reset"), 0); + class_addmethod(history_class, (t_method)history_linear, gensym("linear"), 0); + class_addmethod(history_class, (t_method)history_geometric, gensym("geometric"), 0); + class_addmethod(history_class, (t_method)history_weight, gensym("weight"), 0); + class_addfloat(history_class, history_float); + class_addmethod(history_class, (t_method)history_time, gensym("time"), A_FLOAT, 0); + class_sethelpsymbol(history_class, gensym("maxlib/help-history.pd")); +} + diff --git a/src/ignore.c b/src/ignore.c new file mode 100644 index 0000000..4dedd82 --- /dev/null +++ b/src/ignore.c @@ -0,0 +1,112 @@ +/* ------------------------- ignore ------------------------------------------- */ +/* */ +/* Ignores input that is followed by next value faster than N ms. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +#define MAX_ARG 16 /* maximum number of items to ignore */ +#define IN_SIZE 32 /* size of array that stores the incoming values */ + +static char *version = "ignore v0.1, written by Olaf Matthes "; + +typedef struct ignore +{ + t_object x_ob; + t_clock *x_clock; + t_inlet *x_intime; + t_outlet *x_outfloat; + t_float x_input; + t_float x_lastinput; + t_int x_limit; /* indicates if input is 'blocked' (1) */ + t_int x_time; /* the time in ms */ +} t_ignore; + +static void ignore_tick(t_ignore *x) +{ + x->x_limit = 0; + /* output in case nothing has changed */ + if(x->x_lastinput == x->x_input) + outlet_float(x->x_outfloat, x->x_lastinput); +} + +static void ignore_float(t_ignore *x, t_floatarg f) +{ + x->x_input = f; + if(!x->x_limit) + { + x->x_limit = 1; /* ignore input within next N ms */ + clock_delay(x->x_clock, x->x_time); /* start clock */ + } + else /* ignore / start clock again */ + { + x->x_lastinput = x->x_input; /* save current as last valid value */ + clock_unset(x->x_clock); /* throw out last clock */ + clock_delay(x->x_clock, x->x_time); /* start new clock */ + } +} + +static void ignore_time(t_ignore *x, t_floatarg f) +{ + x->x_time = (t_int)f; +} + +static void ignore_reset(t_ignore *x) +{ + x->x_limit = 0; + post("ignore: reset"); +} + +static void ignore_free(t_ignore *x) +{ + clock_free(x->x_clock); +} + +static t_class *ignore_class; + +static void *ignore_new(t_floatarg f) +{ + t_ignore *x = (t_ignore *)pd_new(ignore_class); + x->x_intime = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("time")); + x->x_outfloat = outlet_new(&x->x_ob, gensym("float")); + x->x_clock = clock_new(x, (t_method)ignore_tick); + +#ifndef MAXLIB + post(version); +#endif + + x->x_time = (t_int)f; + x->x_lastinput = 0; + + return (void *)x; +} + +void ignore_setup(void) +{ + ignore_class = class_new(gensym("ignore"), (t_newmethod)ignore_new, + (t_method)ignore_free, sizeof(t_ignore), 0, A_DEFFLOAT, 0); + class_addmethod(ignore_class, (t_method)ignore_reset, gensym("reset"), 0); + class_addmethod(ignore_class, (t_method)ignore_time, gensym("time"), A_FLOAT, 0); + class_addfloat(ignore_class, ignore_float); + class_sethelpsymbol(ignore_class, gensym("maxlib/help-ignore.pd")); +} + diff --git a/src/iso.c b/src/iso.c new file mode 100644 index 0000000..3ca1779 --- /dev/null +++ b/src/iso.c @@ -0,0 +1,178 @@ +/* iso.c ---- queue up pitch and attack point series */ +/* by Charlie Baker (baker@foxtrot.ccmrc.ucsb.edu) */ +/* Pd port by Olaf Matthes */ + +#include "m_pd.h" +#include + +#define MAXPOLY 32 + +static char *version = "iso v0.1, written for Max by Charlie Baker \n" + " ported to Pd by Olaf Matthes "; + +/* Iso object data structure */ + +typedef struct iso +{ + t_object iso_ob; + t_outlet *iso_out1; /* outlet 1*/ + t_outlet *iso_out2; /* outlet 2*/ + t_inlet *iso_in2; /* inlet 2 (attack list) */ + t_clock *iso_clock; + t_int ptchlength,atklength,curptch,curatk; + t_float pitches[MAXPOLY]; + t_float atks[MAXPOLY]; + t_int loop,stop; + t_float hook,duty; +} t_iso; + +static t_class *iso_class; + +/* take list and create matrix */ + +static void iso_bang(t_iso *x) +{ + x->stop = 0; + x->curptch = 0; + x->curatk = 0; + clock_delay(x->iso_clock, 0); +} + + +static void iso_clock_fun(t_iso *x) +{ + if (!x->stop) { + clock_delay(x->iso_clock, (double)(x->atks[x->curatk] * x->hook)); + outlet_float(x->iso_out2,(t_float)(x->atks[x->curatk] * x->hook * x->duty)); + outlet_float(x->iso_out1,x->pitches[x->curptch] ); + if (x->loop) { + x->curatk = ((x->curatk + 1) % x->atklength); + x->curptch = ((x->curptch + 1) % x->ptchlength); + } + else { + if (((x->curatk + 1) >= x->atklength) || ((x->curptch + 1) >= x->ptchlength)) + x->stop = 1; + else { + x->curptch += 1; + x->curatk += 1; + } + } + } +} + +static void iso_hook(t_iso *x, t_floatarg hook) +{ + if (hook < 1.0) hook = 1.0; + x->hook = (t_float)hook; +} + +static void iso_duty(t_iso *x, t_floatarg duty) +{ + if (duty < 1.0) duty = 1.0; + x->duty = (t_float)duty; +} + +static void iso_list(t_iso *x, t_symbol *s, t_int argc, t_atom* argv) +{ + int i; + if (argc > MAXPOLY) post("iso: only %d values max. allowed in list!", MAXPOLY); + for (i = 0; i < argc; i++) x->atks[i] = argv[i].a_w.w_float; + x->atklength = argc; +} + +static void iso_pitch(t_iso *x, t_symbol *s, t_int argc, t_atom* argv) +{ + int i; + if (argc > MAXPOLY) post("iso: only %d values max. allowed in list!", MAXPOLY); + for (i = 0; i < argc; i++) x->pitches[i] = argv[i].a_w.w_float; + x->ptchlength = argc; +} + +static void iso_start(t_iso *x, t_symbol *s, t_int argc, t_atom* argv) +{ + t_int start = atom_getfloatarg(0, argc, argv); + x->stop = 0; + if (start) { + x->curptch = (t_int)((start - 1) % x->ptchlength); + x->curatk = (t_int)((start - 1) % x->atklength); + } + else { + x->curptch = 0; + x->curatk = 0; + } + clock_delay(x->iso_clock, 0); +} + +static void iso_stop(t_iso *x) +{ + x->stop = 1; + x->curatk = 0; + x->curptch = 0; +} + +static void iso_pause(t_iso *x) +{ + x->stop = 1; +} + +static void iso_loop(t_iso *x) +{ + x->loop = 1; +} + +static void iso_resume(t_iso *x) +{ + x->stop = 0; + clock_delay(x->iso_clock, 0); +} + +static void iso_unloop(t_iso *x) +{ + x->loop = 0; +} + + +static void *iso_new(void) { + t_iso *x = (t_iso *)pd_new(iso_class); /* allocates memory and sticks in an inlet */ + x->iso_clock = clock_new(x, (t_method)iso_clock_fun); + x->iso_out1 = outlet_new(&x->iso_ob, gensym("float")); + x->iso_out2 = outlet_new(&x->iso_ob, gensym("float")); + x->iso_in2 = inlet_new(&x->iso_ob, &x->iso_ob.ob_pd, gensym("list"), gensym("attack")); + x->stop = 0; + x->loop = 1; + x->hook = 1.0; + x->curptch = 0; + x->curatk = 0; + x->ptchlength = 1; + x->atklength = 1; + x->pitches[0] = 60; + x->atks[0] = 500; + x->duty = 1.0; +#ifndef MAXLIB + post(version); +#endif + return (x); /* always return a copy of the created object */ +} + +static void iso_free(t_iso *x) { + + clock_free(x->iso_clock); +} + +void iso_setup(void) { + + iso_class = class_new(gensym("iso"), (t_newmethod)iso_new, + (t_method)iso_free, sizeof(t_iso), 0, 0); + class_addmethod(iso_class, (t_method)iso_duty, gensym("duty"), A_FLOAT, 0); + class_addmethod(iso_class, (t_method)iso_list, gensym("attack"), A_GIMME, 0); + class_addmethod(iso_class, (t_method)iso_start, gensym("start"), A_GIMME, 0); + class_addmethod(iso_class, (t_method)iso_stop, gensym("stop"), 0); + class_addmethod(iso_class, (t_method)iso_pause, gensym("pause"), 0); + class_addmethod(iso_class, (t_method)iso_loop, gensym("loop"), 0); + class_addmethod(iso_class, (t_method)iso_unloop, gensym("unloop"), 0); + class_addmethod(iso_class, (t_method)iso_resume, gensym("resume"), 0); + class_addmethod(iso_class, (t_method)iso_hook, gensym("hook"), A_FLOAT, 0); + class_addbang(iso_class, iso_bang); + class_addlist(iso_class, iso_pitch); + class_sethelpsymbol(iso_class, gensym("maxlib/help-iso.pd")); +} diff --git a/src/lifo.c b/src/lifo.c new file mode 100644 index 0000000..e25203a --- /dev/null +++ b/src/lifo.c @@ -0,0 +1,95 @@ +/* ---------------------------- lifo ------------------------------------------ */ +/* */ +/* lifo buffer of floats, empties itselfe on every bang (in order of coming in) */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* Fifi-code based St. Rainstick fifi.c for Max, */ +/* copyright St. Rainstick, Amsterdam 1995 */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +static char *version = "lifo v0.1, written by Olaf Matthes "; + +typedef struct lifo +{ + t_object d_ob; + t_float *getal; + t_int count, end, size, teller; + t_outlet *out; + +}t_lifo; + +static t_class *lifo_class; + +static void lifo_int(t_lifo *x, t_floatarg n) +{ + x->getal[x->count] = n; + x->end = x->count; + if (x->teller < x->size) x->teller++; + x->count = (x->count + 1) % x->size; +} + +static void lifo_bang(t_lifo *x) +{ + if (x->teller > 0){ + outlet_float(x->out,x->getal[x->end]); + x->teller--; + x->end = (x->end + x->size - 1) % x->size; + } +} + +static void lifo_clear(t_lifo *x) +{ + x->teller = 0; +} + +static void lifo_free(t_lifo *x) +{ + freebytes(x->getal, x->size * sizeof(t_float)); +} + +static void *lifo_new(t_floatarg n) +{ + + t_lifo *x = (t_lifo *)pd_new(lifo_class); + if (n<10) n = 10; + x->size = (t_int)n; + x->teller = 0; + x->end = 0; + x->count = 0; + x->getal = (t_float *)getbytes(x->size * sizeof(t_float)); + x->out = outlet_new(&x->d_ob, gensym("float")); +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +void lifo_setup(void) +{ + lifo_class = class_new(gensym("lifo"), (t_newmethod)lifo_new, + (t_method)lifo_free, sizeof(t_lifo), 0, A_DEFFLOAT, 0); + class_addfloat(lifo_class, lifo_int); + class_addbang(lifo_class, lifo_bang); + class_addmethod(lifo_class, (t_method)lifo_clear, gensym("clear"), 0); + class_sethelpsymbol(lifo_class, gensym("maxlib/help-lifo.pd")); +} diff --git a/src/limit.c b/src/limit.c new file mode 100644 index 0000000..f6b0b70 --- /dev/null +++ b/src/limit.c @@ -0,0 +1,119 @@ +/* ------------------------- limit ------------------------------------------ */ +/* */ +/* limits input to lie within an output range. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +static char *version = "limit v0.1, written by Olaf Matthes "; + +typedef struct limit +{ + t_object x_ob; + t_float x_ol; /* low border of output range */ + t_float x_oh; /* high border of output range */ + t_float x_ratio; /* 'compression ratio' */ + t_outlet *x_outlet1; /* result */ + t_float x_f; +} t_limit; + +static void limit_float(t_limit *x, t_floatarg f) +{ + if(x->x_oh < x->x_ol) /* swap values */ + { + int i = x->x_oh; + x->x_oh = x->x_ol; + x->x_ol = i; + } + if(x->x_ratio == 0) /* 'clip' mode */ + { + if(f > x->x_oh)f = x->x_oh; + else if(f < x->x_ol)f = x->x_ol; + } + else /* 'compress' mode */ + { + int diff; + if(f > x->x_oh) + { + diff = f - x->x_oh; + f = x->x_oh + (diff / x->x_ratio); + } + else if(f < x->x_ol) + { + diff = x->x_ol - f; + f = x->x_ol - (diff / x->x_ratio); + } + } + outlet_float(x->x_outlet1, f); + x->x_f = f; +} + +static void limit_bang(t_limit *x) +{ + limit_float(x, x->x_f); /* recalculate result */ +} + +static t_class *limit_class; + +static void *limit_new(t_floatarg fol, t_floatarg foh, t_floatarg fr) +{ + t_limit *x = (t_limit *)pd_new(limit_class); + + floatinlet_new(&x->x_ob, &x->x_ol); + floatinlet_new(&x->x_ob, &x->x_oh); + floatinlet_new(&x->x_ob, &x->x_ratio); + + x->x_outlet1 = outlet_new(&x->x_ob, gensym("float")); + + /* default values taken from Max's limit */ + x->x_ol = fol; + x->x_oh = foh; + if(x->x_oh < x->x_ol) /* swap values */ + { + int i = x->x_oh; + x->x_oh = x->x_ol; + x->x_ol = i; + } + x->x_ratio = fr; + x->x_f = 0; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void limit_setup(void) +{ + limit_class = class_new(gensym("limit"), (t_newmethod)limit_new, + 0, sizeof(t_limit), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(limit_class, limit_float); + class_addbang(limit_class, limit_bang); +#ifndef MAXLIB + class_sethelpsymbol(limit_class, gensym("help-limit.pd")); +#else + class_sethelpsymbol(limit_class, gensym("maxlib/help-limit.pd")); +#endif +} + diff --git a/src/linear.c b/src/linear.c new file mode 100644 index 0000000..4a6c222 --- /dev/null +++ b/src/linear.c @@ -0,0 +1,72 @@ +/* ---------------------------- rand_linear ----------------------------------- */ +/* */ +/* rand_linear generates a linearly distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX + +static char *version = "linear v0.1, generates linearly distributed random variable\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_linear ------------------------------ */ + +static t_class *rand_linear_class; + +typedef struct _rand_linear +{ + t_object x_obj; +} t_rand_linear; + +static void *rand_linear_new(t_floatarg f) +{ + t_rand_linear *x = (t_rand_linear *)pd_new(rand_linear_class); + srand( (unsigned)time( NULL ) ); + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void rand_linear_bang(t_rand_linear *x) +{ + t_float u1, u2; + u1 = fran(); + u2 = fran(); + if(u2 < u1) + u1 = u2; + outlet_float(x->x_obj.ob_outlet, u1); +} + +void linear_setup(void) +{ + rand_linear_class = class_new(gensym("linear"), (t_newmethod)rand_linear_new, 0, + sizeof(t_rand_linear), 0, A_DEFFLOAT, 0); + class_addbang(rand_linear_class, rand_linear_bang); + class_sethelpsymbol(rand_linear_class, gensym("maxlib/help-linear.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/listfunnel.c b/src/listfunnel.c new file mode 100644 index 0000000..bd47665 --- /dev/null +++ b/src/listfunnel.c @@ -0,0 +1,82 @@ +/* ------------------------- listfunnel ------------------------------------- */ +/* */ +/* Convert list into two-element lists with source index. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +static char *version = "listfunnel v0.1, written by Olaf Matthes "; + +typedef struct listfunnel +{ + t_object x_ob; + t_outlet *x_outlet; /* result */ +} t_listfunnel; + +static void listfunnel_list(t_listfunnel *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + t_atom list[2]; + + for(i = 0; i < argc; i++) + { + SETFLOAT(list, i); + list[1] = argv[i]; // SETFLOAT(list+1, atom_getfloatarg(i, argc, argv)); + outlet_list(x->x_outlet, NULL, 2, list); + } +} + +static void listfunnel_float(t_listfunnel *x, t_floatarg f) +{ + t_atom list[2]; + + SETFLOAT(list, 0); + SETFLOAT(list+1, f); + outlet_list(x->x_outlet, NULL, 2, list); +} + +static t_class *listfunnel_class; + +static void *listfunnel_new(void) +{ + int i; + + t_listfunnel *x = (t_listfunnel *)pd_new(listfunnel_class); + x->x_outlet = outlet_new(&x->x_ob, gensym("float")); + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void listfunnel_setup(void) +{ + listfunnel_class = class_new(gensym("listfunnel"), (t_newmethod)listfunnel_new, + 0, sizeof(t_listfunnel), 0, 0, 0); + class_addfloat(listfunnel_class, listfunnel_float); + class_addlist(listfunnel_class, listfunnel_list); + class_sethelpsymbol(listfunnel_class, gensym("maxlib/help-listfunnel.pd")); +} + diff --git a/src/match.c b/src/match.c new file mode 100644 index 0000000..369aeb5 --- /dev/null +++ b/src/match.c @@ -0,0 +1,268 @@ +/* ------------------------- match ------------------------------------------ */ +/* */ +/* Outputs a list when a list of input values matches the creation args. */ +/* Written by Krzysztof Czaja for his cyclone library. */ +/* Modified to fit into maxlib by Olaf Matthes . */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +/* this is the original copyright notice: */ + +/* Copyright (c) 1997-2002 Miller Puckette and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* LATER compare with match.c from max sdk */ + +#include +#include "m_pd.h" +// #include "hammer.h" + +#define MATCH_INISIZE 16 /* LATER rethink */ + +typedef struct _match +{ + t_object x_ob; + int x_size; /* as allocated */ + int x_patlen; /* as used */ + t_atom *x_pattern; + t_atom x_patini[MATCH_INISIZE]; + int x_quelen; + t_atom *x_queue; + t_atom x_queini[MATCH_INISIZE]; + t_atom *x_queend; + t_atom *x_queptr; /* writing head, post-incremented (oldest-pointing) */ + int x_; +} t_match; + +static t_class *match_class; + +/* a caller must check for nrequested > *sizep */ +/* returns actual number of atoms: requested (success) + or a default value of initial size (failure) */ +/* the result is guaranteed to be >= min(nrequested, inisize) */ +static int match_grow(int nrequested, int *sizep, t_atom **bufp, + int inisize, t_atom *bufini) +{ + int newsize = *sizep * 2; + while (newsize < nrequested) newsize *= 2; + if (*bufp == bufini) + *bufp = (t_atom *)getbytes(newsize * sizeof(**bufp)); + else + *bufp = (t_atom *)resizebytes(*bufp, *sizep * sizeof(**bufp), + newsize * sizeof(**bufp)); + if (*bufp) + *sizep = newsize; + else + { + *bufp = bufini; + nrequested = *sizep = inisize; + } + return (nrequested); +} + +static void match_clear(t_match *x) +{ + x->x_quelen = 0; + x->x_queptr = x->x_queue; +} + +/* x->x_patlen > 0 is assumed */ +/* LATER use a lock to disable reentrant calls. I do not see any + purpose of reentering match, but lets CHECKME first... */ +static void match_checkin(t_match *x) +{ + int i, patlen = x->x_patlen; + t_atom *queptr, *pp, *qp; + if (x->x_queptr >= x->x_queend) + x->x_queptr = x->x_queue; + else x->x_queptr++; + if (x->x_quelen < patlen && ++(x->x_quelen) < patlen) + return; + + qp = queptr = x->x_queptr; + for (i = 0, pp = x->x_pattern; i < patlen; i++, pp++) + { + if (pp->a_type == A_FLOAT) + { + if (qp->a_type != A_FLOAT || qp->a_w.w_float != pp->a_w.w_float) + break; + } + else if (pp->a_type == A_SYMBOL) + { + if (qp->a_type != A_SYMBOL || qp->a_w.w_symbol != pp->a_w.w_symbol) + break; + } + else if (pp->a_type == A_NULL) + { + if (qp->a_type == A_FLOAT || qp->a_type == A_SYMBOL) + { + /* instantiating a pattern */ + *pp = *qp; + qp->a_type = A_NULL; + } + else break; /* LATER rethink */ + } + else break; /* LATER rethink */ + if (qp >= x->x_queend) + qp = x->x_queue; + else qp++; + } + if (i == patlen) + { + pp = x->x_pattern; + if (pp->a_type == A_FLOAT) + { + if (patlen == 1) + outlet_float(((t_object *)x)->ob_outlet, pp->a_w.w_float); + else + outlet_list(((t_object *)x)->ob_outlet, &s_list, patlen, pp); + } + else /* assuming A_SYMBOL (see above) */ + { + if (pp->a_w.w_symbol == &s_symbol /* bypassing typedmess() */ + && patlen == 2 && pp[1].a_type == A_SYMBOL) + outlet_symbol(((t_object *)x)->ob_outlet, pp[1].a_w.w_symbol); + else + outlet_anything(((t_object *)x)->ob_outlet, pp->a_w.w_symbol, + patlen - 1, pp + 1); + } + /* CHECKED: no implicit clear (resolving overlapping patterns) */ + } + /* restoring a pattern */ + for (i = 0, pp = x->x_pattern; i < patlen; i++, pp++) + { + if (queptr->a_type == A_NULL) + { + queptr->a_type = pp->a_type; + pp->a_type = A_NULL; + } + if (queptr >= x->x_queend) + queptr = x->x_queue; + else queptr++; + } +} + +static void match_float(t_match *x, t_float f) +{ + if (x->x_patlen) + { + SETFLOAT(x->x_queptr, f); + match_checkin(x); + } +} + +static void match_symbol(t_match *x, t_symbol *s) +{ + if (s && s != &s_ && x->x_patlen) + { + SETSYMBOL(x->x_queptr, s); + match_checkin(x); + } +} + +/* LATER gpointer */ + +static void match_list(t_match *x, t_symbol *s, int ac, t_atom *av) +{ + while (ac--) + { + if (av->a_type == A_FLOAT) match_float(x, av->a_w.w_float); + else if (av->a_type == A_SYMBOL) match_symbol(x, av->a_w.w_symbol); + av++; + } +} + +static void match_anything(t_match *x, t_symbol *s, int ac, t_atom *av) +{ + match_symbol(x, s); + match_list(x, 0, ac, av); +} + +static void match_set(t_match *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac) /* CHECKED */ + { + t_atom *pp; + t_symbol *ps_nn; + int newlen = ac * 2; + if (newlen > x->x_size) + { + newlen = match_grow(newlen, &x->x_size, &x->x_pattern, + MATCH_INISIZE * 2, x->x_patini); + if (newlen == MATCH_INISIZE * 2) + { + x->x_queue = x->x_queini; + ac = MATCH_INISIZE; + } + else x->x_queue = x->x_pattern + x->x_size / 2; + } + x->x_patlen = ac; + x->x_queend = x->x_queue + ac - 1; + match_clear(x); /* CHECKED */ + memcpy(x->x_pattern, av, ac * sizeof(*x->x_pattern)); + pp = x->x_pattern; + ps_nn = gensym("nn"); + while (ac--) + { + if (pp->a_type == A_SYMBOL && pp->a_w.w_symbol == ps_nn) + pp->a_type = A_NULL; + pp++; + } + } +} + +static void match_free(t_match *x) +{ + if (x->x_pattern != x->x_patini) + freebytes(x->x_pattern, 2 * x->x_size * sizeof(*x->x_pattern)); +} + +static void *match_new(t_symbol *s, int ac, t_atom *av) +{ + t_match *x = (t_match *)pd_new(match_class); + x->x_size = MATCH_INISIZE * 2; + x->x_patlen = 0; + x->x_pattern = x->x_patini; + x->x_queue = x->x_queini; + /* x->x_queend is not used unless x->x_patlen > 0, + LATER consider chosing a more defensive way... */ + outlet_new((t_object *)x, &s_anything); + match_clear(x); + match_set(x, 0, ac, av); + return (x); +} + +void match_setup(void) +{ + match_class = class_new(gensym("match"), (t_newmethod)match_new, + (t_method)match_free, sizeof(t_match), 0, A_GIMME, 0); + class_addfloat(match_class, match_float); + class_addsymbol(match_class, match_symbol); + class_addlist(match_class, match_list); + class_addanything(match_class, match_anything); + class_addmethod(match_class, (t_method)match_set, gensym("set"), A_GIMME, 0); + class_addmethod(match_class, (t_method)match_clear, gensym("clear"), 0); +#ifndef MAXLIB + class_sethelpsymbol(match_class, gensym("help-match.pd")); +#else + class_sethelpsymbol(match_class, gensym("maxlib/help-match.pd")); +#endif +} diff --git a/src/minus.c b/src/minus.c new file mode 100644 index 0000000..58e4d90 --- /dev/null +++ b/src/minus.c @@ -0,0 +1,100 @@ +/* ------------------------- minus ------------------------------------------ */ +/* */ +/* Like '-', but calculates output whenever _any_ of the inlets changes. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAXSIZE 32 + +static char *version = "minus v0.2, written by Olaf Matthes "; + +typedef struct minus +{ + t_object x_ob; + t_inlet *x_inleft; /* leftmost inlet */ + t_inlet *x_inright; /* right inlet */ + t_outlet *x_outlet; /* result */ + t_int x_numvalues; /* number of values / inlets */ + + t_float x_minusvalue[MAXSIZE]; + +} t_minus; + +static void minus_bang(t_minus *x) +{ + int i; + t_float result = x->x_minusvalue[0]; + for(i = 1; i < x->x_numvalues; i++) + result -= x->x_minusvalue[i]; + outlet_float(x->x_outlet, result); +} + +static void minus_float(t_minus *x, t_floatarg f) +{ + x->x_minusvalue[0] = f; + minus_bang(x); /* calculate result */ +} + +static void minus_ft1(t_minus *x, t_floatarg f) +{ + x->x_minusvalue[1] = f; + minus_bang(x); /* calculate result */ +} + +static t_class *minus_class; + +static void *minus_new(t_symbol *s, t_int argc, t_atom* argv) +{ + int i; + + t_minus *x = (t_minus *)pd_new(minus_class); + x->x_inright = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + for(i = 2; i < argc; i++) /* create additional inlets, if any */ + { + // inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + floatinlet_new(&x->x_ob, &x->x_minusvalue[i]); + } + x->x_outlet = outlet_new(&x->x_ob, gensym("float")); + + for(i = 0; i < argc; i++) + { + x->x_minusvalue[i] = atom_getfloatarg(i, argc, argv); + } + x->x_numvalues = i; +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void minus_setup(void) +{ + minus_class = class_new(gensym("minus"), (t_newmethod)minus_new, + 0, sizeof(t_minus), 0, A_GIMME, 0); + class_addfloat(minus_class, minus_float); + class_addmethod(minus_class, (t_method)minus_ft1, gensym("ft1"), A_FLOAT, 0); + class_addbang(minus_class, (t_method)minus_bang); + class_sethelpsymbol(minus_class, gensym("maxlib/help-minus.pd")); +} + diff --git a/src/mlife.c b/src/mlife.c new file mode 100644 index 0000000..03f96d3 --- /dev/null +++ b/src/mlife.c @@ -0,0 +1,497 @@ +/* ------------------------- mlife ------------------------------------------ */ +/* */ +/* A linear cellular automata object for PureData. */ +/* Based on 'mlife' by pauld@koncon.nl */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ +#include "m_pd.h" + + +static char *version = "mlife v0.1, a linear cellular automata object for Pd\n" + " written by Olaf Matthes "; + +#undef DEBUG +//#define DEBUG + + +#define INTSIZE sizeof(unsigned int) * 8 +#define LONGSIZE sizeof(unsigned long) * 8 +#define DEFAULT_DIE_LO 2 +#define DEFAULT_DIE_HI 3 +#define DEFAULT_N_SIZE 3 + +#define MAXSIZE 1024 + +#include +#include + +/* -------------------- random stuff -------------------- */ +static union { + unsigned long next; + struct { + unsigned short : 1; + unsigned short n : 15; + } bits; +} seed = { 1 }; + + +/* + * rand - pseudo-random number generator + * + */ + +static int my_rand(void) +{ + seed.next = seed.next * 1103515245 + 12345; + return(seed.bits.n); +} + + +/* + * srand - seed pseudo-random number generator + * + */ + +static void my_srand(unsigned n) +{ + seed.next = n; +} +/* --------------------------------------------------------- */ + +// +// Maxlife object data structure +// +typedef struct maxlife +{ + t_object ml_ob; // must begin every object + t_int universe[MAXSIZE]; // array of cells - alive and dead + t_outlet *out[MAXSIZE]; // outlets + t_int size; // size of the CA field/world + t_int view_start; // Start of viewport + t_int view_size; // length of viewport and number of outlets + t_int rule_die_lo; // death if less than this + t_int rule_die_hi; // death if greater then this + t_int neighbourhood_size; // # of cells either side to check + t_int closed; // closed universe if true +} t_maxlife; + +// +// Function prototypes for our methods and functions +// +static t_class *mlife_class; // global variable that points to the Maxlife class + +// +// ml_nextgeneration +// Step through the array, applying the rules and reset each cell +// accordingly. For each cell: +// - Check the number of neighbours (watch for "closed") +// using neighbourhood_size +// +// - If neighbours < rule_die_lo the cell is cleared (0) +// +// - If neighbours > rule_die_hi the cell is cleared (0) +// +// - Else the cell is filled (1) +// +// not called by Pd itself +// +static void ml_nextgeneration(t_maxlife *mlp) +{ + register long i, j, k; + register long size, neighbourhood_size, max_neighbours, min_neighbours, neighbours; + register int closed, out_of_bounds; + + // get the important info a little closer to hand + size = mlp->size; + closed = mlp->closed; + neighbourhood_size = mlp->neighbourhood_size; + max_neighbours = mlp->rule_die_hi; + min_neighbours = mlp->rule_die_lo; + +#ifdef DEBUG + post("mlife:next_generation called, vars n_size=%ld, n_max=%ld, n_min=%ld", + neighbourhood_size, max_neighbours, min_neighbours); +#endif + // for each cell... + for(i=0; i size-1L) + k = j - size - 1L; // not size-1 ??? + + if(j != i) // skip our own location in this roundup + if(mlp->universe[k]) // if there's a neighbour inc count + neighbours++; + } + else // not closed + { + out_of_bounds = 0; + if(k < 0L) // start of array + { + out_of_bounds = 1; + k = 0L; + } + if(k > size-1L) + { + out_of_bounds = 1; + k = size-1L; // end of array + } + + if((j != i) && !out_of_bounds) // skip our own location in this roundup + if(mlp->universe[k]) // if there's a neighbour inc count + neighbours++; + } + + } // end of neighbour search + + // based on number of neighbours, fill or clear this cell (i) + if((neighbours < min_neighbours) || (neighbours > max_neighbours)) + mlp->universe[i] = 0; + else + mlp->universe[i] = 1; + } +} + +// +// method to set the die_lo number +// +static void ml_set_die_lo(t_maxlife *mlp, t_floatarg die_lo) +{ + mlp->rule_die_lo = (t_int)die_lo; +} + +// +// method to set the die_hi number +// +static void ml_set_die_hi(t_maxlife *mlp, t_floatarg die_hi) +{ + mlp->rule_die_hi = (t_int)die_hi; +} + +// +// method to set the die_lo number +// +static void ml_set_neighbourhood(t_maxlife *mlp, t_floatarg n_size) +{ + mlp->neighbourhood_size = (t_int)n_size; +} + +// +// bang method outputs bangs for filled cells within the view port +// +static void ml_bang(t_maxlife *mlp) // argument is a pointer to an instance +{ + register long i, view_start; + +#ifdef DEBUG + post("mlife:ml_bang called, sending bangs"); +#endif + + view_start = mlp->view_start; + + // loop through the outlets right->left sending bangs if alive + for(i=view_start+mlp->view_size-2; i>=view_start-1; i--) + { + // send a bang out the appropriate outlet + if(mlp->universe[i]) + outlet_bang(mlp->out[i-view_start+1]); + } + + ml_nextgeneration(mlp); +} + +// +// int method outputs ints for ALL cells in the view port (1=filled, 0=not) +// +static void ml_int(t_maxlife *mlp, t_floatarg dummy) +{ + t_int i, view_start; + +#ifdef DEBUG + post("mlife:ml_int method called"); +#endif + + view_start = mlp->view_start; + + // loop through the outlets right -> left sending ints + for(i = view_start + mlp->view_size - 2; i >= view_start - 1; i--) + { + //outlet_int(mlp->out[i-view_start+1], mlp->universe[i]); + if(mlp->universe[i] == 1) + outlet_float(mlp->out[i-view_start+1], 1); + else if(mlp->universe[i] == 0) + outlet_float(mlp->out[i-view_start+1], 0); + else + error("mlife: corrupted data in universe[] array!"); + } + + ml_nextgeneration(mlp); +} + + +// +// method to print out the array +// +static void ml_display(t_maxlife *mlp) +{ + register long i; + char s[MAXSIZE]; + +#ifdef DEBUG + post("mlife: display method called"); +#endif + + for(i = 0; i < mlp->size; i++) // print the universe array + { + //s[i] = itoa(mlp->universe[i]); // my very primitive itoa() + if(mlp->universe[i]) + s[i] = '1'; + else + s[i] = '0'; + } + s[mlp->size] = '\0'; // null terminate the string + post("%s", s); +} + +// +// method to fill the array with a number +// +static void ml_fill(t_maxlife *mlp, t_floatarg fill_no) +{ + t_int n; + register long i, j; + + for(i=mlp->size-1; i >= 0; i--) // fill the universe array from the back + { + n = (t_int)fill_no; + + for(j=(long)INTSIZE; j>0; j--, i--, n>>=1) + { + if(i < 0L) + { + return; + } + if(n & 01) + mlp->universe[i] = 1; + else + mlp->universe[i] = 0; + } + } +} + +// +// method to fill the array with a random number +// +static void ml_randfill(t_maxlife *mlp) +{ + unsigned int s, rnum; + register unsigned int n; + register long i, j; + +#ifdef DEBUG + post("mlife: randfill method called"); +#endif + + s = (unsigned int)clock_getlogicaltime(); // set seed to a new number + my_srand(s); // reseed the 'random' generator + rnum = (unsigned int)my_rand(); + + for(i=mlp->size - 1; i>=0; i--) // fill the universe array from the back + { + n = rnum; + + for(j=(long)INTSIZE; j>0; j--, i--, n>>=1) + { + if(i < 0L) + { + return; + } + if(n & 01) + mlp->universe[i] = 1; + else + mlp->universe[i] = 0; + } + } +} + +// +// method to seed the array with a number +// +static void ml_seed(t_maxlife *mlp, t_floatarg start, t_floatarg fill_no) +{ + t_int n; + register long i, st, end; + +#ifdef DEBUG + post("mlife: seed method called"); +#endif + + st = (t_int)start; + n = (t_int)fill_no; + + if(st+(t_int)INTSIZE > mlp->size) + i = mlp->size - 1; + else + i = st+(long)INTSIZE - 1; + + // init the universe array from the back i>=start? + for(; i >= start - 1; i--, n>>=1) + { + if(n & 01) + mlp->universe[i] = 1; + else + mlp->universe[i] = 0; + } +} + +// +// method to seed the array with a random number +// +static void ml_randseed(t_maxlife *mlp, t_floatarg start) +{ + unsigned long s, rnum; + register unsigned long n; + register long i, st; + +#ifdef DEBUG + post("mlife: randseed method called, INTSIZE=%ld", (long)INTSIZE); +#endif + //if((start < 1) || (start > mlp->size-(long)INTSIZE)) + if(start < 1) + { + error("Randseed start parameter must be between 1 and %ld", mlp->size); + return; + } + + s = (unsigned long)clock_getlogicaltime(); // set seed to a new number + my_srand(s); // reseed the 'random' generator + rnum = (unsigned long)my_rand(); + n = (unsigned int)rnum; + st = start; + + if(st+(t_int)INTSIZE > mlp->size) + i = mlp->size - 1; + else + i = st+(t_int)INTSIZE - 1; + + // init the universe array from the back + for(; i>=st-1; i--, n>>=1) + { + if(n & 01) + mlp->universe[i] = 1; + else + mlp->universe[i] = 0; + + } +} + + +// +// function to create an instance of the mlife class +// +static void *ml_new(t_floatarg size, t_floatarg view_start, t_floatarg view_size, t_floatarg closed) +{ + long i; + t_maxlife *mlp = (t_maxlife *)pd_new(mlife_class); + + // check all args... + if((size>MAXSIZE) || (size<1)) + { + post("mlife: size argument must be between 1 and %ld", MAXSIZE); + size = 1.0; + } + if(view_start < 1) + { + post("mlife: view_start argument must be between 1 and %ld", size); + view_start = 1.0; + } + if((view_size < 1) || (view_size+view_start > size+1)) + { + post("mlife: viewsize argument must be between 1 and %ld", size-view_start); + view_size = 1.0; + } + + + // set up our structure + mlp->size = (t_int)size; + mlp->view_start = (t_int)view_start; + mlp->view_size = (t_int)view_size; + mlp->rule_die_lo = DEFAULT_DIE_LO; // 2 + mlp->rule_die_hi = DEFAULT_DIE_HI; // 3 + mlp->neighbourhood_size = DEFAULT_N_SIZE; // 3 + mlp->closed = (t_int)closed; + for(i=0; iuniverse[i] = 0; + + // create outlets - last first! + for(i = 0; i < mlp->view_size; i++) + mlp->out[i] = outlet_new(&mlp->ml_ob, gensym("float")); + +#ifdef DEBUG + post("mlife: finished building object"); + post("mlife: INTSIZE=%ld, LONGSIZE=%ld", (long)INTSIZE, (long)LONGSIZE); +#endif + +#ifndef MAXLIB + post(version); +#endif + post("mlife: defaults are: lo=%ld, hi=%ld, nset=%ld", (long)DEFAULT_DIE_LO, (long)DEFAULT_DIE_HI, DEFAULT_N_SIZE); + + return(mlp); // always return a copy of the created object +} + +static void ml_free(t_maxlife *mlp) +{ + long i; + +#ifdef DEBUG + post("mlife:freeing outlet memory"); +#endif +/* for(i=mlp->view_size-1; i>=0; i--) + freeobject(mlp->out[i]); */ +} + +void mlife_setup(void) +{ + mlife_class = class_new(gensym("mlife"), (t_newmethod)ml_new, + (t_method)ml_free, sizeof(t_maxlife), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_randfill, gensym("randfill"), 0); + class_addmethod(mlife_class, (t_method)ml_fill, gensym("fill"), A_FLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_set_die_lo, gensym("lo"), A_FLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_set_die_hi, gensym("hi"), A_FLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_set_neighbourhood, gensym("nset"), A_FLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_randseed, gensym("randseed"), A_FLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_seed, gensym("seed"), A_FLOAT, A_FLOAT, 0); + class_addmethod(mlife_class, (t_method)ml_display, gensym("display"), 0); + class_addfloat(mlife_class, ml_int); + class_addbang(mlife_class, ml_bang); + class_sethelpsymbol(mlife_class, gensym("maxlib/help-mlife.pd")); +} diff --git a/src/multi.c b/src/multi.c new file mode 100644 index 0000000..9b54181 --- /dev/null +++ b/src/multi.c @@ -0,0 +1,99 @@ +/* -------------------------- multi ------------------------------------------ */ +/* */ +/* Like '*', but calculates output whenever _any_ of the inlets changes. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAXSIZE 32 + +static char *version = "multi v0.2, written by Olaf Matthes "; + +typedef struct multi +{ + t_object x_ob; + t_inlet *x_inleft; /* leftmost inlet */ + t_inlet *x_inright; /* right inlet */ + t_outlet *x_outlet; /* result */ + t_int x_numvalues; /* number of values / inlets */ + + t_float x_multivalue[MAXSIZE]; + +} t_multi; + +static void multi_bang(t_multi *x) +{ + int i; + t_float result = x->x_multivalue[0]; + for(i = 1; i < x->x_numvalues; i++) + result *= x->x_multivalue[i]; + outlet_float(x->x_outlet, result); +} + +static void multi_float(t_multi *x, t_floatarg f) +{ + x->x_multivalue[0] = f; + multi_bang(x); /* calculate result */ +} + +static void multi_ft1(t_multi *x, t_floatarg f) +{ + x->x_multivalue[1] = f; + multi_bang(x); /* calculate result */ +} + +static t_class *multi_class; + +static void *multi_new(t_symbol *s, t_int argc, t_atom* argv) +{ + int i; + + t_multi *x = (t_multi *)pd_new(multi_class); + x->x_inright = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + for(i = 2; i < argc; i++) /* create additional inlets, if any */ + { + floatinlet_new(&x->x_ob, &x->x_multivalue[i]); + } + x->x_outlet = outlet_new(&x->x_ob, gensym("float")); + + for(i = 0; i < argc; i++) + { + x->x_multivalue[i] = atom_getfloatarg(i, argc, argv);; + } + x->x_numvalues = i; +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void multi_setup(void) +{ + multi_class = class_new(gensym("multi"), (t_newmethod)multi_new, + 0, sizeof(t_multi), 0, A_GIMME, 0); + class_addfloat(multi_class, multi_float); + class_addmethod(multi_class, (t_method)multi_ft1, gensym("ft1"), A_FLOAT, 0); + class_addbang(multi_class, (t_method)multi_bang); + class_sethelpsymbol(multi_class, gensym("maxlib/help-multi.pd")); +} + diff --git a/src/netclient.c b/src/netclient.c new file mode 100644 index 0000000..7ad252f --- /dev/null +++ b/src/netclient.c @@ -0,0 +1,348 @@ +/* -------------------------- netclient ------------------------------------- */ +/* */ +/* Extended 'netsend', connects to 'netserver'. */ +/* Uses child thread to connect to server. Thus needs pd0.35-test17 or later. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + + +#include "m_pd.h" + +#include +#include +#include +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#else +#include +#endif + +#define STRBUF_SIZE 32 /* maximum numbers of characters to read */ +#define DEFPOLLTIME 20 /* check for input every 20 ms */ + +static char *version = "netclient v0.1, written by Olaf Matthes "; + +static t_class *netclient_class; + +typedef struct _netclient +{ + t_object x_obj; + t_clock *x_clock; + t_clock *x_poll; + t_outlet *x_outdata; + t_outlet *x_outconnect; + int x_fd; + char *x_hostname; + int x_connectstate; + int x_port; + int x_protocol; + /* multithread stuff */ + pthread_t x_threadid; /* id of child thread */ + pthread_attr_t x_threadattr; /* attributes of child thread */ +} t_netclient; + +static void sys_sockerror(char *s) +{ +#ifdef NT + int err = WSAGetLastError(); + if (err == 10054) return; +#endif +#ifdef UNIX + int err = errno; +#endif + post("%s: %s (%d)\n", s, strerror(err), err); +} + +static void sys_closesocket(int fd) { + +#ifdef UNIX + close(fd); +#endif +#ifdef NT + closesocket(fd); +#endif +} + + +static void netclient_tick(t_netclient *x) +{ + outlet_float(x->x_outconnect, 1); +} + +static void *netclient_child_connect(void *w) +{ + t_netclient *x = (t_netclient*) w; + struct sockaddr_in server; + struct hostent *hp; + int sockfd; + int portno = x->x_port; + if (x->x_fd >= 0) + { + error("netclient_connect: already connected"); + return (x); + } + + /* create a socket */ + sockfd = socket(AF_INET, x->x_protocol, 0); +#if 0 + fprintf(stderr, "send socket %d\n", sockfd); +#endif + if (sockfd < 0) + { + sys_sockerror("socket"); + return (x); + } + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + hp = gethostbyname(x->x_hostname); + if (hp == 0) + { + post("bad host?\n"); + return (x); + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + server.sin_port = htons((u_short)portno); + + post("connecting to port %d", portno); + /* try to connect */ + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + sys_sockerror("connecting stream socket"); + sys_closesocket(sockfd); + return (x); + } + x->x_fd = sockfd; + /* outlet_float is not threadsafe ! */ + // outlet_float(x->x_obj.ob_outlet, 1); + x->x_connectstate = 1; + /* use callback instead to set outlet */ + clock_delay(x->x_clock, 0); + return (x); +} + +static void netclient_connect(t_netclient *x, t_symbol *hostname, + t_floatarg fportno) +{ + /* we get hostname and port and pass them on + to the child thread that establishes the connection */ + x->x_hostname = hostname->s_name; + x->x_port = fportno; + x->x_connectstate = 0; + /* start child thread */ + if(pthread_create( &x->x_threadid, &x->x_threadattr, netclient_child_connect, x) < 0) + post("netclient: could not create new thread"); +} + +static void netclient_disconnect(t_netclient *x) +{ + if (x->x_fd >= 0) + { + sys_closesocket(x->x_fd); + x->x_fd = -1; + x->x_connectstate = 0; + outlet_float(x->x_outconnect, 0); + } +} + +static void netclient_send(t_netclient *x, t_symbol *s, int argc, t_atom *argv) +{ + if (x->x_fd >= 0) + { + t_binbuf *b = binbuf_new(); + char *buf, *bp; + int length, sent; + t_atom at; + binbuf_add(b, argc, argv); + SETSEMI(&at); + binbuf_add(b, 1, &at); + binbuf_gettext(b, &buf, &length); + for (bp = buf, sent = 0; sent < length;) + { + static double lastwarntime; + static double pleasewarn; + double timebefore = sys_getrealtime(); + int res = send(x->x_fd, buf, length-sent, 0); + double timeafter = sys_getrealtime(); + int late = (timeafter - timebefore > 0.005); + if (late || pleasewarn) + { + if (timeafter > lastwarntime + 2) + { + post("netclient blocked %d msec", + (int)(1000 * ((timeafter - timebefore) + pleasewarn))); + pleasewarn = 0; + lastwarntime = timeafter; + } + else if (late) pleasewarn += timeafter - timebefore; + } + if (res <= 0) + { + sys_sockerror("netclient"); + netclient_disconnect(x); + break; + } + else + { + sent += res; + bp += res; + } + } + t_freebytes(buf, length); + binbuf_free(b); + } + else error("netclient: not connected"); +} + +static void netclient_rcv(t_netclient *x) +{ + int sockfd = x->x_fd; + int ret; + char resp[STRBUF_SIZE]; + fd_set readset; + fd_set exceptset; + struct timeval ztout; + /* output data */ + t_binbuf *binbuf; + t_atom messbuf[1024]; + int msg, natom; + t_atom *at; + int i; + + if(x->x_connectstate) + { + /* check if we can read/write from/to the socket */ + FD_ZERO(&readset); + FD_ZERO(&exceptset); + FD_SET(x->x_fd, &readset ); + FD_SET(x->x_fd, &exceptset ); + + ztout.tv_sec = 0; + ztout.tv_usec = 0; + + ret = select(sockfd+1, &readset, NULL, &exceptset, &ztout); + if(ret < 0) + { + error("netclient: can not read from socket"); + sys_closesocket(sockfd); + return; + } + if(FD_ISSET(sockfd, &readset) || FD_ISSET(sockfd, &exceptset)) + { + /* read from server */ + ret = recv(sockfd, resp, STRBUF_SIZE, 0); + if(ret > 0) + { + // post("netclient: received: %s, %d bytes, %d stringlen", resp, ret, strlen(resp)); + /* convert string into atoms using a binbuf */ + binbuf = binbuf_new(); + binbuf_text(binbuf, resp, ret); //strlen(resp)); + natom = binbuf_getnatom(binbuf); /* get number of atoms */ + at = binbuf_getvec(binbuf); /* get the atoms */ + /* now split it into several parts at every A_SEMI because + we probably received more than one message at a time */ + for (msg = 0; msg < natom;) + { + int emsg; + for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA + && at[emsg].a_type != A_SEMI; emsg++); + + if (emsg > msg) + { + int ii; + for (ii = msg; ii < emsg; ii++) + if (at[ii].a_type == A_DOLLAR || at[ii].a_type == A_DOLLSYM) + { + pd_error(x, "netserver: -- got dollar sign in message"); + goto nodice; + } + if (at[msg].a_type == A_FLOAT) + { + if (emsg > msg + 1) + outlet_list(x->x_outdata, 0, emsg-msg, at + msg); + else outlet_float(x->x_outdata, at[msg].a_w.w_float); + } + else if (at[msg].a_type == A_SYMBOL) + outlet_anything(x->x_outdata, at[msg].a_w.w_symbol, + emsg-msg-1, at + msg + 1); + } + nodice: + msg = emsg + 1; + } + binbuf_free(binbuf); + } + else post("netclient: read() did not return any data"); + } + } + else post("netclient: not connected"); +} + +static void netclient_poll(t_netclient *x) +{ + if(x->x_connectstate) + netclient_rcv(x); /* try to read in case we're connected */ + clock_delay(x->x_poll, DEFPOLLTIME); /* see you later */ +} + +static void *netclient_new(t_floatarg udpflag) +{ + t_netclient *x = (t_netclient *)pd_new(netclient_class); + x->x_outdata = outlet_new(&x->x_obj, &s_anything); /* received data */ + x->x_outconnect = outlet_new(&x->x_obj, &s_float); /* connection state */ + x->x_clock = clock_new(x, (t_method)netclient_tick); + x->x_poll = clock_new(x, (t_method)netclient_poll); + x->x_fd = -1; + x->x_protocol = (udpflag != 0 ? SOCK_DGRAM : SOCK_STREAM); + /* prepare child thread */ + if(pthread_attr_init(&x->x_threadattr) < 0) + post("netclient: warning: could not prepare child thread" ); + if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0) + post("netclient: warning: could not prepare child thread" ); + clock_delay(x->x_poll, 0); /* start polling the input */ + return (x); +} + +static void netclient_free(t_netclient *x) +{ + netclient_disconnect(x); + clock_free(x->x_poll); + clock_free(x->x_clock); +} + +void netclient_setup(void) +{ + netclient_class = class_new(gensym("netclient"), (t_newmethod)netclient_new, + (t_method)netclient_free, + sizeof(t_netclient), 0, A_DEFFLOAT, 0); + class_addmethod(netclient_class, (t_method)netclient_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(netclient_class, (t_method)netclient_disconnect, gensym("disconnect"), 0); + class_addmethod(netclient_class, (t_method)netclient_send, gensym("send"), A_GIMME, 0); + class_addmethod(netclient_class, (t_method)netclient_rcv, gensym("receive"), 0); + class_addmethod(netclient_class, (t_method)netclient_rcv, gensym("rcv"), 0); + class_sethelpsymbol(netclient_class, gensym("maxlib/help-netclient.pd")); +} \ No newline at end of file diff --git a/src/netdist.c b/src/netdist.c new file mode 100644 index 0000000..4f91c2d --- /dev/null +++ b/src/netdist.c @@ -0,0 +1,304 @@ +/* -------------------------- netdist --------------------------------------- */ +/* */ +/* Distributes incoming data to a changeable list of netreceive objects. */ +/* Uses child thread to connect to clients. Thus needs pd0.35-test17 or later. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + + +#include "m_pd.h" + +#include +#include +#include +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#else +#include +#endif + +#define MAX_REC 32 + +static char *version = "netdist v0.1, written by Olaf Matthes "; + +static t_class *netdist_class; + +typedef struct _netdist +{ + t_object x_obj; + t_clock *x_clock; + int x_fd[MAX_REC]; + char *x_hostname[MAX_REC]; + int x_numconnect; + int x_port[MAX_REC]; + int x_protocol; + /* multithread stuff */ + pthread_t x_threadid; /* id of child thread */ + pthread_attr_t x_threadattr; /* attributes of child thread */ +} t_netdist; + +static void sys_sockerror(char *s) +{ +#ifdef NT + int err = WSAGetLastError(); + if (err == 10054) return; +#endif +#ifdef UNIX + int err = errno; +#endif + post("%s: %s (%d)\n", s, strerror(err), err); +} + +static void sys_closesocket(int fd) { + +#ifdef UNIX + close(fd); +#endif +#ifdef NT + closesocket(fd); +#endif +} + + +static void netdist_tick(t_netdist *x) +{ + outlet_float(x->x_obj.ob_outlet, x->x_numconnect + 1); +} + +static void *netdist_new(t_floatarg udpflag) +{ + int i; + t_netdist *x = (t_netdist *)pd_new(netdist_class); + outlet_new(&x->x_obj, &s_float); + x->x_clock = clock_new(x, (t_method)netdist_tick); + for(i = 0; i < MAX_REC; i++)x->x_fd[i] = -1; + x->x_numconnect = -1; + x->x_protocol = (udpflag != 0 ? SOCK_DGRAM : SOCK_STREAM); +#ifndef MAXLIB + post(version); +#endif + /* prepare child thread */ + if(pthread_attr_init(&x->x_threadattr) < 0) + post("netdist: warning: could not prepare child thread" ); + if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0) + post("netdist: warning: could not prepare child thread" ); + return (x); +} + +static void *netdist_child_connect(void *w) +{ + int i; + + t_netdist *x = (t_netdist*) w; + struct sockaddr_in server; + struct hostent *hp; + int sockfd; + int portno; + i = x->x_numconnect + 1; + portno = x->x_port[i]; + /* create a socket */ + sockfd = socket(AF_INET, x->x_protocol, 0); +#if 0 + fprintf(stderr, "send socket %d\n", sockfd); +#endif + if (sockfd < 0) + { + sys_sockerror("socket"); + return (x); + } + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + hp = gethostbyname(x->x_hostname[i]); + if (hp == 0) + { + post("bad host?\n"); + return (x); + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + server.sin_port = htons((u_short)portno); + + post("connecting to port %d", portno); + /* try to connect */ + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + sys_sockerror("connecting stream socket"); + sys_closesocket(sockfd); + return (x); + } + x->x_fd[i] = sockfd; + /* outlet_float is not threadsafe ! */ + // outlet_float(x->x_obj.ob_outlet, 1); + x->x_numconnect++; /* count connection */ + /* use callback instead to set outlet */ + clock_delay(x->x_clock, 0); + return (x); +} + +static void netdist_connect(t_netdist *x, t_symbol *hostname, + t_floatarg fportno) +{ + int i; + /* we get hostname and port and pass them on + to the child thread that establishes the connection */ + for(i = 0; i <= x->x_numconnect; i++) + { /* check if we are already connected */ + if (hostname->s_name == x->x_hostname[i] && fportno == x->x_port[i]) + { + error("netdist_connect: already connected"); + return; + } + } + x->x_hostname[x->x_numconnect + 1] = hostname->s_name; + x->x_port[x->x_numconnect + 1] = fportno; + + /* start child thread */ + if(pthread_create( &x->x_threadid, &x->x_threadattr, netdist_child_connect, x) < 0) + post("netdist: could not create new thread"); +} + +static void netdist_disconnect(t_netdist *x, t_symbol *hostname, t_floatarg port) +{ + int i, j; + for(i = 0; i <= x->x_numconnect; i++) + { + if((hostname->s_name == x->x_hostname[i]) && ((int)port == x->x_port[i])) + { + /* search for connection */ + if (x->x_fd[i] >= 0) + { + sys_closesocket(x->x_fd[i]); + x->x_fd[i] = -1; + x->x_numconnect--; + outlet_float(x->x_obj.ob_outlet, x->x_numconnect + 1); + for(j = i; j <= x->x_numconnect; j++) + { + x->x_hostname[j] = x->x_hostname[j + 1]; + x->x_port[j] = x->x_port[j + 1]; + x->x_fd[j] = x->x_fd[j + 1]; + } + } + } + } +} + +static void netdist_send(t_netdist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i = 0; + + for(i = 0; i <= x->x_numconnect; i++) + { + if (x->x_fd[i] >= 0) + { + t_binbuf *b = binbuf_new(); + char *buf, *bp; + int length, sent; + t_atom at; + binbuf_add(b, argc, argv); + SETSEMI(&at); + binbuf_add(b, 1, &at); + binbuf_gettext(b, &buf, &length); + for (bp = buf, sent = 0; sent < length;) + { + static double lastwarntime; + static double pleasewarn; + double timebefore = clock_getlogicaltime(); + int res = send(x->x_fd[i], buf, length-sent, 0); + double timeafter = clock_getlogicaltime(); + int late = (timeafter - timebefore > 0.005); + if (late || pleasewarn) + { + if (timeafter > lastwarntime + 2) + { + post("netdist blocked %d msec", + (int)(1000 * ((timeafter - timebefore) + pleasewarn))); + pleasewarn = 0; + lastwarntime = timeafter; + } + else if (late) pleasewarn += timeafter - timebefore; + } + if (res <= 0) + { + sys_sockerror("netdist"); + netdist_disconnect(x, gensym(x->x_hostname[i]), x->x_port[i]); + break; + } + else + { + sent += res; + bp += res; + } + } + t_freebytes(buf, length); + binbuf_free(b); + } + } + if(x->x_numconnect == -1) error("netdist: not connected"); +} + + /* disconnect all */ +static void netdist_clear(t_netdist *x) +{ + int i, j, n; + n = x->x_numconnect; + for (i = n; i >= 0; i--) + { + netdist_disconnect(x, gensym(x->x_hostname[i]), x->x_port[i]); + } +} + +static void netdist_print(t_netdist *x) +{ + int i; + post("netdist: %d connection(s) established:", x->x_numconnect + 1); + for (i = x->x_numconnect; i >= 0; i--) + { + post(" \"%s\", port %d",x->x_hostname[i], x->x_port[i]); + } +} + +static void netdist_free(t_netdist *x) +{ + netdist_clear(x); + clock_free(x->x_clock); +} + +void netdist_setup(void) +{ + netdist_class = class_new(gensym("netdist"), (t_newmethod)netdist_new, + (t_method)netdist_free, sizeof(t_netdist), 0, A_DEFFLOAT, 0); + class_addmethod(netdist_class, (t_method)netdist_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(netdist_class, (t_method)netdist_disconnect, gensym("disconnect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(netdist_class, (t_method)netdist_send, gensym("send"), A_GIMME, 0); + class_addmethod(netdist_class, (t_method)netdist_clear, gensym("clear"), 0); + class_addmethod(netdist_class, (t_method)netdist_print, gensym("print"), 0); + class_sethelpsymbol(netdist_class, gensym("maxlib/help-netdist.pd")); +} + + + diff --git a/src/netrec.c b/src/netrec.c new file mode 100644 index 0000000..0b5d87e --- /dev/null +++ b/src/netrec.c @@ -0,0 +1,435 @@ +/* -------------------------- netrec ---------------------------------------- */ +/* */ +/* A 'netreceive' that tells the IP of the connecting netsend. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_imp.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#else +#include +#include +#include +#endif + +#define MAX_CONNECT 32 /* maximum number of connections */ +#define INBUFSIZE 4096 /* size of receiving data buffer */ + +static char *version = "netrec v0.1, written by Olaf Matthes "; + +/* ----------------------------- netrec ------------------------- */ + +static t_class *netrec_class; +static t_binbuf *inbinbuf; + +typedef void (*t_netrec_socketnotifier)(void *x); +typedef void (*t_netrec_socketreceivefn)(void *x, t_binbuf *b); + +typedef struct _netrec +{ + t_object x_obj; + t_outlet *x_msgout; + t_outlet *x_connectout; + t_outlet *x_clientno; + t_outlet *x_connectionip; + t_symbol *x_host[MAX_CONNECT]; + t_int x_fd[MAX_CONNECT]; + t_int x_sock_fd; + int x_connectsocket; + int x_nconnections; + int x_udp; +} t_netrec; + +typedef struct _netrec_socketreceiver +{ + char *sr_inbuf; + int sr_inhead; + int sr_intail; + void *sr_owner; + int sr_udp; + t_netrec_socketnotifier sr_notifier; + t_netrec_socketreceivefn sr_socketreceivefn; +} t_netrec_socketreceiver; + +static t_netrec_socketreceiver *netrec_socketreceiver_new(void *owner, t_netrec_socketnotifier notifier, + t_netrec_socketreceivefn socketreceivefn, int udp) +{ + t_netrec_socketreceiver *x = (t_netrec_socketreceiver *)getbytes(sizeof(*x)); + x->sr_inhead = x->sr_intail = 0; + x->sr_owner = owner; + x->sr_notifier = notifier; + x->sr_socketreceivefn = socketreceivefn; + x->sr_udp = udp; + if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_netrec_socketreceiver"); + return (x); +} + + /* this is in a separately called subroutine so that the buffer isn't + sitting on the stack while the messages are getting passed. */ +static int netrec_socketreceiver_doread(t_netrec_socketreceiver *x) +{ + char messbuf[INBUFSIZE], *bp = messbuf; + int indx; + int inhead = x->sr_inhead; + int intail = x->sr_intail; + char *inbuf = x->sr_inbuf; + if (intail == inhead) return (0); + for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1)) + { + char c = *bp++ = inbuf[indx]; + if (c == ';' && (!indx || inbuf[indx-1] != '\\')) + { + intail = (indx+1)&(INBUFSIZE-1); + binbuf_text(inbinbuf, messbuf, bp - messbuf); + x->sr_inhead = inhead; + x->sr_intail = intail; + return (1); + } + } + return (0); +} + +static void netrec_socketreceiver_getudp(t_netrec_socketreceiver *x, int fd) +{ + char buf[INBUFSIZE+1]; + int ret = recv(fd, buf, INBUFSIZE, 0); + if (ret < 0) + { + sys_sockerror("recv"); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else if (ret > 0) + { + buf[ret] = 0; +#if 0 + post("%s", buf); +#endif + if (buf[ret-1] != '\n') + { +#if 0 + buf[ret] = 0; + error("dropped bad buffer %s\n", buf); +#endif + } + else + { + char *semi = strchr(buf, ';'); + if (semi) + *semi = 0; + binbuf_text(inbinbuf, buf, strlen(buf)); + outlet_setstacklim(); + if (x->sr_socketreceivefn) + (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf); + else bug("netrec_socketreceiver_getudp"); + } + } +} + +static void netrec_socketreceiver_read(t_netrec_socketreceiver *x, int fd) +{ + if (x->sr_udp) /* UDP ("datagram") socket protocol */ + netrec_socketreceiver_getudp(x, fd); + else /* TCP ("streaming") socket protocol */ + { + char *semi; + int readto = + (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1); + int ret; + + t_netrec *y = x->sr_owner; + + y->x_sock_fd = fd; + + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->sr_inhead) + { + fprintf(stderr, "netrec: dropped message"); + x->sr_inhead = x->sr_intail = 0; + readto = INBUFSIZE; + } + else + { + ret = recv(fd, x->sr_inbuf + x->sr_inhead, + readto - x->sr_inhead, 0); + if (ret < 0) + { + sys_sockerror("recv"); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else if (ret == 0) + { + post("netrec: connection closed on socket %d", fd); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else + { + x->sr_inhead += ret; + if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0; + while (netrec_socketreceiver_doread(x)) + { + outlet_setstacklim(); + if (x->sr_socketreceivefn) + (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf); + else binbuf_eval(inbinbuf, 0, 0, 0); + } + } + } + } +} + +static void netrec_socketreceiver_free(t_netrec_socketreceiver *x) +{ + free(x->sr_inbuf); + freebytes(x, sizeof(*x)); +} + +/* ---------------- main netrec stuff --------------------- */ + +static void netrec_notify(t_netrec *x) +{ + int i, k; + /* remove connection from list */ + for(i = 0; i < x->x_nconnections; i++) + { + if(x->x_fd[i] == x->x_sock_fd) + { + x->x_nconnections--; + post("netrec: \"%s\" removed from list of clients", x->x_host[i]->s_name); + x->x_host[i] = NULL; /* delete entry */ + x->x_fd[i] = -1; + /* rearrange list now: move entries to close the gap */ + for(k = i; k < x->x_nconnections; k++) + { + x->x_host[k] = x->x_host[k + 1]; + x->x_fd[k] = x->x_fd[k + 1]; + } + } + } + outlet_float(x->x_connectout, x->x_nconnections); +} + +static void netrec_doit(void *z, t_binbuf *b) +{ + t_atom messbuf[1024]; + t_netrec *x = (t_netrec *)z; + int msg, natom = binbuf_getnatom(b); + t_atom *at = binbuf_getvec(b); + int i; + /* output clients IP and socket no */ + for(i = 0; i < x->x_nconnections; i++) + { + if(x->x_fd[i] == x->x_sock_fd) + { + outlet_symbol(x->x_connectionip, x->x_host[i]); + break; + } + } + outlet_float(x->x_clientno, x->x_sock_fd); + /* process data */ + for (msg = 0; msg < natom;) + { + int emsg; + for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA + && at[emsg].a_type != A_SEMI; emsg++); + + if (emsg > msg) + { + int ii; + for (ii = msg; ii < emsg; ii++) + if (at[ii].a_type == A_DOLLAR || at[ii].a_type == A_DOLLSYM) + { + pd_error(x, "netrec: got dollar sign in message"); + goto nodice; + } + if (at[msg].a_type == A_FLOAT) + { + if (emsg > msg + 1) + outlet_list(x->x_msgout, 0, emsg-msg, at + msg); + else outlet_float(x->x_msgout, at[msg].a_w.w_float); + } + else if (at[msg].a_type == A_SYMBOL) + outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, + emsg-msg-1, at + msg + 1); + } + nodice: + msg = emsg + 1; + } +} + +static void netrec_connectpoll(t_netrec *x) +{ + struct sockaddr_in incomer_address; + int sockaddrl = (int) sizeof( struct sockaddr ); + int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl); + if (fd < 0) post("netrec: accept failed"); + else + { + t_netrec_socketreceiver *y = netrec_socketreceiver_new((void *)x, + (t_netrec_socketnotifier)netrec_notify, + (x->x_msgout ? netrec_doit : 0), 0); + sys_addpollfn(fd, (t_fdpollfn)netrec_socketreceiver_read, y); + x->x_nconnections++; + x->x_host[x->x_nconnections - 1] = gensym(inet_ntoa(incomer_address.sin_addr)); + x->x_fd[x->x_nconnections - 1] = fd; + + // outlet_symbol( x->x_connectionip, x->x_host[x->x_nconnections - 1]); + post("netrec: accepted connection from %s on socket %d", + x->x_host[x->x_nconnections - 1]->s_name, x->x_fd[x->x_nconnections - 1]); + outlet_float(x->x_connectout, x->x_nconnections); + } +} + +static void netrec_print(t_netrec *x) +{ + int i; + if(x->x_nconnections > 0) + { + post("netrec: %d open connections:", x->x_nconnections); + + for(i = 0; i < x->x_nconnections; i++) + { + post(" \"%s\" on socket %d", + x->x_host[i]->s_name, x->x_fd[i]); + } + } else post("netrec: no open connections"); +} + +static void *netrec_new(t_symbol *compatflag, + t_floatarg fportno, t_floatarg udpflag) +{ + t_netrec *x; + int i; + struct sockaddr_in server; + int sockfd, portno = fportno, udp = (udpflag != 0); + int old = !strcmp(compatflag->s_name , "old"); + /* create a socket */ + sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0); +#if 1 + post("netrec: receive socket %d\n", sockfd); +#endif + if (sockfd < 0) + { + sys_sockerror("socket"); + return (0); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + +#ifdef IRIX + /* this seems to work only in IRIX but is unnecessary in + Linux. Not sure what NT needs in place of this. */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) + post("setsockopt failed\n"); +#endif + + /* assign server port number */ + server.sin_port = htons((u_short)portno); + + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) + { + sys_sockerror("bind"); + sys_closesocket(sockfd); + return (0); + } + x = (t_netrec *)pd_new(netrec_class); + if (old) + { + /* old style, nonsecure version */ + x->x_msgout = 0; + } + else x->x_msgout = outlet_new(&x->x_obj, &s_anything); + + if (udp) /* datagram protocol */ + { + t_netrec_socketreceiver *y = netrec_socketreceiver_new((void *)x, + (t_netrec_socketnotifier)netrec_notify, + (x->x_msgout ? netrec_doit : 0), 1); + sys_addpollfn(sockfd, (t_fdpollfn)netrec_socketreceiver_read, y); + x->x_connectout = 0; + } + else /* streaming protocol */ + { + if (listen(sockfd, 5) < 0) + { + sys_sockerror("listen"); + sys_closesocket(sockfd); + sockfd = -1; + } + else + { + sys_addpollfn(sockfd, (t_fdpollfn)netrec_connectpoll, x); + x->x_connectout = outlet_new(&x->x_obj, &s_float); + x->x_clientno = outlet_new(&x->x_obj, &s_float); + x->x_connectionip = outlet_new(&x->x_obj, &s_symbol); + inbinbuf = binbuf_new(); + } + } + x->x_connectsocket = sockfd; + x->x_nconnections = 0; + x->x_udp = udp; + for(i = 0; i < MAX_CONNECT; i++)x->x_fd[i] = -1; +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +static void netrec_free(t_netrec *x) +{ + /* LATER make me clean up open connections */ + if (x->x_connectsocket >= 0) + { + sys_rmpollfn(x->x_connectsocket); + sys_closesocket(x->x_connectsocket); + } + binbuf_free(inbinbuf); +} + +void netrec_setup(void) +{ + netrec_class = class_new(gensym("netrec"),(t_newmethod)netrec_new, (t_method)netrec_free, + sizeof(t_netrec), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFSYM, 0); + class_addmethod(netrec_class, (t_method)netrec_print, gensym("print"), 0); + class_sethelpsymbol(netrec_class, gensym("maxlib/help-netrec.pd")); +} diff --git a/src/netserver.c b/src/netserver.c new file mode 100644 index 0000000..199b7ee --- /dev/null +++ b/src/netserver.c @@ -0,0 +1,563 @@ +/* -------------------------- netserver ------------------------------------- */ +/* */ +/* A server for bidirectional communication from within Pd. */ +/* Allows to send back data to specific clients connected to the server. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_imp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#else +#include +#include +#include +#endif + +#define MAX_CONNECT 32 /* maximum number of connections */ +#define INBUFSIZE 4096 /* size of receiving data buffer */ + +static char *version = "netserver v0.1 :: bidirectional communication for Pd\n" + " written by Olaf Matthes "; + +/* ----------------------------- netserver ------------------------- */ + +static t_class *netserver_class; +static t_binbuf *inbinbuf; + +typedef void (*t_netserver_socketnotifier)(void *x); +typedef void (*t_netserver_socketreceivefn)(void *x, t_binbuf *b); + +typedef struct _netserver +{ + t_object x_obj; + t_outlet *x_msgout; + t_outlet *x_connectout; + t_outlet *x_clientno; + t_outlet *x_connectionip; + t_symbol *x_host[MAX_CONNECT]; + t_int x_fd[MAX_CONNECT]; + t_int x_sock_fd; + t_int x_connectsocket; + t_int x_nconnections; +} t_netserver; + +typedef struct _netserver_socketreceiver +{ + char *sr_inbuf; + int sr_inhead; + int sr_intail; + void *sr_owner; + t_netserver_socketnotifier sr_notifier; + t_netserver_socketreceivefn sr_socketreceivefn; +} t_netserver_socketreceiver; + +static t_netserver_socketreceiver *netserver_socketreceiver_new(void *owner, t_netserver_socketnotifier notifier, + t_netserver_socketreceivefn socketreceivefn) +{ + t_netserver_socketreceiver *x = (t_netserver_socketreceiver *)getbytes(sizeof(*x)); + x->sr_inhead = x->sr_intail = 0; + x->sr_owner = owner; + x->sr_notifier = notifier; + x->sr_socketreceivefn = socketreceivefn; + if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_netserver_socketreceiver"); + return (x); +} + + /* this is in a separately called subroutine so that the buffer isn't + sitting on the stack while the messages are getting passed. */ +static int netserver_socketreceiver_doread(t_netserver_socketreceiver *x) +{ + char messbuf[INBUFSIZE], *bp = messbuf; + int indx; + int inhead = x->sr_inhead; + int intail = x->sr_intail; + char *inbuf = x->sr_inbuf; + if (intail == inhead) return (0); + for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1)) + { + char c = *bp++ = inbuf[indx]; + if (c == ';' && (!indx || inbuf[indx-1] != '\\')) + { + intail = (indx+1)&(INBUFSIZE-1); + binbuf_text(inbinbuf, messbuf, bp - messbuf); + x->sr_inhead = inhead; + x->sr_intail = intail; + return (1); + } + } + return (0); +} + +static void netserver_socketreceiver_read(t_netserver_socketreceiver *x, int fd) +{ + char *semi; + int readto = (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1); + int ret; + + t_netserver *y = x->sr_owner; + + y->x_sock_fd = fd; + + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->sr_inhead) + { + post("netserver: dropped message"); + x->sr_inhead = x->sr_intail = 0; + readto = INBUFSIZE; + } + else + { + ret = recv(fd, x->sr_inbuf + x->sr_inhead, + readto - x->sr_inhead, 0); + if (ret < 0) + { + sys_sockerror("recv"); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else if (ret == 0) + { + post("netserver: << connection closed on socket %d", fd); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else + { + x->sr_inhead += ret; + if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0; + while (netserver_socketreceiver_doread(x)) + { + outlet_setstacklim(); + if (x->sr_socketreceivefn) + (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf); + else binbuf_eval(inbinbuf, 0, 0, 0); + } + } + } +} + +static void netserver_socketreceiver_free(t_netserver_socketreceiver *x) +{ + free(x->sr_inbuf); + freebytes(x, sizeof(*x)); +} + +/* ---------------- main netserver (send) stuff --------------------- */ + +/* send message to client using socket number */ +static void netserver_send(t_netserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int sockfd, client = -1, i; + if(x->x_nconnections < 0) + { + post("netserver: no clients connected"); + return; + } + if(argc < 2) + { + post("netserver: nothing to send"); + return; + } + /* get socket number of connection (first element in list) */ + if(argv[0].a_type == A_FLOAT) + { + sockfd = atom_getfloatarg(0, argc, argv); + for(i = 0; i < x->x_nconnections; i++) /* check if connection exists */ + { + if(x->x_fd[i] == sockfd) + { + client = i; /* the client we're sending to */ + break; + } + } + if(client == -1) + { + post("netserver: no connection on socket %d", sockfd); + return; + } + } + else + { + post("netserver: no socket specified"); + return; + } + /* process & send data */ + if(sockfd > 0) + { + t_binbuf *b = binbuf_new(); + char *buf, *bp; + int length, sent; + t_atom at; + binbuf_add(b, argc - 1, argv + 1); /* skip first element */ + SETSEMI(&at); + binbuf_add(b, 1, &at); + binbuf_gettext(b, &buf, &length); + + post("netserver: sending data to client %d on socket %d", client + 1, sockfd); + // post("netserver: sending \"%s\"", buf); + + for (bp = buf, sent = 0; sent < length;) + { + static double lastwarntime; + static double pleasewarn; + double timebefore = clock_getlogicaltime(); + int res = send(sockfd, buf, length-sent, 0); + double timeafter = clock_getlogicaltime(); + int late = (timeafter - timebefore > 0.005); + if (late || pleasewarn) + { + if (timeafter > lastwarntime + 2) + { + post("netserver blocked %d msec", + (int)(1000 * ((timeafter - timebefore) + pleasewarn))); + pleasewarn = 0; + lastwarntime = timeafter; + } + else if (late) pleasewarn += timeafter - timebefore; + } + if (res <= 0) + { + sys_sockerror("netserver"); + post("netserver: could not send data to client"); + break; + } + else + { + sent += res; + bp += res; + } + } + t_freebytes(buf, length); + binbuf_free(b); + } + else post("netserver: not a valid socket number (%d)", sockfd); +} + +/* send message to client using client number + note that the client numbers might change in case a client disconnects! */ +static void netserver_client_send(t_netserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int sockfd, client; + if(x->x_nconnections < 0) + { + post("netserver: no clients connected"); + return; + } + if(argc < 2) + { + post("netserver: nothing to send"); + return; + } + /* get number of client (first element in list) */ + if(argv[0].a_type == A_FLOAT) + client = atom_getfloatarg(0, argc, argv); + else + { + post("netserver: no client specified"); + return; + } + sockfd = x->x_fd[client - 1]; /* get socket number for that client */ + + /* process & send data */ + if(sockfd > 0) + { + t_binbuf *b = binbuf_new(); + char *buf, *bp; + int length, sent; + t_atom at; + binbuf_add(b, argc - 1, argv + 1); /* skip first element */ + SETSEMI(&at); + binbuf_add(b, 1, &at); + binbuf_gettext(b, &buf, &length); + + post("netserver: sending data to client %d on socket %d", client, sockfd); + // post("netserver: >> sending \"%s\"", buf); + + for (bp = buf, sent = 0; sent < length;) + { + static double lastwarntime; + static double pleasewarn; + double timebefore = clock_getlogicaltime(); + int res = send(sockfd, buf, length-sent, 0); + double timeafter = clock_getlogicaltime(); + int late = (timeafter - timebefore > 0.005); + if (late || pleasewarn) + { + if (timeafter > lastwarntime + 2) + { + post("netserver blocked %d msec", + (int)(1000 * ((timeafter - timebefore) + pleasewarn))); + pleasewarn = 0; + lastwarntime = timeafter; + } + else if (late) pleasewarn += timeafter - timebefore; + } + if (res <= 0) + { + sys_sockerror("netserver"); + post("netserver: could not send data to cient"); + break; + } + else + { + sent += res; + bp += res; + } + } + t_freebytes(buf, length); + binbuf_free(b); + } + else post("netserver: not a valid socket number (%d)", sockfd); +} + + /* broadcasts a message to all connected clients */ +static void netserver_broadcast(t_netserver *x, t_symbol *s, int argc, t_atom *argv) +{ + int i, client = x->x_nconnections; /* number of clients to send to */ + t_atom at[256]; + for(i = 0; i < argc; i++) + { + at[i + 1] = argv[i]; + } + argc++; + /* enumerate through the clients and send each the message */ + while(client--) + { + SETFLOAT(at, client + 1); /* prepend number of client */ + netserver_client_send(x, s, argc, at); + } +} + + +/* ---------------- main netserver (receive) stuff --------------------- */ + +static void netserver_notify(t_netserver *x) +{ + int i, k; + /* remove connection from list */ + for(i = 0; i < x->x_nconnections; i++) + { + if(x->x_fd[i] == x->x_sock_fd) + { + x->x_nconnections--; + post("netserver: \"%s\" removed from list of clients", x->x_host[i]->s_name); + x->x_host[i] = NULL; /* delete entry */ + x->x_fd[i] = -1; + /* rearrange list now: move entries to close the gap */ + for(k = i; k < x->x_nconnections; k++) + { + x->x_host[k] = x->x_host[k + 1]; + x->x_fd[k] = x->x_fd[k + 1]; + } + } + } + outlet_float(x->x_connectout, x->x_nconnections); +} + +static void netserver_doit(void *z, t_binbuf *b) +{ + t_atom messbuf[1024]; + t_netserver *x = (t_netserver *)z; + int msg, natom = binbuf_getnatom(b); + t_atom *at = binbuf_getvec(b); + int i; + /* output clients IP and socket no. */ + for(i = 0; i < x->x_nconnections; i++) /* search for corresponding IP */ + { + if(x->x_fd[i] == x->x_sock_fd) + { + outlet_symbol(x->x_connectionip, x->x_host[i]); + break; + } + } + outlet_float(x->x_clientno, x->x_sock_fd); /* the socket number */ + /* process data */ + for (msg = 0; msg < natom;) + { + int emsg; + for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA + && at[emsg].a_type != A_SEMI; emsg++); + + if (emsg > msg) + { + int ii; + for (ii = msg; ii < emsg; ii++) + if (at[ii].a_type == A_DOLLAR || at[ii].a_type == A_DOLLSYM) + { + pd_error(x, "netserver: got dollar sign in message"); + goto nodice; + } + if (at[msg].a_type == A_FLOAT) + { + if (emsg > msg + 1) + outlet_list(x->x_msgout, 0, emsg-msg, at + msg); + else outlet_float(x->x_msgout, at[msg].a_w.w_float); + } + else if (at[msg].a_type == A_SYMBOL) + outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, + emsg-msg-1, at + msg + 1); + } + nodice: + msg = emsg + 1; + } +} + +static void netserver_connectpoll(t_netserver *x) +{ + struct sockaddr_in incomer_address; + int sockaddrl = (int) sizeof( struct sockaddr ); + int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl); + if (fd < 0) post("netserver: accept failed"); + else + { + t_netserver_socketreceiver *y = netserver_socketreceiver_new((void *)x, + (t_netserver_socketnotifier)netserver_notify, + (x->x_msgout ? netserver_doit : 0)); + sys_addpollfn(fd, (t_fdpollfn)netserver_socketreceiver_read, y); + x->x_nconnections++; + x->x_host[x->x_nconnections - 1] = gensym(inet_ntoa(incomer_address.sin_addr)); + x->x_fd[x->x_nconnections - 1] = fd; + + post("netserver: ** accepted connection from %s on socket %d", + x->x_host[x->x_nconnections - 1]->s_name, x->x_fd[x->x_nconnections - 1]); + outlet_float(x->x_connectout, x->x_nconnections); + } +} + +static void netserver_print(t_netserver *x) +{ + int i; + if(x->x_nconnections > 0) + { + post("netserver: %d open connections:", x->x_nconnections); + + for(i = 0; i < x->x_nconnections; i++) + { + post(" \"%s\" on socket %d", + x->x_host[i]->s_name, x->x_fd[i]); + } + } else post("netserver: no open connections"); +} + +static void *netserver_new(t_floatarg fportno) +{ + t_netserver *x; + int i; + struct sockaddr_in server; + int sockfd, portno = fportno; + /* create a socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); +#if 0 + post("netserver: receive socket %d", sockfd); +#endif + if (sockfd < 0) + { + sys_sockerror("socket"); + return (0); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + +#ifdef IRIX + /* this seems to work only in IRIX but is unnecessary in + Linux. Not sure what NT needs in place of this. */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) + post("setsockopt failed\n"); +#endif + + /* assign server port number */ + server.sin_port = htons((u_short)portno); + + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) + { + sys_sockerror("bind"); + sys_closesocket(sockfd); + return (0); + } + x = (t_netserver *)pd_new(netserver_class); + x->x_msgout = outlet_new(&x->x_obj, &s_anything); + + /* streaming protocol */ + if (listen(sockfd, 5) < 0) + { + sys_sockerror("listen"); + sys_closesocket(sockfd); + sockfd = -1; + } + else + { + sys_addpollfn(sockfd, (t_fdpollfn)netserver_connectpoll, x); + x->x_connectout = outlet_new(&x->x_obj, &s_float); + x->x_clientno = outlet_new(&x->x_obj, &s_float); + x->x_connectionip = outlet_new(&x->x_obj, &s_symbol); + inbinbuf = binbuf_new(); + } + x->x_connectsocket = sockfd; + x->x_nconnections = 0; + for(i = 0; i < MAX_CONNECT; i++)x->x_fd[i] = -1; +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +static void netserver_free(t_netserver *x) +{ + /* LATER make me clean up open connections */ + if (x->x_connectsocket >= 0) + { + sys_rmpollfn(x->x_connectsocket); + sys_closesocket(x->x_connectsocket); + } + binbuf_free(inbinbuf); +} + +void netserver_setup(void) +{ + netserver_class = class_new(gensym("netserver"),(t_newmethod)netserver_new, (t_method)netserver_free, + sizeof(t_netserver), 0, A_DEFFLOAT, 0); + class_addmethod(netserver_class, (t_method)netserver_print, gensym("print"), 0); + class_addmethod(netserver_class, (t_method)netserver_send, gensym("send"), A_GIMME, 0); + class_addmethod(netserver_class, (t_method)netserver_client_send, gensym("client"), A_GIMME, 0); + class_addmethod(netserver_class, (t_method)netserver_broadcast, gensym("broadcast"), A_GIMME, 0); + class_sethelpsymbol(netserver_class, gensym("maxlib/help-netserver.pd")); +} diff --git a/src/nroute.c b/src/nroute.c new file mode 100644 index 0000000..a0ac702 --- /dev/null +++ b/src/nroute.c @@ -0,0 +1,173 @@ +/* ------------------------- nroute ------------------------------------------ */ +/* */ +/* Route input according to Nth element. */ +/* Written by Olaf Matthes */ +/* Based on code found on the web. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +/* + inlet 1: anything to be routed + inlet 2: anything to be matched to + inlet 3: position to match + out 1: input if match found + out 2: input if match not found +*/ + +#include "m_pd.h" + +static char *version = "nroute v0.1, written by Olaf Matthes "; + +typedef struct nroute +{ + t_object x_obj; + t_outlet *out1; + t_outlet *out2; + t_int pos; + t_atom match; +} t_nroute; + +typedef struct proxy +{ + t_object obj; + t_int index; /* number of proxy inlet(s) */ + t_nroute *x; /* we'll put the other struct in here */ +} t_proxy; + + + /* this is the routine that actually does the routing / matching */ + /* it get's called by all other routines that get any input and */ + /* even handles the second (proxy) inlet ! */ +static void nroute_any(t_nroute *x, t_symbol *s, int argc, t_atom *argv) +{ + if(s) + { + if (x->pos == 1 && x->match.a_type == A_SYMBOL && x->match.a_w.w_symbol == s) + outlet_anything (x->out1,s,argc,argv); + else if (x->pos > 1 && x->pos <= argc + 1 && + argv[x->pos-2].a_type == x->match.a_type && + argv[x->pos-2].a_w.w_float == x->match.a_w.w_float) + outlet_anything (x->out1,s,argc,argv); + else outlet_anything (x->out2,s,argc,argv); + } + else + { + if (x->pos > 0 && x->pos <= argc && + argv[x->pos-1].a_type == x->match.a_type && + argv[x->pos-1].a_w.w_float == x->match.a_w.w_float) + outlet_list (x->out1,0,argc,argv); + else outlet_list (x->out2,0,argc,argv); + } +} + +static void nroute_float(t_nroute *x, float f) +{ + t_atom a; + + SETFLOAT (&a,f); + nroute_any(x,0,1,&a); +} + +static void nroute_list(t_nroute *x, t_symbol *s, int argc, t_atom *argv) +{ + nroute_any(x,0,argc,argv); +} + +static void nroute_setmatch(t_proxy *p, t_symbol *s, int argc, t_atom *argv) +{ + t_nroute *x = (t_nroute *)(p->x); + + if(argc == 0) /* have to match a symbol */ + { + x->match.a_type = A_SYMBOL; + x->match.a_w.w_symbol = s; + } + else /* got a float */ + { + if(argc > 1) + { + post("nroute: middle inlet accepts only (float,symbol) for match"); + return; + } + x->match.a_type = A_FLOAT; + x->match.a_w.w_float = argv[0].a_w.w_float; + } +} + +static void nroute_setpos(t_nroute *x, t_floatarg f) +{ + x->pos = (t_int)f; +} + +static t_class *nroute_class; +static t_class *proxy_class; + +static void *nroute_new(t_symbol *s, int argc, t_atom *argv) +{ + t_nroute *x = (t_nroute *)pd_new(nroute_class); + t_proxy *inlet = (t_proxy *)pd_new(proxy_class); /* for the proxy inlet */ + +#ifndef MAXLIB + post(version); +#endif + + inlet->x = x; /* make x visible to the proxy inlets */ + + x->pos = 1; + x->match.a_type = A_NULL; + if (argc > 2) { error ("nroute: extra arguments"); return 0; } + if (argc > 1) { + if (argv[1].a_type == A_FLOAT) x->pos = argv[1].a_w.w_float; + else { post ("nroute: second argument must be (int) position"); return 0; } + } + if (argc > 0) { + x->match.a_type = argv[0].a_type; + x->match.a_w.w_float = argv[0].a_w.w_float; + } + inlet->index = 0; /* we are going to create a proxy inlet no. 0 */ + /* it belongs to the object t_nroute but the destination is t_proxy */ + inlet_new(&x->x_obj, &inlet->obj.ob_pd, 0,0); + /* and now a 'normal' third inlet */ + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("right")); + x->out1 = outlet_new(&x->x_obj, gensym("list")); + x->out2 = outlet_new(&x->x_obj, gensym("list")); + return (x); +} + +void nroute_setup(void) +{ + /* the object's class: */ + nroute_class = class_new(gensym("nroute"), (t_newmethod)nroute_new, + 0, sizeof(t_nroute), 0, A_GIMME, 0); + /* a class for the proxy inlet: */ + proxy_class = class_new(gensym("proxy"), NULL, NULL, sizeof(t_proxy), + CLASS_PD|CLASS_NOINLET, A_NULL); + + class_addmethod(nroute_class, (t_method)nroute_setpos, gensym("right"), A_FLOAT, 0); + class_addfloat(nroute_class, nroute_float); + class_addlist(nroute_class, nroute_list); + class_addanything(nroute_class, nroute_any); + class_addanything(proxy_class, nroute_setmatch); +#ifndef MAXLIB + class_sethelpsymbol(nroute_class, gensym("help-nroute.pd")); +#else + class_sethelpsymbol(nroute_class, gensym("maxlib/help-nroute.pd")); +#endif +} \ No newline at end of file diff --git a/src/pitch.c b/src/pitch.c new file mode 100644 index 0000000..ce2f426 --- /dev/null +++ b/src/pitch.c @@ -0,0 +1,106 @@ +/* ------------------------- pitch ------------------------------------------ */ +/* */ +/* Get a lot of info about an incoming pitch (class, register, interval...). */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +static char *version = "pitch v0.1b, written by Olaf Matthes "; + +typedef struct pitch +{ + t_object x_ob; + t_inlet *x_inpitch; /* inlet for pitch */ + t_outlet *x_outpitchval; /* pitch as MIDI note number */ + t_outlet *x_outpitchname; /* pitch name, e.g. "C1" */ + t_outlet *x_outpitchclass; /* pitch class */ + t_outlet *x_outintv; /* interval */ + t_outlet *x_outregister; /* register */ + + t_int x_lastpitch; + +} t_pitch; + +static void pitch_float(t_pitch *x, t_floatarg f) { + + char buf[8]; + int r, c, interval = 0, pitch; + + char* notes_up[12] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; + char* notes_down[12] = {"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"}; + + pitch = (t_int)f; + if(pitch < 1) pitch = 0; + if(pitch > 127) pitch = 127; + + if(x->x_lastpitch != 0)interval = pitch - x->x_lastpitch; + x->x_lastpitch = pitch; + + r = (pitch / 12) - 1; + c = pitch % 12; + if(interval >= 0) + { + sprintf(buf, "%s%d", notes_up[c], r); + } + else + { + sprintf(buf, "%s%d", notes_down[c], r); + } + // post("note: %s %d", notes[c], r); + + /* output values from right to left */ + outlet_float(x->x_outregister, r); + outlet_float(x->x_outintv, interval); + outlet_float(x->x_outpitchclass, c); + outlet_symbol(x->x_outpitchname, gensym(buf)); + outlet_float(x->x_outpitchval, pitch); +} + +static t_class *pitch_class; + +static void *pitch_new(t_floatarg f) +{ + t_pitch *x = (t_pitch *)pd_new(pitch_class); + x->x_inpitch = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_outpitchval = outlet_new(&x->x_ob, gensym("float")); + x->x_outpitchname = outlet_new(&x->x_ob, gensym("symbol")); + x->x_outpitchclass = outlet_new(&x->x_ob, gensym("float")); + x->x_outintv = outlet_new(&x->x_ob, gensym("float")); + x->x_outregister = outlet_new(&x->x_ob, gensym("float")); + + x->x_lastpitch = f; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void pitch_setup(void) +{ + pitch_class = class_new(gensym("pitch"), (t_newmethod)pitch_new, + 0, sizeof(t_pitch), 0, A_DEFFLOAT, 0); + class_addfloat(pitch_class, pitch_float); + class_sethelpsymbol(pitch_class, gensym("maxlib/help-pitch.pd")); +} + diff --git a/src/plus.c b/src/plus.c new file mode 100644 index 0000000..279ff5b --- /dev/null +++ b/src/plus.c @@ -0,0 +1,100 @@ +/* --------------------------- plus ------------------------------------------ */ +/* */ +/* Like '+', but calculates output whenever _any_ of the inlets changes. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +#define MAXSIZE 32 + +static char *version = "plus v0.2, written by Olaf Matthes "; + +typedef struct plus +{ + t_object x_ob; + t_inlet *x_inleft; /* leftmost inlet */ + t_inlet *x_inright; /* right inlet */ + t_outlet *x_outlet; /* result */ + t_int x_numvalues; /* number of values / inlets */ + + t_float x_plusvalue[MAXSIZE]; + +} t_plus; + +static void plus_bang(t_plus *x) +{ + int i; + t_float result = x->x_plusvalue[0]; + for(i = 1; i < x->x_numvalues; i++) + result += x->x_plusvalue[i]; + outlet_float(x->x_outlet, result); +} + +static void plus_float(t_plus *x, t_floatarg f) +{ + x->x_plusvalue[0] = f; + plus_bang(x); /* calculate result */ +} + +static void plus_ft1(t_plus *x, t_floatarg f) +{ + x->x_plusvalue[1] = f; + plus_bang(x); /* calculate result */ +} + +static t_class *plus_class; + +static void *plus_new(t_symbol *s, t_int argc, t_atom* argv) +{ + int i; + + t_plus *x = (t_plus *)pd_new(plus_class); + x->x_inright = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + for(i = 2; i < argc; i++) /* create additional inlets, if any */ + { + floatinlet_new(&x->x_ob, &x->x_plusvalue[i]); + } + x->x_outlet = outlet_new(&x->x_ob, gensym("float")); + + for(i = 0; i < argc; i++) + { + x->x_plusvalue[i] = atom_getfloatarg(i, argc, argv);; + } + x->x_numvalues = i; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void plus_setup(void) +{ + plus_class = class_new(gensym("plus"), (t_newmethod)plus_new, + 0, sizeof(t_plus), 0, A_GIMME, 0); + class_addfloat(plus_class, plus_float); + class_addmethod(plus_class, (t_method)plus_ft1, gensym("ft1"), A_FLOAT, 0); + class_addbang(plus_class, (t_method)plus_bang); + class_sethelpsymbol(plus_class, gensym("maxlib/help-plus.pd")); +} + diff --git a/src/poisson.c b/src/poisson.c new file mode 100644 index 0000000..58e9359 --- /dev/null +++ b/src/poisson.c @@ -0,0 +1,82 @@ +/* ---------------------------- rand_poisson ---------------------------------- */ +/* */ +/* rand_poisson generates a poisson distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX +#ifndef M_PI +#define M_PI 3.1415927 +#endif + +static char *version = "poisson v0.1, generates a poisson distributed random variable\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_poisson ------------------------------ */ + +static t_class *rand_poisson_class; + +typedef struct _rand_poisson +{ + t_object x_obj; + t_float x_lambda; +} t_rand_poisson; + +static void *rand_poisson_new(t_floatarg f) +{ + t_rand_poisson *x = (t_rand_poisson *)pd_new(rand_poisson_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_lambda); + outlet_new(&x->x_obj, &s_float); + x->x_lambda = f; + return (x); +} + +static void rand_poisson_bang(t_rand_poisson *x) +{ + t_float u, v; + t_int n = 0; + v = exp(-x->x_lambda); + u = fran(); + while(u > v) + { + u *= fran(); + n++; + } + outlet_float(x->x_obj.ob_outlet, n); +} + +void poisson_setup(void) +{ + rand_poisson_class = class_new(gensym("poisson"), (t_newmethod)rand_poisson_new, 0, + sizeof(t_rand_poisson), 0, A_DEFFLOAT, 0); + class_addbang(rand_poisson_class, rand_poisson_bang); + class_sethelpsymbol(rand_poisson_class, gensym("maxlib/help-poisson.pd")); +#ifndef MAXLIB + post(version); +#endif +} diff --git a/src/pong.c b/src/pong.c new file mode 100644 index 0000000..1db14f2 --- /dev/null +++ b/src/pong.c @@ -0,0 +1,327 @@ +/* --------------------------- pong ------------------------------------------ */ +/* */ +/* Route input according to Nth element. */ +/* Written by Olaf Matthes */ +/* Based on pong (for Max) version 1.5 written by Richard Dudas. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +static char *version = "pong v0.1, ported by Olaf Matthes \n" + " written for Max by Richard Dudas"; + +typedef struct pong +{ + t_object x_obj; + t_outlet *p_bounceout; + t_outlet *p_handout; + t_outlet *p_velout; + t_outlet *p_heightout; + t_clock *p_klok; + + t_int p_ms; // ms count + t_float p_time; // current time div by warp + t_float p_timegrain; // timegrain in seconds + t_float p_timegrainin; // timegrain in ms + t_float p_warp; // timewarp in ms + + t_float p_dinit; // init distance + t_float p_vinit; // init velocity + t_float p_ainit; // base acceleration def -100 + t_float p_damping; // realtime multiplicative damping + t_float p_dhand; // virtual hand distance + t_float p_force; // force of hand 1.0 = no force + + t_float p_accel; // current accel value + t_float p_vi; // current velocity + t_float p_di; // current distance + t_float p_dt; // distance out + t_float p_dtprev; // previous distance out for accel computation + t_float p_vt; // velocity out + t_int p_prevchg; // for logical transition +} t_pong; + +/* ---------------------------------------------------------- */ +/* ---- this stuff is mainly for the timer, on off etc... --- */ +/* ---------------------------------------------------------- */ + +static void pong_reset(t_pong *x) +{ + x->p_di = x->p_dinit; + x->p_dt = x->p_dinit; // added + x->p_dtprev = x->p_dinit; // added + x->p_vi = x->p_vinit; + x->p_vt = x->p_vinit; // added + x->p_ms = 0; + x->p_time = 0.; + x->p_ainit = -100.; // added, but currently disabled + x->p_accel = -100.; // reactivated (?) + x->p_damping = 1.; // i.e. no initial damping + x->p_prevchg = 0; // added + +/* x->p_ms = 0; + x->p_time = 0.; + x->p_timegrain = 0.05; // if ms grain = 50 + x->p_timegrainin = 50; + + x->p_vinit = 0.; + x->p_dinit = 100.; + x->p_ainit = -100.; + x->p_damping = 1.; // i.e. no initial damping + x->p_dhand = 100.; + x->p_force = 1.; // i.e. hand does nothing initially + + x->p_accel = -100.; + x->p_vi = 0.; + x->p_di = 100.; + + x->p_dt = 100.; // changed from 0 to 100 + x->p_dtprev = 100.; // changed from 0 to 100 + x->p_vt = 0.; + x->p_prevchg = 0; +*/ +} + +/* ---------------------------------------------------------- */ + +static void pong_timein(t_pong *x, t_floatarg n) +{ + int thischg; + + x->p_time = n / x->p_warp; + + x->p_dt = ((x->p_accel * (x->p_time*x->p_time)) + (x->p_time * x->p_vi) + x->p_di); + x->p_vt = ((x->p_dt - x->p_dtprev) / x->p_timegrain); + + if (x->p_dt < 0.) + { + x->p_dt *= -1.; + + x->p_di = 0.; + x->p_vi = x->p_vt * (-1. * x->p_damping); // -1 will eventually be a damping variable + //post("vel at bounce %f", x->p_vi); + outlet_bang(x->p_bounceout); + x->p_ms = 0; + //x->p_dtprev= 0.; + } + //else + x->p_dtprev = x->p_dt; + + /* ---------------------------------- virtual hand below ------------ */ + + if (x->p_dt > x->p_dhand) // presuming the hand is initially equal to the dinit + thischg = 1; + else + thischg = 0; + + if (thischg != x->p_prevchg) + { + x->p_ms = 0; + x->p_vi = x->p_vt; + x->p_di = x->p_dhand; + + if (thischg == 0) + { + x->p_accel = -100.; // x->p_ainit in lieu of -100. + outlet_float(x->p_handout, 0); + } + else + { + x->p_accel = (x->p_force * -100.); // x->p_ainit in lieu of -100. + outlet_float(x->p_handout, 1); + } + } + + x->p_prevchg = thischg; + outlet_float(x->p_velout, x->p_vt); + outlet_float(x->p_heightout, x->p_dt); +} + +static void pong_onoff(t_pong *x, t_floatarg n) +{ + if (n != 0) + clock_delay(x->p_klok, 0); + else + clock_unset(x->p_klok); +} + +/* ---------------------------------------------------------- */ + +static void pong_bang(t_pong *x) +{ + x->p_ms = 0; + clock_delay(x->p_klok, 0); +} + +static void pong_stop(t_pong *x) +{ + clock_unset(x->p_klok); +} + +/* ---------------------------------------------------------- */ + +static void pong_tick(t_pong *x) +{ + clock_delay(x->p_klok, (t_int)x->p_timegrainin); + pong_timein(x, x->p_ms); + //outlet_float(x->p_heightout, (float)x->p_ms); + x->p_ms = x->p_ms + x->p_timegrainin; +} + +/* ---------------------------------------------------------- */ + +static void pong_tgrain(t_pong *x, t_floatarg n) +{ + x->p_timegrain = n / x->p_warp; + x->p_timegrainin = n; + post("timegrain %f", x->p_timegrain); +} + +/* ---------------------------------------------------------- */ + +static void pong_warpin(t_pong *x, t_floatarg n) +{ + x->p_warp = n; + x->p_timegrain = x->p_timegrainin / x->p_warp; + post("timewarp %f ms = one sec", x->p_warp); +} + +/* ---------------------------------------------------------- */ +/* ----- these are to receive and store the init values ----- */ +/* ---------------------------------------------------------- */ + +static void pong_initdist(t_pong *x, t_floatarg n) +{ + x->p_dinit = n; +} + +static void pong_initvel(t_pong *x, t_floatarg n) +{ + x->p_vinit = n; +} + +static void pong_damp(t_pong *x, t_floatarg n) +{ + x->p_damping = n; +} + +/* ---------------------------------------------------------- */ + +static void pong_baseacc(t_pong *x, t_floatarg n) +{ + //post ("baseaccel currently disabled", 0); + x->p_ainit = n; + x->p_accel = x->p_ainit; +} + +/* ---------------------------------------------------------- */ + +static void pong_hand(t_pong *x, t_floatarg n) +{ + x->p_dhand = n; +} + +static void pong_force(t_pong *x, t_floatarg n) +{ + x->p_force = n; +} + +/* ---------------------------------------------------------- */ + + +static t_class *pong_class; + +static void *pong_new(t_floatarg n) +{ + t_pong *x = (t_pong *)pd_new(pong_class); + +#ifndef MAXLIB + post(version); +#endif + + x->p_klok = clock_new(x, (t_method)pong_tick); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("dist")); // distance + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("velo")); // velocity + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("damp")); // damping + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("force")); // hand force 1.0 = no force + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("hand")); // virtual hand (distance) + + + if (n > 0) + x->p_warp = n; + else + x->p_warp = 1000.; + + x->p_ms = 0; + x->p_time = 0.; + x->p_timegrain = 0.05; // if ms grain = 50 + x->p_timegrainin = 50.0; + + x->p_vinit = 0.; + x->p_dinit = 100.; + x->p_ainit = -100.; + x->p_damping = 1.; // i.e. no initial damping + x->p_dhand = 100.; + x->p_force = 1.; // i.e. hand does nothing initially + + x->p_accel = -100.; + x->p_vi = 0.; + x->p_di = 100.; + + x->p_dt = 100.; // changed from 0 to 100 + x->p_dtprev = 100.; // changed from 0 to 100 + x->p_vt = 0.; + x->p_prevchg = 0; + + x->p_bounceout = outlet_new(&x->x_obj, gensym("bang")); + x->p_handout = outlet_new(&x->x_obj, gensym("float")); + x->p_velout = outlet_new(&x->x_obj, gensym("float")); + x->p_heightout = outlet_new(&x->x_obj, gensym("float")); + + return (x); +} + +void pong_setup(void) +{ + pong_class = class_new(gensym("pong"), (t_newmethod)pong_new, + 0, sizeof(t_pong), 0, A_DEFFLOAT, 0); + /* method handlers for inlets */ + class_addmethod(pong_class, (t_method)pong_initdist, gensym("dist"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_initvel, gensym("velo"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_damp, gensym("damp"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_force, gensym("force"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_hand, gensym("hand"), A_FLOAT, 0); + /* method handlers for other messages to first inlet */ + class_addmethod(pong_class, (t_method)pong_tgrain, gensym("timegrain"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_warpin, gensym("timewarp"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_baseacc, gensym("baseaccel"), A_FLOAT, 0); + class_addmethod(pong_class, (t_method)pong_reset, gensym("reset"), 0); + class_addmethod(pong_class, (t_method)pong_stop, gensym("stop"), 0); + + class_addfloat(pong_class, pong_onoff); + class_addbang(pong_class, pong_bang); +#ifndef MAXLIB + class_sethelpsymbol(pong_class, gensym("help-pong.pd")); +#else + class_sethelpsymbol(pong_class, gensym("maxlib/help-pong.pd")); +#endif +} \ No newline at end of file diff --git a/src/pulse.c b/src/pulse.c new file mode 100644 index 0000000..8633eee --- /dev/null +++ b/src/pulse.c @@ -0,0 +1,265 @@ +/* pulse.c ---- a more accurate replacement for the tempo object */ +/* updated for CW 68K / PPC summer 96 -RD */ +/* written for Max by James McCartney */ +/* ported to Pd by Olaf Matthes */ + +#include "m_pd.h" +#include + +#define MAXSIZE 32 + +static char *version = "pulse v0.1b, written by James McCartney for Max \n" + " ported to Pd by Olaf Matthes "; + +/* Pulse object data structure */ +typedef struct pulse +{ + t_object p_ob; + t_clock *p_clock; + t_outlet *p_out1; /* outlet */ + t_outlet *p_out2; /* outlet */ + t_int p_onoff, p_changenumer, p_changedenom; + t_int p_tempo, p_durnumer, p_durdenom, p_maxbeats, p_count; + double p_starttime, p_endtime, p_startremain, p_endremain, p_mspbquotient; + t_int p_newdurnumer, p_newdurdenom; + t_int p_mspbnumer, p_mspbdenom, p_mspbremainder; +} Pulse; + +static t_class *pulse_class; + +static void durcalc(Pulse *x) +{ + /* recalc duration */ + x->p_mspbnumer = 240000 * x->p_durnumer; + if (x->p_tempo * x->p_durdenom != 0) /* bug fix by Frank Barknecht */ + x->p_mspbdenom = x->p_tempo * x->p_durdenom; + x->p_mspbquotient = x->p_mspbnumer / x->p_mspbdenom; + x->p_mspbremainder = x->p_mspbnumer % x->p_mspbdenom; + if (x->p_mspbquotient < 5) { + x->p_mspbquotient = 5; + x->p_mspbremainder = 0; + } +} + +static void pulse_onoff(Pulse *x, t_floatarg f) +{ + int i = (int)f; + if (i && !x->p_onoff) { + x->p_onoff = 1; + x->p_count = 0; + outlet_float(x->p_out1, x->p_count); + if (x->p_changedenom) { + x->p_durdenom = x->p_newdurdenom; + x->p_changedenom = 0; + } + if (x->p_changenumer) { + x->p_durnumer = x->p_newdurnumer; + x->p_changenumer = 0; + } + durcalc(x); + x->p_startremain = 0; + x->p_endremain = x->p_mspbremainder; + x->p_starttime = clock_getlogicaltime(); + x->p_endtime = x->p_starttime + x->p_mspbquotient; + // clock_set(x->p_clock, x->p_endtime); + clock_delay(x->p_clock, x->p_mspbquotient); + } else if (i==0 && x->p_onoff) { + x->p_onoff = 0; + clock_unset(x->p_clock); + } +} + +static void pulse_bang(Pulse *x) +{ + if (!x->p_onoff) { + x->p_onoff = 1; + x->p_count = 0; + outlet_float(x->p_out1, x->p_count); + if (x->p_changedenom) { + x->p_durdenom = x->p_newdurdenom; + x->p_changedenom = 0; + } + if (x->p_changenumer) { + x->p_durnumer = x->p_newdurnumer; + x->p_changenumer = 0; + } + durcalc(x); + x->p_startremain = 0; + x->p_endremain = x->p_mspbremainder; + x->p_starttime = clock_getlogicaltime(); + x->p_endtime = x->p_starttime + x->p_mspbquotient; + clock_set(x->p_clock, x->p_endtime); + } else { + x->p_onoff = 0; + clock_unset(x->p_clock); + } +} + +/* clock tick routine */ +static void pulse_tick(Pulse *x) +{ + x->p_count ++; + if ((x->p_maxbeats > 0) && (x->p_count >= x->p_maxbeats)) { /* turn off time */ + x->p_onoff = 0; + outlet_bang(x->p_out2); + } else { + outlet_float(x->p_out1, x->p_count); + x->p_startremain = x->p_endremain; /* save in case we have to re do it */ + if (x->p_changenumer || x->p_changedenom) { /* duration changed */ + if (x->p_changedenom) { + /* this statement may cause a slight drift of (1/(tempo*denom) msecs) */ + x->p_startremain = (x->p_startremain * x->p_newdurdenom + (x->p_durdenom>>1)) + /x->p_durdenom; + x->p_durdenom = x->p_newdurdenom; + x->p_changedenom = 0; + } + if (x->p_changenumer) { + x->p_durnumer = x->p_newdurnumer; + x->p_changenumer = 0; + } + durcalc(x); + } + x->p_endremain = x->p_startremain + x->p_mspbremainder; + x->p_starttime = x->p_endtime; + x->p_endtime = x->p_starttime + x->p_mspbquotient; + if (x->p_endremain >= x->p_mspbdenom) { + x->p_endremain -= x->p_mspbdenom; + x->p_endtime ++; + } + // clock_set(x->p_clock, x->p_endtime); + clock_delay(x->p_clock, x->p_mspbquotient); + } +} + +/* deal with tempo change */ +static void pulse_tempo(Pulse *x, t_floatarg t) +{ + double time, msecdur, tickdur, fracremain; + t_int fracnumer, fracquotient, oldtempo; + oldtempo = x->p_tempo; + x->p_tempo = (t<5) ? 5 : ((t>500) ? 500 : t); + if (x->p_onoff) { + /* calculate fraction of the beat we have done */ + time = clock_getlogicaltime(); + if (time != x->p_endtime) { + /* if pulse_tempo is called as a result of a call from pulse_tick + (call chain from outlet_float()) + then this stuff doesn't need to be done (time will == x->p_endtime) + */ + msecdur = time - x->p_starttime; + tickdur = msecdur * x->p_mspbdenom - x->p_startremain; + fracnumer = (t_int)(x->p_mspbnumer - tickdur); + + durcalc(x); + + /* calculate end time */ + fracquotient = fracnumer / x->p_mspbdenom; + fracremain = fracnumer % x->p_mspbdenom; + + x->p_endtime = time + fracquotient; + x->p_endremain = fracremain; + + /* recalculate starttime so future tempo changes work */ + x->p_starttime = x->p_endtime - x->p_mspbquotient; + x->p_startremain = x->p_mspbdenom - x->p_mspbremainder + fracremain; + if (x->p_mspbremainder > fracremain) { + x->p_startremain = x->p_mspbdenom - x->p_mspbremainder + fracremain; + x->p_starttime --; + } else { + x->p_startremain = fracremain - x->p_mspbremainder; + } + clock_unset(x->p_clock); + clock_set(x->p_clock, x->p_endtime); + // clock_delay(x->p_clock, fracquotient); + } + } +} + +static void pulse_numer(Pulse *x, t_floatarg n) +{ + int i = (t_int)n; + if(i >= 0) + { + if (x->p_onoff) { + if (x->p_durnumer != i) { + x->p_changenumer = 1; + x->p_newdurnumer = i; + } + } else { + x->p_durnumer = i; + } + } +} + +static void pulse_denom(Pulse *x, t_floatarg n) +{ + int i = (t_int)n; + if(i >= 0) + { + if (x->p_onoff) { + if (x->p_durdenom != i) { + x->p_changedenom = 1; + x->p_newdurdenom = i; + } + } else { + x->p_durdenom = i; + } + } +} + +static void pulse_beat(Pulse *x, t_floatarg n) +{ + int i = (t_int)n; + if(i >= 0) + { + x->p_maxbeats = i; + } +} + + +static void pulse_free(Pulse *x) +{ + clock_free(x->p_clock); +} + + +/* function run to create a new instance of the Pulse class */ +static void *pulse_new(t_floatarg t, t_floatarg n, t_floatarg d, t_floatarg b) +{ + Pulse *x; + + x = (Pulse *)pd_new(pulse_class); /* allocates memory and sticks in an inlet */ + + inlet_new(&x->p_ob, &x->p_ob.ob_pd, gensym("float"), gensym("tempo")); + inlet_new(&x->p_ob, &x->p_ob.ob_pd, gensym("float"), gensym("numer")); + inlet_new(&x->p_ob, &x->p_ob.ob_pd, gensym("float"), gensym("denom")); + inlet_new(&x->p_ob, &x->p_ob.ob_pd, gensym("float"), gensym("beat")); + x->p_out1 = outlet_new(&x->p_ob, gensym("float")); + x->p_out2 = outlet_new(&x->p_ob, gensym("float")); + x->p_clock = clock_new(x, (t_method)pulse_tick); + x->p_tempo = (t==0) ? 120 : ((t<5) ? 5 : ((t>500) ? 500 : t)); + x->p_durnumer = (n<=0) ? 1 : n; + x->p_durdenom = (d<=0) ? 4 : d; + x->p_maxbeats = (b<=0) ? 0 : b; + x->p_changenumer = 0; + x->p_changedenom = 0; + x->p_onoff = 0; +#ifndef MAXLIB + post(version); +#endif + return (x); /* always return a copy of the created object */ +} + +void pulse_setup(void) +{ + pulse_class = class_new(gensym("pulse"), (t_newmethod)pulse_new, + (t_method)pulse_free, sizeof(Pulse), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(pulse_class, (t_method)pulse_beat, gensym("beat"), A_FLOAT, 0); + class_addmethod(pulse_class, (t_method)pulse_denom, gensym("denom"), A_FLOAT, 0); + class_addmethod(pulse_class, (t_method)pulse_numer, gensym("numer"), A_FLOAT, 0); + class_addmethod(pulse_class, (t_method)pulse_tempo, gensym("tempo"), A_FLOAT, 0); + class_addfloat(pulse_class, pulse_onoff); + class_addbang(pulse_class, pulse_bang); + class_sethelpsymbol(pulse_class, gensym("maxlib/help-pulse.pd")); +} + diff --git a/src/remote.c b/src/remote.c new file mode 100644 index 0000000..6697113 --- /dev/null +++ b/src/remote.c @@ -0,0 +1,84 @@ +/* ------------------------ remote ------------------------------------------ */ +/* */ +/* Send data to receive obejct . */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +#include +#include + +#define MAX_REC 64 /* maximum number of receive objects */ +#define MAX_ARG 32 /* maximum number of arguments to pass on */ + +static char *version = "remote v0.1, written by Olaf Matthes "; + +static t_class *remote_class; + +typedef struct _remote +{ + t_object x_obj; +} t_remote; + + /* send 'anything' to receiver */ +static void remote_anything(t_remote *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + t_atom av[MAX_ARG]; /* the 'new' t_atom without first element */ + t_int ac = argc - 1; /* the 'new' number of arguments */ + + if(argc < 1) /* need */ + { + post("remote: too few arguments!"); + return; + } + if(ac > MAX_ARG) + { + post("remote: too many arguments!"); + return; + } + + for(i = 1; i < argc; i++) + { + av[i - 1] = argv[i]; /* just copy, don't care about types */ + } + /* send only argument-part to receivers */ + if (s->s_thing) pd_forwardmess(s->s_thing, argc, argv); +} + +static void *remote_new(void) +{ + t_remote *x = (t_remote *)pd_new(remote_class); + +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +void remote_setup(void) +{ + remote_class = class_new(gensym("remote"), (t_newmethod)remote_new, 0, + sizeof(t_remote), 0, 0); + class_addanything(remote_class, remote_anything); + class_sethelpsymbol(remote_class, gensym("maxlib/help-remote.pd")); +} diff --git a/src/rhythm.c b/src/rhythm.c new file mode 100644 index 0000000..7567495 --- /dev/null +++ b/src/rhythm.c @@ -0,0 +1,329 @@ +/* --------------------------- rhythm ---------------------------------------- */ +/* */ +/* Detect the beats per minute of a MIDI stream. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code written by Robert Rowe. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +#define ALPHA 10 +#define ADAPT_ARRAY_SIZE 1000 + +#ifndef M_PI +#define M_PI 3.14159265358979 +#endif +#ifndef TWO_PI +#define TWO_PI 2.0*M_PI +#endif + +static char *version = "rhythm v0.1, written by Olaf Matthes "; + +typedef struct rhythm +{ + t_object x_ob; + t_clock *x_tick; + t_outlet *x_out_bpm; /* beats per minute */ + t_outlet *x_out_period; /* beats in milliseconds */ + t_outlet *x_out_pulse; + t_int x_print; /* switch printing to console window on / off */ + t_int x_ticking; /* indicates if clock is ticking or not */ + + t_int x_model; /* algorhythm to use: 0 - Large & Kolen, 1 - Toiviainen */ + t_float x_long_term[ADAPT_ARRAY_SIZE]; + t_float x_short_term[ADAPT_ARRAY_SIZE]; + t_float x_phi_at_pulse; /* phase at latest pulse */ + t_float x_phiVel_at_pulse; /* phase velocity */ + + t_float x_adapt; + t_float x_errFunc; /* error function */ + t_float x_etaLong; /* strength of long-term adaptation */ + t_float x_etaShort; /* strength of short-term adaptation */ + t_float x_gamma; /* gain parameter */ + double x_lastIoi; /* last inter-onset interval */ + double x_lastPulseTime; /* time of last pulse */ + t_float x_output; /* current output value of the oscillator */ + t_float x_phi; /* phase */ + double x_expected; /* estimated time of arrival */ + t_float x_period; + t_float x_periodStrength; + t_float x_phaseStrength; + double x_startTime; + + t_int x_pitch; + t_int x_velo; + /* helpers needed to do the time calculations */ + double x_last_input; +} t_rhythm; + +/* --------------- rhythm stuff ------------------------------------------------ */ + /* bang at the rhythm's pulse */ +static void rhythm_tick(t_rhythm *x) +{ + outlet_bang(x->x_out_pulse); + clock_delay(x->x_tick, x->x_period); +} + +static t_float rhythm_get_adapt_long(t_rhythm *x, t_float arg) +{ + int address; + if (arg > 1.0) + address = ADAPT_ARRAY_SIZE - 1; + else if (arg < -1.0) + address = ADAPT_ARRAY_SIZE - 1; + else + address = abs((int)(arg*1000.0)); + return x->x_long_term[address]; +} + +static t_float rhythm_get_adapt_short(t_rhythm *x, t_float arg) +{ + int address; + if (arg > 1.0) + address = ADAPT_ARRAY_SIZE - 1; + else if (arg < -1.0) + address = ADAPT_ARRAY_SIZE - 1; + else + address = abs((int)(arg*1000.0)); + return x->x_short_term[address]; +} + + + /* Large & Kolen adaptation model */ +static void rhythm_large(t_rhythm *x, t_int pulse, double time) +{ + while (time > (x->x_expected+(x->x_period/2))) // move the expectation point + x->x_expected += x->x_period; // to be within one period of onset + x->x_phi = (t_float)(time - x->x_expected) / x->x_period; // calculate phi + + if (pulse) { // if this was an onset + x->x_adapt = x->x_gamma * (cos(TWO_PI*x->x_phi)-1.0); + x->x_adapt = 1.0 / cosh(x->x_adapt); + x->x_adapt *= x->x_adapt; + x->x_adapt *= sin(TWO_PI*x->x_phi); + x->x_adapt *= (x->x_period / TWO_PI); + x->x_period += (x->x_periodStrength*x->x_adapt); // update period + x->x_expected += (x->x_phaseStrength *x->x_adapt); // and phase + x->x_phi = (t_float)(time - x->x_expected) / x->x_period; + } + + x->x_output = 1+tanh(x->x_gamma*(cos(TWO_PI*x->x_phi)-1.0)); // oscillator output +} + /* Toiviainen adaptation model */ +static void rhythm_toiviainen(t_rhythm *x, t_int pulse, double time) +{ + t_float deltaTime, varPhi, adaptLong, adaptShort; + + /* if just starting, initialize phi */ + if(x->x_lastPulseTime < 0) + { + x->x_phi = x->x_phi_at_pulse + x->x_phiVel_at_pulse * ((t_float)(time-x->x_startTime) / 1000.0); + } + else + { + deltaTime = time - x->x_lastPulseTime; + varPhi = (deltaTime/1000.0) * x->x_phiVel_at_pulse; + adaptLong = rhythm_get_adapt_long(x, varPhi); // get long adaptation from table + adaptShort = rhythm_get_adapt_short(x, varPhi); // get short adaptation from table + x->x_phi = x->x_phi_at_pulse + varPhi + x->x_errFunc * (x->x_etaLong*adaptLong + x->x_etaShort*adaptShort); + if (pulse) // change tempo if on pulse + x->x_phiVel_at_pulse = x->x_phiVel_at_pulse * (1 + x->x_etaLong * x->x_errFunc * adaptShort); + } + + if (pulse) { + x->x_output = 1+tanh(x->x_gamma*(cos(TWO_PI*x->x_phi)-1.0)); + x->x_errFunc = x->x_output * (x->x_output - 2.0) * sin(TWO_PI * x->x_phi); + x->x_phi_at_pulse = x->x_phi; + } + + x->x_period = 1000.0 / x->x_phiVel_at_pulse; // update period +} + +static void rhythm_move(t_rhythm *x, t_int pulse, double time) +{ + switch (x->x_model) /* choose adaptation model */ + { + case 0: + rhythm_large(x, pulse, time); + break; + + case 1: + rhythm_toiviainen(x, pulse, time); + break; + } + + if(x->x_ticking == 0) + { + x->x_ticking = 1; /* prevent us from further calls */ + clock_delay(x->x_tick, 0); /* start pulse bangs */ + } +} + + /* main processing function */ +static void rhythm_float(t_rhythm *x, t_floatarg f) +{ + t_int velo = x->x_velo; + double time = clock_gettimesince(x->x_last_input); + x->x_pitch = (t_int)f; + + if(velo != 0) /* note-on received */ + { + if (x->x_startTime == 0) { + x->x_startTime = time; + return; + } + + if (x->x_period < 2.0) { + x->x_period = (t_float)(time - x->x_startTime); + x->x_phiVel_at_pulse = 1000.0 / x->x_period; + } + + rhythm_move(x, 1, time); + + if (x->x_lastPulseTime >= 0) + { + x->x_lastIoi = time - x->x_lastPulseTime; + } + x->x_lastPulseTime = time; + x->x_last_input = clock_getlogicaltime(); + + outlet_float(x->x_out_period, x->x_period); + outlet_float(x->x_out_bpm, 60000.0/x->x_period); + } + return; +} + /* get velocity */ +static void rhythm_ft1(t_rhythm *x, t_floatarg f) +{ + x->x_velo = (t_int)f; +} + + /* toggle printing on/off (not used right now!) */ +static void rhythm_print(t_rhythm *x) +{ + if(x->x_print)x->x_print = 0; + else x->x_print = 1; +} + /* initialise array for Toiviainen adaptation model */ +static void rhythm_calculate_adaptations(t_rhythm *x) +{ + int i; + t_float f; + + for(i = 0; i < ADAPT_ARRAY_SIZE; i++) + { + f = (t_float)i/(t_float)ADAPT_ARRAY_SIZE; + x->x_long_term[i] = f+(ALPHA*f*f/2.0+2.0*f+3.0/ALPHA)*exp(-ALPHA*f)-3.0/ALPHA; + x->x_short_term[i] = 1.0-(ALPHA*ALPHA*f*f/2.0+ALPHA*f+1.0)*exp(-ALPHA*f); + } +} + +static void rhythm_reset(t_rhythm *x) +{ + if(x->x_ticking)clock_unset(x->x_tick); + x->x_ticking = 0; + + x->x_gamma = 1.0; /* default value for gain parameter */ + x->x_phi = 0.0; + x->x_output = 1+tanh(x->x_gamma*(cos(TWO_PI*x->x_phi)-1.0)); + x->x_expected = 0; + x->x_lastIoi = 0; + x->x_lastPulseTime = -1; + x->x_period = 1.0; + x->x_periodStrength = 0.2; + x->x_phaseStrength = 0.2; + + x->x_errFunc = 0.0; + x->x_etaLong = 0.2; + x->x_etaShort = 0.2; + x->x_phi_at_pulse = 0.0; + x->x_phiVel_at_pulse = 0.9; + x->x_startTime = 0; + + rhythm_calculate_adaptations(x); +} + +static void rhythm_model(t_rhythm *x, t_floatarg f) +{ + if(f == 1) + { + x->x_model = 1; /* Toiviainen model */ + rhythm_reset(x); + post("rhythm: using \"Toiviainen\" adaptation model"); + } + else + { + x->x_model = 0; /* Large and Kolen model */ + rhythm_reset(x); + post("rhythm: using \"Large and Kolen\" adaptation model"); + } +} + +static t_class *rhythm_class; + +static void rhythm_free(t_rhythm *x) +{ + clock_free(x->x_tick); +} + +static void *rhythm_new(t_floatarg f) +{ + t_rhythm *x = (t_rhythm *)pd_new(rhythm_class); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_out_bpm = outlet_new(&x->x_ob, gensym("float")); + x->x_out_period = outlet_new(&x->x_ob, gensym("float")); + x->x_out_pulse = outlet_new(&x->x_ob, gensym("bang")); + x->x_tick = clock_new(x, (t_method)rhythm_tick); + + rhythm_reset(x); + +#ifndef MAXLIB + post(version); +#endif + if(f == 1) + { + x->x_model = 1; /* Toiviainen model */ + post("rhythm: using \"Toiviainen\" adaptation model"); + } + else + { + x->x_model = 0; /* Large and Kolen model */ + post("rhythm: using \"Large and Kolen\" adaptation model"); + } + + return (void *)x; +} + +void rhythm_setup(void) +{ + rhythm_class = class_new(gensym("rhythm"), (t_newmethod)rhythm_new, + (t_method)rhythm_free, sizeof(t_rhythm), 0, A_DEFFLOAT, 0); + class_addcreator((t_newmethod)rhythm_new, gensym("max.rhythm"), A_DEFFLOAT, 0); + class_addfloat(rhythm_class, rhythm_float); + class_addmethod(rhythm_class, (t_method)rhythm_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(rhythm_class, (t_method)rhythm_model, gensym("model"), A_FLOAT, 0); + class_addmethod(rhythm_class, (t_method)rhythm_reset, gensym("reset"), 0); + class_addmethod(rhythm_class, (t_method)rhythm_print, gensym("print"), 0); + class_sethelpsymbol(rhythm_class, gensym("maxlib/help-rhythm.pd")); +} + diff --git a/src/scale.c b/src/scale.c new file mode 100644 index 0000000..03f6b5f --- /dev/null +++ b/src/scale.c @@ -0,0 +1,133 @@ +/* ------------------------- scale ------------------------------------------ */ +/* */ +/* Scales input to lie within an output range. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +static char *version = "scale v0.2, written by Olaf Matthes "; + +typedef struct scale +{ + t_object x_ob; + t_float x_f; /* current input value */ + t_float x_il; /* low border of input range */ + t_float x_ih; /* high border of input range */ + t_float x_ol; /* low border of output range */ + t_float x_oh; /* high border of output range */ + t_float x_logcoeff; /* log-coefficient */ + t_outlet *x_outlet1; /* result */ +} t_scale; + +static void scale_float(t_scale *x, t_floatarg f) +{ + t_float ir = x->x_ih - x->x_il; + t_float or = x->x_oh - x->x_ol; + double oq; + double result; + double k; + if(ir == 0) + { + post("scale: input range must not be 0"); + return; + } + /* we accept an output range of 0 in case someone really wants this */ + if(!x->x_logcoeff) /* linear */ + { + k = (or / ir); + result = ((f - x->x_il) * k) + x->x_ol; + } + else /* logarythmical scale */ + { + oq = x->x_oh / x->x_ol; + // k = (log((double)oq)/log(x->x_logcoeff))/((double)ir); + k = log((double)oq)/((double)ir); + + if(x->x_ol) + { + // result = (double)x->x_ol*exp(k*(double)(f - x->x_il)*log(x->x_logcoeff)); + result = (double)x->x_ol*exp(k*(double)(f - x->x_il)); + } + else + { + /* in case the low output is 0 we have to cheat... */ + /* okay, here's the chating: we calculate for a lower out limit + of 1 and remove this shift after the calculation */ + result = ((double)(x->x_ol+1)*exp(k*(double)(f - x->x_il)))-1.0; + } + } + + x->x_f = f; /* save current input value */ + + outlet_float(x->x_outlet1, result); +} + +static void scale_bang(t_scale *x) +{ + scale_float(x, x->x_f); /* recalculate result */ +} + +static t_class *scale_class; + +static void *scale_new(t_floatarg fil, t_floatarg fih, t_floatarg fol, t_floatarg foh, t_floatarg flc) +{ + t_scale *x = (t_scale *)pd_new(scale_class); + + floatinlet_new(&x->x_ob, &x->x_il); + floatinlet_new(&x->x_ob, &x->x_ih); + floatinlet_new(&x->x_ob, &x->x_ol); + floatinlet_new(&x->x_ob, &x->x_oh); + floatinlet_new(&x->x_ob, &x->x_logcoeff); + + x->x_outlet1 = outlet_new(&x->x_ob, gensym("float")); + + /* default values taken from Max's scale */ + x->x_il = fil; + x->x_ih = fih; + if(!x->x_ih)x->x_ih = 127.0; + x->x_ol = fol; + x->x_oh = foh; + if(!x->x_oh)x->x_oh = 1.0; + x->x_logcoeff = flc; + x->x_f = 0; + +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void scale_setup(void) +{ + scale_class = class_new(gensym("scale"), (t_newmethod)scale_new, + 0, sizeof(t_scale), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(scale_class, scale_float); + class_addbang(scale_class, scale_bang); +#ifndef MAXLIB + class_sethelpsymbol(scale_class, gensym("help-scale.pd")); +#else + class_sethelpsymbol(scale_class, gensym("maxlib/help-scale.pd")); +#endif +} + diff --git a/src/score.c b/src/score.c new file mode 100644 index 0000000..66c0bc8 --- /dev/null +++ b/src/score.c @@ -0,0 +1,293 @@ +/* ------------------------- score ------------------------------------------ */ +/* */ +/* Simple score following / orientation. Incoming data gets compared to a */ +/* score stored in an array or table. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +#define MAX_NOTES 32 /* maximum number of notes that can be stored */ + +static char *version = "score v0.1, score follower written by Olaf Matthes "; + +typedef struct score +{ + t_object x_ob; + t_inlet *x_invelo; /* inlet for velocity */ + t_inlet *x_inreset; /* inlet to reset the object */ + t_outlet *x_outindex; /* index :: position in given score */ + t_outlet *x_outerror; /* indicates lost orientation */ + t_symbol *x_sym; /* name of array that contains the score */ + t_garray *x_buf; /* the above array itselfe */ + + t_int x_state; /* indicates state of score following: */ + /* running = 1, record = -1, stop = 0 */ + t_int x_skipindex; /* max. number of notes to skip */ + t_float x_skiptime; /* max time in ms to skip */ + t_int x_index; /* position in array / score */ + t_int x_lastpitch; + t_int x_error; + + t_int x_notecount; + t_int x_pitch; + t_int x_velo; + /* helpers needed to do the calculations */ + double x_starttime[MAX_NOTES]; + double x_laststarttime; + t_int x_alloctable[MAX_NOTES]; + +} t_score; + +static void score_float(t_score *x, t_floatarg f) +{ + /* This is the score following algorhythm: + + first, we check if the note we got is in the score. In case + it's not the next note, we'll search 'skipnotes' in advance. + In case that fails we go back 'skipnotes' and check them. As + extra these notes have to be 'younger' than 'skiptime' (to + avoid going back too far in case of slow melodies) + As last resort we check if we probably just got the same not + again (double trigger from keyboard or the like) + */ + + t_int velo = x->x_velo; /* get the velocity */ + t_garray *b = x->x_buf; /* make local copy of array */ + float *tab; /* we'll store notes in here */ + int items; + int i, j, n, check; + + x->x_pitch = (t_int)f; + x->x_error = 0; + + /* check our array */ + if (!b) + { + post("score: no array selected!"); + x->x_error = 1; + goto output; + } + if (!garray_getfloatarray(b, &items, &tab)) + { + post("score: couldn't read from array!"); + x->x_error = 1; + goto output; + } + + if (x->x_state) /* score follower is running */ + { + n = check = x->x_notecount; /* make local copys */ + + if (x->x_velo != 0) /* store note-on in alloctable */ + { + /* store note in alloctable */ + x->x_alloctable[n] = (t_int)x->x_pitch; + /* store note-on time */ + x->x_starttime[n] = clock_getlogicaltime(); + if(++x->x_notecount >= MAX_NOTES)x->x_notecount = 0; /* total number of notes has increased */ + } else return; /* we don't care about note-off's */ + + /* first we try to find a match within the skip area */ + /* ( probably looking ahead in the score ) */ + for (i = x->x_index + 1; i < (x->x_index + x->x_skipindex + 1); i++) + { + // post("%d: %d -> %d", i, x->x_alloctable[n], (t_int)tab[i]); + if(x->x_alloctable[n] == (t_int)tab[i]) + { + if(i - x->x_index != 1) post("score: skipped %d notes!", i - x->x_index - 1); + x->x_alloctable[n] = -1; /* delete note, we've matched it! */ + x->x_index = i; + goto output; + } + } + + /* then we look back within the boudaries of skiptime */ + for (i = x->x_index - 1; i > (x->x_index - x->x_skipindex) - 1; i--) + { + check = n; /* get current notecount */ + + for (j = 0; j < MAX_NOTES; j++) /* check with every note from our alloctable */ + { + if (x->x_alloctable[check] == (t_int)tab[i]) /* this one would fit */ + { + /* check the time restrictions */ + if (clock_gettimesince(x->x_starttime[check]) < x->x_skiptime) + { + if (i != x->x_index) post("score: skipped %d notes in score!", x->x_index - i); + if (j != 0) post("score: skipped %d notes from input!", j); + post("score: going back by %g milliseconds!", clock_gettimesince(x->x_starttime[check])); + x->x_index = i; + /* new notecount: we assume the notes we skipped are errors made by the */ + /* performer. new notes will be added right behind the last valid one */ + x->x_notecount = (check++) % MAX_NOTES; + x->x_alloctable[x->x_notecount - 1] = -1; /* delete note since we've matched it */ + goto output; + } + else /* ough, too old ! */ + { + post("score: matching note is too old! (ignored)"); + x->x_alloctable[check] = 0; /* delete note since it's too old */ + x->x_error = 1; + goto output; /* stop with first match as all others would be far older */ + } + } + if(--check < 0) check = MAX_NOTES - 1; /* decrease counter */ + /* as we want to go back in time */ + } + } + /* or is it just the same note again ??? (double trigger...) */ + if(x->x_pitch == x->x_lastpitch) + { + post("score: repetition! (ignored)"); + x->x_alloctable[x->x_notecount - 1] = -1; /* forget this one */ + return; + } + + /* in case we found nothing: indicate that! */ + x->x_error = 1; + post("score: couldn't find any matches !"); + x->x_lastpitch = x->x_pitch; + goto output; + } + else return; + +output: + /* output index */ + outlet_float(x->x_outindex, x->x_index); + /* bang in case of error */ + if(x->x_error) outlet_bang(x->x_outerror); +} + +static void score_ft1(t_score *x, t_floatarg f) +{ + x->x_velo = (t_int)f; +} + + /* start following the previoisly recorded score */ +static void score_start(t_score *x, t_symbol *s, t_int argc, t_atom* argv) +{ + x->x_index = (t_int)atom_getfloatarg(0, argc, argv); + if(x->x_index > 0) + { + post("score: starting at note %d", x->x_index); + } + else post("score: start following"); + x->x_index--; /* because our array starts with 0 */ + x->x_state = 1; +} + /* resume following the previoisly recorded score */ +static void score_resume(t_score *x) +{ + x->x_state = 1; + post("score: resume following"); +} + + /* stop following the previoisly recorded score */ +static void score_stop(t_score *x) +{ + x->x_state = 0; + post("score: stop following"); +} + + /* choose the array that holds the score */ +void score_set(t_score *x, t_symbol *s) +{ + t_garray *b; + + x->x_sym = s; + + if ((b = (t_garray *)pd_findbyclass(s, garray_class))) + { + post("score: array set to \"%s\"", s->s_name); + x->x_buf = b; + } else { + post("score: no array \"%s\" (error %d)", s->s_name, b); + x->x_buf = 0; + } +} + +static void score_reset(t_score *x) +{ + int i; + + x->x_state = 0; /* don't follow */ + x->x_error = 0; + x->x_index = -1; + x->x_notecount = 0; + x->x_lastpitch = 0; + for(i = 0; i < MAX_NOTES; i++)x->x_alloctable[i] = -1; + + post("score: reset"); +} + +static void score_free(t_score *x) +{ + // nothing to do +} + +static t_class *score_class; + +static void *score_new(t_symbol *s, t_floatarg fskipindex, t_floatarg fskiptime) +{ + int i; + + t_score *x = (t_score *)pd_new(score_class); + x->x_invelo = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("ft1")); + x->x_inreset = inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("bang"), gensym("reset")); + x->x_outindex = outlet_new(&x->x_ob, gensym("float")); + x->x_outerror = outlet_new(&x->x_ob, gensym("float")); +#ifndef MAXLIB + post(version); +#endif + x->x_sym = s; /* get name of array */ + score_set(x,x->x_sym); /* set array */ + if(!fskipindex)fskipindex = 2; + if(!fskiptime)fskiptime = 300.0; + x->x_skipindex = (t_int)fskipindex; + x->x_skiptime = (t_float)fskiptime; + post("score: skipindex set to %d, skiptime set to %g milliseconds", x->x_skipindex, x->x_skiptime); + + x->x_state = 0; /* don't follow */ + x->x_error = 0; + x->x_index = -1; + x->x_notecount = 0; + x->x_pitch = x->x_lastpitch = -1; + for(i = 0; i < MAX_NOTES; i++)x->x_alloctable[i] = -1; + + return (void *)x; +} + +void score_setup(void) +{ + score_class = class_new(gensym("score"), (t_newmethod)score_new, + (t_method)score_free, sizeof(t_score), 0, A_SYMBOL, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(score_class, (t_method)score_reset, gensym("reset"), 0); + class_addmethod(score_class, (t_method)score_resume, gensym("resume"), 0); + class_addmethod(score_class, (t_method)score_start, gensym("start"), A_GIMME, 0); + class_addmethod(score_class, (t_method)score_stop, gensym("stop"), 0); + class_addmethod(score_class, (t_method)score_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(score_class, (t_method)score_reset, gensym("reset"), A_GIMME, 0); + class_addmethod(score_class, (t_method)score_set, gensym("set"), A_SYMBOL, 0); + class_addfloat(score_class, score_float); + class_sethelpsymbol(score_class, gensym("maxlib/help-score.pd")); +} + diff --git a/src/speedlim.c b/src/speedlim.c new file mode 100644 index 0000000..1cd5862 --- /dev/null +++ b/src/speedlim.c @@ -0,0 +1,227 @@ +/* ------------------------- speedlim ----------------------------------------- */ +/* */ +/* Lets information through only every N milliseconds. */ +/* Written by Krzysztof Czaja for his cyclone library. */ +/* Modified to fit into maxlib by Olaf Matthes . */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ +/* this is the original copyright notice: */ +/* Copyright (c) 1997-2002 Miller Puckette and others. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* LATER 'clock' method */ + +#include +#include "m_pd.h" +// #include "hammer.h" + +#define SPEEDLIM_INISIZE 32 /* LATER rethink */ +#define SPEEDLIM_MAXSIZE 256 /* not used */ + +typedef struct _speedlim +{ + t_object x_ob; + int x_open; + t_float x_delta; + t_symbol *x_selector; + t_float x_float; + t_symbol *x_symbol; + t_gpointer *x_pointer; + int x_size; /* as allocated */ + int x_natoms; /* as used */ + t_atom *x_message; + t_atom x_messini[SPEEDLIM_INISIZE]; + int x_entered; + t_clock *x_clock; +} t_speedlim; + +static t_class *speedlim_class; + +/* a caller must check for nrequested > *sizep */ +/* returns actual number of atoms: requested (success) + or a default value of initial size (failure) */ +/* the result is guaranteed to be >= min(nrequested, inisize) */ +static int speedlim_grow(int nrequested, int *sizep, t_atom **bufp, + int inisize, t_atom *bufini) +{ + int newsize = *sizep * 2; + while (newsize < nrequested) newsize *= 2; + if (*bufp == bufini) + *bufp = (t_atom *)getbytes(newsize * sizeof(**bufp)); + else + *bufp = (t_atom *)resizebytes(*bufp, *sizep * sizeof(**bufp), + newsize * sizeof(**bufp)); + if (*bufp) + *sizep = newsize; + else + { + *bufp = bufini; + nrequested = *sizep = inisize; + } + return (nrequested); +} + +static void speedlim_dooutput(t_speedlim *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_open = 0; /* so there will be no reentrant calls of dooutput */ + x->x_entered = 1; /* this prevents a message from being overridden */ + clock_unset(x->x_clock); + if (s == &s_bang) + outlet_bang(((t_object *)x)->ob_outlet); + else if (s == &s_float) + outlet_float(((t_object *)x)->ob_outlet, x->x_float); + else if (s == &s_symbol && x->x_symbol) + { + /* if x_symbol is null, then symbol &s_ is passed + by outlet_anything() -> typedmess() */ + outlet_symbol(((t_object *)x)->ob_outlet, x->x_symbol); + x->x_symbol = 0; + } + else if (s == &s_pointer && x->x_pointer) + { + /* LATER */ + x->x_pointer = 0; + } + else if (s == &s_list) + outlet_list(((t_object *)x)->ob_outlet, &s_list, ac, av); + else if (s) + outlet_anything(((t_object *)x)->ob_outlet, s, ac, av); + x->x_selector = 0; + x->x_natoms = 0; + if (x->x_delta > 0) + clock_delay(x->x_clock, x->x_delta); + else + x->x_open = 1; + x->x_entered = 0; +} + +static void speedlim_tick(t_speedlim *x) +{ + if (x->x_selector) + speedlim_dooutput(x, x->x_selector, x->x_natoms, x->x_message); + else + x->x_open = 1; +} + +static void speedlim_anything(t_speedlim *x, t_symbol *s, int ac, t_atom *av) +{ + if (x->x_open) + speedlim_dooutput(x, s, ac, av); + else if (s && s != &s_ && !x->x_entered) + { + if (ac > x->x_size) + /* MAXSIZE not used, not even a warning... + LATER consider clipping */ + ac = speedlim_grow(ac, &x->x_size, &x->x_message, + SPEEDLIM_INISIZE, x->x_messini); + x->x_selector = s; + x->x_natoms = ac; + if (ac) + memcpy(x->x_message, av, ac * sizeof(*x->x_message)); + } +} + +static void speedlim_bang(t_speedlim *x) +{ + x->x_selector = &s_bang; + speedlim_anything(x, x->x_selector, 0, 0); +} + +static void speedlim_float(t_speedlim *x, t_float f) +{ + x->x_selector = &s_float; + x->x_float = f; + speedlim_anything(x, x->x_selector, 0, 0); +} + +static void speedlim_symbol(t_speedlim *x, t_symbol *s) +{ + x->x_selector = &s_symbol; + x->x_symbol = s; + speedlim_anything(x, x->x_selector, 0, 0); +} + +/* LATER gpointer */ + +static void speedlim_list(t_speedlim *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_selector = &s_list; + speedlim_anything(x, x->x_selector, ac, av); +} + +static void speedlim_ft1(t_speedlim *x, t_floatarg f) +{ + if (f < 0) + f = 0; /* redundant (and CHECKED) */ + x->x_delta = f; + /* CHECKED: no rearming -- + if clock is set, then new delta value is not used until next tick */ +} + +static void speedlim_free(t_speedlim *x) +{ + if (x->x_message != x->x_messini) + freebytes(x->x_message, x->x_size * sizeof(*x->x_message)); + if (x->x_clock) + clock_free(x->x_clock); +} + +static void *speedlim_new(t_floatarg f) +{ + t_speedlim *x = (t_speedlim *)pd_new(speedlim_class); + x->x_open = 1; /* CHECKED */ + x->x_delta = 0; + x->x_selector = 0; + x->x_float = 0; + x->x_symbol = 0; + x->x_pointer = 0; + x->x_size = SPEEDLIM_INISIZE; + x->x_natoms = 0; + x->x_message = x->x_messini; + x->x_entered = 0; + inlet_new((t_object *)x, (t_pd *)x, &s_float, gensym("ft1")); + outlet_new((t_object *)x, &s_anything); + x->x_clock = clock_new(x, (t_method)speedlim_tick); + speedlim_ft1(x, f); + return (x); +} + +void speedlim_setup(void) +{ + speedlim_class = class_new(gensym("speedlim"), (t_newmethod)speedlim_new, + (t_method)speedlim_free, sizeof(t_speedlim), 0, A_DEFFLOAT, 0); + class_addbang(speedlim_class, speedlim_bang); + class_addfloat(speedlim_class, speedlim_float); + class_addsymbol(speedlim_class, speedlim_symbol); + class_addlist(speedlim_class, speedlim_list); + class_addanything(speedlim_class, speedlim_anything); + class_addmethod(speedlim_class, (t_method)speedlim_ft1, gensym("ft1"), A_FLOAT, 0); +#ifndef MAXLIB + class_sethelpsymbol(speedlim_class, gensym("help-speedlim.pd")); +#else + class_sethelpsymbol(speedlim_class, gensym("maxlib/help-speedlim.pd")); +#endif +} diff --git a/src/step.c b/src/step.c new file mode 100644 index 0000000..4473923 --- /dev/null +++ b/src/step.c @@ -0,0 +1,167 @@ +/* -------------------------- step ------------------------------------------ */ +/* */ +/* Step to a new value in N milliseconds (similar to line). */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" + +/* -------------------------- step ------------------------------ */ +static char *version = "step v0.1, written by Olaf Matthes "; + +static t_class *step_class; + +typedef struct _step +{ + t_object x_obj; + t_clock *x_clock; + double x_targettime; + t_float x_targetval; + double x_prevtime; + t_float x_setval; + int x_gotinlet; + t_float x_grain; /* time interval for output */ + t_float x_step; /* step size for output */ + t_float x_steptime; /* length for one step */ + t_int x_stepcall; + double x_1overtimediff; + double x_in1val; +} t_step; + +static void step_tick(t_step *x) +{ + t_float outvalue; + double timenow = clock_getsystime(); + double msectogo = - clock_gettimesince(x->x_targettime); + if (msectogo < 1E-9) + { + outlet_float(x->x_obj.ob_outlet, x->x_targetval); + } + else + { + if(x->x_setval < x->x_targetval) + { /* count upwards */ + outvalue = x->x_setval + x->x_stepcall * x->x_step; + } + else + { /* count downwards */ + outvalue = x->x_setval - x->x_stepcall * x->x_step; + } + outlet_float(x->x_obj.ob_outlet, outvalue); + clock_delay(x->x_clock, (x->x_steptime > msectogo ? msectogo : x->x_steptime)); + } + x->x_stepcall++; +} + +static void step_float(t_step *x, t_float f) +{ + double timenow = clock_getsystime(); + if (x->x_gotinlet && x->x_in1val > 0 && x->x_step != 0 && f != x->x_setval) + { + if (timenow > x->x_targettime) x->x_setval = x->x_targetval; + else x->x_setval = x->x_setval + x->x_1overtimediff * + (timenow - x->x_prevtime) + * (x->x_targetval - x->x_setval); + x->x_prevtime = timenow; + x->x_targetval = f; /* where to end */ + x->x_stepcall = 0; + /* how long does it take ? */ + x->x_targettime = clock_getsystimeafter(x->x_in1val); + if(x->x_setval < x->x_targetval) + { + x->x_steptime = x->x_in1val / (int)((x->x_targetval - x->x_setval) / x->x_step); + } + else + { + x->x_steptime = x->x_in1val / (int)((x->x_setval - x->x_targetval) / x->x_step); + } + // post("steptime %g", x->x_steptime); + step_tick(x); + x->x_gotinlet = 0; + x->x_1overtimediff = 1./ (x->x_targettime - timenow); + /* call tick function */ + clock_delay(x->x_clock, x->x_steptime); + + } + else + { + clock_unset(x->x_clock); + x->x_targetval = x->x_setval = f; + outlet_float(x->x_obj.ob_outlet, f); + } + x->x_gotinlet = 0; +} + +static void step_ft1(t_step *x, t_floatarg g) +{ + x->x_in1val = g; + x->x_gotinlet = 1; +} + +static void step_ft2(t_step *x, t_floatarg g) +{ + if (g <= 0) g = 1; + x->x_step = g; + x->x_gotinlet = 1; +} + +static void step_stop(t_step *x) +{ + x->x_targetval = x->x_setval; + clock_unset(x->x_clock); +} + +static void step_free(t_step *x) +{ + clock_free(x->x_clock); +} + +static void *step_new(t_floatarg f, t_floatarg step, t_floatarg grain) +{ + t_step *x = (t_step *)pd_new(step_class); + x->x_targetval = x->x_setval = f; + x->x_gotinlet = 0; + x->x_1overtimediff = 1; + x->x_clock = clock_new(x, (t_method)step_tick); + x->x_targettime = x->x_prevtime = clock_getsystime(); + if (grain <= 0) grain = 20; + x->x_grain = grain; + if (step <= 0) step = 1; + x->x_step = step; + outlet_new(&x->x_obj, gensym("float")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft2")); +#ifndef MAXLIB + post(version); +#endif + return (x); +} + +void step_setup(void) +{ + step_class = class_new(gensym("step"), (t_newmethod)step_new, + (t_method)step_free, sizeof(t_step), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(step_class, (t_method)step_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(step_class, (t_method)step_ft2, gensym("ft2"), A_FLOAT, 0); + class_addmethod(step_class, (t_method)step_stop, gensym("stop"), 0); + class_addfloat(step_class, (t_method)step_float); + class_sethelpsymbol(step_class, gensym("maxlib/help-step.pd")); +} diff --git a/src/subst.c b/src/subst.c new file mode 100644 index 0000000..e62f1a9 --- /dev/null +++ b/src/subst.c @@ -0,0 +1,408 @@ +/* ------------------------- subst ------------------------------------------ */ +/* */ +/* Performs 'self-similar' substitution of a given list of values. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ +#include "m_pd.h" + + +static char *version = "subst v0.1, self-similar substitution of rows (list or array)\n" + " written by Olaf Matthes "; + +#undef DEBUG +//#define DEBUG + +#define MAXSIZE 1024 + +#include +#include + + +// +// Maxlife object data structure +// +typedef struct subst +{ + t_object x_obj; // must begin every object + t_outlet *x_outlist; // outlet for the processed list + t_outlet *x_outlength; + t_atom x_row[MAXSIZE]; // row of values to get processed + t_int x_length; // length of row + t_int x_order; // size of the CA field/world + t_symbol *x_array; // name of array that holds the data + t_garray *x_buf; // content of that array +} t_subst; + +// +// Function prototypes for our methods and functions +// +static t_class *subst_class; // global variable that points to the Maxlife class + +// +// get the sum of intervals from no a to no b +// Parameters: the row, it's length, intv a, intev b +// +static int sum_intv(t_atom *argv, int argc, int a, int b) +{ + int i; + int summe = 0; // sum of intervals + + if(a==b) + return(0); // same index + if(atom_getintarg(a, argc, argv) == atom_getintarg(b, argc, argv)) + return(0); // same value + + for(i=a;i atom_getintarg(i, argc, argv)) // positive intv. + { + summe += ((atom_getintarg(i + 1, argc, argv) - atom_getintarg(i, argc, argv)) % 12); + } + else // negative interval + { + summe -= ((atom_getintarg(i + 1, argc, argv) - atom_getintarg(i, argc, argv)) % 12); + } + } + return(summe); +} +//----- Anzahl Partialreihen mit Interval d ------------------------------- +static int no_part(t_atom *argv, int argc, int a, int b, int d) // nn +{ + int i,j,r = 0; + + if(a = b)return(0); + + for(i = a; i < b; i++) + { + for(j=a+1;j no_part(argv, argc, a, b, d)) + return(-1); + for(i = 1; i = (b - a); i++) + { + for(j = 1; j = b; j++) + { + if(sum_intv(argv, argc, i, j) == d) + r++; + } + } + return(r); +} +//----- Test, ob Partialreihe der Ordnung o mit Interval d existiert ---------- +static int check_part_intv(t_atom *argv, int argc, int o, int d) +{ + int z; + + for(z = 0; z < argc - o; z++) + { + if(sum_intv(argv, argc, z, z + o) == d) + return(z); // Abstand von Reihenanfang + } + + return(-1); +} + +static int my_random(int range) { + int ret = rand(); + ret = ret % range; // limit to desired output range + return(ret); +} + +// +// the substitution algorhythm +// +static int subst_calc(t_subst *x, int n) +{ + int i,j,k,l,o = x->x_order; + int s = -1; + int intv; + t_atom newrow[MAXSIZE]; + t_garray *A = x->x_buf; + int npoints; + t_float *vec; + + if(x->x_length <= 1) + { + post("subst: need some data first!"); + return(-1); + } + srand((unsigned int)clock_getlogicaltime()); + + if(n == -1) // no interval given: choose one by chance + { + do + { + n = my_random(x->x_length - 1); // choose interval + intv = sum_intv(x->x_row, x->x_length, n, n + 1); // size of this interval + } + while(intv == 0); // ...until we found one that is not 0! + } + else intv = sum_intv(x->x_row, x->x_length, n, n + 1); + +#ifdef DEBUG + post("subst: substitution of %dth interval (%d halftones)", n+1, intv); +#endif + + /* for-Schleife für möglichst lange Substitutionen + for(j=anzahlReihe(alteReihe);j>2;j--) */ + for(j = x->x_order; j < x->x_length; j++) // prefer lower orders (min. 2) + { // search for possible order... + s = check_part_intv(x->x_row, x->x_length, j, intv); + if(s != -1) // check if there is a partial row with the interval we want + { + o = j; // save actual order, might be larger then x->x_order + break; // ... do it! + } + if(o == j)break; // found one + } + + for(i = 0; i < x->x_length; i++) + { + if(i <= n) // just copy values before substitution + { + newrow[i] = x->x_row[i]; + } + if((i == n) && (s != -1)) // now substitute + { + for(k=1;kx_row, x->x_length, s+k-1, s+k))); +#ifdef DEBUG + post("subst: new interval[%d]: %d ", k, sum_intv(x->x_row, x->x_length, s+k-1, s+k)); +#endif + } + post("subst: replaced interval %d (%d halftones) with %d new intervals", n, intv, o); + } + else if((i == n) && (s == -1)) // not partial row found + { + o = 1; // order is 1 -> now substitution + newrow[i] = x->x_row[i]; // copy the next value of the row + post("subst: coundn't find any partial rows to fit in!"); + } + + if(i>n) // behind substitution + { + newrow[i+(o-1)] = x->x_row[i]; // copy rest or row + } + } + + // copy stuff back... + x->x_length = l = x->x_length + o - 1; + for(i = 0; i < x->x_length; i++) + x->x_row[i] = newrow[i]; + + // write to array + if(x->x_array)if (!(A = (t_garray *)pd_findbyclass(x->x_array, garray_class))) + error("subst: %s: no such array", x->x_array->s_name); + else if (!garray_getfloatarray(A, &npoints, &vec)) + error("subst: %s: bad template ", x->x_array->s_name); + else + { + i = 0; + + if (l >= npoints) // keep end of array + { + while(npoints--) + { + *vec++ = atom_getfloat(x->x_row + i); + i++; + } + } + else // update + { + npoints -= l; + while (l--) + { + *vec++ = atom_getfloat(x->x_row + i); + i++; + } + while (npoints--) *vec++ = 0; + } + garray_redraw(A); + } + + // output stuff + outlet_float(x->x_outlength, x->x_length); + outlet_list(x->x_outlist, NULL, x->x_length, x->x_row); + + return(0); +} + +static void subst_list(t_subst *x, t_symbol *s, int argc, t_atom *argv) +{ + t_garray *b = x->x_buf; /* make local copy of array */ + float *tab; /* we'll store notes in here */ + int items; + int i; + + for(i = 0; i < argc; i++) + { + x->x_row[i] = argv[i]; // just copy input + } + x->x_length = argc; + +} + +// +// choose the array that holds the processed row (output!!) +// +void subst_set(t_subst *x, t_symbol *s) +{ + t_garray *b; + + x->x_array = s; + + if ((b = (t_garray *)pd_findbyclass(s, garray_class))) + { + post("subst: array set to \"%s\"", s->s_name); + x->x_buf = b; + } else { + post("subst: no array \"%s\" (error %d)", s->s_name, b); + x->x_buf = 0; + } +} + +// +// load row from array (input!!) +// +static void subst_load(t_subst *x, t_symbol *s) +{ + t_garray *b; /* make local copy of array */ + t_float *tab; /* the content itselfe */ + int items, i; + + if ((b = (t_garray *)pd_findbyclass(s, garray_class))) + { + post("subst: array set to \"%s\"", s->s_name); + } else { + post("subst: no array \"%s\" (error %d)", s->s_name, b); + return; + } + + // read from our array + if (!garray_getfloatarray(b, &items, &tab)) + { + post("subst: couldn't read from array!"); + return; + } + for(i = 0; i < items; i++) + { + SETFLOAT(x->x_row + i, tab[i]); // copy array into x->x_row + } + x->x_length = items; + post("subst: loaded %d values from array \"%s\"", items, s->s_name); +} + +// +// substitute an interval choosen by chance +// +static void subst_bang(t_subst *x) +{ + subst_calc(x, -1); +} + +// +// substitute the Nth interval +// +static void subst_intv(t_subst *x, t_floatarg f) +{ + int i = (int)f; + if(i > x->x_length) i = x->x_length; + subst_calc(x, i); +} + +// +// set the minimum order of substitution +// +static void subst_set_order(t_subst *x, t_floatarg f) +{ + x->x_order = (t_int)f; + if(x->x_order < 2)x->x_order = 2; + post("subst: set order to %d", x->x_order); +} + +// +// method to print out: but what? +// +static void subst_display(t_subst *x) +{ +} + +// +// function to create an instance of the subst class +// +static void *subst_new(t_symbol *s, int argc, t_atom *argv) +{ + long i; + t_symbol *sym; + t_subst *x = (t_subst *)pd_new(subst_class); +#ifndef MAXLIB + post(version); +#endif + // read in order... + x->x_order = 3; + if(argc == 1) + { + x->x_order = atom_getintarg(0, argc, argv); + } + else if(argc == 2) + { + sym = atom_getsymbolarg(0, argc, argv); + x->x_order = atom_getintarg(1, argc, argv); + subst_set(x, sym); + } + + // create outlets + x->x_outlist = outlet_new(&x->x_obj, gensym("list")); + x->x_outlength = outlet_new(&x->x_obj, gensym("float")); + + return(x); // always return a copy of the created object +} + +static void subst_free(t_subst *x) +{ +} + +void subst_setup(void) +{ + subst_class = class_new(gensym("subst"), (t_newmethod)subst_new, + (t_method)subst_free, sizeof(t_subst), 0, A_GIMME, 0); + class_addmethod(subst_class, (t_method)subst_set_order, gensym("order"), A_FLOAT, 0); + class_addmethod(subst_class, (t_method)subst_intv, gensym("interval"), A_FLOAT, 0); + class_addmethod(subst_class, (t_method)subst_set, gensym("set"), A_SYMBOL, 0); + class_addmethod(subst_class, (t_method)subst_load, gensym("load"), A_SYMBOL, 0); + class_addmethod(subst_class, (t_method)subst_display, gensym("display"), 0); + class_addlist(subst_class, subst_list); + class_addbang(subst_class, subst_bang); + class_sethelpsymbol(subst_class, gensym("maxlib/help-subst.pd")); +} diff --git a/src/temperature.c b/src/temperature.c new file mode 100644 index 0000000..078ad27 --- /dev/null +++ b/src/temperature.c @@ -0,0 +1,109 @@ +/* -------------------------- temperature ------------------------------------- */ +/* */ +/* Calculates temperature: number of 'events' within N milliseconds. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include + +static char *version = "temperature v0.1, written by Olaf Matthes "; + +typedef struct temperature +{ + t_object x_ob; + t_clock *x_clock; + t_outlet *x_outfloat; /* output the temperature */ + t_int x_index; /* the number of elements to average */ + t_int x_time; + +} t_temperature; + +static void temperature_tick(t_temperature *x) +{ + outlet_float(x->x_outfloat, x->x_index); + x->x_index = 0; + clock_delay(x->x_clock, x->x_time); +} + +static void temperature_float(t_temperature *x, t_floatarg f) +{ + x->x_index++; /* just count number of 'events' */ +} + +static void temperature_anything(t_temperature *x, t_symbol *s, int argc, t_atom *argv) +{ + x->x_index++; /* just count number of 'events' */ +} + +static void temperature_time(t_temperature *x, t_floatarg f) +{ + x->x_time = (t_int)f; + if(x->x_time < 1) x->x_time = 1; + clock_unset(x->x_clock); + clock_delay(x->x_clock, x->x_time); +} + +static void temperature_reset(t_temperature *x) +{ + x->x_index = 0; + post("temperature: reset"); +} + +static void temperature_free(t_temperature *x) +{ + clock_free(x->x_clock); +} + +static t_class *temperature_class; + +static void *temperature_new(t_floatarg f) +{ + t_temperature *x = (t_temperature *)pd_new(temperature_class); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("time")); + x->x_outfloat = outlet_new(&x->x_ob, gensym("float")); + x->x_clock = clock_new(x, (t_method)temperature_tick); + + x->x_time = (t_int)f; + if(x->x_time < 1) + { + x->x_time = 1; + post("temperature: set time to %d ms", x->x_time); + } + x->x_index = 0; + clock_delay(x->x_clock, x->x_time); +#ifndef MAXLIB + post(version); +#endif + return (void *)x; +} + +void temperature_setup(void) +{ + temperature_class = class_new(gensym("temperature"), (t_newmethod)temperature_new, + (t_method)temperature_free, sizeof(t_temperature), 0, A_DEFFLOAT, 0); + class_addmethod(temperature_class, (t_method)temperature_reset, gensym("reset"), 0); + class_addfloat(temperature_class, temperature_float); + class_addmethod(temperature_class, (t_method)temperature_time, gensym("time"), A_FLOAT, 0); + class_addanything(temperature_class, temperature_anything); + class_sethelpsymbol(temperature_class, gensym("maxlib/help-temperature.pd")); +} + diff --git a/src/tilt.c b/src/tilt.c new file mode 100644 index 0000000..138e9ca --- /dev/null +++ b/src/tilt.c @@ -0,0 +1,176 @@ +/* ------------------------- tilt --------------------------------------------- */ +/* */ +/* Monitor input for changes. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Inspired by code written by Trond Lossius. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include + +#define MAXSIZE 32 + +static char *version = "tilt v0.1, written by Olaf Matthes "; + +typedef struct tilt +{ + t_object x_ob; + t_outlet *x_out; /* result */ + t_clock *x_clock; + + t_float x_tilt; /* the result */ + t_float x_start_tilt; + t_float x_t; + t_float x_sa; + t_float x_sb; + t_float x_offset; + t_float x_time; + t_float x_wait; + t_float x_hi_limit; + t_float x_low_limit; + t_float x_trip_point; +} t_tilt; + +static void tilt_tick(t_tilt *x) +{ + x->x_sb = x->x_t - x->x_offset; + if((x->x_sb - x->x_sa) > x->x_hi_limit) + { + x->x_sa = x->x_sb; + clock_delay(x->x_clock, x->x_wait); + return; + } + else + { + if((x->x_sb - x->x_sa) > x->x_trip_point) + { + outlet_bang(x->x_out); + clock_delay(x->x_clock, x->x_wait); + return; + } + if((x->x_sb - x->x_sa) < x->x_low_limit) + { + x->x_time++; + if(x->x_time > 15) + { + x->x_start_tilt = x->x_sa; + x->x_time = 0; + } + } + if((x->x_sb - x->x_start_tilt) > x->x_tilt) + { + outlet_bang(x->x_out); + clock_delay(x->x_clock, x->x_wait); + } + else + { + x->x_sa = x->x_sb; + clock_delay(x->x_clock, x->x_wait); + return; + } + } +} + +static void tilt_float(t_tilt *x, t_floatarg f) +{ + x->x_t = f; +} + +static void tilt_intv(t_tilt *x, t_floatarg f) +{ + x->x_wait = f; +} + +static void tilt_tilt(t_tilt *x, t_floatarg f) +{ + x->x_tilt = f; + post("tilt: set tilt to %g", x->x_tilt); +} + +static void tilt_hi_limit(t_tilt *x, t_floatarg f) +{ + x->x_hi_limit = f; + post("tilt: set high limit to %g", x->x_hi_limit); +} + +static void tilt_low_limit(t_tilt *x, t_floatarg f) +{ + x->x_low_limit = f; + post("tilt: set low limit to %g", x->x_low_limit); +} + +static void tilt_trip_point(t_tilt *x, t_floatarg f) +{ + x->x_trip_point = f; + post("tilt: set trip point to %g", x->x_trip_point); +} + +static void tilt_free(t_tilt *x) +{ + clock_free(x->x_clock); +} + +static t_class *tilt_class; + +static void *tilt_new(t_floatarg f, t_floatarg f2) +{ + int i; + + t_tilt *x = (t_tilt *)pd_new(tilt_class); + inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("float"), gensym("intv")); + x->x_out = outlet_new(&x->x_ob, gensym("float")); + x->x_clock = clock_new(x, (t_method)tilt_tick); + + x->x_t = f; /* set initial value */ + if(f2 > 4) + x->x_wait = f2; + else + x->x_wait = 4000; + x->x_offset = 0; + x->x_sa = 0; + x->x_sb = 0; + x->x_time = 0; + x->x_tilt = 0; + x->x_start_tilt = x->x_sa = x->x_t - x->x_offset; + x->x_hi_limit = x->x_low_limit = x->x_trip_point = 0; + clock_delay(x->x_clock, x->x_wait); /* wait 4 sec and start calculation */ + +#ifndef MAXLIB + post(version); +#endif + post("tilt: set interval to %g msec", x->x_wait); + return (void *)x; +} + +void tilt_setup(void) +{ + tilt_class = class_new(gensym("tilt"), (t_newmethod)tilt_new, + (t_method)tilt_free, sizeof(t_tilt), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(tilt_class, tilt_float); + class_addmethod(tilt_class, (t_method)tilt_intv, gensym("intv"), A_FLOAT, 0); + class_addmethod(tilt_class, (t_method)tilt_tilt, gensym("tilt"), A_FLOAT, 0); + class_addmethod(tilt_class, (t_method)tilt_hi_limit, gensym("hi"), A_FLOAT, 0); + class_addmethod(tilt_class, (t_method)tilt_low_limit, gensym("low"), A_FLOAT, 0); + class_addmethod(tilt_class, (t_method)tilt_trip_point, gensym("trip"), A_FLOAT, 0); + class_sethelpsymbol(tilt_class, gensym("maxlib/help-tilt.pd")); +} + diff --git a/src/triang.c b/src/triang.c new file mode 100644 index 0000000..8ea83e2 --- /dev/null +++ b/src/triang.c @@ -0,0 +1,70 @@ +/* ---------------------------- rand_triang ----------------------------------- */ +/* */ +/* rand_triang generates a triangularly distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX + +static char *version = "triang v0.1, generates triangularly distributed random variable\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_triang ------------------------------ */ + +static t_class *rand_triang_class; + +typedef struct _rand_triang +{ + t_object x_obj; +} t_rand_triang; + +static void *rand_triang_new(t_floatarg f) +{ + t_rand_triang *x = (t_rand_triang *)pd_new(rand_triang_class); + srand( (unsigned)time( NULL ) ); + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void rand_triang_bang(t_rand_triang *x) +{ + t_float u1, u2; + u1 = fran(); + u2 = fran(); + outlet_float(x->x_obj.ob_outlet, 0.5*(u1+u2)); +} + +void triang_setup(void) +{ + rand_triang_class = class_new(gensym("triang"), (t_newmethod)rand_triang_new, 0, + sizeof(t_rand_triang), 0, A_DEFFLOAT, 0); + class_addbang(rand_triang_class, rand_triang_bang); + class_sethelpsymbol(rand_triang_class, gensym("maxlib/help-triang.pd")); +#ifndef MAXLIB + post(version); +#endif +} \ No newline at end of file diff --git a/src/velocity.c b/src/velocity.c new file mode 100644 index 0000000..0f2d481 --- /dev/null +++ b/src/velocity.c @@ -0,0 +1,102 @@ +/* ------------------------- velocity ----------------------------------------- */ +/* */ +/* Get velocity of input in digits per second. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Originally written for Max by Trond Lossius. */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +// velocity.c: (1000*(x[n]-x[n-1]))/Æt +// (C) Trond Lossius/BEK 2000 +// Last revision: 4/7 2000 +// +// Input: +// float +// int (converted to float) +// set (set new value but no output) +// Argument (optional, defaults to 0): +// Initial stored value +// Output: +// float: Velocity as change per second (not ms) + +#include "m_pd.h" + +static char *version = "velocity v0.1, written by Olaf Matthes "; + +typedef struct velocity +{ + t_object x_ob; + t_outlet *x_out; + t_float x_xn1; /* input (floats) */ + t_float x_xn; + double x_lasttime; +} t_velocity; + +static void velocity_bang(t_velocity *x) +{ + double thistime; + t_float vel; + + thistime = clock_getlogicaltime(); + vel = (1000 * (x->x_xn - x->x_xn1) ) / (clock_gettimesince(x->x_lasttime)); + x->x_lasttime = thistime; + outlet_float(x->x_out, vel); +} + +static void velocity_float(t_velocity *x, t_floatarg f) +{ + x->x_xn1 = x->x_xn; + x->x_xn = f; + velocity_bang(x); +} + +static void velocity_free(t_velocity *x) +{ + // nothing to do +} + +static t_class *velocity_class; + +static void *velocity_new(t_floatarg f) +{ + t_velocity *x = (t_velocity *)pd_new(velocity_class); + x->x_out = outlet_new(&x->x_ob, gensym("float")); +#ifndef MAXLIB + post(version); +#endif + x->x_lasttime = clock_getlogicaltime(); + + return (void *)x; +} + +void velocity_setup(void) +{ + velocity_class = class_new(gensym("velocity"), (t_newmethod)velocity_new, + (t_method)velocity_free, sizeof(t_velocity), 0, A_DEFFLOAT, 0); + class_addfloat(velocity_class, velocity_float); + class_addbang(velocity_class, velocity_bang); + class_sethelpsymbol(velocity_class, gensym("maxlib/help-velocity.pd")); +} + diff --git a/src/weibull.c b/src/weibull.c new file mode 100644 index 0000000..02b8a4e --- /dev/null +++ b/src/weibull.c @@ -0,0 +1,85 @@ +/* ---------------------------- rand_weibull ---------------------------------- */ +/* */ +/* rand_weibull generates a weibull distributed random variable. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Based on code found in Dodge/Jerse "Computer Music" */ +/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */ +/* */ +/* 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 */ +/* of the License, 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 for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include +#include +#include + +#define fran() (t_float)rand()/(t_float)RAND_MAX +#ifndef M_PI +#define M_PI 3.1415927 +#endif + +static char *version = "weibull v0.1, generates a weibull distributed random variable\n" + " written by Olaf Matthes "; + +/* -------------------------- rand_weibull ------------------------------ */ + +static t_class *rand_weibull_class; + +typedef struct _rand_weibull +{ + t_object x_obj; + t_float x_s; + t_float x_t; +} t_rand_weibull; + +static void *rand_weibull_new(t_floatarg s, t_floatarg t) +{ + t_rand_weibull *x = (t_rand_weibull *)pd_new(rand_weibull_class); + srand( (unsigned)time( NULL ) ); + floatinlet_new(&x->x_obj, &x->x_s); + floatinlet_new(&x->x_obj, &x->x_t); + outlet_new(&x->x_obj, &s_float); + x->x_s = s; + x->x_t = t; + return (x); +} + +static void rand_weibull_bang(t_rand_weibull *x) +{ + t_float u, a, t, tinv; + t = (x->x_t <= 0 ? 0.0001 : x->x_t); + tinv = 1/t; + do + { + u = fran(); + } + while(u == 0 || u == 1); + a = 1/(1 - u); + outlet_float(x->x_obj.ob_outlet, x->x_s*pow(log(a), tinv)); +} + +void weibull_setup(void) +{ + rand_weibull_class = class_new(gensym("weibull"), (t_newmethod)rand_weibull_new, 0, + sizeof(t_rand_weibull), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addbang(rand_weibull_class, rand_weibull_bang); + class_sethelpsymbol(rand_weibull_class, gensym("maxlib/help-weibull.pd")); +#ifndef MAXLIB + post(version); +#endif +} -- cgit v1.2.1