//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Nov 20 00:08:27  2002
// Last Modified: Wed Nov 20 00:08:39  2002
// Filename:      ...sig/doc/examples/improv/synthImprov/pdiscal.cpp
// Syntax:        C++; synthImprov 2.0
//  
// Description: Generate calibration data for squelching echo data from
//              the PianoDisk player piano.
//
#include "synthImprov.h"      // includes the default Win95 console interface
                              // for the synthImprov environment
/*----------------- beginning of improvization algorithms ---------------*/
Array< Array< Array<short> > > deltatimes;
Array< Array< Array<char> > >  velchange;
int  currentnote 	=   0;
int  notecount 		=   0;
int  nextnotetime 	=   0;
int  duration 		= 100;
int  currvoice 		=   0;
int  returnpitch 	=   0;
int  returnvelocity 	=   0;
int  returntime 	=   0;
char vchange		=   0;
int  playstate          =   0;
int maxvoices = 16;
Array<Voice> voices;
Array<int>   outtimes;
Array<int>   outvels;
// function declarations:
double average(Array<short>& data);
/*--------------------- maintenance algorithms --------------------------*/
//////////////////////////////
//
// description -- this function is called by the improv interface
//     whenever a capital "D" is pressed on the computer keyboard.
//     Put a description of the program and how to use it here.
//
void description(void) {
   cout << " spacebar starts test, c = print note count " << endl;
   cout << " p = print data file " << endl;
} 
//////////////////////////////
//
// initialization -- this function is called by the improv
//     interface once at the start of the program.  Put items
//     here which need to be initialized at the beginning of
//     the program.
//
void initialization(void) { 
   outtimes.setSize(128);
   outtimes.setAll(0);
   outvels.setSize(128);
   outvels.setAll(0);
   voices.setSize(maxvoices);
   int i, j;
   for (i=0; i<voices.getSize(); i++) {
      voices[i].setPort(synth.getInputPort());
   }
   nextnotetime = t_time;
   deltatimes.setSize(128);
   for (i=0; i<deltatimes.getSize(); i++) {
      deltatimes[i].setSize(128);
      for (j=0; j<deltatimes[i].getSize(); j++) {
         deltatimes[i][j].setSize(1000);
         deltatimes[i][j].setGrowth(1000);
         deltatimes[i][j].setSize(0);
      }
   }
   velchange.setSize(128);
   for (i=0; i<velchange.getSize(); i++) {
      velchange[i].setSize(128);
      for (j=0; j<velchange[i].getSize(); j++) {
         velchange[i][j].setSize(1000);
         velchange[i][j].setGrowth(1000);
         velchange[i][j].setSize(0);
      }
   }
}
//////////////////////////////
//
// finishup -- this function is called by the improv interface
//     whenever the program is exited.  Put items here which
//     need to be taken care of when the program is finished.
//
void finishup(void) { }
/*-------------------- main loop algorithms -----------------------------*/
//////////////////////////////
//
// mainloopalgorithms -- this function is called by the improv interface
//   continuously while the program is running.  The global variable t_time
//   which stores the current time is set just before this function is
//   called and remains constant while in this functions.
//
MidiMessage message;
int velocity;
short dtime;
void mainloopalgorithms(void) { 
   while (synth.getNoteCount() > 0) {
      message = synth.extractNote();
      if (message.is_note_off()) {
         continue;
      }
      returnpitch = message.p1();
      returnvelocity = message.p2();
      // returntime = message.time;
      returntime = t_time;
      
      dtime = (short)(returntime - outtimes[returnpitch]);
      if (dtime > 500) {   // ignore return times which take more than 0.5 sec
         continue;
      }
      vchange = returnvelocity - outvels[returnpitch];
      velchange[returnpitch][outvels[returnpitch]].append(vchange);
      deltatimes[returnpitch][outvels[returnpitch]].append(dtime);
   }
   if (!playstate) {
      return;
   }
   if (nextnotetime <= t_time) {
      currentnote += 11;
      if (currentnote > 127) {
         currentnote -= 127;
      }
      if (currentnote < A0 || currentnote > C8) {
         return;
      }
      velocity = rand() % 127 + 1;   // random velocity between 1 and 127
      voices[currvoice].off();
      outvels[currentnote] = velocity;
      outtimes[currentnote] = mainTimer.getTime();
      voices[currvoice++].play(0, currentnote, velocity);
      if (currvoice >= maxvoices) {
         currvoice = 0;
      }
      notecount++;
      nextnotetime = t_time + duration;
   }
}
void printDataFile(void) {
   const char* filename = "pdiscal.dat";
   fstream file;
   file.open(filename, ios::out);
   if (!file.is_open()) {
      cout << "ERROR: Cannot open file to write!" << endl;
      return;
   }
 
   int count;
   int i, j, k;
   for (i=0; i<127; i++) {
      for (j=0; j<127; j++) {
         file << "p" << i << "\tv" << j << "\t";
         count = deltatimes[i][j].getSize();
         for (k=0; k<count; k++) {
            file << "[" << (int) deltatimes[i][j][k] << ":" 
                 << (int) velchange[i][j][k] << "]";
            if (k<count-1) {
               file << " ";
            }
         }
         file << "\n";
      }
      
   }
}
void printMathFile(void) {
   const char* filename = "pdiscal.ma";
   fstream file;
   file.open(filename, ios::out);
   if (!file.is_open()) {
      cout << "ERROR: Cannot open file to write!" << endl;
      return;
   }
 
   file << "data = {";
   int i, j;
   for (i=0; i<128; i++) {
      file << "{";
      for (j=0; j<128; j++) {
         file << average(deltatimes[i][j]);
         if (j<deltatimes[i].getSize()-1) {
            file << ", ";
         }
      }
      if (i<deltatimes.getSize()-1) {
         file << ", ";
      }
      file << "},\n";
   }
   file << "};\n\n";
   file << "Print[\"Show[SurfaceGraphics[data]]\"]\n";
   file << "Show[SurfaceGraphics[data]]\n";
}
double average(Array& data) {
   int i;
   double output = 0.0;
   for (i=0; i<data.getSize(); i++) {
      output += data[i];
   }
   if (i > 0) {
      output = output / data.getSize();
   }
   return output;
}
/*-------------------- triggered algorithms -----------------------------*/
///////////////////////////////
//
// keyboardchar -- this function is called by the improv interface
//     whenever a key is pressed on the computer keyboard.
//     Put commands here which will be executed when a key is
//     pressed on the computer keyboard.
//
void keyboardchar(int key) { 
   switch (key) {
      case ' ':   // turn test on/off
         playstate = !playstate;
         cout << "PLAYSTATE: " << playstate << endl;
         break;
      case 'c':  // display current note count
         cout << "TOTAL NOTES: " << notecount << endl;
         break;
      case 'p':  // print data file
         printDataFile();
         cout << "DATA file written" << endl;
         break;
      case 'm': // print mathematica data
         printMathFile();
         cout << "MATHEMATICA file written" << endl;
         break;
      case '-':  // slow down duration
         duration++;
         cout << "Duration = " << duration << " milliseconds" << endl;
         break;
      case '+':  // speed up duration
         duration--;
         if (duration < 20) {
            duration = 20;
         }
         cout << "Duration = " << duration << " milliseconds" << endl;
         break;
   }
}
/*------------------ end improvization algorithms -----------------------*/
// md5sum: 78c25019bd1506271d30e4a78a61185a pdiscal.cpp [20050403]