//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Programmer:    Sachiko Deguchi <deguchi@ccrma.stanford.edu>
// Creation Date: Thu Jun 27 12:17:39 PDT 2002
// Last Modified: Sat Aug 16 13:15:53  2003
// Filename:      ...sig/doc/examples/all/kotomel2.cpp
// Syntax:        C++; batonImprov 2.0
//  
// Description:   Melodic pattern player.  Reads an input file
//		  example syntax given at bottom of file.
//                Tries to read k-pattern.txt in current directory.
//

#define USE_TABLET
#include "batonImprov.h"

#include "Array.h"

#include <ctype.h>

#ifndef OLDCPP
   #include <fstream>
   #include <iostream>
   using namespace std;
#else
   #include <fstream.h>
   #include <iostream.h>
#endif


// function declarations
void readfile(const char* filename);
void processLine(const char* line);
void clearpatterns(Array<Array<Array<Array<int> > > >& dp);
void printpatterns(void);

// variables
Array<int> degrees;
Array<int> positions;
Array<Array<Array<Array<int> > > > degpatterns; // scale degree patterns
Voice voice;
int   transpose = 2;         // transpose value for tonic (0 = C)
int   cpat = 0;              // current pattern
int   csub = 0;              // current subpattern
int   npat = 0;              // new pattern choosen by stick 2
int   nsub = 0;              // new subpattern choosen by stick 2
int   cycle= 0;              // used to chose the next note in pattern
int   csd  = 0;              // current scale degree


///////////////////////////////////////////////////////////////////////////
//
// Improv interface functions
//


//////////////////////////////
//
// description --
//

void description(void) {
   cout <<
   "kotomel2\n"
   " Left Hand: choose melodic pattern\n"
   " Right Hand: choose starting scale degree\n"
   " , = koto  . = chinese  / = german melodic patterns\n"
   " space = print current pattern,   \\ = print all patterns\n"
   " transpose tonic note:   [ = down,   ] = up\n"
   "Keyboard control:\n"
   "\n"
   "      Patterns:    Scale Degrees:\n"
   "       123456          67890\n"
   "       qwerty          yuiop\n"
   "       asdfgh\n"
   "       zxcvbn\n"
   << endl;
} 



//////////////////////////////
//
// initialization --
//

void initialization(void) {
   voice.setPort(synth.getPort());
   voice.setChannel(0);

   degrees.setSize(7);
   degrees.allowGrowth(0);
   // put mode in for default:
   // major: {0, 2, 4, 5, 7, 9, 11}
   // koto: {0, 1, 5, 6, 7,  8, 10}
   degrees[0] = 0;
   degrees[1] = 1;
   degrees[2] = 5;
   degrees[3] = 6;
   degrees[4] = 7;
   degrees[5] = 8;
   degrees[6] = 10;

   positions.setSize(5);
   positions.allowGrowth(0);
   positions[0] = 0;
   positions[1] = 1;
   positions[2] = 2;
   positions[3] = 4;
   positions[4] = 5;

   clearpatterns(degpatterns);
   readfile("k-pattern.txt");
}



//////////////////////////////
//
// stick1trig -- chooses next note to play based
//   on pattern chosen by stick2trig.
//

void stick1trig(void) { 
   static int oldsd = -1;
   int xchoice = midiscale(baton.x1t, 0, 4);
   int ychoice = midiscale(baton.y1t, 0, 1);

   csd = positions[xchoice]; 

   if (csd != oldsd) {
      oldsd = csd;
      cycle = 0;
      cpat = npat;
      csub = nsub;
      cout << "Starting pattern " << cpat << (csub == 0 ? 'a' : 'b')
           << " on scale degree " << csd + 1 << endl;
   }

   if (cycle >= 4) {
      cycle = 0;
   }

   if (cycle == 0) {
      cpat = npat;
      csub = nsub;
   }

   int octave = ychoice + 4;
   int basepitch = degrees[csd] + 12 * octave + transpose; 
   int note = basepitch + degpatterns[csd][cpat][csub][cycle++];

   voice.play(note, 127);
   cout << "playing note: " << note << "\t(based on s"<< csd+1 
        << "-" << cpat << (csub == 0? 'a':'b')
        << "[" << cycle << "])" << endl;
}



//////////////////////////////
//
// stick2trig -- chooses the melodic pattern
//

