// cond.cpp -- conductor program -- float tempo -- 100ms retrigger time
// Date:  Thu Aug 17 18:04:10 PDT 2000
//


#include "batonImprov.h"

typedef unsigned char  uchar;
typedef unsigned short ushort;
typedef unsigned int   uint;

#define NOTECONTROL(c+1)
// #define NOTECONTROL (1)

////////////////////////////////////////////////////////////////////////////
//
// #include "cond.h"
//

#define NOCONTROL    16     /* max number of patchchords for control changes */
#define CHORDSIZE    15     /* max no of notes in a chord */
#define VOICES       25     /* maximum no of voices */
#define SCORERECSIZE 30     /* max no bytes in a score record */
#define NOTE          1
#define TRIG          2     /* / */
#define REST          4     /* r */
#define PROGRAMCHANGE 5     /* t */
#define NOOP          6
#define SETCHANNEL    7     /* h */
#define TEMPO         8     /* v or V */
#define UPPEDAL       9     /* P */
#define PEDAL        10     /* p */
#define CONTTEMPO    11     /* w */
#define MARK         12     /* m */
#define KEYVELOCITY  13     /* k */
#define BATON2       14     /* T */
#define DOTEQUALS    15     /* j */
#define INIT         19     /* I */
#define FTEMP        20     /* W */
#define CONTCONTROL  21     /* q */
#define MNOTEON      22
#define MNOTEOFF     23
#define NOACTION     24
#define STICKLEVELS  25
#define SYSEX        26
#define PITCHBEND    27
#define MEASURE      36

// nc.c and ns constants
#define SMOOTHING     0.5  /* 0.0==infinite smoothing, 1.0==no smoothing */
#define X             1
#define Y             2
#define Z             3
#define P             4
#define V             5
#define S             6

short  a[28];
short  abeatset;
short  arg1;
short  arg2;
long   beatsum;
long   beat_time;
short  b[28],baton2;
short  bufchan;
uchar  bufp;
short  b1;
short  b2;
uchar  calf[2048];
short  chan;
short  channel[VOICES];
short  chord[VOICES*CHORDSIZE];
short  chordpi[VOICES];
short  chordp[VOICES];
short  controler;
short  cont4;
short  contp;
short  ctempo;
short  contpatch;
short  contch[NOCONTROL];
short  contno[NOCONTROL];              // arrays defining patchcords
short  controlerfn[NOCONTROL];
short  controlerfnv[NOCONTROL];
short  contbalance[NOCONTROL];
short  sustped[16];
long   contime;
short  dataa;
short  dist;
short  dotchange;
short  dotro;
short  dotrn;
short  diff[11];
short  dx;
short  dr0;
short  drumset1;
short  drumset2;
short  dynamic[VOICES];
short  dyntype[VOICES];
long   dur;
short  endscoreflag;
short  hungnote;
short  hungnoteno;
long   ibeat;
long   itim;
short  interval;
short  kk;
short  keych,keyno,keyvel;
long   keytime;
short  mark;
short  midich;
short  midichar;
short  minus14;
short  minus15;
short  mm;
uchar  midibuf[256];
uchar  midirbuf[256];
uchar  midipi;
uchar  midipo;
uchar  midirpi;
uchar  midirpo;
short  num,nextkeyno,noteon,nextbeat,nobat,needscore;
short  notenumb1,notenumb2,noteon1,noteon2;
long   nexttime,nextnotetime;
short  zpause;
short  plus14;
short  plus15;
short  potvalue,pot[5];
long   pt_time,pt_beat;
short  prog;
short  recordmark,retoff,retseg,restpoint,recordcnt,recordlength;
long   samptime;
short  samp,scanmark,scancount,sustain;
short  scorestate,scoreadp,scorkd[SCORERECSIZE],scorkdend;
short  stick,st0,st1,st2,set12p,sticklevelflag;
short  sum;
short  suminc;
short  range;
short  t[28];
short  temposet;
short  thru;
long   threshtime;
short  tranftype;
extern long t_time;long t_times,t_tdata,t_tkey;
short  volume[16],vtemp,volumeconst,volx;
short  ivalue,ipvalue,invalue;
float  value,pvalue,nvalue;
float  contval[NOCONTROL];
short  whack;
short  x1,x2,x[3];
short  x1s,x1cont[16],x1type[16],x2s,x2cont[16],x2type[16];
short  y11,y2,y[3];
short  y1s,y1cont[16],y1type[16],y2s,y2cont[16],y2type[16];
short  zp,z1,z2,z[3];
short  zero1x,zero1y,zero2x,zero2y,zcalib;
char   midif[100];
char   scoref[100];
char   scorepf[100];
short  newdotp;
short  scoresok;


