text: #include "MSPd.h"
  
  
  static t_class *expflam_class;
  
  #define OBJECT_NAME "expflam~"
  
  #define MAXFLAMS (64)
  #define MAXATTACKS (128)
  #define STOPGAIN (.001)
  
  
  typedef struct
  {
    int attack_count; // number of triggers per flam event
    float *attack_times; // trigger times in seconds
    int *attack_points; // trigger times in samples
    int fdex; // current flam
    float gainatten; // attenuation factor
    float amp; // current amp
    int atks;// number of attacks per flam
    long counter; // internal clock
    short active; // flag that flam is turned on
  
  
  } t_flam;
  
  typedef struct _expflam
  {
  
    t_object x_obj;
    float x_f;
    t_flam *flams; // contain flams
    float start_delay; // initial flam delay
    float end_delay;// end delay
    float atten; // attenuation factor
    float slope;// slope of curve
    int atks;// number of attacks per flam
    float sr;
    float *trigvec; // hold input vector (to protect from memory sharing)
    float *bypvec; // ditto for flamgate vector
    short flamall; // flag to put a flam on everything
    short bypass; // flag to copy input to output without flam
    short flamgate_connected; // flag that a flamgate logical signal is connected to inlet 2
  
  } t_expflam;
  
  
  static void *expflam_new(void);
  static t_int *expflam_perform(t_int *w);
  static void expflam_dsp(t_expflam *x, t_signal **sp);
  static void expflam_setflam(t_expflam *x, t_symbol *msg, int argc, t_atom *argv);
  static void expflam_free(t_expflam *x);
  static void expflam_assist(t_expflam *x, void *b, long msg, long arg, char *dst);
  static void expflam_flamall(t_expflam *x, t_floatarg tog);
  static void expflam_bypass(t_expflam *x, t_floatarg tog);
  
  
  void expflam_tilde_setup(void)
  {
    expflam_class = class_new(gensym("expflam~"),(t_newmethod)expflam_new,
                              (t_method)expflam_free, sizeof(t_expflam), 0, 0);
    CLASS_MAINSIGNALIN(expflam_class,t_expflam, x_f );
    class_addmethod(expflam_class,(t_method)expflam_dsp,gensym("dsp"),A_CANT,0);
    class_addmethod(expflam_class,(t_method)expflam_setflam,gensym("setflam"),A_GIMME,0);
    class_addmethod(expflam_class,(t_method)expflam_flamall,gensym("flamall"),A_FLOAT,0);
    class_addmethod(expflam_class,(t_method)expflam_bypass,gensym("bypass"),A_FLOAT,0);
    potpourri_announce(OBJECT_NAME);
  }
  
  void expflam_flamall(t_expflam *x, t_floatarg tog)
  {
    x->flamall = (short) tog;
  }
  
  void expflam_bypass(t_expflam *x, t_floatarg tog)
  {
    x->bypass = (short) tog;
  }
  
  void expflam_assist(t_expflam *x, void *b, long msg, long arg, char *dst)
  {
    if (msg==1) {
      switch (arg) {
      case 0: sprintf(dst,"(signal) Trigger Click"); break;
      case 1: sprintf(dst,"(signal) Flam Gate"); break;
      }
    } else if (msg==2) {
      sprintf(dst,"(signal) Flam Clicks");
    }
  }
  
  void *expflam_new(void)
  {
    int i;
    t_expflam *x = (t_expflam *)pd_new(expflam_class);
    inlet_new(&x->x_obj, &x->x_obj.ob_pd,gensym("signal"), gensym("signal"));
    outlet_new(&x->x_obj, gensym("signal"));
    x->flams = (t_flam *) calloc(MAXFLAMS, sizeof(t_flam));
    for(i = 0; i < MAXFLAMS; i++) {
      x->flams[i].attack_times = (float *) calloc(MAXATTACKS, sizeof(float));
      x->flams[i].attack_points = (int *) calloc(MAXATTACKS, sizeof(int));
    }
  
    x->trigvec = malloc(8192 * sizeof(float)); // maximum vector size
    x->bypvec = malloc(8192 * sizeof(float)); // maximum vector size
    x->sr = sys_getsr();
    x->start_delay = .025;
    x->end_delay = 0.1;
    x->slope = -3.0;
    x->atks = 8;
    x->atten = 0.8;
    x->bypass = 0;
    x->flamall = 0;
  
    return x;
  }
  
  void expflam_setflam(t_expflam *x, t_symbol *msg, int argc, t_atom *argv)
  {
    if( argc != 5 ) {
      error("%s: setflam format: startdelay enddelay attacks slope gainatten",OBJECT_NAME);
      return;
    }
    x->start_delay = atom_getfloatarg(0,argc,argv) * 0.001;
    x->end_delay = atom_getfloatarg(1,argc,argv) * 0.001;
    x->atks = (int) atom_getfloatarg(2,argc,argv);
    x->slope = atom_getfloatarg(3,argc,argv);
    x->atten = atom_getfloatarg(4,argc,argv);
    if(x->slope == 0)
      x->slope = .0001;
    if(x->start_delay <= 0)
      x->start_delay = .00001;
    if(x->end_delay <= 0)
      x->end_delay = .00001;
    if(x->atks < 2)
      x->atks = 2;
    if(x->atks > MAXATTACKS) {
      post("%s: exceeded maximum of %d attacks",OBJECT_NAME, MAXATTACKS);
      x->atks = MAXATTACKS;
    }
  }
  
  void expflam_free(t_expflam *x)
  {
    int i;
  
    free(x->trigvec);
    free(x->bypvec);
    for(i = 0; i < MAXFLAMS; i++) {
      free(x->flams[i].attack_times);
      free(x->flams[i].attack_points);
    }
    free(x->flams);
  
  }
  
  t_int *expflam_perform(t_int *w)
  {
    int i,j,k;
    t_expflam *x = (t_expflam *) (w[1]);
    float *in_vec = (t_float *)(w[2]);
    float *in2_vec = (t_float *)(w[3]);
    float *out_vec = (t_float *)(w[4]);
    int n = (int) w[5];
  
    float *trigvec = x->trigvec;
    float *flamgate_vec = x->bypvec;
    t_flam *flams = x->flams;
    int atks = x->atks;
    float atten = x->atten;
    float slope = x->slope;
    float start_delay = x->start_delay;
    float end_delay = x->end_delay;
    float sr = x->sr;
    short flamgate_connected = x->flamgate_connected;
    short flamall = x->flamall;
  
    /* in flamgate mode copy input to output and return */
    if(x->bypass) {
      memcpy( (void *)out_vec, (void *)in_vec, n * sizeof(float) );
      return (w+6);
    }
    /* copy input vectors */
    memcpy( (void *)flamgate_vec, (void *)in2_vec, n * sizeof(float) );// the order of these mcopies matters
    memcpy( (void *)trigvec, (void *)in_vec, n * sizeof(float) );
    memcpy( (void *)out_vec, (void *)in_vec, n * sizeof(float) );// copy triggers to output for a start
  
    /* look for activation triggers */
    for(i = 0; i < n; i++) {
      if(trigvec[i] && (flamgate_vec[i] || ! flamgate_connected || flamall ) ) {
  //    post("triggered with t %f and flamgate %f",trigvec[i],flamgate_vec[i]);
        j = 0;
        while(flams[j].active && j < MAXFLAMS) {
          ++j;
        }
        if(j >= MAXFLAMS) {
          post("too many flams");
        }
        else {
  //        post("inserting flam at location %d",j);
          flams[j].active = 1;
          flams[j].attack_times[0] = 0.0;
          flams[j].attack_points[0] = i;
          flams[j].gainatten = atten;
          flams[j].amp = trigvec[i];
          flams[j].counter = 0;
          flams[j].fdex = 0;
          flams[j].atks = atks;
  
          for(k = 1; k < atks; k++) {
            flams[j].attack_times[k] = start_delay + (end_delay - start_delay) * ((1.0 - exp((float)k * slope/((float)atks-1.0)))/(1.0-exp(slope)));
            flams[j].attack_times[k] += flams[j].attack_times[k - 1];
            flams[j].attack_points[k] = flams[j].attack_times[k] * sr + i;
          }
        }
      }
    }
    /* now iterate through active flams */
    for( i = 0; i < n; i++) {
      for(j = 0; j < MAXFLAMS; j++) {
        if(flams[j].active) {
          if(flams[j].counter >= flams[j].attack_points[flams[j].fdex]) {
            out_vec[i] += flams[j].amp;
            flams[j].amp *= flams[j].gainatten;
            if( flams[j].amp <= STOPGAIN ) {
              flams[j].active = 0;
            }
            flams[j].fdex++;
            if(flams[j].fdex >= flams[j].atks) {
              flams[j].active = 0;
            }
          }
          flams[j].counter++;
        }
      }
    }
  
    return w+6;
  }
  
  void expflam_dsp(t_expflam *x, t_signal **sp)
  {
  
    x->flamgate_connected = 1;
    dsp_add(expflam_perform, 5, x,
            sp[0]->s_vec,
            sp[1]->s_vec,
            sp[2]->s_vec,
            (t_int)sp[0]->s_n
      );
  }
  