//
// Programmer:    Leland Stanford, Jr. <leland@stanford.edu>
// Creation Date: Wed May 12 00:18:31 PDT 1999
// Last Modified: Wed May 12 01:47:06 PDT 1999
// Filename:      .../improv/examples/synthImprov/markov1/markov1.cpp
// Syntax:        C++; Visual C++ 6.0; synthImprov
//  
// Description: Generate/create markov transition tables for playing a 
//              melody.
//

#include "synthImprov.h"      
#include <math.h>

#ifndef OLDCPP
   #include <iostream>
   #include <iomanip>
   using namespace std;
#else
   #include <iostream.h>
   #include <iomanip.h>
#endif

#ifdef VISUAL
   double drand48(void) {
      return (double)rand()/RAND_MAX;
   }
#endif


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

#define MARKOV_SIZE 12

double markov[MARKOV_SIZE][MARKOV_SIZE] = {
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05},
   {0.10, 0.05, 0.10, 0.10, 0.50, 0.10, 0.10, 0.10, 0.10, 0.10, 0.05, 0.05}};

int    markovRecord  = 0;      // true if recording markov chain data
double tempo         = 120;    // the tempo of the performed melody
int    markovNote    = 0;      // the markov note for performance
int    octave        = 4;      // the octave of the markov note for performance
Voice  markovVoice;            // for keeping track of note-offs
int    lastMarkovNote = 0;     // for counting markov notes
int    newMarkovNote = 0;      // for counting markov notes
int    i, j;                   // for counting
SigTimer metronome;            // for determing the rhythm
int    constant = 0;           // for constant/non constant rhythm

// function declarations:
void processNoteForMarkov(void);
void performNoteFromMarkov(void);
void setNextEventTime(double tempo, SigTimer& metronome);
int  chooseNoteFromMarkovArray(int markovNote);
void zeroMarkovTable(void);
void generateMarkovTable(void);


/*--------------------- 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 << 
   "Markov -- play/record markov melodies\n"
   "   keyboard commands: \n"
   "      , = slow down tempo\n"
   "      . = speed up tempo\n"
   "      p = print markov table\n"
   "    1-7 = set the octave to play markov note\n"
   "    ' ' = switch between recording and playing a markov melody\n"
   << endl;

} 



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

void initialization(void) { 
   markovVoice.pc(0);
   markovVoice.setChannel(0);
   metronome.setTempo(60);
}



//////////////////////////////
//
// 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) { }


/*-------------------- 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) { 
   if (markovRecord) {
      processNoteForMarkov();
   } else if (metronome.expired()) {
      performNoteFromMarkov();
      setNextEventTime(tempo, metronome);
   }
}


/*-------------------- 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) { 
   if (isdigit(key)) {
      octave = key - '0';
      if (octave < 1) {
         octave = 1;
      }
      if (octave > 7) {
         octave = 7;
      }
      return;
   }

   switch (key) {
      case ',':            // slow down the tempo which will be
         tempo /= 1.05;    // activated at the next note to be played
         if (tempo < 20) {
            tempo = 20;
         }
         cout << "Tempo is: " << tempo << endl;
         break;

      case '.':            // speed up the tempo which will be
         tempo *= 1.05;    // activated at the next note to be played
         if (tempo > 500) {
            tempo = 500;
         }
         cout << "Tempo is: " << tempo << endl;
         break;

      case 'p':            // print the markov table
         cout << "Markov transition table:" << endl;
         for (i=0; i<MARKOV_SIZE; i++) {
            for (j=0; j<MARKOV_SIZE; j++) {
               cout << setw(5) << markov[i][j] << " ";
            }
            cout << endl;
         }
         break;
 
      case ' ':            // switch between recording and playing 
         markovRecord = !markovRecord;
         if (markovRecord) {
            cout << "Starting to record Markov table..." << endl;
            zeroMarkovTable();
            newMarkovNote = -100;
            markovVoice.off();
         } else {
            cout << "Finished recording Markov table." << endl;
            generateMarkovTable();
         }
         break;
            
      case 'c':            // constant/not constant rhythm
         constant = !constant;
         cout << "The rhythm set is: " << constant << endl;
         break;

   }
}


/*------------------ begining of assisting functions --------------------*/