void stick2trig(void) { 
   int xchoice = midiscale(baton.x2t, 0, 4);
   int ychoice = midiscale(baton.y2t, 0, 3);

   switch (10 * ychoice + xchoice) {
      case 0:	npat = 4; nsub = 1; break;
      case 1:	npat = 5; nsub = 1; break;
      case 2:	npat = 6; nsub = 1; break;
      case 3:	npat = 7; nsub = 1; break;
      case 4:	voice.off(); break;
      case 10:	npat = 4; nsub = 0; break;
      case 11:	npat = 5; nsub = 0; break;
      case 12:	npat = 6; nsub = 0; break;
      case 13:	npat = 7; nsub = 0; break;
      case 14:	voice.off(); break;
      case 20:	npat = 0; nsub = 1; break;
      case 21:	npat = 1; nsub = 1; break;
      case 22:	npat = 2; nsub = 1; break;
      case 23:	npat = 3; nsub = 1; break;
      case 24:	voice.off(); break;
      case 30:	npat = 0; nsub = 0; break;
      case 31:	npat = 1; nsub = 0; break;
      case 32:	npat = 2; nsub = 0; break;
      case 33:	npat = 3; nsub = 0; break;
      case 34:	voice.off(); break;
   }

   cout << "Next Pattern: " << npat << (nsub == 0? 'a' : 'b') << endl;
}



//////////////////////////////
//
// keyboardchar --
//

void keyboardchar(int key) { 
   int i;
   switch (key) {
      case '\\': printpatterns(); break;
      case ' ': 
         cout << "current pattern:\ts" << csd+1 << ":"
              << cpat << (csub == 0? 'a':'b') << "=[ ";
         for (i=1; i<4; i++) {
            cout << degpatterns[csd][cpat][csub][i] - 
                    degpatterns[csd][cpat][csub][i-1];
            if (i<3) {
               cout << ", ";
            } else { 
               cout << " ";
            }
         }
         cout << "]" << endl;
         break;
      case ',': clearpatterns(degpatterns); readfile("k-pattern.txt"); break;
      case '.': clearpatterns(degpatterns); readfile("c-pattern.txt"); break;
      case '/': clearpatterns(degpatterns); readfile("g-pattern.txt"); break;

      case '[': transpose--; cout << "Tonic: " << transpose << endl; break;
      case ']': transpose++; cout << "Tonic: " << transpose << endl; break;

      case '1': baton.x2t =  12; baton.y2t = 112; stick2trig(); break;
      case '2': baton.x2t =  36; baton.y2t = 112; stick2trig(); break;
      case '3': baton.x2t =  60; baton.y2t = 112; stick2trig(); break;
      case '4': baton.x2t =  84; baton.y2t = 112; stick2trig(); break;
      case '5': baton.x2t = 108; baton.y2t = 112; stick2trig(); break;
      case 'q': baton.x2t =  12; baton.y2t =  80; stick2trig(); break;
      case 'w': baton.x2t =  36; baton.y2t =  80; stick2trig(); break;
      case 'e': baton.x2t =  60; baton.y2t =  80; stick2trig(); break;
      case 'r': baton.x2t =  84; baton.y2t =  80; stick2trig(); break;
      case 't': baton.x2t = 108; baton.y2t =  80; stick2trig(); break;
      case 'a': baton.x2t =  12; baton.y2t =  48; stick2trig(); break;
      case 's': baton.x2t =  36; baton.y2t =  48; stick2trig(); break;
      case 'd': baton.x2t =  60; baton.y2t =  48; stick2trig(); break;
      case 'f': baton.x2t =  84; baton.y2t =  48; stick2trig(); break;
      case 'g': baton.x2t = 108; baton.y2t =  48; stick2trig(); break;
      case 'z': baton.x2t =  12; baton.y2t =  16; stick2trig(); break;
      case 'x': baton.x2t =  36; baton.y2t =  16; stick2trig(); break;
      case 'c': baton.x2t =  60; baton.y2t =  16; stick2trig(); break;
      case 'v': baton.x2t =  84; baton.y2t =  16; stick2trig(); break;
      case 'b': baton.x2t = 108; baton.y2t =  16; stick2trig(); break;

      case '6': baton.x1t =  12; baton.y1t = 112; stick1trig(); break;
      case '7': baton.x1t =  36; baton.y1t = 112; stick1trig(); break;
      case '8': baton.x1t =  60; baton.y1t = 112; stick1trig(); break;
      case '9': baton.x1t =  84; baton.y1t = 112; stick1trig(); break;
      case '0': baton.x1t = 108; baton.y1t = 112; stick1trig(); break;
      case 'y': baton.x1t =  12; baton.y1t =  16; stick1trig(); break;
      case 'u': baton.x1t =  36; baton.y1t =  16; stick1trig(); break;
      case 'i': baton.x1t =  60; baton.y1t =  16; stick1trig(); break;
      case 'o': baton.x1t =  84; baton.y1t =  16; stick1trig(); break;
      case 'p': baton.x1t = 108; baton.y1t =  16; stick1trig(); break;
      
   }
}


