//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sat Mar  2 22:32:12 PST 1996
// Last Modified: 5 February 1998
// Last Modified: Tue Nov 10 16:59:59 PST 1998
// Last Modified: Sat Oct 13 18:14:07 PDT 2001 (allow for 0x80 note-offs)
// Filename:      ...sig/doc/examples/improv/synthImprov/gliss/gliss.cpp
// Syntax:        C++; synthImprov 0.5
//  
//

#include "synthImprov.h" 


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

EventBuffer eventBuffer;  // for future notes (2048 notes max)
int direction = 1;        // direction of glissandos, 1=up, -1=down
int step = 1;		  // step to take for each note in glissando
int rate = 300;		  // tempo of gliss notes
int channel = 0;          // channel to play the glisses on.
MidiMessage message;      // for reading keyno and velocity (and time)


// function declarations:
void sillyKeyboard(int key, int chan = 0);
void playgliss(MidiMessage aMessage);
int limit(int value, int min, int max);


/*--------------------- Event Algorithms --------------------------------*/


//////////////////////////////
//
// GlissNoteFunction -- creates glissandos.  To be
//      used with the FunctionNote class; Notes are generated one at a
//      time in the EventBuffer from a FunctionNote.
//
// Global variables needed by this function:
//      direction : The direction of the glissando (up or down)
//      step:       The interval step size for the glissando
//

static void GlissNoteFunction(FunctionEvent& p, EventBuffer& midiOutput) {
   static NoteEvent note;            // temporary note before placing in buffer

   // set the parameters for the output note:
   note.setOnDur(t_time, p.getOffTime()); // off time holds dur
   note.setVel(p.getVel());
   note.setChan(p.getChan());
   note.setKey(p.getKey());

   // update the parameters for the gliss function:
   p.setKey(p.getKey()+step*direction);
   p.setOnTime(p.getOnTime() + p.getDur());  // OffTime stores duration

   note.activate();
   note.action(midiOutput);       // start right now, avoiding any buffer delay
   midiOutput.insert(note);       // the note off message is being buffered

   if (p.getKey() > C8 || p.getKey() < A0) {
      p.off(midiOutput);
   }
}




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

void description(void) {
   printboxtop();
   psl(
   "   GLISS -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 5 Feb 1998");
   psl("");
   psl(
   "  Description: Creates glissandos when you press a key on the keyboard.");
   psl("    Computer keyboard keys are assigned random attack velocities.");
   printintermediateline();
   psl("  User commands:");
   psl(
   "     \"-\" = decrease step   \"=\" = increase step   \"\\\" = change "
   "direction");
   psl("     \"[\" = increase rate   \"]\" = decrease rate ");
   psl("      \"0\"-\"9\" = octave number of computer keyboard notes");
   psl("      Notes:           s   d      g    h   j   ");
   psl("                     z   x   c   v   b   n   m  ");
   printboxbottom();
} 


void initialization(void) { 
   eventBuffer.setPollPeriod(10);
}


void finishup(void) { }


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


void mainloopalgorithms(void) { 
   if (eventBuffer.checkPoll());        // see if any notes to play

   while (synth.getNoteCount() > 0) {
      message = synth.extractNote();
      if (((message.p0() & 0xf0) == 0x90) && (message.p2() != 0)) {
         playgliss(message);

         // lowest A on the keyboard will switch the gliss directions
         if ((message.p0() & 0xf0 == 0x90) && (message.p1() == A0)) {
            direction = -direction;
         }
      }
   }
}
      


////////////////////////////
//
// playgliss -- inserts a FunctionEvent into the eventBuffer which
//     plays a glissando.  The glissando will die after the 
//     note falls off of the keyboard.
//

void playgliss(MidiMessage aMessage) { 
   static FunctionEvent tn;   // a Temporary Note for copying into eventBuffer

   // setting the fields of the function note
   tn.setFunction(GlissNoteFunction);
   tn.setChannel(channel);
   tn.setKeyno(aMessage.p1());
   tn.setVelocity(aMessage.p2());
 
   tn.setStatus(EVENT_STATUS_ACTIVE);

   // start time of function and the duration between calling it
   tn.setOnDur(t_time, rate);

   int location = eventBuffer.insert(tn);

   cout << "GLISS: StartKey = " << (int)aMessage.p1()
        << "\tVelocity = "      << (int)aMessage.p2()
        << "\tRate = "          << rate
        << "\tLocation = "      << location
        << endl;
}



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