/////////////////////////////
//
// processNoteForMarkov -- read any MIDI input notes and
//    store occurances for generating a Markov transition table.
//

void processNoteForMarkov(void) {
   MidiMessage message;
   while (synth.getNoteCount() > 0) {
      message = synth.extractNote();
      if (message.p2() != 0) {
         lastMarkovNote = newMarkovNote;
         newMarkovNote = message.p1() % 12;
         if (newMarkovNote == (21%12) && message.p1() == 21) {
            newMarkovNote = -1;
         } else if (lastMarkovNote >= 0) {
            markov[lastMarkovNote][newMarkovNote] += 1;
         } 
      }
   }
}



/////////////////////////////
//
// performNoteFromMarkov -- generate a note from the last played note.
//

void performNoteFromMarkov(void) {
   MidiMessage message;
   int newnote = 0;
   if (synth.getNoteCount() != 0) {
      while (synth.getNoteCount() > 0) {
         message = synth.extractNote();
         if (message.p2() != 0) {
            newnote = message.p1() % 12;
         } 
      }
      newnote = chooseNoteFromMarkovArray(newnote);
   } else {
      newnote = chooseNoteFromMarkovArray(markovNote);
   }
   cout << "Last note: " << markovNote << "\tNew note: " << newnote << endl;
   markovVoice.play(newnote + octave * 12, rand()%20 + 60);
   markovNote = newnote;
}



/////////////////////////////
//
// chooseNoteFromMarkovArray -- get the next markov note, returning
//   B if any under-valuing the probabilities.
//

int chooseNoteFromMarkovArray(int markovNote) {
   markovNote = markovNote % 12;
   double targetSum = drand48();
   double sum = 0.0;
   int targetNote = 0;

   while (targetNote < MARKOV_SIZE &&
      sum+markov[markovNote][targetNote] < targetSum) {
      sum += markov[markovNote][targetNote];
      targetNote++;
   }

   return targetNote;
}



/////////////////////////////
//
// setNextEventTime -- choose the duration of the current note.
//


void setNextEventTime(double tempo, SigTimer& metronome) {
   int random = (int)(drand48() * 100);
   if (constant) {
      metronome.setTempo(tempo*2);   
   } else if (random < 10) {
      metronome.setTempo(tempo/4.0);   
   } else if (random < 20) {
      metronome.setTempo(tempo/4.0*2);
   } else if (random < 30) {
      metronome.setTempo(tempo/4.0*3);
   } else if (random < 40) {
      metronome.setTempo(tempo/4.0*4);
   } else if (random < 50) {
      metronome.setTempo(tempo/4.0*6);
   } else if (random < 60) {
      metronome.setTempo(tempo/4.0*8);
   } else if (random < 70) {
      metronome.setTempo(tempo/4.0*12);
   } else if (random < 80) {
      metronome.setTempo(tempo/4.0*16);
   } else if (random < 90) {
      metronome.setTempo(tempo/4.0*4.5);
   } else {
      metronome.setTempo(tempo/4.0*0.5);
   }
   metronome.reset();
}



//////////////////////////////
//
// zeroMarkovTable -- replace all values with zeros.
//

void zeroMarkovTable(void) {
   int i, j;
   for (i=0; i<MARKOV_SIZE; i++) {
      for (j=0; j<MARKOV_SIZE; j++) {
         markov[i][j] = 0.0;
      }
   }
}



//////////////////////////////
//
// generateMarkovTable -- calculate markov table from counting array.
//

void generateMarkovTable(void) {
   double total;
   int i, j;

   for (i=0; i<MARKOV_SIZE; i++) {
      total = 0;
      for (j=0; j<MARKOV_SIZE; j++) {
         total += markov[i][j];
      }
      if (total == 0.0) {
         markov[i][0] = 1.0;
      } else {
         for (j=0; j<MARKOV_SIZE; j++) {
            markov[i][j] /= total;
         }
      }
   }
}


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



// md5sum: 6f41d97973624cb0dcb4eac9cdaee698 markov1.cpp [20050403]