//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Apr 28 14:37:28 PDT 2000
// Last Modified: Fri Apr 28 14:37:32 PDT 2000
// Filename:      ...sig/doc/examples/all/ghost/ghost.cpp
// Syntax:        C++; synthImprov 2.0
//  
// Description:   When a note is played on the keyboard, it generates
//		  a ghost image which will play sometime within the next
//		  minute on the same pitch, half as loud as the original pitch.
//                The ghost note has the same duration as the original
//                note.
//

#include "synthImprov.h" 

#define MAXTIME 60     /* maximum time to start ghost note */
#define MINTIME 10     /* minimum time to start ghost note */

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

EventBuffer eventBuffer;            // used to store algorithms for performance
Array<int> noteontimes(128);        // list of the last time a note was played
Array<int> noteonvels(128);         // list of the last time a note was played

// function declarations:
void sillyKeyboard(int key, int chan = 0);
void createGhost(int key, int velocity, int channel, int duration);
void processNote(MidiMessage message);


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

void description(void) {
   printboxtop();
   psl(
   "   GHOST -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 28 April 2000");
   psl("Description:   When a note is played on the keyboard, it generates");
   psl("a ghost image which will play sometime within the next");
   psl("minute on the same pitch, half as loud as the original pitch.");
   psl("The ghost note has the same duration as the original");
   psl("note.");
   psl("");
   printboxbottom();
} 


void initialization(void) { 
   eventBuffer.setPollPeriod(10);
   noteontimes.zero();
   noteonvels.zero();
}


void finishup(void) { }


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


void mainloopalgorithms(void) { 
   eventBuffer.checkPoll();             // see if any notes need playing

   while (synth.getNoteCount() > 0) {
      processNote(synth.extractNote());
   }
}
      


////////////////////////////
//
// processNote -- 
//

void processNote(MidiMessage message) {
   int key = message.p1();
   int velocity = message.p2() / 2;
   int channel = message.p0() & 0x0f;
   int command = message.p0() & 0xf0;
   int duration;

   int status = 1;
   if (command == 0x80 || velocity == 0) {
      status = 0;
   }

   if (status == 0) {
      duration = t_time - noteontimes[key];
      createGhost(key, noteonvels[key], channel, duration);
   } else {
      noteontimes[key] = t_time;
      noteonvels[key] = velocity;
   }
}


////////////////////////////
//
// createGhost -- set up the parameters for the ghost note.
//

void createGhost(int key, int velocity, int channel, int duration) {
   static NoteEvent tn;   // a Temporary Note for copying into eventBuffer

   // setting the fields of the function note
   tn.setChannel(channel);
   tn.setKeyno(key);
   tn.setVelocity(velocity);

   tn.setStatus(EVENT_STATUS_ACTIVE);

   int starttime = (rand() % (MAXTIME - MINTIME) + MINTIME) * 1000;
   tn.setOnDur(t_time + starttime, duration);

   eventBuffer.insert(tn);

   cout << "Ghost = " << key << " in " << starttime/1000
        << " seconds " << endl;
}



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

void keyboardchar(int key) { 
   switch (key) {
      case 'p':
         cout << "current list in eventBuffer: " << endl;
         eventBuffer.print();
         cout << endl;
         cout << "Event[0] status: " << eventBuffer[0].getStatus() << endl;
         break;
      default:
         sillyKeyboard(key);
   }
}



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: 9931db184b6d501cc5ea77c4e9894a12 ghost.cpp [20050403]