////////////////////////////////////////////////////////////////////////////


// Global variables:

int   temp1;                   //
int   temp2;                   //
int   playmode;                //
short sysexmessage;            //
short midich1;                 //
short maxscore;                //
short minscore;                //
short sendingscore;            //
short timecount;               //
uchar midi_port;               //
uchar midififo[256];           //
uint  midiip;                  //
uint  midiop;                  //
uint  midiipsav;               //
short midimessages;            //
short sysexcase;               //
short scorechars;              //
short charcount;               //
short endplayscoreflag;        //
uint  scoreip;                 //
uint  scoreop;                 //
long  controlchangetime;       //
long  lastwhacktime;           //
int   startmeasure;            //
int   scan;                    //
int   discard;                 //
int   measure;                 //
int   readscore;               //
int   ii;                      //
FILE *fp2;                     //
short buf[17];                 //
int   b14minusdown;            //
int   b14minusup;              //
int   b15minusdown;            //
int   b15minusup;              //
float tempo;                   //
float temposave;               //


// Function definitions
int         keyvelfun(int);
int         getch(void);
int         getnextchar(void);
void        smoothandsend(void);
void        pcc(void);
void        loadplay(void);
void        playrecord(void);
void        readscorez(void);
void        stkcontrol(void);

/*--------------------beginning improvization algorithms------------------*/


/*---------------------initialization algorithms--------------------------*/


void description(void) {
   cout << 
   "CONDUCTOR COMMANDS s--select score   z--start playing\n"
   "                   b--stop playing   n--toggle no baton mode on/off\n"
   << endl;
}

void initialization(void) {
   startmeasure=1;
   eventIdler.setPeriod(0);
}


void initializecond(void) {
   int i;
   //whackint1=0;
   sysexcase = midiip = midiop = midimessages = 0;
   for (i=0; i<VOICES; i++) {
      chordpi[i] = chordp[i] = CHORDSIZE*i;
   }
   for(i=0; i<NOCONTROL; i++) {
      contval[i]=0;
   }
   tempo = temposave = temposet = 1; 
   scan = ctempo = vtemp = zpause = 0;
   whack1 = whack2 = b14minusdown = b14minusup = b15minusdown = b15minusup = 0;
   scan=0;
   measure=1;
   if (startmeasure!=1) {
      scan=1;
   }
   endscoreflag=0;
   scoreop = scoreip = scoreadp = needscore = scorestate = scorechars = 0;
   endplayscoreflag=0;
   threshtime = nexttime = pt_time = pt_beat = controlchangetime = t_time;
   nexttime=0;
   readscorez();
   playmode = 1;
}


/*--------------------main loop algorithms -------------------------------*/

