//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Wed Apr 24 20:15:12 PDT 2002
// Last Modified: Sat Jan 24 09:27:33 PST 2004
// Filename:      ...sig/examples/all/rism2kern2.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/rism2kern2.cpp
// Syntax:        C++; museinfo
//
// Description:   Converts RISM II/A Plaine and Easie Code into **kern data.
//
// Todo:          interpret beam groupings (e.g.: 6{ABCD} -> aLL b c dJJ
//                identify fermatas
//                interpret trills
//                handle tuplets
//                measure repeat indicator
//                repeat barlines
//                i,F repetitions
//                Figure out what ^! means (in RISM 1000117692)
//                natural sign indicator
//                clef changes (%F-4 )
//                grace notes
//                acciaccatura notes (q and qq..r types)
//
// Done:          key signature
//                ties
//                beam groupings
//                accidentals (except for naturals, and tied accidentals)
//                full measure rests (single and multiple)
//                time signature
//
// Fixed:         initial octave (Sun Jan 25 10:46:10 PST 2004)
// Fixed:         initial rhythm 6 (Sun Jan 25 10:46:10 PST 2004)
//

#include "humdrum.h"

#include <string.h>
#include <math.h>

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

typedef Array<char> ArrayChar;

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

class NoteObject {
   public:
             NoteObject(void) { clear(); };
      void   clear(void) {
                pitch = tie = beam = clef = clefnum = 0;
                wholerest = 0; measureduration = 0.0;
                duration = 0.0;
             };
      int    pitch;
      double duration;
      int    tie;
      int    beam;
      int    clef;
      int    clefnum;
      double measureduration;
      int    wholerest;   // number of whole rests to process
};



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

// function declarations:
void      checkOptions(Options& opts, int argc, char** argv);
void      example(void);
void      usage(const char* command);
void      convertRism112ToKern(HumdrumFile& hfile, int incipitnum);
const char* getKeyInfo(Array<int>& key, HumdrumFile& hfile, 
                                 int incipitnum);
const char* getTimeInfo(Array<double>& timeinfo, HumdrumFile& hfile, 
                                 int incipitnum);
void      printKeyInfo(ostream& out, Array<int>& key, 
                                 const char* keystring);
void      printTimeInfo(ostream& out, const char* timestring);
void      generateMusicData(Array<NoteObject>& notes, HumdrumFile& infile,
                                 Array<int>& key, int incipitnum,
                                 double measureduration);
void      printPitches(ostream& out, Array<NoteObject>& notes);
int       getLineIndex(Array<ArrayChar>& pieces, const char* string);
int       getIncipitNumber(HumdrumFile& hfile);
const char* getField(const char* field, HumdrumFile& hfile, 
                                 int incipitnum);
double    getMeasureDur(Array<double>& timeinfo);
int       getIncipitCount(HumdrumFile& hfile);
const char* getKey(HumdrumFile& hfile, int incipitnum);


// User interface variables:
Options   options;
int       debugQ = 0;          // used with the --debug option


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

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

   HumdrumFile hfile;
   hfile.read(options.getArg(1));

   int incipitnum = getIncipitNumber(hfile);
   cout << hfile;
   convertRism112ToKern(hfile, incipitnum);

   return 0;
}

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


//////////////////////////////
//
// getKey --
//