//
// Other improv interface functions
//

void b14plustrig(void) { }
void b15plustrig(void) { }
void b14minusuptrig(void) { }
void b14minusdowntrig(void) { }
void b15minusuptrig(void) { }
void b15minusdowntrig(void) { }
void finishup(void) { }
void mainloopalgorithms(void) { }


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



//////////////////////////////
//
// clearpatterns --
//

void clearpatterns(Array > > >& dp) {
   int sd, pat, sub, i;

   dp.setSize(7);
   dp.allowGrowth(0);
   for (sd=0; sd<dp.getSize(); sd++) {
      dp[sd].setSize(8);
      dp[sd].allowGrowth(0);
      for (pat=0; pat<dp[sd].getSize(); pat++) {
         dp[sd][pat].setSize(2);
         dp[sd][pat].allowGrowth(0);
         for (sub=0; sub<dp[sd][pat].getSize(); sub++) {
            dp[sd][pat][sub].setSize(4);
            dp[sd][pat][sub].allowGrowth(0);
            for (i=0; i<dp[sd][pat][sub].getSize(); i++) {
               dp[sd][pat][sub][i] = 0;
            }
         }
      }
   }
}



//////////////////////////////
//
// readfile --
//

void readfile(const char* filename) {

   ifstream infile(filename);
   if (!infile.is_open()) {
      cout << "Cannot open file: " << filename << endl;
      return;
   }

   char buffer[2048] = {0};
   infile.getline(buffer, 256, '\n'); 
   while (!infile.eof()) {
      processLine(buffer);
      infile.getline(buffer, 256, '\n'); 
   }
}



//////////////////////////////
//
// processLine --
//

void processLine(const char* line) {
   int length = strlen(line);
   if (length > 256) {
      return;
   }
   char buffer[2048] = {0};
   strcpy(buffer, line);
   char* ptr = strtok(buffer, " \t\n");
   if (ptr == NULL) {
      // cout << "nothing on line" << endl;
      return;
   }

   if (ptr[0] == '#') {
      // print comment line
      cout << line << endl;
      return;
   }
 
   // first data field must start with an 's' or 'p':
   if (ptr[0] == 'S') {
      ptr[0] = 's';
   }
   if (ptr[0] == 'P') {
      ptr[0] = 'p';
   }
   if ((ptr[0] != 's') && (ptr[0] != 'p')) {
      return;
   }

   int scaledegree = -1;
   int position = -1;
   int pattern = -1;
   int count = 0;
   int tpose;

   if (strchr(ptr, '=') != NULL) {
      if (ptr[0] == 'p') {
         count = sscanf(ptr, "p%d=s%d", &position, &scaledegree);
         if (count != 2) {
            return;
         }
         if (count != 2) {
            cout << "First token invalid" << endl;
            return;
         }
         position--;
         scaledegree--;
         positions[position] = scaledegree;
         return;
      } else {
         count = sscanf(ptr, "s%d=%d", &scaledegree, &tpose);
         if (count != 2) {
            return;
         }
   //    cout << "SCALE DEGREE " << scaledegree << " is " << tpose
   //         << " half-steps above the tonic." << endl;
   
         if (scaledegree < 1 || scaledegree > 7) {
            cout << "Scale degree out of range: " << scaledegree << endl;
         }
         scaledegree = scaledegree - 1;   // offset from zero
    
         if (scaledegree != 0) {
            degrees[scaledegree] = tpose;
         } else {
            transpose = tpose;
         }
         return;
      }
   } else {
      count = sscanf(ptr, "s%d:%d", &scaledegree, &pattern);
      if (count != 2) {
         cout << "First token invalid" << endl;
         return;
      }
   }

   char subpattern = ' ';
   int len2 = strlen(ptr);
   if (isalpha(ptr[len2-1])) {
      subpattern = tolower(ptr[len2-1]);
   }

// cout << "S=" << scaledegree << "\tP=" << pattern 
//      << "\tV=" << subpattern;


   scaledegree = scaledegree - 1;   // offset from zero
   if (scaledegree < 0 || scaledegree > 6) {
      cout << "Scale degree is out of range:" << scaledegree << endl;
      return;
   }

   if (pattern < 0 || pattern > 7) {
      cout << "Pattern is out of range:" << pattern << endl;
      return;
   }

   if (subpattern != ' ' && subpattern != 'a' && subpattern != 'b') {
      cout << "Sub pattern is out of range:" << subpattern << endl;
      return;
   }

   ptr = strtok(NULL, " ,:\t\n;");
   int degree;
   int icount = 0;
   Array<int> intervals;
   intervals.setSize(3);
   intervals.setAll(0);
   intervals.allowGrowth(0);
   while (ptr != NULL) {
      count = sscanf(ptr, "%d", °ree);
      if (count == 1) {
         // cout << "\t" << degree << "\t";
         intervals[icount++] = degree;
      }
      ptr = strtok(NULL, " ,:\t\n;");
      if (icount >= 3) {  // only 3 intervals allowed
         break;    
      }
   }
   // cout << endl;

   int i;
   if (subpattern == 'a' || subpattern == ' ') {
      degpatterns[scaledegree][pattern][0][0] = 0;
      for (i=1; i<4; i++) {
         degpatterns[scaledegree][pattern][0][i] = 
               degpatterns[scaledegree][pattern][0][i-1] + intervals[i-1];
      }
   }
   if (subpattern == 'b' || subpattern == ' ') {
      degpatterns[scaledegree][pattern][1][0] = 0;
      for (i=1; i<4; i++) {
         degpatterns[scaledegree][pattern][1][i] = 
               degpatterns[scaledegree][pattern][1][i-1] + intervals[i-1];
      }
   }
}