void mainloopalgorithms(void){
   if (playmode) {
      while (scan) {                            //jump to next measure
         if(scorkd[0]==NOTE)    goto readsk;
         if(scorkd[0]==MNOTEON) goto readsk;     //skip midi note on's
         if(scorkd[0]==MNOTEOFF)goto readsk;    //skip midi note on's
         if(scorkd[0]==REST)    goto readsk;    //skip midi note on's
         playrecord();                          //play score record
readsk:
         if (playmode) {
            readscorez();                       //read next score record
         }
      }
      if (t_time > controlchangetime) {  // if midibuf[] empty send cont changes
         controlchangetime = t_time+2;
         if (contno[contp]) {
            stkcontrol();         //send data from next patchchord
         }
         /*contno[j] is zero for patchchord which is not in use */
         if (++contp==NOCONTROL) {
            contp=0;
         }
      }
      if (zpause) goto endmainloop;
   
      if (nobat) {   // in nobaton mode, play score at constant tempo (=tempo)
         if (t_time > nexttime) {
            playrecord();
            if (playmode) {
               readscorez();
            }
            nexttime = t_time + (nextbeat*(int)tempo);
         }
      } else {
         if (ctempo) {                 // continuous tempo control by y1
            if (t_time > nexttime){
               playrecord();
               if (playmode) {
                  readscorez();
               }
               if (zt1>set1 && vtemp) {
                  if (!yt1) {
                     yt1=1;
                  }
                  tempo = ((int)temposave*64)/yt1;
               }
               nexttime = t_time + (nextbeat*(int)tempo);
            }
         } else {
            if (whack1) {                    // baton 1 control of tempo
               whack1 = 0;
               if ((t_time-lastwhacktime)<100) {
                  goto falsewhack;
               }
               lastwhacktime = t_time;
               beat_time = trigtime1;
               itim = trigtime1 - pt_time;
               while (scorkd[0]!=TRIG) {
                  if (scorkd[0] == NOTE) {
                     scorkd[0]=REST;  /*skip unplayed notes*/
                  }
                  playrecord();
                  if (playmode) {
                     readscorez();
                  }
               }
   
               // compute new tempo from time between last two baton beats 
               ibeat = beatsum-pt_beat;
               pt_beat=beatsum;
               pt_time=beat_time;
               if (dotchange) {
                  ibeat = (ibeat*dotrn)/dotro;
                  dotchange = 0;
               }
               if (!temposet) {
                  tempo = temposave = (short)(itim/ibeat);
               } else {
                  temposet=0;
               }
               if (playmode) {
                  readscorez();
               }
               nexttime = t_time+(nextbeat*(int)tempo);
               falsewhack:;
            } else {
               if(scorkd[0]!=TRIG){
                  if(t_time>nexttime){
                     playrecord();
                     if (playmode) {
                        readscorez();
                     }
                     nexttime = t_time + (nextbeat*(int)tempo);
                  }
               }
            }
         }
      }
   endmainloop:;
   } 
}



/*--------------------triggered algorithms--------------------------------*/

void stick1trig(void) { }
void stick2trig(void) { }

void b14plustrig(void) {
    // clmidi();offmidi();t_sched_off();t_stop();exit(0);
}


void b15plustrig(void) { 
   cout << "b15+ trigger" << endl; 
}


void b14minusuptrig(void){
   b14minusdown=1;
   scan=1;
   startmeasure=measure;
}

void b14minusdowntrig(void)  {b14minusup   =1; }
void b15minusuptrig(void)    {b15minusdown =1; }
void b15minusdowntrig(void)  {b15minusup   =1; }

void keyboardchar(int testch){
   switch(testch){

   case 'b': 
      playmode = 0; 
      break;

   case 's':               //select and compile score
      baton.positionReportingOff();
      cout << "type name of score file: ";
      echoKeysOn();
      cin.getline(scoref, 64, '\n');
      echoKeysOff();
cout << "Got here and scoref = " << scoref << endl;
      strcpy(scorepf, scoref);
      strcat(scorepf, ".p");
      strcpy(midif, scoref);
      strcat(midif, ".mid");
      if ((fp2=fopen(scorepf,"r+b")) != NULL) {
         cout << "playing existing .p file" << endl;
         cout << "type COMMAND (type z to start playing)" << endl;
         newdotp=0;
      } else{ 
         cout << "SCORE NOT FOUND" << endl;
      }
      break;

   case 'z':
      baton.positionReportingOn();
      loadplay();
      pcc();
      break;

   case 'n':                                          //toggle no baton mode
      if(nobat){nobat=0;printf("NOBAT MODE OFF\n");}
      else{nobat=1;printf("NOBAT MODE ON\n");}
      break;

   case 'm':                                         //set startmeasure
      cout << "startmeasure = ";
      echoKeysOn();
      cin  >> startmeasure;
      echoKeysOff();
      break;

   default: 
      printf("UNKNOWN COMMAND \n");
      pcc();break;
   }
}