const char* getKey(HumdrumFile& hfile, int incipitnum) {
   int i;
   const char* keystring = NULL;
   for (i=0; i<hfile.getNumLines(); i++) {
      if (strncmp(hfile[i][0], "!!!RISM-026:", 12) == 0) {
         keystring = hfile[i][1];
      }
   }

   if (keystring == NULL) {
      return "*X2:";
   }

   char buffer[4096] = {0};
   strcpy(buffer, keystring);
   char* ptr = strtok(buffer, " \t\n;");
   char* first = ptr;
   int count = 1;
   while ((count < incipitnum) && (ptr != NULL)) {
      ptr = strtok(NULL, " \t\n;");
      count++;
      if (count == 1) {
         first = ptr;
      }
   }

   if (ptr == NULL) {
      ptr = first;
   }

   if (ptr == NULL) {
      return "*X3:";
   }

   if (strcmp(ptr, "G") == 0) {   // 5,690 cases
      return "*G:";
   } else if (strcmp(ptr, "C") == 0) {   // 4,804 cases
      return "*C:";
   } else if (strcmp(ptr, "D") == 0) {   // 4,594 cases
      return "*D:";
   } else if (strcmp(ptr, "F") == 0) {   // 3,759 cases
      return "*F:";
   } else if (strcmp(ptr, "B>") == 0) {   // 2,702 cases
      return "*B-:";
   } else if (strcmp(ptr, "A") == 0) {   // 2,634 cases
      return "*A:";
   } else if (strcmp(ptr, "E>") == 0) {   // 1,869 cases
      return "*E-:";
   } else if (strcmp(ptr, "g") == 0) {   // 1,807 cases
      return "*g:";
   } else if (strcmp(ptr, "d") == 0) {   // 1,711 cases
      return "*d:";
   } else if (strcmp(ptr, "a") == 0) {   // 1,492 cases
      return "*a:";
   } else if (strcmp(ptr, "c") == 0) {   // 797 cases
      return "*c:";
   } else if (strcmp(ptr, "e") == 0) {   // 689 cases
      return "*e:";
   } else if (strcmp(ptr, "E") == 0) {   // 598 cases
      return "*E:";
   } else if (strcmp(ptr, "f") == 0) {   // 253 cases
      return "*f:";
   } else if (strcmp(ptr, "b") == 0) {   // 206 cases
      return "*b:";
   } else if (strcmp(ptr, "A>") == 0) {   // 78 cases
      return "*A-:";
   } else if (strcmp(ptr, "f#") == 0) {   // 47 cases
      return "*f#:";
   } else if (strcmp(ptr, "B") == 0) {   // 18 cases
      return "*B:";
   } else if (strcmp(ptr, "c#") == 0) {   // 7 cases
      return "*c#:";
   } else if (strcmp(ptr, "Bb") == 0) {   // 7 cases
      return "*B-:";
   } else if (strcmp(ptr, "Eb") == 0) {   // 7 cases
      return "*E-:";
   } else if (strcmp(ptr, "D>") == 0) {   // 5 cases
      return "*D-:";
   } else if (strcmp(ptr, "F#") == 0) {   // 4 cases
      return "*F#:";
   } else if (strcmp(ptr, "e>") == 0) {   // 4 cases
      return "*e-:";
   } else if (strcmp(ptr, "b>") == 0) {   // 3 cases
      return "*b-:";
   } else if (strcmp(ptr, "g#") == 0) {   // 1 case
      return "*g#:";
   } else if (strcmp(ptr, "G#") == 0) {   // 1 case
      return "*G#:";
   } else if (strcmp(ptr, "G>") == 0) {   // 1 case
      return "*G-:";
   }

   return "*X4:";
}



//////////////////////////////
//
// getIncipitNumber --
//

int getIncipitNumber(HumdrumFile& hfile) {
   int i;
   int output = 0;
   int count = 0;
   const char* ptr = NULL;
   for (i=0; i<hfile.getNumLines(); i++) {
      if (strncmp(hfile[i][0], "!!!RISM-incipit:", 16) == 0) {
         ptr = strrchr(hfile[i][1], '-');
         if (ptr != NULL) {
            sscanf(ptr, "-%d", &output);
         }
      } else if (strncmp(hfile[i][0], "!!!RISM-112F:", 13) == 0) {
         count++;
      }
   }

   if (count == 0) {
      return 0;
   } else if (output == 0) {
      return 1;
   } else {
      return output;
   }
}



//////////////////////////////
//
// getIncipitCount --
//

int getIncipitCount(HumdrumFile& hfile) {
   int i;
   int output = 0;
   for (i=0; i<hfile.getNumLines(); i++) {
      if (strncmp(hfile[i][0], "!!!RISM-112F:", 13) == 0) {
         output++;
      }
   }

   return output;
}



//////////////////////////////
//
// convertRism112ToKern --
//

