//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Dec 13 13:35:37 PST 2000
// Last Modified: Wed Dec 13 13:35:41 PST 2000
// Filename:      ...sig/examples/all/kern2melisma.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/kern2melisma.cpp
// Syntax:        C++; museinfo
// Reference:     http://bobo.link.cs.cmu.edu/music-analysis
//
// Description:   Generates a list of pitches in a Humdrum file 
//                according to the Melisma Music Analyzer file format
//                for notes.  
//

#include "humdrum.h"

#include <string.h>
#include <ctype.h>
#include <stdio.h>

#ifndef OLDCPP
   #include <iostream>
#else
   #include <iostream.h>
#endif


// function declarations:
void      checkOptions(Options& opts, int argc, char** argv);
void      example(void);
void      preparePitch(char* buffer2, const char* buffer1);
void      printOutput(HumdrumFile& hfile);
void      usage(const char* command);

// User interface variables:
Options   options;
int       barlinesQ = 1;      // used with the -B option
int       midinoteQ = 0;      // used with the -m option
int       classQ    = 0;      // used with the -c option
double    tdefault  = 60.0;   // used with the -t option

//////////////////////////////////////////////////////////////////////////

int main(int argc, char** argv) {
   // process the command-line options
   checkOptions(options, argc, argv);

   HumdrumFile hfile(options.getArg(1));
   hfile.analyzeRhythm("4");
   printOutput(hfile);
   return 0;
}

//////////////////////////////////////////////////////////////////////////



//////////////////////////////
//
// checkOptions -- 
//

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("B|nobarlines=b", "don't display barlines");
   opts.define("m|midi|MIDI=b", "display pitches as MIDI note numbers");
   opts.define("c|class=b", "display pitches in pitch class notation");
   opts.define("t|tempo|default-tempo=d:60.0", "tempo if none specified");

   opts.define("author=b",  "author of program"); 
   opts.define("version=b", "compilation info");
   opts.define("example=b", "example usages");   
   opts.define("h|help=b",  "short description");
   opts.process(argc, argv);
   
   // handle basic options:
   if (opts.getBoolean("author")) {
      cout << "Written by Craig Stuart Sapp, "
           << "craig@ccrma.stanford.edu, May 1998" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 3 July 1998" << endl;
      cout << "compiled: " << __DATE__ << endl;
      cout << MUSEINFO_VERSION << endl;
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   } else if (opts.getBoolean("example")) {
      example();
      exit(0);
   }

   if (opts.getBoolean("nobarlines")) {
      barlinesQ = 0;
   } else {
      barlinesQ = 1;
   }

   if (opts.getBoolean("class")) {
      classQ = 1;
   } else {
      classQ = 0;
   }

   tdefault = opts.getDouble("default-tempo");
   if (tdefault == 0.0) {
      cout << "Error default tempo cannot be zero." << endl;
      exit(1);
   }
   if (tdefault < 0.0) {
      cerr << "Warning: converting tempo to be positive" << endl;
      tdefault = -tdefault;
   }
}



//////////////////////////////
//
// example --
//

void example(void) {


}



//////////////////////////////
//
// printOutput -- one small bug: the duration of the notes
//   are calculated under constant tempo.  A tempo change during
//   a tied note will not give the correct ending point of the
//   tied note.  This bug is minor and the work to fix it is major.
//

