//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: 15 October 1998
// Last Modified: 15 October 1998
// Filename:      ...sig/doc/examples/improv/synthImprov/keykey/keykey.cpp
// Syntax:        C++; synthImprov 2.0
//  
// Description: Using a synthesizer keyboard as a typewriter
//     by using combination (pairs) of keys to generate a single
//     character.  This is an example of how to generate a
//     large number of events (2^10-1 from only 10 keys).              
//

#include "synthImprov.h" 

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

int lastEventTime  = 0;   // last message that came in
int nextActionTime = 0;   // last time an event was created

int lastOnTime[11] = {0};  // for monitoring key on times
int lastOffTime[11] = {0}; // for monitoring key off times
int keyCount = 0;          // number of keys currently being pressed
int repeatRate = 500;      // rate at which to repeate keys
int onKey1;
int onKey2;
int event = -1;
int shift = 0;

MidiMessage message;       // for processing incoming messages
CircularBuffer<int> keysOn(100); 

// function declarations:
int makeEventDecision(void);

/*--------------------- maintenance algorithms --------------------------*/

void description(void) {
   cout << "\nKeykey:  (Craig Stuart Sapp <craig@ccrma.stanford.edu>)"
           "15 Oct 1998\n"
        << "This program is a demonstration of how to use a few keys\n"
        << "to generate a lot of events (10 keys --> 2^10 - 1 events\n"
        << "Use the white midi keys C-G below middle C for the left hand\n"
        << "and middle C-G white keys for the right hand\n"
        << "left hand: C=5, D=4, E=3, F=2, G=1\n"
        << "right hand: C=1, D=4, E=3, F=4, G=5\n"
        << "Here are the key combinations (LH col, RH row):\n\n"
        << "     5 E J O T Y\n"
        << "     4 D I N S X        For example:\n"
        << "     3 C H M R W           LH3+RH2 = F\n"
        << "     2 B G L Q V           LH5+RH3 = O\n"
        << "     1 A F K P U           LH1+RH1 = A\n"
        << "       1 2 3 4 5"
        << endl;
} 

void initialization(void) { }

void finishup(void) { }


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

void mainloopalgorithms(void) { 
   if (synth.getNoteCount() > 0) {
      nextActionTime = t_time;
      message = synth.extractNote();
      if (message.p2() == 0) {   // note off
         keyCount--;
         if (keyCount < 0) {
            keyCount = 0;
         }
         switch (message.p1()) {
            case C3:   lastOffTime[1]  = t_time;   break;
            case D3:   lastOffTime[2]  = t_time;   break;
            case E3:   lastOffTime[3]  = t_time;   break;
            case F3:   lastOffTime[4]  = t_time;   break;
            case G3:   lastOffTime[5]  = t_time;   break;
            case C4:   lastOffTime[6]  = t_time;   break;
            case D4:   lastOffTime[7]  = t_time;   break;
            case E4:   lastOffTime[8]  = t_time;   break;
            case F4:   lastOffTime[9]  = t_time;   break;
            case G4:   lastOffTime[10] = t_time;   break;
         }
      } else {                   // note on
         keyCount++;
         keysOn.insert(message.p1());
         onKey2 = onKey1;
         switch (message.p1()) {
            case C3:   lastOnTime[1]  = t_time;  onKey1 = 1;   break;
            case D3:   lastOnTime[2]  = t_time;  onKey1 = 2;   break;
            case E3:   lastOnTime[3]  = t_time;  onKey1 = 3;   break;
            case F3:   lastOnTime[4]  = t_time;  onKey1 = 4;   break;
            case G3:   lastOnTime[5]  = t_time;  onKey1 = 5;   break;
            case C4:   lastOnTime[6]  = t_time;  onKey1 = 6;   break;
            case D4:   lastOnTime[7]  = t_time;  onKey1 = 7;   break;
            case E4:   lastOnTime[8]  = t_time;  onKey1 = 8;   break;
            case F4:   lastOnTime[9]  = t_time;  onKey1 = 9;   break;
            case G4:   lastOnTime[10] = t_time;  onKey1 = 10;  break;
         }
      }
   }

   if (nextActionTime + repeatRate <= t_time) {
      nextActionTime = t_time;
   }    

   event = makeEventDecision();
   if (event > 0) {
      if (shift) {
         event = toupper(event);
      }
      cout << (char)event << flush;
   }

}


int makeEventDecision(void) {
   int event = -1;
   int combination;
   if (keyCount == 2 && nextActionTime <= t_time) {
      nextActionTime = t_time + repeatRate;
      if (onKey1 < onKey2) {
         combination = 100 * (onKey1-1) + (onKey2-1);
      } else {
         combination = 100 * (onKey2-1) + (onKey1-1);
      }
      switch (combination) {
         case 5:   event = 'e';   break;
         case 6:   event = 'j';   break;
         case 7:   event = 'o';   break;
         case 8:   event = 't';   break;
         case 9:   event = 'y';   break;

         case 105:   event = 'd';   break;
         case 106:   event = 'i';   break;
         case 107:   event = 'n';   break;
         case 108:   event = 's';   break;
         case 109:   event = 'x';   break;

         case 205:   event = 'c';   break;
         case 206:   event = 'h';   break;
         case 207:   event = 'm';   break;
         case 208:   event = 'r';   break;
         case 209:   event = 'w';   break;

         case 305:   event = 'b';   break;
         case 306:   event = 'g';   break;
         case 307:   event = 'l';   break;
         case 308:   event = 'q';   break;
         case 309:   event = 'v';   break;

         case 405:   event = 'a';   break;
         case 406:   event = 'f';   break;
         case 407:   event = 'k';   break;
         case 408:   event = 'p';   break;
         case 409:   event = 'u';   break;

         default:   event = -1; nextActionTime = t_time;
      }
   } else if (keyCount == 1 && nextActionTime <= t_time) {
      switch (keysOn[0]) {
         case Cs3:  event = ' ';  nextActionTime = t_time + repeatRate; break;
         case Ds3:  event = '\n'; nextActionTime = t_time + repeatRate; break;
         case Fs3:  event = 'z';  nextActionTime = t_time + repeatRate; break; 
         case Gs3:  event = ',';  nextActionTime = t_time + repeatRate; break;
         case As3:  event = '.';  nextActionTime = t_time + repeatRate; break;
         case Cs4:  shift = !shift; nextActionTime = t_time + repeatRate;
                    event = -1; break;
         default:    event = -1;  nextActionTime = t_time;
      }
   } else if (keyCount > 2) {
      nextActionTime = t_time;
   }

   return event;
}


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

void keyboardchar(int key) { }


/*------------------ end improvization algorithms -----------------------*/


// md5sum: 845dfc6b7d455f8e3e713897f5368552 keykey.cpp [20050403]