void convertRism112ToKern(HumdrumFile& hfile, int incipitnum) {

   Array<NoteObject> notes;
   Array<int> key(7);
   Array<double> timeinfo(2);
   timeinfo.setAll(0);
   key.setAll(0);
   const char* keystring = getKeyInfo(key, hfile, incipitnum);
   const char* timestring = getTimeInfo(timeinfo, hfile, incipitnum);
   const char* a112 = getField("112A", hfile, incipitnum);

   int incipitcount = getIncipitCount(hfile);
   int length;
   int jj;

   if (incipitnum > 0) {
      cout << "**kern\n";
      if (incipitcount > 1) {
      cout << "!! Incipit number: " << incipitnum << "/"
           << incipitcount <<  " (";
      length = strlen(a112);
      for (jj=0; jj<length-1; jj++) {
         cout << a112[jj];
      }
      cout << ")" << endl;
      }
      printKeyInfo(cout, key, keystring);
      cout << getKey(hfile, incipitnum) << "\n";
      double measuredur = getMeasureDur(timeinfo);
      printTimeInfo(cout, timestring);
      generateMusicData(notes, hfile, key, incipitnum, measuredur);
      printPitches(cout, notes);
      cout << "*-\n\n";
   }
}



//////////////////////////////
//
// getMeasureDur -- return the duration of the full measure
//

double getMeasureDur(Array& timeinfo) {
   return timeinfo[0] * timeinfo[1];
}



//////////////////////////////
//
// generateMusicData --
//