void printOutput(HumdrumFile& hfile) {
   Array<double> tempo;
   hfile.analyzeTempoMarkings(tempo, tdefault);

   double currentmillisecond = 0.0;
   double lastduration = 0.0;
   double endmillisecond;

   int i, j, k;
   char buffer1[1024] = {0};
   char buffer2[1024] = {0};
   double duration;
   for (i=0; i<hfile.getNumLines(); i++) {
      currentmillisecond = currentmillisecond + 
            lastduration * 60000.0 / tempo[i];
      lastduration = hfile[i].getDuration();

      if (hfile[i].getType() == E_humrec_global_comment) {
         cout << "Comment\t" << &(hfile[i].getLine()[3]) << endl;
      }

      if (hfile[i].getType() == E_humrec_bibliography) {
         cout << "Reference " << &(hfile[i].getLine()[3]) << endl;
      }

      if (hfile[i].getType() == E_humrec_interpretation) {
         for (j=0; j<hfile[i].getFieldCount(); j++) {
            if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
               continue;
            }
            int direction = 0;
            int acount = 0;
            if (strncmp(hfile[i][j], "*k[", 3) == 0) {
               if (strchr(hfile[i][j], '-') != NULL) {
                  direction = -1;
               } else if (strchr(hfile[i][j], '#') != NULL) {
                  direction = 1;
               }
               acount = (strlen(hfile[i][j]) - 4)/2;
               if (direction == 0) {
                  cout << "Info keysig no accidentals" << endl;
                  break;
               } else {
                  cout << "Info keysig " << acount;
                  if (acount < 0) {
                     cout << " flat";
                  } else {
                     cout << " sharp";
                  }
                  if (acount > 1) {
                     cout << "s";
                  }
                  cout << endl;
                  break;
               }
            }
         }
      }

      // look for key interpretations
      if (hfile[i].getType() == E_humrec_interpretation) {
         for (j=0; j<hfile[i].getFieldCount(); j++) {
            if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
               continue;
            }
            int length = strlen(hfile[i][j]);
            if (length < 3 || length > 4) {
               continue;
            }
            if (hfile[i][j][length-1] == ':') {
               cout << "Info key ";
               cout << (char)toupper(hfile[i][j][1]);
               if (hfile[i][j][2] == '-') {
                  cout << "-flat";
               }
               if (hfile[i][j][2] == '#') {
                  cout << "-sharp";
               }
               if (islower(hfile[i][j][1])) {
                  cout << " Minor";
               } else {
                  cout << " Major";
               }
               cout << endl;
               break;
            }
         }
      }

      // look for tempo markings
      if (hfile[i].getType() == E_humrec_interpretation) {
         for (j=0; j<hfile[i].getFieldCount(); j++) {
            if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
               continue;
            }
            double tempomark = 0.0;
            if (strncmp(hfile[i][j], "*MM", 3) == 0 && 
                  isdigit(hfile[i][j][3])) {
               sscanf(hfile[i][j], "*MM%lf", &tempomark);
               cout << "Info Tempo " << tempomark << " MM per quarter note" 
                    << endl;
               break;
            }
         }
      }

      if (hfile[i].getType() != E_humrec_data) {
         continue;
      }

      for (j=0; j<hfile[i].getFieldCount(); j++) {
         if (hfile[i].getExInterpNum(j) != E_KERN_EXINT) {
            continue;
         }

         for (k=0; k<hfile[i].getTokenCount(j); k++) {
            if (strcmp(hfile[i][j], ".") == 0) {
               continue;
            }
            hfile[i].getToken(buffer1, j, k);
            if (strchr(buffer1, '_') != NULL) {
               // ignore notes which are tied
               continue;
            }
            if (strchr(buffer1, ']') != NULL) {
               // ignore notes which are tied
               continue;
            }

            preparePitch(buffer2, buffer1);
            duration = hfile.getTiedDuration(i, j, k);

            int note = Convert::kernToMidiNoteNumber(buffer2);
            if (classQ) {
               note = note % 12;
            }
            if (note < 0) {
               // don't display rests.
               continue;
            }

            cout << "Note\t";
            // cout << hfile.getAbsBeat(i) << "\t";
            cout << (int)(currentmillisecond+0.5) << "\t";

            // cout << duration << "\t";
            endmillisecond = currentmillisecond + duration * 60000 / tempo[i];
            if (int(endmillisecond+0.5) == int(currentmillisecond+0.5)) {
               // give grace notes 1 millisecond duration...
               cout << (int)(endmillisecond+0.5)+1 << "\t";
            } else {
               cout << (int)(endmillisecond+0.5) << "\t";
            }

            cout << note;

            cout << "\n";
         }
      }
   }
}



//////////////////////////////
//
// preparePitch --
//

void preparePitch(char* buffer2, const char* buffer1) {
   strcpy(buffer2, buffer1);
}


//////////////////////////////
//
// usage --
//

void usage(const char* command) {


}



// md5sum: 07eaaa60a484c3c4f66eb68cabcc9c8f kern2melisma.cpp [20080205]