void finishup(void) { }


//////////////////////////////
//
// loadplay --
//

void loadplay(void) {
   int ii;
   for (ii=0;ii<16;ii++) {
      control_change(ii, 64, 0);
   }
   nexttime = t_time;
   while (t_time < nexttime);
   if ((fp2=fopen(scorepf,"r+b")) != NULL) {
      cout << "PLAY SCORE.P FILE" << endl;
      initializecond();
   } else {
      cout << "SCORE.P FILE NOT FOUND\ntype COMMAND" << endl; 
      scoresok = 0; 
   }
}



//////////////////////////////
//
// stkcontrol -- coupling batons to control changes
//

void stkcontrol(void) {
   int i,j;
   value=-1;
   i=controlerfn[contp];
   j=controlerfnv[contp];
   switch(i){
   case X:
      if (j == 1) {
         value=xt1; 
         smoothandsend();
      }
      if(j==2) {
         value=xt2;
         smoothandsend();
      }
      if(j==11) {
         value=127-xt1; 
         smoothandsend();
      }
      if (j==21) {
         value = 127-xt2;
         smoothandsend();
      }
      break;
   case Y:
      if(j==1 ){value=yt1;smoothandsend();}
      if(j==2 ){value=yt2;smoothandsend();}
      if(j==11 ){value=127-yt1;smoothandsend();}
      if(j==21 ){value=127-yt2;smoothandsend();}
      break;
   case Z:
      if(j==1){value=zt1;smoothandsend();}
      if(j==2){value=zt2;smoothandsend();}
      if(j==11){value=127-zt1;smoothandsend();}
      if(j==21){value=127-zt2;smoothandsend();}            
      break;
   case P:
      switch(j){
         case 1: value=pt1; smoothandsend(); break;
         case 2: value=pt2; smoothandsend(); break;
         case 3: value=pt3; smoothandsend(); break;
         case 4: value=pt4; smoothandsend(); break;
      }            
      break;
   case S:
      if (j==15) {            //buf15- trigger
         if (!sustped[contch[contp]] && b15minusdown) {   //down trig
            b15minusdown = 0;
            sustped[contch[contp]] = 1;
            control_change(contch[contp], contno[contp], 127);
         }
         if (sustped[contch[contp]] && b15minusup) {      //up trig
            b15minusup=0;
            sustped[contch[contp]]=0; 
            control_change(contch[contp], contno[contp],0);
         }
      }
      break;
   }
}



//////////////////////////////
//
// smoothandsend --
//

void smoothandsend(void) {
   pvalue = contval[contp];
   value -= contbalance[contp];       
   if (value < 1) {
      value = 1.0; 
   }
   if (value > 127.0) {
      value = 127.0;  
   }
   contval[contp] = pvalue + (value-pvalue)*SMOOTHING;
   invalue = (short)contval[contp];
   ipvalue = (short)pvalue;
   if(invalue != ipvalue) {
      //printf("nvalue,pvalue= %f %f\n",contval[contp],pvalue);
      control_change(contch[contp]+1, contno[contp], invalue);
   }
}




//////////////////////////////
//
// playrecord -- execute a score record -- for example starting a note.  
//     It is called and is executed at the real-time moment that the note is
//     suppose to be played.  it looks at the record stored in scorkd[]. it
//     looks at the op code of the record and does a switch depending on
//     the op code to different blocks of code which handle the different
//     op codes. 