void generateMusicData(Array<NoteObject>& notes, HumdrumFile& hfile,
      Array<int>& key, int incipitnum, double measureduration) {

   notes.setSize(0);
   NoteObject tempnote;

   const char* musicdata = getField("112F", hfile, incipitnum);
   int length = strlen(musicdata);

   // measure octave at each position in the data string
   Array<int> octave;
   octave.setSize(length);
   octave.setAll(0);
   int i;
   if (musicdata[0] == '\'') {
      octave[0] = 1;
   } else if (musicdata[0] == ',') {
      octave[0] = -1;
   }
   for (i=1; i<length; i++) {
      switch (musicdata[i]) {
         case '\'':
            if (musicdata[i-1] == '\'') {
               octave[i] = octave[i-1] + 1;
            } else {
               octave[i] = 1;
            }
            break;
         case ',':
            if (musicdata[i-1] == ',') {
               octave[i] = octave[i-1] - 1;
            } else {
               octave[i] = -1;
            }
            break;
         default:
            octave[i] = octave[i-1];
            break;
      }
   }
   for (i=0; i<length; i++) {
      switch (octave[i]) {
         case  0:  octave[i] = 4;  break;
         case  1:  octave[i] = 4;  break;
         case  2:  octave[i] = 5;  break;
         case  3:  octave[i] = 6;  break;
         case  4:  octave[i] = 7;  break;
         case  5:  octave[i] = 8;  break;
         case  6:  octave[i] = 9;  break;
         case -1:  octave[i] = 3;  break;
         case -2:  octave[i] = 2;  break;
         case -3:  octave[i] = 1;  break;
         case -4:  octave[i] = 0;  break;
         default:  octave[i] = 4;
      }
   }


   Array<int> acc;
   acc.setSize(length);
   acc.setAll(0);
   // measure accidentals at each position in the data string
   for (i=0; i<length; i++) {
      switch (musicdata[i]) {
         case 'x': acc[i] = acc[i-1] + 1;  break;
         case 'b': acc[i] = acc[i-1] - 1;  break;
         case 'n': acc[i] = 100;           break;
      }
   }
   int currentacc = 0;
   for (i=1; i<length; i++) {
      if (musicdata[i] == 'x' || musicdata[i] == 'b' ||
          musicdata[i] == 'n') {
         currentacc = acc[i];
      } else if ((musicdata[i]-'A'>=0) && (musicdata[i]-'A'<7)) {
         acc[i] = currentacc;
         currentacc = 0;
      }
   }

   // calculate slur information
   Array<int> tempslur;
   tempslur.setSize(length);
   tempslur.setAll(0);
   int lasti = 0;
   int ii;
   for (i=0; i<length; i++) {
      if ((musicdata[i] - 'A' >= 0) && (musicdata[i] - 'A' < 7)) {
         lasti = i;
      } else if (musicdata[i] == '+') {
         if (tempslur[lasti]) {
            tempslur[lasti] = 3;
         } else {
            tempslur[lasti] = 1;
         }
         for (ii=i; ii<length; ii++) {
            if ((musicdata[ii]-'A'>= 0) && (musicdata[ii]-'A'<7)) {
               tempslur[ii] = 2;
               i = ii;
               break;
            }
         }
      }
   }

   // calculate beam grouping information
   Array<int> tempbeam;
   tempbeam.setSize(length);
   tempbeam.setAll(0);
   lasti = 0;
   for (i=0; i<length; i++) {
      if ((musicdata[i] - 'A' >= 0) && (musicdata[i] - 'A' < 7)) {
         lasti = i;
      } else if (musicdata[i] == '}') {
         tempbeam[lasti] = 2;
      } else if (musicdata[i] == '{') {
           for (ii=i; ii<length; ii++) {
              if ((musicdata[ii]-'A'>= 0) && (musicdata[ii]-'A'<7)) {
                 tempbeam[ii] = 1;
                 i = ii;
                 lasti = i;
                 break;
              }
           }
      }
   }


   // calculate durations for all positions in data string
   Array<double> duration;
   duration.setSize(length);
   duration.setAll(0.0);
   int dots = 0;
   double basedur = 0.0;
   for (i=0; i<length; i++) {
     switch (musicdata[i]) {
         case '0': duration[i] = 16.0; basedur = duration[i]; dots = 0;
            break;
         case '1': duration[i] = 4.0; basedur = duration[i]; dots = 0;
            break;
         case '2': duration[i] = 2.0; basedur = duration[i]; dots = 0;
            break;
         case '3': duration[i] = 4.0/32.0; basedur = duration[i]; dots = 0;
            break;
         case '4': duration[i] = 1.0; basedur = duration[i]; dots = 0;
            break;
         case '5': duration[i] = 4.0/64.0; basedur = duration[i]; dots = 0;
            break;
         case '6': duration[i] = 0.25; basedur = duration[i]; dots = 0;
            break;
         case '7': duration[i] = 4.0/128.0; basedur = duration[i]; dots = 0;
            break;
         case '8': duration[i] = 0.5; basedur = duration[i]; dots = 0;
            break;
         case '9': duration[i] = 8.0; basedur = duration[i]; dots = 0;
            break;
         case '.':
            dots++;
            if (i > 0) {
               duration[i] = duration[i-1] + basedur * pow(2.0, -dots);
            }
            break;
         default:
            dots = 0;
            if (i > 0) {
               duration[i] = duration[i-1];
            }
            break;
      }
   }
   // adjust for tuplet durations here...


   Array<int> kkey;
   kkey.setSize(key.getSize());
   for (i=0; i<kkey.getSize(); i++) {
      if (key[i] < 0) {
         kkey[i] = -1;
      } else if (key[i] > 0) {
         kkey[i] = +1;
      } else {
         kkey[i] = 0;
      }
   }

   int wholerestcount = 0;
   int accidental;
   Array<int> mkey(7);
   mkey = kkey;
   int natural = 0;
   int pitch;
   for (i=0; i<length; i++) {
      wholerestcount = 0;
      if (musicdata[i] == '/') {
         mkey = kkey;
      }
      accidental = acc[i];
      if (accidental > 5) {
         // accidental = 0;
         natural = 1;
      }
      pitch = 1000;
      switch (musicdata[i]) {
         case 'A': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 31 + 40 * octave[i];
                   mkey[4] = 0;
                   natural = 0;
               } else {
                   pitch = 31 + 40 * octave[i] + accidental;
                   mkey[4] = accidental;
               }
            } else {
                pitch = 31 + 40 * octave[i] + mkey[4];
            }
            break;
         case 'B': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 37 + 40 * octave[i];
                   mkey[6] = 0;
                   natural = 0;
               } else {
                   pitch = 37 + 40 * octave[i] + accidental;
                   mkey[6] = accidental;
               }
            } else {
                pitch = 37 + 40 * octave[i] + mkey[6];
            }
            break;
         case 'C': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 2 + 40 * octave[i];
                   mkey[1] = 0;
                   natural = 0;
               } else {
                   pitch = 2 + 40 * octave[i] + accidental;
                   mkey[1] = accidental;
               }
            } else {
                pitch = 2 + 40 * octave[i] + mkey[1];
            }
            break;
         case 'D': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 8 + 40 * octave[i];
                   mkey[3] = 0;
                   natural = 0;
               } else {
                   pitch = 8 + 40 * octave[i] + accidental;
                   mkey[3] = accidental;
               }
            } else {
                pitch = 8 + 40 * octave[i] + mkey[3];
            }
            break;
         case 'E': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 14 + 40 * octave[i];
                   mkey[5] = 0;
                   natural = 0;
               } else {
                   pitch = 14 + 40 * octave[i] + accidental;
                   mkey[5] = accidental;
               }
            } else {
                pitch = 14 + 40 * octave[i] + mkey[5];
            }
            break;
         case 'F': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 19 + 40 * octave[i];
                   mkey[0] = 0;
                   natural = 0;
               } else {
                   pitch = 19 + 40 * octave[i] + accidental;
                   mkey[0] = accidental;
               }
            } else {
                pitch = 19 + 40 * octave[i] + mkey[0];
            }
            break;
         case 'G': 
            if (accidental != 0) {
               if (natural) {
                   pitch = 25 + 40 * octave[i];
                   mkey[2] = 0;
                   natural = 0;
               } else {
                   pitch = 25 + 40 * octave[i] + accidental;
                   mkey[2] = accidental;
               }
            } else {
                pitch = 25 + 40 * octave[i] + mkey[2];
            }
            break;
         case '/': pitch = 0; break;
         case '-': pitch = -1000; break;
         case '=': 
            pitch = -1000; 
            wholerestcount = 1;
            if (isdigit(musicdata[i+1])) {
               sscanf(&(musicdata[i+1]), "%d", &wholerestcount);
               // warning: possible error -- maybe need to increment
               // pointer to the value after the whole rest count
               // incase there is no rhythm marker before the next pitch.
            }
            break;
         default:
            break;
      }
      if (pitch < 1000) {
         tempnote.pitch     = pitch;
         tempnote.duration  = duration[i];
         tempnote.tie       = tempslur[i];
         tempnote.beam      = tempbeam[i];
         tempnote.wholerest = wholerestcount;
         tempnote.measureduration = measureduration;
         notes.append(tempnote);
      }
   }

}