//////////////////////////////
//
// printpatterns --
//

void printpatterns(void) {
   int deg, pat, i;

   for (deg = 0; deg < 7; deg++) {
      for (pat = 0; pat < 8; pat++) {
         cout << "s" << deg+1 << ":" << pat << " :: a = ";
         for (i=1; i<4; i++) {
            cout << degpatterns[deg][pat][0][i] -
                    degpatterns[deg][pat][0][i-1] << " ";
         }
         cout << "\tb =";
         for (i=1; i<4; i++) {
            cout << degpatterns[deg][pat][1][i] -
                    degpatterns[deg][pat][1][i-1] << " ";
         }
         cout << endl;
      }
   }
}



/*  sample pattern file:
# Chinese melodic patterns

p1=s1	# position 1 = scale degree 1
p2=s2	# position 2 = scale degree 2
p3=s3	# position 3 = scale degree 3
p4=s5	# position 4 = scale degree 5
p5=s6	# position 5 = scale degree 6

s1=2    # scale degree 1 is 2 half-steps above C(i.e.: tonic is on D)
s2=2	# scale degree 2 is 2 half-step  above the tonic(s1)
s3=4	# scale degree 3 is 4 half-steps above the tonic(s1)
s5=7	# scale degree 5 is 7 half-steps above the tonic(s1)
s6=9	# scale degree 6 is 9 half-steps above the tonic(s1)

s1:0	-3 -2 -3
s1:1a	-3 -2 2
s1:1b	-3 -2 5
s1:2	-3 3 -3
s1:3	-3 3 2
s1:4	2 -2 -3
s1:5	2 -2 2
s1:6	2 2 -2
s1:7	2 2 3

s2:0	-2 -3 -2
s2:1a	-2 -3 3
s2:1b	-2 -3 5
s2:2	-2 2 -2
s2:3	-2 2 2
s2:4a	2 -2 -2
s2:4b	5 -3 -2
s2:5a	2 -2 2
s2:5b	2 -4 2
s2:6a	2 3 -3
s2:6b	2 3 -5
s2:7	2 3 2

s3:0	-2 -2 -3
s3:1	-2 -2 2
s3:2	-2 2 -2
s3:3	-2 2 3
s3:4	3 -3 -2
s3:5a	3 -3 3
s3:5b	3 -5 2
s3:6	3 2 -2
s3:7	3 2 3

s5:0	-3 -2 -2
s5:1a	-3 -2 2
s5:1b	-3 -2 5
s5:2	-3 3 -3
s5:3	-3 3 2
s5:4a	2 -2 -3
s5:4b	5 -3 -2
s5:5	2 -2 2
s5:6	2 3 -5
s5:7	2 3 2

s6:0	-2 -3 -2
s6:1a	-2 -3 3
s6:1b	-2 -3 5
s6:2	-2 2 -2
s6:3	-2 2 3
s6:4	3 -3 -2
s6:5a	3 -3 3
s6:5b	3 -5 2
s6:6	3 2 -2
s6:7	3 2 2

*/



// md5sum: 47cb15d6c3221eef0686ba7e65938192 kotomel2.cpp [20050403]