#include "MSPd.h"

#define MAX_ATTACKS (256)
#define MAX_PATTERNS (128)


#define OBJECT_NAME "dmach~"

#if __PD__
static t_class *dmach_class;
#endif

#if __MSP__
void *dmach_class;
#endif

typedef struct
{
  float trigger_point;
  float increment;
  float amplitude;
} t_attack;

typedef struct
{
  short active; // flag for if this drum slot is used in current pattern
  int attack_count; // number of attacks in this pattern
  int adex; // index to current attack
  t_attack *attacks; // array containing attack data
} t_drumline;

typedef struct
{
  float beats; // how many beats in this pattern
  float dsamps; //duration of pattern in samples
  t_drumline *drumlines;
} t_pattern;

typedef struct _dmach
{
#if __MSP__
  t_pxobject x_obj;
#endif
#if __PD__
  t_object x_obj;
  float x_f;
#endif
	short mute; // global mute
	float clocker; // global sample counter clock
	float tempo;
	float tempo_factor; // multiplier to get actual beat duration
	t_pattern *patterns; // contains all drum patterns
	short *stored_patterns;// which locations contain a pattern
	float *gtranspose;// transpose factor for each individual drum slot
	float *gains; // gain factor for each individual drum slot
	float *current_increment;// maintains increment for sustained output
	int this_pattern; // number of current pattern
	int next_pattern; // number of pattern to call at end of current pattern
	float global_gain;
	float global_transpose;
	float sr;
	int drum_count; // number of drum slots to instantiate
	int outlet_count; // number of outlets on object
	short virgin; // no patterns stored - turn off performance
	/* sequencer */
	short playsequence; // flag to play through a stored sequence once
	short loopsequence; // flag to loop repeatedly through sequence
	int *sequence; // contains the sequence of bars to play
	int sequence_length; // how many bars are stored in sequence
	int seqptr; // keep track of current sequencer position
	float zeroalias; // use this to send a coded "zero" message (i.e. bar number is zero)
	t_atom *listdata; // for list output
	void *listraw_outlet;// send a list
	short clickincr; //flag that click increment is on (i.e. no sample and hold)
	short *attackpattern; // holds full attack sequence including zeros (for list output)
	int attackpattern_count;// how many ticks in attack pattern
	t_attack *tmpatks; // hold local copy of new slot pattern
	short *connected; // list of vector connections
	short *muted; // state of each slot
	short instant_recall; // flag that pattern gets loaded immediately (loops only)
} t_dmach;