//////////////////////////////
//
// printPitches --
//

void printPitches(ostream& out, Array& notes) {
   char buffer[1024] = {0};
   int i, ii;
   for (i=0; i<notes.getSize(); i++) {
      if (notes[i].pitch < 0) {
         if (notes[i].tie == 1) {
            out << "[";
         }
         if (notes[i].wholerest == 0) {
            out << Convert::durationToKernRhythm(buffer, notes[i].duration);
            out << "r";
         } else {
            out << Convert::durationToKernRhythm(buffer, 
                         notes[i].measureduration);
            out << "rr\n";
            for (ii=1; ii<notes[i].wholerest; ii++) {
               out << "=\n";
               out << Convert::durationToKernRhythm(buffer, 
                         notes[i].measureduration);
               out << "rr";
            }
         }

         if (notes[i].beam == 1) {
            out << "L";
         } else if (notes[i].beam == 2) {
            out << "J";
         }

         if (notes[i].tie == 2) {
            out << "]";
         } else if (notes[i].tie == 3) {
            out << "_";
         }

      } else if (notes[i].pitch == 0) {
         out << "=";
      } else {
         if (notes[i].tie == 1) {
            out << "[";
         }
         out << Convert::durationToKernRhythm(buffer, notes[i].duration);
         out << Convert::base40ToKern(buffer, notes[i].pitch);
         if (notes[i].tie == 2) {
            out << "]";
         } else if (notes[i].tie == 3) {
            out << "_";
         }
         if (notes[i].beam == 1) {
            out << "L";
         } else if (notes[i].beam == 2) {
            out << "J";
         }
      }
      out << "\n";
   }


}