void playrecord(void) {
   int i;
   int c;
   int v;
   int keyno;
   int keyvel;
   int beg;
   int end;
   int accent;
   int keyvela;
   int tp;
   c = scorkd[0];
   switch (c) {

   case NOTE:                    // turns on a note or a chord 
      // printf("note= %d %d %d %d %d",scorkd[0],scorkd[1],scorkd[2],
      // scorkd[3],scorkd[4]);
      v = scorkd[2]; 
      c = channel[v]; 
      beg = chordpi[v];
      end = chordp[v];
      keyvel = keyvelfun(v);
      for (i=beg; i<end; i++) {
         //turns off notes that are playing
         note_on(NOTECONTROL,chord[i],0);
         i=3;
         while (i < scorkdend) {
            // turns on new note or notes 
            keyno = scorkd[i++];
            keyvela = scorkd[i++] + keyvel;
            if (keyvela < 1) {
               keyvela = 1;
            }
            if (keyvela > 127) {
               keyvela = 127;
            }

            //printf("%d %d %d\n",c,keyno,keyvela);
            note_on(NOTECONTROL,keyno,keyvela);
            chord[beg++] = keyno;
         }
         chordp[v]=beg;
      }
      break;

   case TRIG:               // record produced by a "/" in score
      break;
   case REST:              // turns off all notes playing in a given voice
      v = scorkd[2];
      c = channel[v];
      beg = chordpi[v];
      end = chordp[v];
      for (i=beg; i<end; i++) {
         note_on(NOTECONTROL,chord[i],0);
      }
      chordp[v] = beg;
      keyvel=keyvelfun(v);
      break;

   case MNOTEON:      // turns on a single note. note must be ended with a
                      // subsequent MNOTEOFF record
      v = scorkd[2];
      c = channel[v];
      keyno = scorkd[3];
      accent = scorkd[4];
      keyvel = keyvelfun(v);
      keyvela = accent + keyvel;
      if (keyvela<1) {
         keyvela = 1;
      }
      if (keyvela>127) {
         keyvela = 127;
      }
      note_on(NOTECONTROL, keyno, keyvela);
      break;

   case MNOTEOFF:      // turns off a single note
      v=scorkd[2];c=channel[v];keyno=scorkd[3];accent=scorkd[4];
      keyvel=keyvelfun(v);
      keyvela=accent+keyvel;
      if(keyvela<1)keyvela=1;if(keyvela>127)keyvela=127;
      note_on(NOTECONTROL,keyno,0);
      break;

   case MEASURE:                         //print measure number, stop scan
      cout << "measure= " << measure++ << "   \r";
      if (measure > startmeasure) {
         scan=0;
      }
      break;

   case PITCHBEND:
      v = scorkd[2];
      c = channel[v];
      synth.pw(c, scorkd[3], scorkd[4]);
      break;

   case PEDAL:            // "p" in score turns sustain pedal on
      v = scorkd[2];
      c = channel[v];
      control_change(c, 64, 127);
      break;

   case UPPEDAL:         // "P" in score turns sustain pedal off
      v = scorkd[2];
      c = channel[v];
      control_change(c,64,0);
      break;

   case CONTTEMPO:  // "w"--continuous tempo control with y1, ignore beats
      if (ctempo) {
         ctempo = 0;
         vtemp = 0;
      } else {
         ctempo = 1;
         vtemp = 1;
      }
      break;

   case FTEMP:     // "W"--fixed tempo control, ignore beats
      if (ctempo) {
         ctempo=0;
         vtemp=0;
      } else {
         ctempo=1;
         vtemp=0;
      }
      break;

   case PROGRAMCHANGE:    // "t" in score will cause a midi program change
      v = scorkd[2];
      c = channel[v];
      program_change(c+1, scorkd[3]);
      break;

   case SETCHANNEL:   //  the midi channel for a given voice is kept in
                      //  channel[]. it is set with an "h" in the score
      channel[scorkd[2]] = scorkd[3];
      break;

   case TEMPO:        // "v"--set a fixed tempo starting at next event 
      tempo = temposave = (scorkd[2]+(scorkd[3]<<6))>>2;
      temposet=1;
      break;

   case KEYVELOCITY:    // "k"--setup keyvelocity control 
      dynamic[scorkd[2]] = scorkd[3];
      dyntype[scorkd[2]] = scorkd[4];
      break;

   case BATON2:      // T in score turna baton 2 on or off as a source of beats
      if (baton2) {
         baton2 = 0;
      } else {
         baton2 = 1;
      }
      break;

   case MARK:        //rehersal mark--displayed during performance
      mark = scorkd[3];
      break;

   case DOTEQUALS:   // change ratio of dot to comma 
      tempo = ((int)tempo*scorkd[3])/scorkd[4];
      dotchange = 1;
      dotro = scorkd[3];
      dotrn = scorkd[4];
      break;

   case CONTCONTROL:    // setup patchcord for continuous control     
      if (scorkd[6] == V)  {
         // if control change is a constant, send only once:
         tp = scorkd[7] - scorkd[8];
         if (tp < 0) {
            tp = 0;
         }
         control_change(scorkd[5]+1, scorkd[4], tp);
      } else {           
         // if control change is baton position, setup patchchord
         contpatch = scorkd[3];
         contno[contpatch] = scorkd[4];
         contch[contpatch] = scorkd[5];
         controlerfn[contpatch] = scorkd[6];
         controlerfnv[contpatch] = scorkd[7];
         contbalance[contpatch] = scorkd[8];
      }
      break;

   case INIT:                   //initialization
      for (i=1; i<17; i++) {
         channel[i]=i-1;
      } 
      tempo = temposave = temposet = 1;
      ctempo = vtemp = 0;
      break;

   case NOACTION:
      break;
   }

} 