void dmach_store(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void *dmach_new(t_symbol *s, int argc, t_atom *argv);
t_int *dmach_perform(t_int *w);
void dmach_mute(t_dmach *x, t_floatarg toggle);
void dmach_dsp(t_dmach *x, t_signal **sp, short *count);
void dmach_assist(t_dmach *x, void *b, long m, long a, char *s);
void dmach_dsp_free(t_dmach *x);
void dmach_init_pattern(t_dmach *x, int pnum);
void dmach_show(t_dmach *x, t_floatarg fn);
void dmach_tempo(t_dmach *x, t_floatarg new_tempo);
void dmach_recall(t_dmach *x, t_floatarg pnf);
void dmach_transpose(t_dmach *x, t_floatarg slotf, t_floatarg new_transpose_factor);
void dmach_gain(t_dmach *x, t_floatarg slotf, t_floatarg new_gain_factor);
void dmach_arm(t_dmach *x, t_floatarg pnf);
void dmach_playsequence(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_slotamps(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_printraw(t_dmach *x, t_floatarg fn);
void dmach_readraw(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_slotincrs(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_loopsequence(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_muteslot(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_slotampsfull(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_nosequence(t_dmach *x);
void dmach_copypattern(t_dmach *x, t_floatarg pn1, t_floatarg pn2);
//void dmach_listraw(t_dmach *x, t_floatarg fn);
void dmach_listraw(t_dmach *x, t_symbol *s, int argc, t_atom *argv);
void dmach_clickincr(t_dmach *x, t_floatarg toggle);
void dmach_instant_recall(t_dmach *x, t_floatarg toggle);

#if __MSP__
void main(void)
{
    setup((t_messlist **)&dmach_class,(method) dmach_new, (method)dmach_dsp_free, 
		  (short)sizeof(t_dmach), 0, A_GIMME, 0);
    addmess((method)dmach_dsp, "dsp", A_CANT, 0);
    addmess((method)dmach_assist,"assist",A_CANT,0);
    addmess((method)dmach_mute,"mute",A_FLOAT,0);
    addmess((method)dmach_show,"show",A_FLOAT,0);
    addmess((method)dmach_recall,"recall",A_FLOAT,0);
    addmess((method)dmach_store,"store",A_GIMME,0);
    addmess((method)dmach_transpose,"transpose",A_FLOAT,A_FLOAT,0);
    addmess((method)dmach_tempo,"tempo",A_FLOAT,0);
	
    addmess((method)dmach_gain,"gain",A_FLOAT,A_FLOAT,0);    
    addmess((method)dmach_listraw,"listraw",A_GIMME,0);
    addmess((method)dmach_printraw,"printraw",A_FLOAT,0);
    addmess((method)dmach_recall,"recall",A_FLOAT,0);
    addmess((method)dmach_arm,"arm",A_FLOAT,0);
    addmess((method)dmach_readraw,"readraw",A_GIMME,0);
    addmess((method)dmach_playsequence,"playsequence",A_GIMME,0);
    addmess((method)dmach_slotamps,"slotamps",A_GIMME,0);
    addmess((method)dmach_slotampsfull,"slotampsfull",A_GIMME,0);
    addmess((method)dmach_slotincrs,"slotincrs",A_GIMME,0);
    addmess((method)dmach_loopsequence,"loopsequence",A_GIMME,0);
    addmess((method)dmach_muteslot,"muteslot",A_GIMME,0);
	addmess((method)dmach_nosequence,"nosequence",0);
	addmess((method)dmach_copypattern,"copypattern",A_FLOAT,A_FLOAT,0);
	addmess((method)dmach_clickincr,"clickincr",A_FLOAT,0);
	addmess((method)dmach_instant_recall,"instant_recall",A_FLOAT,0);
	
    dsp_initclass();
 	post("%s %s",OBJECT_NAME, LYONPOTPOURRI_MSG);
}
#endif
#if __PD__
void dmach_tilde_setup(void){
	dmach_class = class_new(gensym("dmach~"), (t_newmethod)dmach_new, 
							(t_method)dmach_dsp_free,sizeof(t_dmach), 0,A_GIMME,0);
	CLASS_MAINSIGNALIN(dmach_class, t_dmach, x_f);
	class_addmethod(dmach_class,(t_method)dmach_dsp,gensym("dsp"),0);
	class_addmethod(dmach_class,(t_method)dmach_mute,gensym("mute"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_show,gensym("show"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_tempo,gensym("tempo"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_store,gensym("store"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_recall,gensym("recall"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_transpose,gensym("transpose"),A_FLOAT,A_FLOAT, 0);
	class_addmethod(dmach_class,(t_method)dmach_gain,gensym("gain"),A_FLOAT,A_FLOAT, 0);
	
	class_addmethod(dmach_class,(t_method)dmach_printraw,gensym("printraw"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_arm,gensym("arm"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_readraw,gensym("readraw"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_listraw,gensym("listraw"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_playsequence,gensym("playsequence"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_slotamps,gensym("slotamps"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_slotampsfull,gensym("slotampsfull"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_slotincrs,gensym("slotincrs"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_loopsequence,gensym("loopsequence"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_muteslot,gensym("muteslot"),A_GIMME,0);
	class_addmethod(dmach_class,(t_method)dmach_nosequence,gensym("nosequence"),0);
	class_addmethod(dmach_class,(t_method)dmach_copypattern,gensym("copypattern"),A_FLOAT,A_FLOAT, 0);
	class_addmethod(dmach_class,(t_method)dmach_clickincr,gensym("clickincr"),A_FLOAT,0);
	class_addmethod(dmach_class,(t_method)dmach_instant_recall,gensym("instant_recall"),A_FLOAT,0);
	
 	post("%s %s",OBJECT_NAME, LYONPOTPOURRI_MSG);
}
#endif

void dmach_muteslot(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int slot;
	int drum_count = x->drum_count;
	short mutestate;
	
	if(argc < 2){
		post("muteslot: pattern number, slot number");
		return;
	}
	
	slot = (int)atom_getfloatarg(0,argc,argv);
	mutestate = (short)atom_getfloatarg(1,argc,argv);
	
	
	if(slot < 0 || slot > drum_count - 1){
		error("muteslot: illegal slot index: %d",slot);
		return;
	}
	x->muted[slot] = mutestate;
}

void dmach_nosequence(t_dmach *x)
{
	x->playsequence = 0;
	x->loopsequence = 0;
}

void dmach_playsequence(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	int pnum;
	
	if(argc < 1){
		error("%s: zero length sequence",OBJECT_NAME);
		return;
	}
	/* need safety check here */
	for(i = 0; i < argc; i++){
		pnum = (int) atom_getfloatarg(i,argc,argv);
		if(! x->stored_patterns[pnum]){
			error("%d is not currently stored",pnum);
			return;
		}
	}

	for(i = 0; i < argc; i++){
		x->sequence[i] = (int) atom_getfloatarg(i,argc,argv);
	}

	// 
	
	
	if(x->instant_recall){
		x->this_pattern = x->sequence[0];
		x->clocker = x->patterns[x->this_pattern].dsamps;
	} else {
		x->next_pattern = x->sequence[0];
	}
	
	x->mute = 0;
	x->playsequence = 1;
	x->loopsequence = 0;
	x->sequence_length = argc;
	x->seqptr = 0;
}
void dmach_loopsequence(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	int pnum;
	
	if(argc < 1){
		error("%s: zero length sequence",OBJECT_NAME);
		return;
	}
	
	for(i = 0; i < argc; i++){
		pnum = (int) atom_getfloatarg(i,argc,argv);
		if(! x->stored_patterns[pnum]){
			error("%d is not currently stored",pnum);
			return;
		}
	}
	
	for(i = 0; i < argc; i++){
		x->sequence[i] = (int) atom_getfloatarg(i,argc,argv);
	}

	if(x->instant_recall){
		x->this_pattern = x->sequence[0];
		x->clocker = x->patterns[x->this_pattern].dsamps;
	} else {
		x->next_pattern = x->sequence[0];
	}
	
	x->mute = 0;
	x->playsequence = 1;
	x->loopsequence = 1;
	x->sequence_length = argc;
	x->seqptr = 0;
}

void dmach_gain(t_dmach *x, t_floatarg slotf, t_floatarg new_gain_factor)
{
	int slot = slotf;
//	float ratio;
//	float gain_factor;
//	short *stored_patterns = x->stored_patterns;
	float *gains = x->gains;
//	t_pattern *p = x->patterns;
	int drum_count = x->drum_count;
//	int i,j,k;
	
	if(slot < 0 || slot > drum_count - 1){
		error("illegal slot index: %d",slot);
		return;
	}
	/* if(new_gain_factor <= 0){
		error("illegal gain factor %f", new_gain_factor);
		return;
	} */
//	gain_factor = gains[slot];
//	ratio = new_gain_factor / gain_factor;
	gains[slot] = new_gain_factor;
/*
	for(i = 0; i < MAX_PATTERNS; i++){
		if(stored_patterns[i]){
		    if(p[i].drumlines[slot].active){
		      for(k = 0; k < p[i].drumlines[slot].attack_count; k++){
						p[i].drumlines[slot].attacks[k].amplitude *= ratio;
		      }
		    }
		}
	}*/

}

void dmach_transpose(t_dmach *x, t_floatarg slotf, t_floatarg new_transpose_factor)
{
	int slot = slotf;
	float *gtranspose = x->gtranspose;
	int drum_count = x->drum_count;
	
	if(slot < 0 || slot > drum_count - 1){
		error("%s: transpose given illegal slot index: %d",OBJECT_NAME, slot);
		return;
	}
	if(new_transpose_factor == 0){
		error("illegal transpose factor %f", new_transpose_factor);
		return;
	}
	gtranspose[slot] = new_transpose_factor;
	
}


void dmach_recall(t_dmach *x, t_floatarg pnf)
{
	int pnum = pnf;
	
	/* post("requested recall of %d, ignored",pnum);
	return;*/
	if(pnum < 0){
		error("requested index is less than zero");
		return;
	}
	if(pnum >= MAX_PATTERNS){
		error("requested index is greater than the maximum of %d",MAX_PATTERNS-1);
		return;
	}
	if(! x->stored_patterns[pnum]){
		error("%d is not currently stored",pnum);
		return;
	}
//	x->this_pattern = x->next_pattern = pnum;
	x->mute = 0;
	x->next_pattern = pnum;
}

void dmach_arm(t_dmach *x, t_floatarg pnf)
{
	int pnum = pnf;
	int i;
	t_pattern *p = x->patterns;
		
	if(pnum < 0){
		error("requested index is less than zero");
		return;
	}
	if(pnum > MAX_PATTERNS){
		error("%s: requested index is greater than the maximum of %d",OBJECT_NAME,MAX_PATTERNS-1);
		return;
	}
	if(! x->stored_patterns[pnum]){
		error("%s: %d is not currently stored",OBJECT_NAME,pnum);
		return;
	}
	x->mute = 1;
	x->clocker = 0;
	x->next_pattern = x->this_pattern = pnum;
	for(i = 0; i < x->drum_count; i++){ /* reset pointers */
		p[x->this_pattern].drumlines[i].adex = 0;
	}
}


void dmach_tempo(t_dmach *x, t_floatarg new_tempo)
{
	float ratio;
	int i, j, k;
	short *stored_patterns = x->stored_patterns;
	t_pattern *p = x->patterns;
	int drum_count = x->drum_count;
	float sr = x->sr;
	float tempo_factor = x->tempo_factor;
	if(new_tempo <= 0.0){
		error("tempo must be greater than zero, but was %f",new_tempo);
		return;
	}
	ratio = x->tempo / new_tempo;
	x->clocker *= ratio;
	x->tempo = new_tempo;
  tempo_factor = (60.0/new_tempo);	

	for(i = 0; i < MAX_PATTERNS; i++){
		if(stored_patterns[i]){
		  p[i].dsamps = p[i].beats * tempo_factor * sr;
			for(j = 0; j < drum_count; j++){
			    if(p[i].drumlines[j].active){
			      for(k = 0; k < p[i].drumlines[j].attack_count; k++){
					p[i].drumlines[j].attacks[k].trigger_point *= ratio;
			      }
			    }
			}
		}
	}
	x->tempo_factor = tempo_factor;
}
void dmach_show(t_dmach *x, t_floatarg fn)
{
	int i,j;
	int pnum = (int) fn;
  t_pattern *p = x->patterns;
  t_attack *ptr;
  int drum_count = x->drum_count;
  
    if(pnum < 0 || pnum > MAX_PATTERNS-1){
    	error("illegal pattern number: %d",pnum);
    	return;
    }
    
	if(! x->stored_patterns[pnum]){
		error("%d is not currently stored",pnum);
		return;
	}
	post("showing pattern %d",pnum);
  /* need to check if pattern is valid */

  for(j = 0; j < drum_count; j++){
    if(p[pnum].drumlines[j].active){
      post("*** drum line for slot %d ***",j);
      ptr = p[pnum].drumlines[j].attacks;
	  post("there are %d attacks",p[pnum].drumlines[j].attack_count);
      for(i = 0; i < p[pnum].drumlines[j].attack_count; i++){
	       post("amp: %f, transp: %f, trigger: %f",
	       ptr->amplitude, ptr->increment, ptr->trigger_point);
	       *ptr++;
      } 
    }
  }
}

void dmach_printraw(t_dmach *x, t_floatarg fn)
{
	int i,j;
	int pnum = (int) fn;
  t_pattern *p = x->patterns;
  t_attack *ptr;
  int drum_count = x->drum_count;
  float normalized_trigger;
  float tempo_factor = x->tempo_factor;
  float sr = x->sr;
  
    if(pnum < 0 || pnum > MAX_PATTERNS-1){
    	error("illegal pattern number: %d",pnum);
    	return;
    }
    
	if(! x->stored_patterns[pnum]){
		error("%d is not currently stored",pnum);
		return;
	}
 if(!tempo_factor){
	 		error("tempo factor is zero!");
	 		return;
	 }

	post("readraw %d %f",pnum, p[pnum].beats);
  for(j = 0; j < drum_count; j++){
    if(p[pnum].drumlines[j].active){
      ptr = p[pnum].drumlines[j].attacks;
		  post("%d %d",j, p[pnum].drumlines[j].attack_count);
		  
      for(i = 0; i < p[pnum].drumlines[j].attack_count; i++){
/* scale attack times to factor out sample rate and tempo */
     		 normalized_trigger = ptr->trigger_point / (tempo_factor * sr);
	       post("%f %f %f",
	       ptr->amplitude, ptr->increment, normalized_trigger);
	       *ptr++;
      } 
    }
  }
}

void dmach_listraw(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int i,j;
	int pnum;
  t_pattern *p = x->patterns;
  t_attack *ptr;
  int drum_count = x->drum_count;
  float normalized_trigger;
  float tempo_factor = x->tempo_factor;
  float sr = x->sr;
  int ldex = 0;
  t_atom *listdata = x->listdata;
  
  if(argc < 1){
  	pnum = x->this_pattern;
  } else {
  		pnum = (int) atom_getfloatarg(0,argc,argv);
  }
  if(pnum < 0 || pnum > MAX_PATTERNS-1){
  	error("illegal pattern number: %d",pnum);
  	return;
  }
    
	if(! x->stored_patterns[pnum]){
		error("%d is not currently stored",pnum);
		return;
	}
 if(!tempo_factor){
	 		error("tempo factor is zero!");
	 		return;
	 }

/* note: format of MACROS requires that ldex be incremented outside of
call. Also note that traditional indexing A[x] cannot be used; instead must
use A+x format. */
#if __MSP__
  SETSYM(listdata + ldex, gensym("readraw")); ++ldex;
#endif
#if __PD__
  SETSYMBOL(listdata + ldex, gensym("readraw")); ++ldex;
#endif

  SETFLOAT(listdata + ldex, (float)pnum);  ++ldex;
	SETFLOAT(listdata + ldex, p[pnum].beats);  ++ldex;

  for(j = 0; j < drum_count; j++){
    if(p[pnum].drumlines[j].active){
      ptr = p[pnum].drumlines[j].attacks;
      SETFLOAT(listdata + ldex, (float)j); ++ldex;
      SETFLOAT(listdata + ldex, (float)(p[pnum].drumlines[j].attack_count)); ++ldex;
		  
      for(i = 0; i < p[pnum].drumlines[j].attack_count; i++){
     		 normalized_trigger = ptr->trigger_point / (tempo_factor * sr);
     		 SETFLOAT(listdata + ldex, ptr->amplitude); ++ldex;
     		 SETFLOAT(listdata + ldex, ptr->increment); ++ldex;
     		 SETFLOAT(listdata + ldex, normalized_trigger); ++ldex;
	       *ptr++;
      } 
    }
  } 
  outlet_list(x->listraw_outlet,0,ldex,listdata);
}

void dmach_copypattern(t_dmach *x, t_floatarg pn1, t_floatarg pn2)
{
	int i,j;
	int pnum_from = (int) pn1;
	int pnum_to = (int) pn2;
  t_pattern *p = x->patterns;
  t_attack *ptr_from, *ptr_to;
  int drum_count = x->drum_count;
  
    if(pnum_from < 0 || pnum_from > MAX_PATTERNS-1){
    	error("illegal source pattern number: %d",pnum_from);
    	return;
    }
    if(pnum_to < 0 || pnum_to > MAX_PATTERNS-1){
    	error("illegal dest pattern number: %d",pnum_to);
    	return;
    }
  if(pnum_from == pnum_to){
  	error("source and dest patterns are the same");
  	return;
  } 
	if(! x->stored_patterns[pnum_from]){
		error("%d is not currently stored",pnum_from);
		return;
	}
	dmach_init_pattern(x,pnum_to);
//	post("readraw %d %f %f",pnum, p[pnum].beats, p[pnum].dsamps);

	p[pnum_to].beats = p[pnum_from].beats;
	p[pnum_to].dsamps = p[pnum_from].dsamps;
	
  for(j = 0; j < drum_count; j++){
  	p[pnum_to].drumlines[j].active = p[pnum_from].drumlines[j].active;
    if(p[pnum_from].drumlines[j].active){
      ptr_from = p[pnum_from].drumlines[j].attacks;
      ptr_to = p[pnum_to].drumlines[j].attacks;
      p[pnum_to].drumlines[j].attack_count = p[pnum_from].drumlines[j].attack_count;
      for(i = 0; i < p[pnum_from].drumlines[j].attack_count; i++){
	       ptr_to->amplitude = ptr_from->amplitude;
	       ptr_to->increment = ptr_from->increment;
	       ptr_to->trigger_point = ptr_from->trigger_point;
	       *ptr_from++;
	       *ptr_to++;
      } 
    }
  }
  x->stored_patterns[pnum_to] = 1;// assert that a legal pattern is now stored there
}

void dmach_readraw(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int i;
	int pnum;
	int pdex = 0;
	int slot;
  t_pattern *p = x->patterns;
  t_attack *ptr;
//  int drum_count = x->drum_count;
	float tempo_factor = x->tempo_factor;
	float sr = x->sr;
	short mutein;
	
	mutein = x->mute;
	x->mute = 1;
	pnum = (int) atom_getfloatarg(pdex++,argc,argv);

	  
    if(pnum < 0 || pnum > MAX_PATTERNS-1){
    	error("%s: illegal pattern number: %d",OBJECT_NAME,pnum);
    	return;
    }
    
	if(! x->stored_patterns[pnum]){
	  x->stored_patterns[pnum] = 1; // means there's something there now
	  dmach_init_pattern(x,pnum);
	  post("readraw: loading pattern %d",pnum);

	} else {
		post("readraw: reloading pattern %d",pnum);
	}
	p[pnum].beats = atom_getfloatarg(pdex++,argc,argv);
//	p[pnum].dsamps = atom_getfloatarg(pdex++,argc,argv);
	p[pnum].dsamps = p[pnum].beats * tempo_factor * sr;
//	post("dsamps calculated to be %f", p[pnum].dsamps);
	while(pdex < argc){
		slot = (int) atom_getfloatarg(pdex++,argc,argv);
		p[pnum].drumlines[slot].active = 1;
		p[pnum].drumlines[slot].attack_count = (int) atom_getfloatarg(pdex++,argc,argv);
		p[pnum].drumlines[slot].adex = 0;
		ptr = p[pnum].drumlines[slot].attacks;
		for(i = 0; i < p[pnum].drumlines[slot].attack_count; i++){
  		ptr->amplitude = atom_getfloatarg(pdex++,argc,argv);
  		ptr->increment = atom_getfloatarg(pdex++,argc,argv);
  		ptr->trigger_point = (sr * tempo_factor) * atom_getfloatarg(pdex++,argc,argv);
			*ptr++;
		} 
		
	}
	x->this_pattern = x->next_pattern = pnum;
	x->virgin = 0;
	x->mute = mutein;	
}

void dmach_slotamps(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int pdex,i;
	int slot = 0;
	float beatseg;
//	float tmpbeats;
	float subdiv;
	float beat_samps;
	float tempo_factor;
	int attack_count;
	int local_attacks;
	float trigger_point;
	float val;
	int pnum;
	t_pattern *p = x->patterns;
	float tempo = x->tempo;
	float sr = x->sr;
	t_attack *tmpatks = x->tmpatks;
	

	
	pdex = 0;  
	pnum = atom_getfloatarg(pdex++,argc,argv);
/*
	post("skipping slotamps for %d",pnum);
	return;*/
		
	if(pnum < 0 || pnum > MAX_PATTERNS - 1){
		error("%s: invalid pattern number: %d",OBJECT_NAME,pnum);
		return;
	}
	
	if(!x->stored_patterns[pnum]){
		error("%s: no pattern found at location : %d",OBJECT_NAME,pnum);
		return;
	}
		
	slot = (int) atom_getfloatarg(pdex++,argc,argv);
	if(slot < 0 || slot >= x->drum_count){
		post("%s: %d is an illegal slot",OBJECT_NAME,slot);
		return;
	}
//	post("filling slotamps %d for %d",slot, pnum);
	if(tempo <= 0){
		tempo = 60;
		error("zero tempo found");
	}
	tempo_factor = (60.0/tempo);
	
	beatseg = p[pnum].beats; // less general but we're going for ease here
	subdiv = atom_getfloatarg(pdex++,argc,argv);
	beat_samps = (beatseg/subdiv) * tempo_factor * sr;  
    trigger_point = 0;
    attack_count = 0;
	/* read attack cycle and store any non-zero attacks */
	local_attacks = 0;
	// clean me
	memset((void *)tmpatks, 0, MAX_ATTACKS * sizeof(t_attack) );
	
	for(i = 0; i < subdiv; i++){
		val = atom_getfloatarg(pdex++,argc,argv);
		if(val){
			tmpatks[local_attacks].amplitude = val;
			// force to integer sample point 
			tmpatks[local_attacks].trigger_point = (int)trigger_point;
			++local_attacks;
		}
		trigger_point += beat_samps;
	}

	/* initialize with increment of 1.0 for each non-zero amplitude attack. */
	for(i = 0; i < local_attacks; i++){
		if(tmpatks[i].amplitude) {
			tmpatks[i].increment = 1.0;
		} else {
			tmpatks[i].increment = 0.0;
		}
	}
	p[pnum].drumlines[slot].active = 0;
	memcpy((void *)p[pnum].drumlines[slot].attacks,(void *)tmpatks, 
		MAX_ATTACKS * sizeof(t_attack));
	p[pnum].drumlines[slot].attack_count = local_attacks;
	p[pnum].drumlines[slot].adex = 0;
	p[pnum].drumlines[slot].active = 1;
	x->this_pattern = x->next_pattern = pnum; // set pattern to what we're working on


}



/* more general version */
void dmach_slotampsfull(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
	int pdex,i;
	int slot = 0;
	float beatseg;
	float tmpbeats;
	float subdiv;
	float beat_samps;
	float tempo_factor;
	int attack_count;
	int local_attacks;
	float trigger_point;
	float val;
	int pnum;
	t_pattern *p = x->patterns;
	float tempo = x->tempo;
	float sr = x->sr;
	
	if(argc > MAX_ATTACKS + 1){
		post("%s: %d is too long an atk message",OBJECT_NAME,argc);
		return;
	}
	pdex = 0;  
	pnum = atom_getfloatarg(pdex++,argc,argv);
	
	if(pnum < 0 || pnum > MAX_PATTERNS - 1){
		error("%s: invalid pattern number: %d",OBJECT_NAME,pnum);
		return;
	}
	
	if(!x->stored_patterns[pnum]){
		error("%s: no pattern found at location : %d",OBJECT_NAME,pnum);
		return;
	}
	
	x->this_pattern = x->next_pattern = pnum; // set current pattern to what we're working on
	
	slot = (int) atom_getfloatarg(pdex++,argc,argv);
	p[pnum].drumlines[slot].active = 1;
	tempo_factor = (60.0/tempo);
	tmpbeats = p[pnum].beats;
	
	
    trigger_point = 0;
    attack_count = 0;
    while(tmpbeats > 0){
		local_attacks = 0;		
		beatseg = atom_getfloatarg(pdex++,argc,argv);
		subdiv = atom_getfloatarg(pdex++,argc,argv);
		beat_samps = (beatseg/subdiv) * tempo_factor * sr;  
		/* read attack cycle and store any non-zero attacks */
		local_attacks = 0;
		for(i = 0; i < subdiv; i++){
			val = atom_getfloatarg(pdex++,argc,argv);
			if(val){
				p[pnum].drumlines[slot].attacks[attack_count + local_attacks].amplitude = val;
				/* force to integer sample point (couldn't get round() to work) */
				p[pnum].drumlines[slot].attacks[attack_count + local_attacks].trigger_point = (int)trigger_point;
				++local_attacks;
			}
			trigger_point += beat_samps;
		}
	  for(i = 0; i < local_attacks; i++){
			p[pnum].drumlines[slot].attacks[i + attack_count].increment = 1.0;
	  }
	  tmpbeats -= beatseg;
	  attack_count += local_attacks;
	}
	p[pnum].drumlines[slot].attack_count = attack_count;

}

void dmach_slotincrs(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
  int pdex,i;
  int slot = 0;
//  float beatseg;
//  float tmpbeats;
//  float subdiv;
//  float beat_samps;
//  float tempo_factor;
//  int attack_count;
  int local_attacks;
//  float trigger_point;
 // float val;
	int pnum;
  t_pattern *p = x->patterns;
//  float tempo = x->tempo;
//  float sr = x->sr;
//  short mutein = x->mute;
  
//  x->mute = 1;
	if(argc > MAX_ATTACKS + 1){
		post("%s: %d is too long a slotincrs message",OBJECT_NAME,argc);
		return;
	}
  pdex = 0;  
	pnum = (int) atom_getfloatarg(pdex++,argc,argv); 
  slot = (int) atom_getfloatarg(pdex++,argc,argv); 
	if(slot < 0 || slot >= x->drum_count){
		post("%s: %d is an illegal slot",OBJECT_NAME,slot);
		return;
	}
  local_attacks = p[pnum].drumlines[slot].attack_count;
  if(argc != local_attacks + 2){
  	// post("rejected slotincrs with %d local atks, argc = %d",local_attacks, argc - 2);
  	return;
  }
//  post("%d local atks, argc = %d",local_attacks, argc);
   
 
  if(pnum < 0 || pnum >= MAX_PATTERNS){
    error("%s: slotincrs sent invalid pattern number: %d",OBJECT_NAME,pnum);
    return;
  }

  if(!x->stored_patterns[pnum]){
    error("%s: slotincrs: no pattern found at location : %d",OBJECT_NAME,pnum);
		return;
  }
	
  



//  post("filling slotincr for slot %d pnum %d",slot, pnum);



  for(i = 0; i < local_attacks; i++){
		p[pnum].drumlines[slot].attacks[i].increment = atom_getfloatarg(pdex++,argc,argv);
  }
  x->this_pattern = x->next_pattern = pnum; // set current pattern to what we're working on

//  x->mute = mutein;
}

void dmach_store(t_dmach *x, t_symbol *s, int argc, t_atom *argv)
{
  int pdex,i;
  int slot = 0;
  float beatseg;
  float tmpbeats;
  float subdiv;
  float beat_samps;
  float tempo_factor = x->tempo_factor;
  int attack_count;
  int local_attacks;
  float trigger_point;
  float val;
	int pnum;
  t_pattern *p = x->patterns;
  float tempo = x->tempo;
  float sr = x->sr;
  
  
  
  pnum = atom_getfloatarg(0,argc,argv);
  if(pnum < 0 || pnum > MAX_PATTERNS - 1){
    error("invalid pattern number: %d",pnum);
    return;
  }
//  post("%d arguments to \"store\" at pattern %d",argc,pnum);

  dmach_init_pattern(x,pnum);

  p[pnum].beats = atom_getfloatarg(1,argc,argv);
  if(p[pnum].beats <= 0){
  	post("illegal beats at pnum %d: %f",pnum,p[pnum].beats);
  	p[pnum].beats = 4;
  }
  if(tempo <= 0){
  	error("zero tempo in store msg");
  	tempo = 60;
  }
  tempo_factor = (60.0/tempo);
  p[pnum].dsamps = p[pnum].beats * tempo_factor * sr;
  pdex = 2;

//  post("%f beats %f samps in this pattern",p[pnum].beats,p[pnum].dsamps );
  while(pdex < argc){
    slot = atom_getfloatarg(pdex++,argc,argv);
    p[pnum].drumlines[slot].active = 1;
    tmpbeats = p[pnum].beats;

    trigger_point = 0;
    attack_count = 0;
    while(tmpbeats > 0){
      local_attacks = 0;
      beatseg = atom_getfloatarg(pdex++,argc,argv);
      subdiv = atom_getfloatarg(pdex++,argc,argv);
      beat_samps = (beatseg/subdiv) * tempo_factor * sr;

      /* read attack cycle and store any non-zero attacks */
      for(i = 0; i < subdiv; i++){
				val = atom_getfloatarg(pdex++,argc,argv);
				if(val){
				  p[pnum].drumlines[slot].attacks[attack_count + local_attacks].amplitude = val;
				  /* force to integer sample point (couldn't get round() to work) */
				  p[pnum].drumlines[slot].attacks[attack_count + local_attacks].trigger_point = (int)trigger_point;
				  ++local_attacks;
				}
				trigger_point += beat_samps;
      }
      /* we now know number of attacks and read that many transpose factors */

      for(i = 0; i < local_attacks; i++){
				p[pnum].drumlines[slot].attacks[i + attack_count].increment = atom_getfloatarg(pdex++,argc,argv);
      }
      tmpbeats -= beatseg;
      attack_count += local_attacks;
    }
    p[pnum].drumlines[slot].attack_count = attack_count;
//	post("%d attacks in slot %d for pattern %d",attack_count, slot, pnum);
  }
  // new - set internal pointer to start of array
  p[pnum].drumlines[slot].adex = 0;
  // set current pattern to this (to avoid crash if pnum 0 is uninitialized)
  x->this_pattern = x->next_pattern = pnum;
  x->virgin = 0; // now at least one pattern is stored
  x->stored_patterns[pnum] = 1; // means there's something there now  
  x->tempo_factor = tempo_factor;//restore this value
//  post("pattern stored at %d with %f beats",pnum,p[pnum].beats);
}


void dmach_init_pattern(t_dmach *x, int pnum)
{
  int i;
  int drum_count = x->drum_count;
  t_pattern *p = x->patterns;
  if(pnum < 0 || pnum >= MAX_PATTERNS){
    error("invalid pattern number: %d",pnum);
    return;
  }


  
  if( x->stored_patterns[pnum] ){
  	// post("replacing pattern stored at %d",pnum);
  }
  
  if(p[pnum].drumlines == NULL){
  	// post("initializing drumline memory at location %d",pnum);
  	p[pnum].drumlines = (t_drumline *)malloc(drum_count * sizeof(t_drumline));
  }  
  

  for(i = 0; i < drum_count; i++){
    p[pnum].drumlines[i].attacks = (t_attack *)calloc(MAX_ATTACKS, sizeof(t_attack));
    p[pnum].drumlines[i].adex = 0;
    p[pnum].drumlines[i].active = 0;
    p[pnum].drumlines[i].attack_count = 0;
  }


}

void dmach_dsp_free( t_dmach *x )
{
#if __MSP__
  dsp_free((t_pxobject *) x);
#endif
/* need some freeing action here! */
	free(x->patterns);
	free(x->stored_patterns);
	free(x->current_increment);
	free(x->gtranspose);
	free(x->gains);
	free(x->sequence);
	free(x->listdata);
	free(x->connected);
	free(x->tmpatks);
	free(x->muted);
}

void dmach_mute(t_dmach *x, t_floatarg toggle)
{
	x->mute = (short)toggle;
}

void dmach_instant_recall(t_dmach *x, t_floatarg toggle)
{
	x->instant_recall = (short)toggle;
}

void dmach_clickincr(t_dmach *x, t_floatarg toggle)
{
	x->clickincr = (short)toggle;
}

#if __MSP__
void dmach_assist (t_dmach *x, void *b, long msg, long arg, char *dst)
{
//	int i;
	
	if (msg==1) {
		switch (arg) {
			case 0: sprintf(dst,"(signal) Sync Click"); break;
		}
	} else if (msg==2) {
	  if(arg == x->outlet_count){
				sprintf(dst,"(list) Raw Pattern Data");
	  }
		else if(arg == x->outlet_count - 1){
			sprintf(dst,"(signal) Sync Trigger");
		} else if(!(arg % 2) ) { // even
			sprintf(dst,"(signal) Trigger %ld",arg/2 + 1);
		} else {
			sprintf(dst,"(signal) Increment %ld",(arg-1)/2 + 1);
		}
	}
}
#endif

void *dmach_new(t_symbol *s, int argc, t_atom *argv)
{
  	int i;
	t_dmach *x;
#if __MSP__
    x = (t_dmach *)newobject(dmach_class);
#endif
#if __PD__
	x = (t_dmach *)pd_new(dmach_class);
#endif
	
	if(argc >= 1)
		x->tempo = atom_getfloatarg(0,argc,argv);
	else 
		x->tempo = 120;
	if(argc >= 2)
		x->drum_count = atom_getfloatarg(1,argc,argv);
	else
		x->drum_count = 8;
	
	x->outlet_count = x->drum_count * 2 + 1; // one extra for pattern start click
	
#if __MSP__
	x->listraw_outlet = listout((t_pxobject *)x);
    dsp_setup((t_pxobject *)x,1);
    for(i = 0; i < x->outlet_count; i++){
    	outlet_new((t_pxobject *)x, "signal");
    }
    x->x_obj.z_misc |= Z_NO_INPLACE;
#endif
#if __PD__
	
    for(i = 0; i < x->outlet_count; i++){
 		 outlet_new(&x->x_obj, gensym("signal"));
    }
	x->listraw_outlet = outlet_new(&x->x_obj, gensym("list"));
#endif
	x->patterns = (t_pattern *) malloc(MAX_PATTERNS * sizeof(t_pattern));
	x->stored_patterns = (short *) malloc(MAX_PATTERNS * sizeof(short));
	x->current_increment = (float *) malloc(x->drum_count * sizeof(float)); // for sample + hold of increment
	x->gtranspose = (float *) malloc(x->drum_count * sizeof(float));
	x->gains = (float *) malloc(x->drum_count * sizeof(float));
	x->sequence = (int *) malloc(1024 * sizeof(int));
	x->listdata = (t_atom *) malloc(1024 * sizeof(t_atom));
	x->connected = (short *) malloc(1024 * sizeof(short));
	x->tmpatks = (t_attack *)calloc(MAX_ATTACKS, sizeof(t_attack));
	x->muted = (short *)calloc(x->drum_count, sizeof(short)); // by default mute is off on each slot

	x->seqptr = 0;
	x->sequence_length = 0;
	x->playsequence = 0;
	x->loopsequence = 0;
	x->zeroalias = -1.0;
	x->clickincr = 0;
	x->instant_recall = 0;
	
	
	if(x->tempo <= 0 || x->tempo > 6666)
		x->tempo = 60.0;
		
//	post("initial tempo is %f",x->tempo);
	for(i = 0; i < MAX_PATTERNS; i++){
		x->patterns[i].drumlines = NULL;
		x->stored_patterns[i] = 0;
	}
	x->this_pattern = x->next_pattern = 0;
	x->mute = 0;

	x->clocker = 0;
	x->sr = sys_getsr();
	x->tempo_factor = 60.0 / x->tempo ;

	for(i = 0; i < x->drum_count; i++){
		x->gains[i] = 1.0;
		x->gtranspose[i] = 1.0;
		x->current_increment[i] = 1.0;
	}
	// safety - init memory for all drum patterns
	for(i = 0; i < MAX_PATTERNS; i++){
		dmach_init_pattern(x,i);
	}
	x->virgin = 1;
  return (x);
}



t_int *dmach_perform(t_int *w)
{
int i,j;
t_float *trig_outlet, *incr_outlet;

	t_dmach *x = (t_dmach *) w[1];
	int outlet_count = x->outlet_count;
//	t_float *in_sync = (t_float *) w[2]; // input for sync trigger (not implemented yet)
	t_float *sync = (t_float *) w[outlet_count + 2];
	t_int n = w[outlet_count + 3];

	int this_pattern = x->this_pattern;
	int next_pattern = x->next_pattern;
	t_pattern *p = x->patterns;
	float *current_increment = x->current_increment;
	float clocker = x->clocker;
	float dsamps = p[this_pattern].dsamps;
	int drum_count = x->drum_count;
	int adex;
	/* sequence stuff */
	int *sequence = x->sequence;
	short playsequence = x->playsequence;
	short loopsequence = x->loopsequence;
	short clickincr = x->clickincr;
	int seqptr = x->seqptr;
	int sequence_length = x->sequence_length;
	short *connected = x->connected;
	short *muted = x->muted;
	float *gtranspose = x->gtranspose;
	float *gains = x->gains;
	
	/* clean pnum click outlet */
	memset((void *)sync, 0, n * sizeof(float));
	
	if( x->mute || x->virgin ){
		for(i = 0; i < drum_count; i++){
			if(connected[i * 2 + 1]){
				// post("cleaning outlet pair %d", i);
				trig_outlet = (t_float *) w[i * 2 + 3];
				memset((void *)trig_outlet, 0, n * sizeof(float));
			}
		}
		return w + (outlet_count + 4);
	}
	
	/* pre-clean all connected trigger vectors */
	for(i = 0; i < drum_count; i++){
		if(connected[i * 2 + 1]){
			trig_outlet = (t_float *) w[i * 2 + 3];
			memset((void *)trig_outlet, 0, n * sizeof(float));
		}
	}
	
	for(j = 0; j < n; j++){
		if(clocker >= dsamps){ // ready for next pattern now
			clocker -= dsamps;
 			/* this is the pattern sequencer */
 			if(playsequence){
  				if (seqptr >= sequence_length){
					if(loopsequence){
						seqptr = 0;
					} else {
						/* this is fine, the playthrough has concluded and we now mute external */
						x->mute = 1;
						goto escape;
					}
 				}
 				this_pattern = sequence[seqptr++];

 			}
 			/* we do this if we're not pattern sequencing: */
			else if(next_pattern != this_pattern){ 
				this_pattern = next_pattern;
			}
			
			for(i = 0; i < drum_count; i++){ /* reset pointers */
				p[this_pattern].drumlines[i].adex = 0;
			}
			/* send current bar number if in sequence. Note kludge for if
			bar is 0. We cannot send a 0 click so we alias it to something else. 
			*/
			if(playsequence){
				if(this_pattern){
					sync[j] = this_pattern;
				} else {
					sync[j] = x->zeroalias;
				}
			} else{
				sync[j] = 1; /* send a sync click */
			}
		} 
		else {
			sync[j] = 0;
		}
		for(i = 0; i < drum_count; i++){
			// float locamp;
			if(p[this_pattern].drumlines[i].active && ! muted[i]){
				trig_outlet = (t_float *) w[i * 2 + 3];
				incr_outlet = (t_float *) w[i * 2 + 4];
				
				adex = p[this_pattern].drumlines[i].adex; // overflow danger ???

				if((int)clocker == (int)p[this_pattern].drumlines[i].attacks[adex].trigger_point){
					current_increment[i] = 
						p[this_pattern].drumlines[i].attacks[adex].increment * gtranspose[i];
					/* put sync click into sample j of the output vector */

					trig_outlet[j] = 
						p[this_pattern].drumlines[i].attacks[adex].amplitude * gains[i];
					++adex;
					p[this_pattern].drumlines[i].adex = adex;
					incr_outlet[j] = current_increment[i];
					/*
					post("t: sample %d, slot %d, amp: %f, incr: %f",
						(int)clocker, i, locamp, current_increment[i]); */
				} 
				else {
					trig_outlet[j] = 0; // not sure we need these assignments ??
					if(clickincr){
						incr_outlet[j] = 0;
					} else {
						incr_outlet[j] = current_increment[i]; // samp + hold the increment to outlet
					}
				}
				
			} 
		}
		++clocker;
	}
	
	escape:
	
	x->clocker = clocker;
	x->this_pattern = this_pattern;
	x->seqptr = seqptr;
	
	return w + (outlet_count + 4);
}		

void dmach_dsp(t_dmach *x, t_signal **sp, short *count)
{
	long i;
	t_int **sigvec;
	int pointer_count;
	

	if(x->virgin){
		post("%s: no patterns are stored",OBJECT_NAME);
//		return;
	}
	if(x->sr != sp[0]->s_sr){
		x->sr = sp[0]->s_sr;
/* still need to adjust every stored pattern dsamps variable */
	}
	
	pointer_count = x->outlet_count + 3; // all outlets, 1 inlet, object and s_n
	
//	post("pointer count %d", pointer_count);


// copy vector to be safe
#if __MSP__
	for(i = 0; i < pointer_count - 2; i++){
		x->connected[i] = count[i];
	}
#endif
#if __PD__
	for(i = 0; i < pointer_count - 2; i++){
		x->connected[i] = 1;
	}
#endif

	sigvec  = (t_int **) calloc(pointer_count, sizeof(t_int *));	
	for(i = 0; i < pointer_count; i++){
		sigvec[i] = (t_int *) calloc(sizeof(t_int),1);
	}
	sigvec[0] = (t_int *)x; // first pointer is to the object

	sigvec[pointer_count - 1] = (t_int *)sp[0]->s_n; // last pointer is to vector size (N)
	
	for(i = 1; i < pointer_count - 1; i++){ // now attach the inlet and all outlets
		sigvec[i] = (t_int *)sp[i-1]->s_vec;
	}	
#if __MSP__
	dsp_addv(dmach_perform, pointer_count, (void **) sigvec); 
#endif
#if __PD__
	dsp_addv(dmach_perform, pointer_count, (t_int *) sigvec); 
#endif
	free(sigvec);
}