//////////////////////////////
//
// getField -- 
//

const char* getField(const char* field, HumdrumFile& hfile, int incipitnum) {
   Array<int> endindex;
   endindex.setSize(100);
   endindex.setSize(0);
   endindex.allowGrowth();

   if (incipitnum == 0) {
      return "";
   }

   int count = 0;
   int i;
   for (i=0; i<hfile.getNumLines(); i++) {
      if (strncmp(hfile[i][0], "!!!RISM-112F:", 13) == 0) {
         count++; 
         endindex.append(i);
      }
   }
   
   char searchstring[1024] = {0};
   strcpy(searchstring, "!!!RISM-");
   strcat(searchstring, field);
   strcat(searchstring, ":");
   int length = strlen(searchstring);
   
   const char* output = "";
   for (i=endindex[incipitnum-1]; i>=0; i--) {
      if (strncmp(hfile[i][0], "!!!RISM-112", 11) != 0) {
         break;
      } else if ((i < endindex[incipitnum-1]) &&
            (strncmp(hfile[i][0], "!!!RISM-112F", 12) == 0)) {
         break;
      } else if (strncmp(hfile[i][0], searchstring, length) == 0) {
         output = hfile[i][1];
         break;
      }
   }

   return output;
}




//////////////////////////////
//
// getTimeInfo -- read the key signature.
//