//////////////////////////////
//
// keyvelfun -- keyvelocity control, v is voice


int keyvelfun(int v) {
   int type;
   int keyvel = 0;
   type=dyntype[v];
   switch(type){
   case X:
      if (dynamic[v] == 1) {
         keyvel=xt1;
      } else {
         keyvel=xt2;
      }
      break;
   case Y:
      if (dynamic[v] == 1) {
         keyvel=yt1;
      } else {
         keyvel=yt2;
      }
      break;
   case Z:
      if (dynamic[v] == 1) {
         keyvel=zt1;
      } else {
         keyvel=zt2;
      }
      break;
   case P:
      if (dynamic[v]<4) { 
         //pots 1-3
         keyvel = buf[10+dynamic[v]]>>5;
      } else {
         // pot 4
         keyvel=buf[5];
      } 
      break;
   case V:
      keyvel = dynamic[v];
      break;
   }

   return keyvel;
}


//////////////////////////////
//
// getnextchar --
//

int getnextchar(void) {
   int nextchar;
   nextchar = getc(fp2);
   if (nextchar==EOF) {
      playmode = 0; 
      cout << "END OF SCORE" << endl;
      pcc();
   }
   
   return nextchar;
}



//////////////////////////////
//
// readscorez --
//

void readscorez(void) {
   int iii;
   int opcode;

   if (fp2==NULL || !playmode) {
      return; 
   }
   nextbeat=0;

nextrecord:
   charcount = getnextchar();
   if (!playmode) {
      return; 
   }

   opcode = getnextchar();
   if (!playmode) {
      return; 
   }

   nextbeat += getnextchar();
   if (!playmode) {
      return; 
   }
   if (opcode == NOOP) {
      goto nextrecord;
   }
   scorkd[0] = opcode;
   scorkd[1] = nextbeat;
   scorkdend = charcount-1;

   if (charcount > 3) {
      for (iii=2; iii<scorkdend; iii++) {
         scorkd[iii] = getnextchar();
         if (!playmode) {
            return; 
         }
      }
   }
   scorkd[scorkdend] = 0;
   // printf("scorkd= %d %d %d %d %d %d %d %d \n",scorkd[0],scorkd[1],
   // scorkd[2],scorkd[3],scorkd[4],scorkd[5],scorkd[6],scorkd[7]);
   beatsum += nextbeat;
}



//////////////////////////////
//
// pcc --
//

void pcc(void) {
   cout << "type NEXT COMMAND  (type Q to quit, type ? to print "
           "list of commands)" << endl;
}