//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Jul 17 14:40:30 PDT 2000
// Last Modified: Mon Jul 17 14:40:33 PDT 2000
// Filename:      ...sig/examples/all/drumstick.cpp
// Syntax:        C++; stickImprov 2.3
//  
// Description: This is a template which you fill with algorithms 
//              in the following functions to create a stickImprov program.
//

#include "stickImprov.h"      // includes the default Win95/Linux console 
                              // interface for the stickImprov environment
#ifndef OLDCPP
   #include <fstream>
   using namespace std;
#else
   #include <fstream.h>
#endif

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

int channel = 9;              // channel to play notes on
Voice voice;                  // controls note-offs
SigTimer beat;                // for controlling the beat location
int beattime = 0;             // last time a beat was played
int current = 0;              // current beat subdivision being played
fstream infile;

int lowtarget = 3;            // position that defines the lower beat
int hitarget = 123;           // position that defined the upper beat
int clocktick = -1;           // current time location
double ipredict = -1;         // instantaneous prediction
int slowdirection = 0;        // low-passed direction info
double alpha = 0.0;           // for linear prediction
double beta = 0.0;            // for linear prediction

CircularBuffer<int> location;  // history of the positions
CircularBuffer<int> direction; // history of the positions

// function definitions:
void analyzeTempo(void);
int determineDirection(CircularBuffer<int>& history, int curr);

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

//////////////////////////////
//
// description -- this function is called by the improv interface
//     whenever a capital "D" is pressed on the computer keyboard.
//     Put a description of the program and how to use it here.
//

void description(void) {
   cout << 
   " STICKBEAT -- Craig Stuart Sapp <craig@ccrma.stanford.edu>, 19 July 2000\n";
   cout << endl;
} 



//////////////////////////////
//
// initialization -- this function is called by the improv
//     interface once at the start of the program.  Put items
//     here which need to be initialized at the beginning of
//     the program.
//

void initialization(void) { 
   voice.setChannel(channel);
   infile.open("test.dat", ios::out);
   location.setSize(1000);
   direction.setSize(1000);
   location.reset();
   direction.reset();
   location.insert(0);
   direction.insert(0);
}


//////////////////////////////
//
// finishup -- this function is called by the improv interface
//     whenever the program is exited.  Put items here which
//     need to be taken care of when the program is finished.
//

void finishup(void) { 
   infile.close();
}


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

//////////////////////////////
//
// mainloopalgorithms -- this function is called by the improv interface
//   continuously while the program is running.  The global variable t_time
//   which stores the current time is set just before this function is
//   called and remains constant while in this functions.
//

void mainloopalgorithms(void) { }


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

///////////////////////////////
//
// keyboardchar -- this function is called by the improv interface
//     whenever a key is pressed on the computer keyboard.
//     Put commands here which will be executed when a key is
//     pressed on the computer keyboard.
//

void keyboardchar(int key) { }



///////////////////////////////
//
// stickresponse -- this function is called whenever a complete set
//     of stick positoin/pressure values have been received.
//


void stickresponse(void) { 
   clocktick++;

   // don't monitor for beats if not pressing FSR3
   if (stick.getState(3) != 1) {
      return;
   }

   location.insert(stick.loc3_7());

   if (location[0] < location[1]) {
      direction.insert(-1);
   } else if (location[0] > location[1]) {
      direction.insert(1);
   } else {
      direction.insert(0);
   }

   slowdirection = determineDirection(direction, location[0]);
   if (slowdirection == 0) {
      // don't predict if uncertain of direction
      ipredict = -100;
   } else if (slowdirection == 1) {
      alpha = hitarget - location[1];
      beta  = hitarget - location[0];
      ipredict = alpha / (alpha - beta); // + clocktick;
      // going up on the fsr
   } else if (slowdirection == -1) {
      // going down on the fsr
      alpha = location[1] - lowtarget;
      beta  = location[0] - lowtarget;
      ipredict = alpha / (alpha - beta); // + clocktick;
   } else {
      cout << "Unknown direction: " << slowdirection << endl;
      exit(1);
   }

   cout << clocktick     << "\t" 
        << location[0]   << "\t" 
//      << direction[0]  << "\t" 
        << slowdirection << "\t"
        << ipredict 
        << endl;

   infile << clocktick   << "\t" 
        << location[0]   << "\t" 
//      << direction[0]  << "\t" 
        << slowdirection << "\t"
        << ipredict 
        << endl;


}



///////////////////////////////
//
// fsr 1 triggers -- fsr1ontrig will be called when the FSR is
//    pressed, and fsr1offtrig will be called when the FSR is
//    released.
//

void fsr1ontrig(void) { }
void fsr1offtrig(void) { }



///////////////////////////////
//
// fsr 2 triggers -- fsr2ontrig will be called when the FSR is
//    pressed, and fsr2offtrig will be called when the FSR is
//    released.
//

void fsr2ontrig(void) { }
void fsr2offtrig(void) { }



///////////////////////////////
//
// fsr 3 triggers -- fsr3ontrig will be called when the FSR is
//    pressed, and fsr3offtrig will be called when the FSR is
//    released.
//

void fsr3ontrig(void) { }
void fsr3offtrig(void) { }


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



//////////////////////////////
//
// determineDirection -- determine the direction that you are heading
//

int determineDirection(CircularBuffer& history, int curr) {
   int highregion = 115;    // place likely to be 0
   int lowregion = 12;      // place likely to be 0
   int decision = 0;

   int a = history[0];
   int b = history[1];
   int c = history[2];
   int d = history[3];

   if (a == b && a == c) {
      decision = a;
   } else {
      decision = 0;
   }

   if (decision == 0 && curr > highregion || curr < lowregion) {
      if (a == b && a == c && a == d) {
         decision = a;
      } else {
         decision = 0;
      }
   }

   return decision;
}





// md5sum: 82df9097e3ef8e68aab8120b2d4a8fc2 stickbeat.cpp [20050403]