const char* getTimeInfo(Array<double>& timeinfo, HumdrumFile& hfile, 
      int incipitnum) {

   const char* timeline = getField("112E", hfile, incipitnum);
   const char* output = "*M4/4";

   if (strcmp(timeline, "&C;") == 0) {   // 19,511 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      output = "*M4/4\n*met(C)"; 
   } else if (strcmp(timeline, "3/4;") == 0) {   // 7,733 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/4.0;
      output = "*M3/4";
   } else if (strcmp(timeline, "2/4;") == 0) {   // 6,873 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/4.0;
      output = "*M2/4";
   } else if (strcmp(timeline, "\"&alla\";") == 0) {   // 6,865 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      output = "*M2/2\n*met(C|)";
   } else if (strcmp(timeline, "\"&alla;\"") == 0) {   // 6,865 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      output = "*M2/2\n*met(C|)";
   } else if (strcmp(timeline, "\"&alla;\";") == 0) {   // 6,865 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      output = "*M2/2\n*met(C|)";
   } else if (strcmp(timeline, "6/8;") == 0) {   // 3,855 cases
      timeinfo[0] = 6.0; timeinfo[1] = 4.0/8.0;
      output = "*M6/8";
   } else if (strcmp(timeline, "3/8;") == 0) {   // 2,789 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/8.0;
      output = "*M3/8";
   } else if (strcmp(timeline, "[3/4];") == 0) {   // 1,742 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/4.0;
      output = "*M3/4\n!! inferred meter";
   } else if (strcmp(timeline, "[&C];") == 0) {   // 824 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      output = "*M4/4\n*met(C)\n!! inferred meter";
   } else if (strcmp(timeline, "[4/2];") == 0) {   // 794 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/2.0;
      output = "*M4/2\n!! inferred meter";
   } else if (strcmp(timeline, "[2/2];") == 0) {   // 778 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      output = "*M2/2\n!! inferred meter";
   } else if (strcmp(timeline, "3/2;") == 0) {   // 689 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/2.0;
      output = "*M3/2";
   } else if (strcmp(timeline, "12/8;") == 0) {   // 482 cases
      timeinfo[0] = 12.0; timeinfo[1] = 4.0/8.0;
      output = "*M12/8";
   } else if (strcmp(timeline, "\"[&alla;]\";") == 0) {   // 417 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      output = "*M2/2\n*met(C|)\n!! inferred meter";
   } else if (strcmp(timeline, "[3/2];") == 0) {   // 374 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/2.0;
      output = "*M3/2\n!! inferred meter";
   } else if (strcmp(timeline, "[2/2];") == 0) {   // 272 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/4.0;
      output = "*M2/4\n!! inferred meter";
   } else if (strcmp(timeline, "6/4;") == 0) {   // 196 cases
      timeinfo[0] = 6.0; timeinfo[1] = 4.0/4.0;
      output = "*M6/4";
   } else if (strcmp(timeline, "[6/8];") == 0) {   // 169 cases
      timeinfo[0] = 6.0; timeinfo[1] = 4.0/8.0;
      output = "*M6/8\n!! inferred meter";
   } else if (strcmp(timeline, "[6/4];") == 0) {   // 157 cases
      timeinfo[0] = 6.0; timeinfo[1] = 4.0/4.0;
      output = "*M6/4\n!! inferred meter";
   } else if (strcmp(timeline, "[3/8];") == 0) {   // 123 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/8.0;
      output = "*M3/8\n!! inferred meter";
   } else if (strcmp(timeline, "9/8;") == 0) {   // 82 cases
      timeinfo[0] = 9.0; timeinfo[1] = 4.0/8.0;
      output = "*M9/8";
   } else if (strcmp(timeline, "2/2;") == 0) {   // 80 cases
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      output = "*M2/2";
   } else if (strcmp(timeline, "[6/2];") == 0) {   // 42 cases
      timeinfo[0] = 6.0; timeinfo[1] = 4.0/2.0;
      output = "*M6/2\n!! inferred meter";
   } else if (strcmp(timeline, "[3/1];") == 0) {   // 36 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/1.0;
      output = "*M3/1\n!! inferred meter";
   } else if (strcmp(timeline, "4/4;") == 0) {   // 35 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      output = "*M4/4";
   } else if (strcmp(timeline, "3/1;") == 0) {   // 33 cases
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/1.0;
      output = "*M3/1";
   } else if (strcmp(timeline, "[4/4];") == 0) {   // 18 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      output = "*M4/4\n!! inferred meter";
   } else if (strcmp(timeline, "4/8;") == 0) {   // 10 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/8.0;
      output = "*M4/8";
   } else if (strcmp(timeline, "4/2;") == 0) {   // 7 cases
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/2.0;
      output = "*M4/2";
   } else if (strcmp(timeline, "12/16;") == 0) {   // 7 cases
      timeinfo[0] = 12.0; timeinfo[1] = 4.0/16.0;
      output = "*M12/16";
   } else if (strcmp(timeline, "[12/8];") == 0) {   // 6 cases
      timeinfo[0] = 12.0; timeinfo[1] = 4.0/8.0;
      output = "*M12/8\n!! inferred meter";
   } else if (strcmp(timeline, "8/8;") == 0) {   // 5 cases
      timeinfo[0] = 8.0; timeinfo[1] = 4.0/8.0;
      output = "*M8/8";
   } 

   else {
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      output = "!! unknown meter";
   }

/*  Other time signature cases not yet processed
      4	&c;
      3	[9/8];
      3	9/4;
      3	6/16;
      3	[4/8];
      3	[12/4];
      2	[&C/31];
      2	&C 2/4;
      2	"&alla; [2/2]";
      2	9/16;
      2	6/8-3/8;
      2	[4/1];
      2	2/8;
      2	24/16;
      2	0;
      2	[?];
      1	C6/8;
      1	C6/4;
      1	C/3/4;
      1	&C,3/4;
      1	C3/2;
      1	&Alla.;
      1	&alla.;
      1	&alla;
      1	&alla,;
      1	"[&alla;?]";
      1	[9/4];
      1	[9/2];
      1	[6/8;
      1	[6/16];
      1	5/4;
      1	2/1;
      1	[2/1];
      1	11/4;

// weird time signatures

      1	[?&C/3?];
      1	bB;
      1	"&alla;/3i";
      1	2/3;
      1	31;
      1	[?3/1?];
      1	!3/4!;
      1	3/8 [6/8];
      1	2 [2/4];
      1	2/4[3/4];
      8	&C 3/8;
      3	&C/31;
      3	[&C/3];
      5	03;
    128	3;
     37	\;
     36	2;
     18	&C/3;
     15	[3];
      1	0/3;

*/
   
   return output;
}