void keyboardchar(int key) { 
   switch (key) {
      case '-':
         step = limit(--step, 1, 12);
         cout << "Step = " << step << endl;
         break;
      case 'p':
         cout << "current list in eventBuffer: " << endl;
         eventBuffer.print();
         cout << endl;
         cout << "Event[0] status: " << eventBuffer[0].getStatus() << endl;
         break;
      case '=':
         step = limit(++step, 1, 12);
         cout << "Step = " << step << endl;
         break;
      case '\\':
         direction *= -1;
         if (direction == 1) {
            cout << "Up" << endl;
         } else {
            cout << "Down" << endl;
         }
         break;
      case '[':
         rate -= 20;
         rate = limit(rate, 20, 200000);
         cout << "Rate = " << rate << endl;
         break;
      case ']':
         rate += 20;
         rate = limit(rate, 20, 200000);
         cout << "Rate = " << rate << endl;
         break;
      default:
         sillyKeyboard(key);
   }
}



int limit(int value, int min, int max) {
   if (value < min) {
      return min;
   } else if (value > max) {
      return max;
   } else {
      return value;
   }
}



void sillyKeyboard(int key, int chan /* = 0 */) {
   static int octave = 4;
   static int newkey = 0;
   static Voice voice;
   static MidiMessage message;

   // check to see if adjusting the octave:
   if (isdigit(key)) {
      octave = key - '0';
      return;
   }

   switch (key) {
      case 'z':  newkey = 12 * octave + 0;   break;   // C
      case 's':  newkey = 12 * octave + 1;   break;   // C#
      case 'x':  newkey = 12 * octave + 2;   break;   // D
      case 'd':  newkey = 12 * octave + 3;   break;   // D#
      case 'c':  newkey = 12 * octave + 4;   break;   // E
      case 'v':  newkey = 12 * octave + 5;   break;   // F
      case 'g':  newkey = 12 * octave + 6;   break;   // F#
      case 'b':  newkey = 12 * octave + 7;   break;   // G
      case 'h':  newkey = 12 * octave + 8;   break;   // G#
      case 'n':  newkey = 12 * octave + 9;   break;   // A
      case 'j':  newkey = 12 * octave + 10;  break;   // A#
      case 'm':  newkey = 12 * octave + 11;  break;   // B
      case ',':  newkey = 12 * octave + 12;  break;   // C
      case 'l':  newkey = 12 * octave + 12;  break;   // C#
      case '.':  newkey = 12 * octave + 12;  break;   // D
      case '\'': newkey = 12 * octave + 12;  break;   // D#
      case '/':  newkey = 12 * octave + 12;  break;   // E
      default: return;         // don't do anything if not a key
   }

   // prevent any invalid key numbers:
   if (newkey < 0) {
      newkey = 0;
   } else if (newkey > 127) {
      newkey = 127;
   }

   // put note-off message in synth's input buffer:
   message.time = t_time;
   message.p0() = 0x90 | voice.getChan();
   message.p1() = voice.getKey();
   message.p2() = 0;
   synth.insert(message);

   // turn off the last note:
   voice.off();

   // set parameters for next note-on:
   voice.setChan(chan & 0x0f);      // limit channel to range from 0 to 15
   voice.setVel(rand() % 127 +1);   // random attack in range from 1 to 127
   voice.setKey(newkey);            // use the newly selected key number

   // play the MIDI note:
   voice.play();

   // insert the played note into synth's input MIDI buffer:
   message.command() = 0x90 | voice.getChan();
   message.p1() = voice.getKey();
   message.p2() = voice.getVel();
   synth.insert(message);

}


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


// md5sum: 7c0d8687dbdfec710196ff88639060a6 gliss.cpp [20050403]