//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: 9 January 1998
// Last Modified: 9 January 1998
// Filename:      ...sig/doc/examples/improv/synthImprov/shadow2/shadow2.cpp
// Syntax:        C++; synthImprov 2.0
//  
// Description: echos a note played on the synthesizer keyboard
//     Unlike shadow.cpp, this program also imitates the duration
//     of the echoing note and is polyphonic.

#include "synthImprov.h" 


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

int shadowDistance = 12;     // number of half-steps to place shadow at
int shadowSide     = 1;      // 1 = higher, -1 = lower
MidiMessage noteMessage;     // for reading keyno and velocity (and time)
int shadowNote = 0;          // note to play as a shadow
int keyray[128] = {0};       // keyboard array which keeps track of notes

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

void description(void) {
   printboxtop();
   psl(
"   Shadow2 -- by Craig Stuart Sapp <craig@ccrma.stanford.edu> -- 5 Jan 1998");
   psl("");
   psl(
"   This program will echo the input notes at a certain distance from the");
   psl(
"   original notes.  The range of the shadow is from 1 to 24 half-steps on");
   psl("   either side of the original note.");
   printintermediateline();
   psl("  User commands:");
   psl("      \"-\" = decrease shadow length   \"=\" = increase shadow length");
   psl("      \"\\\" = switch shadow side");
   printboxbottom();
} 

void initialization(void) { }

void finishup(void) { }


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

void interpretNote(MidiMessage message);

void mainloopalgorithms(void) { 
   while (synth.getNoteCount() > 0) {
      interpretNote(synth.extractNote());
   }
}

void interpretNote(MidiMessage message) {
   int newNote = message.p1();
   int newVelocity = message.p2();

   if (newVelocity != 0) {
      // pick a shadow note
      shadowNote = newNote + shadowSide * shadowDistance;
      // ignore the shadow note if it is out of keyboard range:
      if (shadowNote > 0 && shadowNote < 128) {
         // turn off any old note (should never happen, but be safe):
         if (keyray[newNote] != 0) {
            synth.play(0, keyray[newNote], 0);
         }
         keyray[newNote] = shadowNote;
         synth.play(0, shadowNote, newVelocity);
      }
   } else {
      // a note off command
      if (keyray[newNote] != 0) {
         synth.play(0, keyray[newNote], 0);
         keyray[newNote] = 0;
      }
   }
}
      


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

void keyboardchar(int key) { 
   switch (key) {
      case '-': 
         shadowDistance--; 
         if (shadowDistance < 1) shadowDistance = 1;
         cout << "Shadow = " << shadowSide * shadowDistance << endl;
         break;
      case '=':
         shadowDistance++; 
         if (shadowDistance > 24) shadowDistance = 24;
         cout << "Shadow = " << shadowSide * shadowDistance << endl;
         break;
      case '\\':
         shadowSide *= -1;
         cout << "Shadow = " << shadowSide * shadowDistance << endl;
         break;
      default:
         charsynth(key);
   }
}


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


/*   some functions and variables provided by the support program

   program_change(channel, instrument); -------- sets the timbre for a channel
   control_change(channel, controller, value); - sends a continuous controller 
   note_on(channel, keynumber, keyvelocity); --- plays a MIDI note
   note_off(channel, keynumber); --------------- same as note_on with 0 vel.
   raw_send(channel, command, data1, data2); --- send some midi command
   long t_time; -------------------------------- current time in milliseconds

*/

// md5sum: 6c9160fd4f09124be14ede7eec7ecbe9 shadow2.cpp [20050403]