//////////////////////////////
//
// getKeyInfo -- read the key signature.
//

const char* getKeyInfo(Array& key, HumdrumFile& hfile, int incipitnum) {

   const char* keyline = getField("112D", hfile, incipitnum);
   
   // at the key information line, extract data
   int type  = 1;  		 // 1 = sharps, 2 = flats
   int paren = 1;  		 // 1 = normal, 2 = inside of square brackets
   int length = strlen(keyline);
   int i = 0;
   while (i < length) {
      switch (keyline[i]) {
         case 'b': type  = -1; break;
         case 'x': type  =  1; break;
         case '[': paren =  2; break;
         case ']': paren =  1; break;
         case 'F': key[0] = type * paren; break;
         case 'C': key[1] = type * paren; break;
         case 'G': key[2] = type * paren; break;
         case 'D': key[3] = type * paren; break;
         case 'A': key[4] = type * paren; break;
         case 'E': key[5] = type * paren; break;
         case 'B': key[6] = type * paren; break;
         case ' ': break;
         default:
            if (debugQ) {
               cout << "Error: unknown character: " << keyline[i]
                    << endl;
               exit(1);
            }
            break;
      }
      i++;
   }

   return keyline;
}



//////////////////////////////
//
// printTimeInfo --
//

void printTimeInfo(ostream& out, const char* timestring) {
   out << timestring;
   int length = strlen(timestring);
   if (length > 0 && timestring[length-1] != '\n') {
      out << "\n";
   }
}



//////////////////////////////
//
// printKeyInfo -- not worrying about mixed sharp/flat key sigs 
//   for now...
//

void printKeyInfo(ostream& out, Array& key, const char* keystr) {
   //if (keystr[0] != '\0') {
   //   out << "!! Key signature: " << keystr << "\n";
   //}

   out << "*k[";
   if (key[0] > 0) {
      if (key[0] > 0) out << "f#"; else if (key[0] < 0) out << "f-";
      if (key[1] > 0) out << "c#"; else if (key[0] < 0) out << "c-";
      if (key[2] > 0) out << "g#"; else if (key[0] < 0) out << "g-";
      if (key[3] > 0) out << "d#"; else if (key[0] < 0) out << "d-";
      if (key[4] > 0) out << "a#"; else if (key[0] < 0) out << "a-";
      if (key[5] > 0) out << "e#"; else if (key[0] < 0) out << "e-";
      if (key[6] > 0) out << "b#"; else if (key[0] < 0) out << "b-";
   } else {
      if (key[6] < 0) out << "b-"; else if (key[0] > 0) out << "b#";
      if (key[5] < 0) out << "e-"; else if (key[0] > 0) out << "e#";
      if (key[4] < 0) out << "a-"; else if (key[0] > 0) out << "a#";
      if (key[3] < 0) out << "d-"; else if (key[0] > 0) out << "d#";
      if (key[2] < 0) out << "g-"; else if (key[0] > 0) out << "g#";
      if (key[1] < 0) out << "c-"; else if (key[0] > 0) out << "c#";
      if (key[0] < 0) out << "f-"; else if (key[0] > 0) out << "g#";
   }
   out << "]" << endl;
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("debug=b",  "print debug information"); 

   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, April 2002" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 24 April 2002" << 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);
   }
   
   debugQ      = opts.getBoolean("debug");
}



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

void example(void) {


}



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

void usage(const char* command) {

}




//////////////////////////////
//
// getLineIndex -- get the index location of the given string.
//

int getLineIndex(Array& pieces, const char* string) {
   int index = -1;
   int i;
   for (i=0; i<pieces.getSize(); i++) {
      if (strstr(pieces[i].getBase(), string) != NULL) {
         index = i;
         break;
      }
   }
   return index;
}



// md5sum: 00d5e9dedeb47c815390eac97f8c9f42 rism2kern2.cpp [20050403]