//
// Programmer:    Laurent Pugin <puginl@ccrma.stanford.edu>
// Creation Date: Lun 13 oct 2008 11:51:41 PDT
// Last Modified: Mer  5 nov 2008 15:40:54 PST
// Last Modified: Tue Dec  2 19:19:27 PST 2008 (added -q and -Q options)
// Filename:      ...sig/examples/all/pae2kern.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/pae2kern.cpp
// Syntax:        C++; museinfo
//
// Description:   Converts RISM II/A Plaine and Easie Code into **kern data.
//                Partially based on rism2kern.cpp by Craig Stuart Sapp
//                http://sig.sapp.org/examples/museinfo/humdrum/rism2kern2.cpp
//                Follows IAML Plaine and Easie specifications
//                http://www.iaml.info/activities/projects/plain_and_easy_code
//
// Todo:          repeat barlines (currently not interpreted in humdrum)
//
// Done:          interpret rythmic models 4.66CDEDEF = 4.C6DE4.D6EF
//                key signature (and changes $)
//                measure repeat indicator (i)
//                !,f repetitions
//                ties
//                beam groupings
//                natural sign indicator
//                accidentals (except for tied accidentals)
//                identify fermatas
//                interpret trills
//                handle tuplets
//                full measure rests (single and multiple)
//                time signature (and changes @)
//                clef (and changes %)
//                grace notes (g)
//                acciaccatura notes (q and qq..r types)
//
// Fixed:         
//

#include "humdrum.h"

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

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


typedef Array<char> ArrayChar;

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

class NoteObject {
   public:
             NoteObject(void) { clear(); };
      void   clear(void) {
                pitch = octave = accident = dot = tie = beam = appoggiatura = 0;
                acciaccatura = appoggiatura_multiple = fermata = trill = false;
                duration = 0.0;
                tuplet = 1.0;
             };
      int    pitch;
      int    octave;
      int    accident;
      double duration;
      int    dot;
      double tuplet;
      int    tie;
      int    beam;
      bool   acciaccatura;
      int    appoggiatura;
      bool   appoggiatura_multiple;
      bool   fermata;
      bool   trill;
};


class MeasureObject {
   public:
             MeasureObject(void) { clear(); };
      void   clear(void) {
                a_timeinfo.setSize(2);
                a_key.setSize(7);
                a_timeinfo.setAll(0);
                a_key.setAll(0);
                durations.setSize(1);
                dots.setSize(1);
                durations.setAll(1.0);
                dots.setAll(0);
                durations_offset = 0;
                reset();
             };
      void   reset(void) {
                notes.setSize(0);
                clef = s_key = s_timeinfo = barline = "";
                wholerest = 0; 
                measure_duration = 0.0;
                abbreviation_offset = -1;
             };
      string    clef;
      double measure_duration;
      Array<NoteObject> notes;
      Array<int> a_key;
      string s_key;
      Array<double> a_timeinfo;   
      string s_timeinfo; 
      Array<double> durations;
      Array<int> dots; // use the same offset as durations, they are used in parallel
      int durations_offset;
      string barline;
      int    abbreviation_offset;  
      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      convertPlainAndEasyToKern( istream &infile, ostream &outfile);

// parsing functions
int       getKeyInfo(const char* incipit, Array<int>& key, string *output, int index = 0);
int       getTimeInfo(const char* incipit, Array<double>& timeinfo, string *output, int index = 0);
int       getClefInfo(const char* incipit, string *output, int index = 0 );
int       getBarline(const char* incipit, string *output, int index = 0 );
int       getAccidental(const char* incipit, int *accident, int index = 0);
int       getOctave(const char* incipit, int *octave, int index = 0 );
int       getDurations(const char* incipit, MeasureObject *measure, int index = 0);
int       getTupletFermata(const char* incipit, double current_duration, NoteObject *note, int index = 0);
int       getTupletFermataEnd(const char* incipit, NoteObject *note, int index = 0);
int       getGraceNote(const char* incipit, NoteObject *note, int index = 0);
int       getWholeRest(const char* incipit, int *wholerest, int index );
int       getAbbreviation(const char* incipit, MeasureObject *measure, int index = 0 ); 
int       getNote(const char* incipit, NoteObject *note, MeasureObject *measure, int index = 0 );

int       getPitch(char c_note, int octave, int accidental, Array<int>& a_key );
double    getDurationWithDot(double duration, int dot);
double    getMeasureDur(Array<double>& timeinfo);
void      getKey(const char* key_str, string *output);


// output functions
void      printMeasure(ostream& out, MeasureObject *measure);

// input functions
void      getAtRecordKeyValue(Array<char>& key, Array<char>& value, const char* input);


// User interface variables:
Options   options;
int       debugQ = 0;                // used with --debug option
int       stdoutQ = 0;
char      outdir[1024] = {0};        // used with -d option
char      extension[1024] = {0};     // used with -e option
char      hum2abc[1024] = {0};       // used with -a option
int       quietQ  = 0;               // used with -q option
int       quiet2Q = 0;               // used with -Q option

// Global variables:
char data_line[10001] = {0};
Array<char> data_key;
Array<char> data_value;


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

int main(int argc, char** argv) {
   // process the command-line options
   checkOptions(options, argc, argv);
   
   // input file
   ifstream infile;
   infile.open(options.getArg(1));
   if (!infile.is_open()) {
      std::cerr << "File: " << options.getArg(1) << " cannot be read" << endl;
      exit(1);
   }

   // output file variables
   int i = 0; // number of converted incipits used for filenames when not supplied
   char outfilename[1024] = {0};
   ofstream outfile;

   while (!infile.eof()) {
      infile.getline(data_line, 10000, '\n');
      if (infile.eof()) {
         break;
      }
      //std::cout << "LINE: " << line << endl;
      getAtRecordKeyValue(data_key, data_value, data_line);
      if (strcmp(data_key.getBase(),"start")==0) {
      
         if (!stdoutQ) {
            if (strlen(data_value.getBase())==0) {
               sprintf( outfilename, "%s%015d", outdir, i );
            } else {
               sprintf( outfilename, "%s%s", outdir, data_value.getBase() );
            }
            // add extention
            strcat(outfilename, "." );  
            strcat(outfilename, extension );        
            #ifndef OLDCPP
               outfile.open(outfilename);
            #else
               outfile.open(outfilename, ios::noreplace);
            #endif
            if (!outfile.is_open()) {
               cout << "Error: cannot write to file: " << outfilename << endl;
            }
            convertPlainAndEasyToKern( infile, outfile);
            outfile.close();
         } else {
            convertPlainAndEasyToKern( infile, cout);
         }

         //std::cout << "\tKEY:   " << key.getBase()   << endl;
         //std::cout << "\tVALUE: " << value.getBase() << endl;
         i++;
      }
   }

   return 0;
}

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



//////////////////////////////
//
// convertPlainAndEasyToKern --
//

void convertPlainAndEasyToKern(istream &infile, ostream &out) {

   // buffers
   char c_clef[1024] = {0};
   char c_key[1024] = {0};
   char c_keysig[1024] = {0};
   char c_timesig[1024] = {0};
   char c_alttimesig[1024] = {0};
   char incipit[10001] = {0};

   string s_key;
   MeasureObject current_measure;
   NoteObject current_note;
   Array<int> current_key; // not the measure one, which will be altered by temporary alterations
   current_key.setSize(7);
   current_key.setAll(0);
   
   Array<MeasureObject> staff;
   staff.setSize(0);

   // read values
   while (!infile.eof()) {
      infile.getline(data_line, 10000, '\n');
      if (infile.eof()) {
         std::cerr << "Truncated file or ending tag missing" << endl;
         exit(1);
      }
      getAtRecordKeyValue(data_key, data_value, data_line);
      if (strcmp(data_key.getBase(),"end")==0) {   
         break;
      } else if (strcmp(data_key.getBase(),"clef")==0) { 
         strcpy( c_clef, data_value.getBase() );
      } else if (strcmp(data_key.getBase(),"key")==0) { 
         strcpy( c_key, data_value.getBase() );
      } else if (strcmp(data_key.getBase(),"keysig")==0) { 
         strcpy( c_keysig, data_value.getBase() );
      } else if (strcmp(data_key.getBase(),"timesig")==0) { 
         strcpy( c_timesig, data_value.getBase() );
      } else if (strcmp(data_key.getBase(),"alttimesig")==0) { 
         strcpy( c_alttimesig, data_value.getBase() );
      } else if (strcmp(data_key.getBase(),"data")==0) { 
         strcpy( incipit, data_value.getBase() );
      } else if (strncmp(data_line,"!!", 2) == 0) { 
            out << data_line << "\n";
      }
   }
   
   // write as comment in the output
   if (!quietQ) {
      out << "!!!clef:" << c_clef << "\n";
      out << "!!!key:" << c_key << "\n";
      out << "!!!keysig:" << c_keysig << "\n";
      out << "!!!timesig:" << c_timesig << "\n";
      out << "!!!alttimesig:" << c_alttimesig << "\n";
      out << "!!!incipit:" << incipit << "\n";
   }

   
   if (strlen(c_clef)) {
      getClefInfo(c_clef, ¤t_measure.clef );    // do we need to put a default clef?
   }
   if (strlen(c_key)) {
      getKey(c_key, &s_key); // key is not stored in the measure, only for the entire piece
   }
   if (strlen(c_keysig)) {
      getKeyInfo( c_keysig, current_measure.a_key, ¤t_measure.s_key);
      current_key = current_measure.a_key;
   }
   if (strlen(c_timesig)) {
      getTimeInfo( c_timesig, current_measure.a_timeinfo, ¤t_measure.s_timeinfo);
      current_measure.measure_duration = getMeasureDur(current_measure.a_timeinfo);
   }   
   
   // read the incipit string
   int length = strlen(incipit);
   int i = 0;
	while(i < length) {
   // eat the input...
   
		if (incipit[i] == ' ') {
      // just skip
         i++;
      }
      
      // octaves
      if ((incipit[i] == '\'') || (incipit[i] == ',')) {
         i += getOctave( incipit, ¤t_note.octave, i );
      }
      
      // rhythmic values
      else if (isdigit(incipit[i]) != 0) {
         i += getDurations( incipit, ¤t_measure, i );
      }

      //accidentals (1 = n; 2 = x; 3 = xx; 4 = b; 5 = bb)    
      else if (incipit[i] == 'n' || incipit[i] == 'x' || incipit[i] == 'b') {
         i += getAccidental( incipit, ¤t_note.accident, i );
      }
      
      // beaming starts
		else if (incipit[i] == '{') {
			current_note.beam = 1;
      }
         
      // beaming ends
		else if (incipit[i] == '}') {
			current_note.beam = 0; // should not have to be done, but just in case
		}
		
      // slurs are read when adding the note
		else if (incipit[i] == '+') {
      }
      
		// beginning tuplets & fermatas
		else if (incipit[i] == '(') {
         i += getTupletFermata( incipit, getDurationWithDot( current_measure.durations[0], current_measure.dots[0]), ¤t_note, i );
		}
      
      // end of tuplets
		else if ((incipit[i] == ';') || (incipit[i] == ')')) {
         i += getTupletFermataEnd( incipit, ¤t_note, i );
		}

		// trills are read when adding the note
		else if (incipit[i] == 't') {
      }

		//grace notes
		else if ((incipit[i] == 'g') || (incipit[i] == 'q')) {
			i += getGraceNote( incipit, ¤t_note, i );
		}
		
      
      // end of appogiatura
		else if (incipit[i] == 'r') {
			current_note.appoggiatura = 0; // should not have to be done, but just in case
		}
      
      //note and rest
      else if (((incipit[i]-'A'>=0) && (incipit[i]-'A'<7)) || (incipit[i]=='-')) {
         i += getNote( incipit, ¤t_note, ¤t_measure, i );
      }
      
  		// whole rest
		else if (incipit[i] == '=') {
         i += getWholeRest( incipit, ¤t_measure.wholerest, i );		
		} 
      
		// abbreviation
      else if (incipit[i] == '!') {
         i += getAbbreviation( incipit, ¤t_measure, i );
      }
      
      // measure repetition
      else if ((incipit[i] == 'i') && staff.getSize()) {
         MeasureObject last_measure = staff[staff.getSize()-1];
         current_measure.notes = last_measure.notes;
         current_measure.a_timeinfo = last_measure.a_timeinfo;
         current_measure.measure_duration = getMeasureDur(last_measure.a_timeinfo);
         current_measure.a_key = current_key;
      }
      
      //barline
      else if ((incipit[i] == ':') || (incipit[i] == '/')) {
         i += getBarline(incipit, ¤t_measure.barline, i);
         current_measure.abbreviation_offset = 0; // just in case...
         staff.append( current_measure );
         current_measure.reset();
         current_measure.a_key = current_key;
         current_measure.measure_duration = getMeasureDur(current_measure.a_timeinfo);
      }
      
		//clef change
		else if ((incipit[i] == '%') && (i+1 < length)) {
         i += getClefInfo(incipit, ¤t_measure.clef, i + 1);
      }

		//time signature change
		else if ((incipit[i] == '@') && (i+1 < length)) {
         i += getTimeInfo( incipit, current_measure.a_timeinfo, ¤t_measure.s_timeinfo, i + 1);
         current_measure.measure_duration = getMeasureDur(current_measure.a_timeinfo);
      } 
   
  		//key signature change
		else if ((incipit[i] == '$') && (i+1 < length)) {
         i += getKeyInfo( incipit, current_measure.a_key, ¤t_measure.s_key, i + 1);
         current_key = current_measure.a_key;
		} 
      
      i++;
   }
   
   // we need to add the last measure if it has no barline at the end
   if (current_measure.notes.getSize() != 0) {
         //current_measure.barline = "=-";
         staff.append( current_measure );
   }
   

   // output
   out << "**kern\n";
   out << "*MM120\n";
   if ( s_key.length() ) {
      out << s_key << "\n";
   }
   int j = 0;
   for (j = 0; j < staff.getSize(); j++) {
      printMeasure( out, &staff[j] );
   }
   out << "*-\n\n";

   if (strlen(hum2abc)) {
      out << "!!!hum2abc:" << hum2abc << "\n";
   }
}



//////////////////////////////
//
// getOctave --
//

int getOctave(const char* incipit, int *octave, int index ) {

   int i = index;
   int length = strlen(incipit);
   if (incipit[i] == '\'') {
      *octave = 1;
      while ((i+1 < length) && (incipit[i+1] == '\'')) {
         (*octave)++;
         i++;
      }
   } else if (incipit[i] == ',') {
   //negative octave
      *octave = -1;
      while ((i+1 < length) && (incipit[i+1] == ',')) {
         (*octave)--;
         i++;
      }
   }
   
   // humdrum octave
   switch (*octave) {
      case  0:  *octave = 4;  break;
      case  1:  *octave = 4;  break;
      case  2:  *octave = 5;  break;
      case  3:  *octave = 6;  break;
      case  4:  *octave = 7;  break;
      case  5:  *octave = 8;  break;
      case  6:  *octave = 9;  break;
      case -1:  *octave = 3;  break;
      case -2:  *octave = 2;  break;
      case -3:  *octave = 1;  break;
      case -4:  *octave = 0;  break;
      default:  *octave = 4;
   }
   
   return i - index;
}



//////////////////////////////
//
// getDuration --
//

int getDuration(const char* incipit, double *duration, int *dot, int index ) {

   int i = index;
   int length = strlen(incipit);

   switch (incipit[i]) {
      case '0': *duration = 16.0; break;
      case '1': *duration = 4.0; break;
      case '2': *duration = 2.0; break;
      case '3': *duration = 4.0/32.0; break;
      case '4': *duration = 1.0; break;
      case '5': *duration = 4.0/64.0; break;
      case '6': *duration = 0.25; break;
      case '7': *duration = 4.0/128.0; break;
      case '8': *duration = 0.5; break;
      case '9': *duration = 8.0; break;
   }
   *dot=0;
   if ((i+1 < length) && (incipit[i+1] == '.')) {
   // one dot
      (*dot)++;
      i++;
   }
   if ((i+1 < length) && (incipit[i+1] == '.')) {
   // two dots
      (*dot)++;
      i++;
   }
   if ((*dot == 1) && (*duration == 7)) {
   // neumatic notation
      *duration = 1.0;
      *dot = 0;
      cout << "Warning: found a note in neumatic notation (7.), using quarter note instead" << endl;				
   }
   
   return i - index;
}



//////////////////////////////
//
// getDurations --
//

int getDurations(const char* incipit, MeasureObject* measure, int index ) {

   int i = index;
   int length = strlen(incipit);

   measure->durations_offset = 0;

   int j = 0;
   do {
      measure->durations.setSize(j+1);
      measure->dots.setSize(j+1);
      i += getDuration(incipit, &measure->durations[j], &measure->dots[j], i );
      j++;
      if ((i+1 < length) && isdigit(incipit[i+1])) {
         i++;
      } else {
         break;
      }
   } while ( 1 );
   //cout << "duration count:" << j << endl;
      
   return i - index;
}



//////////////////////////////
//
// getAccidental --
//

int getAccidental(const char* incipit, int *accident, int index ) {

   int i = index;
   int length = strlen(incipit);

   if (incipit[i] == 'n') {
      *accident = 100; // we use 100 as a code, because 0 would be ignored
   } else if (incipit[i] == 'x') {
      *accident = 1;
      if ((i+1 < length) && (incipit[i+1] == 'x')) {
         (*accident)++;
         i++;
      }
   } else if (incipit[i] == 'b') {
      *accident = -1;
      if ((i+1 < length) && (incipit[i+1] == 'b')) {
         (*accident)--;
         i++;
      }
   }
   return i - index;
}



//////////////////////////////
//
// getTupletOrFermata --
//

int getTupletFermata(const char* incipit, double current_duration, NoteObject *note, int index ) {

   int i = index;
   int length = strlen(incipit);

   // detect if it is a fermata or a tuplet
   regex_t re;
   regcomp(&re, "^([^)]*[ABCDEFG-][^)]*[ABCDEFG-][^)]*)", REG_EXTENDED);
   int is_tuplet = regexec(&re, incipit + i, 0, NULL, 0);
   regfree(&re);
   
   if (is_tuplet == 0) {
      int t = i;
      double note_duration = current_duration; // just in case the duration is missing in the tuplet
      bool is_triplet = false; // in triplets, there is no duration given before the tuplet.
      bool is_first_event = false; // we use the first duration if there is one (they should be one)
      int note_count = 0; // we need to count notes for triplets as sometimes in the data several triplets appear together
                          // we need to change the tuplet duration in consequence
      int note_dot = 0;
      // the duration for the tuplet given before it, or by the first duration value in triplets
      if ((index == 0) || (isdigit(incipit[index-1]) == 0)) {
         is_triplet = true; // this means that we have to keep the first value for triplets
         current_duration *= 2; // in case there is no value, use the current value
                                // this is wrong syntax wise but it appears in the data
         is_first_event = true;
      }
      
      double tuplet_duration = 0.0;
      while ((t < length) && (incipit[t] != ')') && (incipit[t] != ';')) {
         if (isdigit(incipit[t]) != 0) { // new duration in the tuplet
            t += getDuration( incipit, ¬e_duration, ¬e_dot, t );
            note_duration = getDurationWithDot(note_duration, note_dot);
            if (is_triplet && is_first_event) { // it is a triplet
               current_duration = note_duration * 2;
               is_first_event = false; // we don't need to get the value again
            }
         } else if (((incipit[t]-'A'>=0) && (incipit[t]-'A'<7)) || (incipit[t]=='-')) { // note or rest
            //cout << "tuplet note:" << incipit[t] << endl;
            tuplet_duration += note_duration;
            is_first_event = false;
            note_count++;
         }
         t++;
      }
      
      // the overall tuplet duration
      if (!is_triplet) {
         note->tuplet = tuplet_duration / current_duration;
      } else {
         // how many triplets we have is given by the note_count
         // several triplet in one tuplet is wrong syntax wise but it appears in the data
         note->tuplet = tuplet_duration / (current_duration * ceil(double(note_count)/3));
      }
      
   } else {
      if ( note->tuplet != 1.0 ) {
         cout << "Warning: fermata within a tuplet. Won't be handled correctly" << endl;
      }
      note->fermata = true;
   }
   
   return i - index;

}



//////////////////////////////
//
// getTupletFermataEnd --
//

int getTupletFermataEnd(const char* incipit, NoteObject *note, int index ) {

   int i = index;
   int length = strlen(incipit);
   
   if (incipit[i] == ';') {
      while ((i+1 < length) && (incipit[i+1] != ')')) {
         // we don't need the number of notes in humdrum, just skip it
         i++;
      }
   }
			
   // TODO currently fermatas inside tuplets won't be handled correctly
   // close both now
   note->tuplet = 1.0;
   note->fermata = false;
      
   return i - index;
}



//////////////////////////////
//
// getGraceNote --
//

int getGraceNote(const char* incipit, NoteObject *note, int index ) {

   int i = index;
   int length = strlen(incipit);

   //acciaccatura
   if (incipit[i] == 'g') {
      note->acciaccatura = true;
   }
		
   // appoggiatura
   else if (incipit[i] == 'q') {
      note->appoggiatura = 1;
      if ((i+1 < length) && (incipit[i+1] == 'q')) {
         note->appoggiatura_multiple = true;
         i++;
         int r = i;
         while ((r < length) && (incipit[r] != 'r')) {
            if ((incipit[r]-'A'>=0) && (incipit[r]-'A'<7)) {
               note->appoggiatura++;
               //cout << note->appoggiatura << endl; 
            }
            r++;
         }
      }
   }
   return i - index;
}



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

void getKey(const char* key_str, string *output) {

    ostringstream sout;  

   if (strcmp(key_str, "G") == 0) {
      sout << "*G:";
   } else if (strcmp(key_str, "C") == 0) {
      sout << "*C:";
   } else if (strcmp(key_str, "D") == 0) {
      sout << "*D:";
   } else if (strcmp(key_str, "F") == 0) {
      sout << "*F:";
   } else if (strcmp(key_str, "Bb") == 0) {
      sout << "*B-:";
   } else if (strcmp(key_str, "A") == 0) {
      sout << "*A:";
   } else if (strcmp(key_str, "Eb") == 0) {
      sout << "*E-:";
   } else if (strcmp(key_str, "g") == 0) {
      sout << "*g:";
   } else if (strcmp(key_str, "d") == 0) {
      sout << "*d:";
   } else if (strcmp(key_str, "a") == 0) {
      sout << "*a:";
   } else if (strcmp(key_str, "c") == 0) {
      sout << "*c:";
   } else if (strcmp(key_str, "e") == 0) {
      sout << "*e:";
   } else if (strcmp(key_str, "E") == 0) {
      sout << "*E:";
   } else if (strcmp(key_str, "f") == 0) {
      sout << "*f:";
   } else if (strcmp(key_str, "b") == 0) {
      sout << "*b:";
   } else if (strcmp(key_str, "Ab") == 0) {
      sout << "*A-:";
   } else if (strcmp(key_str, "f#") == 0) {
      sout << "*f#:";
   } else if (strcmp(key_str, "B") == 0) {
      sout << "*B:";
   } else if (strcmp(key_str, "c#") == 0) {
      sout << "*c#:";
   } else if (strcmp(key_str, "Bb") == 0) {
      sout << "*B-:";
   } else if (strcmp(key_str, "Eb") == 0) {
      sout << "*E-:";
   } else if (strcmp(key_str, "D>") == 0) {
      sout << "*D-:";
   } else if (strcmp(key_str, "F#") == 0) {
      sout << "*F#:";
   } else if (strcmp(key_str, "eb") == 0) {
      sout << "*e-:";
   } else if (strcmp(key_str, "bb") == 0) {
      sout << "*b-:";
   } else if (strcmp(key_str, "g#") == 0) {
      sout << "*g#:";
   } else if (strcmp(key_str, "G#") == 0) {
      sout << "*G#:";
   } else if (strcmp(key_str, "Gb") == 0) {
      sout << "*G-:";
   }  else {
      sout << "!! unknown key";
      cout << "Warning: unknown key: " << key_str << endl;
   }

   *output = sout.str();
   
}




//////////////////////////////
//
// getPitch --
//

int getPitch( char c_note, int octave, int accidental, Array& a_key ) {

   int pitch = 1000;
   //cout << "note " << c_note << "; octave " << octave << "; accidental " << accidental << endl;
   int natural = 0;
   if (accidental == 100) {
      //accidental = 0;
      natural = 1;
   }
   switch (c_note) {
      case 'A':
         if (accidental != 0) {
            if (natural) {
                pitch = 31 + 40 * octave;
                a_key[4] = 0;
                natural = 0;
            } else {
                pitch = 31 + 40 * octave + accidental;
                a_key[4] = accidental;
            }
         } else {
             pitch = 31 + 40 * octave + a_key[4];
         }
         break;
      case 'B': 
         if (accidental != 0) {
            if (natural) {
                pitch = 37 + 40 * octave;
                a_key[6] = 0;
                natural = 0;
            } else {
                pitch = 37 + 40 * octave + accidental;
                a_key[6] = accidental;
            }
         } else {
             pitch = 37 + 40 * octave + a_key[6];
         }
         break;
      case 'C': 
         if (accidental != 0) {
            if (natural) {
                pitch = 2 + 40 * octave;
                a_key[1] = 0;
                natural = 0;
            } else {
                pitch = 2 + 40 * octave + accidental;
                a_key[1] = accidental;
            }
         } else {
             pitch = 2 + 40 * octave + a_key[1];
         }
         break;
      case 'D': 
         if (accidental != 0) {
            if (natural) {
                pitch = 8 + 40 * octave;
                a_key[3] = 0;
                natural = 0;
            } else {
                pitch = 8 + 40 * octave + accidental;
                a_key[3] = accidental;
            }
         } else {
             pitch = 8 + 40 * octave + a_key[3];
         }
         break;
      case 'E': 
         if (accidental != 0) {
            if (natural) {
                pitch = 14 + 40 * octave;
                a_key[5] = 0;
                natural = 0;
            } else {
                pitch = 14 + 40 * octave + accidental;
                a_key[5] = accidental;
            }
         } else {
             pitch = 14 + 40 * octave + a_key[5];
         }
         break;
      case 'F': 
         if (accidental != 0) {
            if (natural) {
                pitch = 19 + 40 * octave;
                a_key[0] = 0;
                natural = 0;
            } else {
                pitch = 19 + 40 * octave + accidental;
                a_key[0] = accidental;
            }
         } else {
             pitch = 19 + 40 * octave + a_key[0];
         }
         break;
      case 'G': 
         if (accidental != 0) {
            if (natural) {
                pitch = 25 + 40 * octave;
                a_key[2] = 0;
                natural = 0;
            } else {
                pitch = 25 + 40 * octave + accidental;
                a_key[2] = accidental;
            }
         } else { 
             pitch = 25 + 40 * octave + a_key[2];
         }
         break;
      case '-': pitch = -1000; break;
      default:
         break;
   }
   return pitch;
}



//////////////////////////////
//
// getDurationWithDot -- return the duration note given the note duration and the dot
//

double getDurationWithDot(double duration, int dot) {

  double duration_with_dot = duration;
  int i;
  for (i = 0; i < dot; i++) {
     duration_with_dot = duration_with_dot + duration * pow(2.0, -(i+1));
  }
  return duration_with_dot;
}



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

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



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

int getTimeInfo( const char* incipit, Array& timeinfo, string *output, int index) {

   int i = index;
   int length = strlen(incipit);
   
   if (!isdigit(incipit[i]) && (incipit[i] != 'c') && (incipit[i] != 'o'))
       return 0;

   // find the end of time signature end
   i++; // the time signature length is a least 1
   while (i < length) {
      if (!isdigit(incipit[i]) && (incipit[i] != '/') && (incipit[i] != '.')) {
         break;
      }
      i++;
   }
   
   timeinfo.setAll(0);
   
   // use a substring for the time signature 
   char timesig_str[1024];
   memset( timesig_str, 0, 1024 );
   // strncpy not always put the \0 in the end!
   strncpy( timesig_str, incipit + index, i - index); 
      
   ostringstream sout;
   regex_t re;
   
   // check if format X/X or one digit only
   regcomp(&re, "^[0-9]*/[0-9]*$", REG_EXTENDED);
   int is_standard = regexec(&re, timesig_str, 0, NULL, 0);
   regfree(&re);
   regcomp(&re, "^[0-9]*$", REG_EXTENDED);
   int is_one_number = regexec(&re, timesig_str, 0, NULL, 0);
   regfree(&re);
   
   if ( is_standard == 0) {
      char buf_str[1024];
      strcpy(buf_str, timesig_str);
      int beats = atoi(strtok(buf_str, "/"));
      int note_value = atoi(strtok(NULL, "/")); 
      timeinfo[0] = (double)beats; timeinfo[1] = 4.0/(double)note_value;
      sout << "*M" << beats << "/" << note_value;
      //cout << output << endl;
   } else if ( is_one_number == 0) {
      int beats = atoi(timesig_str);
      timeinfo[0] = (double)beats; timeinfo[1] = 4.0/1.0;
      sout << "*M" << beats << "/1\n*met(" << beats << ")";
      //cout << output << endl;
   } else if (strcmp(timesig_str, "c") == 0) {
      // C
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      sout << "*M4/4\n*met(C)"; 
   } else if (strcmp(timesig_str, "c/") == 0) {
      // C|
      timeinfo[0] = 2.0; timeinfo[1] = 4.0/2.0;
      sout << "*M2/2\n*met(C|)";
   } else if (strcmp(timesig_str, "c3") == 0) {
      // C3
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/1.0;
      sout << "*M3/1\n*met(c3)";
   } else if (strcmp(timesig_str, "c3/2") == 0) {
      // C3/2
      timeinfo[0] = 3.0; timeinfo[1] = 4.0/2.0;
      sout << "*M3/2\n*met(c3/2)";
   } else {
      timeinfo[0] = 4.0; timeinfo[1] = 4.0/4.0;
      sout << "*M4/4\n!! unknown time signature";
      cout << "Warning: unknown time signature: " << timesig_str << endl;
   }
   *output = sout.str();
   return i - index;
}



//////////////////////////////
//
// getClefInfo -- read the key signature.
//

int getClefInfo( const char *incipit, string *output, int index ) {

   // a clef is maximum 3 character length
   // go through the 3 character and retrieve the letter (clef) and the line
   // mensural clef (with + in between) currently ignored
   // clef with octava correct?
   int length = strlen(incipit);
   int i = 0;
   char clef = 'G';
   char line = '2';
   while ((index < length) && (i < 3)) {
      if (i == 0) {
         clef = incipit[index];
      } else if (i == 2) {
         line = incipit[index];
      }
      i++;
      index++;
   }

   ostringstream sout;   
   sout << "*clef" << clef << line;
   *output = sout.str();
   //cout << output << endl;
   return i;
}



//////////////////////////////
//
// getWholeRest -- read the getWholeRest.
//

int getWholeRest( const char *incipit, int *wholerest, int index ) {

   int length = strlen(incipit);
   int i = index;

   *wholerest = 1;
   if ((i+1 < length) && isdigit(incipit[i+1])) {
      sscanf(&(incipit[i+1]), "%d", wholerest);
      char buf[10];
      memset( buf, 0, 10 );
      sprintf( buf, "%d", *wholerest );
      i += strlen( buf );
   }
   return i - index;
}



//////////////////////////////
//
// getBarline -- read the barline.
//

int getBarline( const char *incipit, string *output, int index ) {

   regex_t re;
   regcomp(&re, "^://:", REG_EXTENDED);
   int is_rep_db_rep = regexec(&re, incipit + index, 0, NULL, 0);
   regfree(&re);
   regcomp(&re, "^://", REG_EXTENDED);
   int is_rep_db = regexec(&re, incipit + index, 0, NULL, 0);
   regfree(&re);
   regcomp(&re, "^//:", REG_EXTENDED);
   int is_db_rep = regexec(&re, incipit + index, 0, NULL, 0);
   regfree(&re);
   regcomp(&re, "^//", REG_EXTENDED);
   int is_db = regexec(&re, incipit + index, 0, NULL, 0);
   regfree(&re);

   int i = 0; // number of characters
   if (is_rep_db_rep == 0) {
      *output = "=:||:";
      i = 3;
   } else if (is_rep_db == 0) {
      *output = "=:|!";
      i = 2;
   } else if (is_db_rep == 0) {
      *output = "=!|:";
      i = 2;
   } else if (is_db == 0) {
      *output = "=||";
      i = 1;
   } else {
      *output = "=";
      i = 0;
   }
   return i;
}



//////////////////////////////
//
// getAbbreviation -- read abbreviation
//

int getAbbreviation(const char* incipit, MeasureObject *measure, int index ) {

   int length = strlen(incipit);
   int i = index;
   int j;

   if (measure->abbreviation_offset == -1) { // start
      measure->abbreviation_offset = measure->notes.getSize();
   } else { //
      int abbreviation_stop = measure->notes.getSize();
      while ((i+1 < length) && (incipit[i+1]=='f')) {
         i++;
         for(j=measure->abbreviation_offset; j<abbreviation_stop; j++) {
            measure->notes.append( measure->notes[j] );
         }
      }
      measure->abbreviation_offset = -1;   
   }
   
   return i - index;
}



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

int getKeyInfo(const char *incipit, Array& key, string *output, int index ) {

   //const char* keyline = getField("112D", hfile, incipitnum);
   
   key.setAll(0);
   
   // at the key information line, extract data
   int type  = 1;  		 // 1 = sharps, -1 = flats
   int paren = 1;  		 // 1 = normal, 2 = inside of square brackets
                         // currently ignored. Don't know what it means
   int length = strlen(incipit);
   int i = index;
   bool end_of_keysig = false;
   while ((i < length) && (!end_of_keysig)) {
      switch (incipit[i]) {
         case 'b': type  = -1; break;
         case 'x': type  =  1; break;
         case '[': paren =  1; break; // change to 2 to enable paren. Meaning is unclear, though
         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;
         default:
            end_of_keysig = true;
            //if (debugQ) {
            //  cout << "Warning: unknown character: " << keysig_str[i] << " in key signature " << keysig_str
            //       << endl;
               //exit(1);
            //}
            break;
      }
      if (!end_of_keysig)
         i++;
   }
   
   ostringstream sout;
   sout << "*k[";
   if (key[0] > 0) {
      if (key[0] > 0) sout << "f#"; else if (key[0] < 0) sout << "f-";
      if (key[1] > 0) sout << "c#"; else if (key[0] < 0) sout << "c-";
      if (key[2] > 0) sout << "g#"; else if (key[0] < 0) sout << "g-";
      if (key[3] > 0) sout << "d#"; else if (key[0] < 0) sout << "d-";
      if (key[4] > 0) sout << "a#"; else if (key[0] < 0) sout << "a-";
      if (key[5] > 0) sout << "e#"; else if (key[0] < 0) sout << "e-";
      if (key[6] > 0) sout << "b#"; else if (key[0] < 0) sout << "b-";
   } else {
      if (key[6] < 0) sout << "b-"; else if (key[0] > 0) sout << "b#";
      if (key[5] < 0) sout << "e-"; else if (key[0] > 0) sout << "e#";
      if (key[4] < 0) sout << "a-"; else if (key[0] > 0) sout << "a#";
      if (key[3] < 0) sout << "d-"; else if (key[0] > 0) sout << "d#";
      if (key[2] < 0) sout << "g-"; else if (key[0] > 0) sout << "g#";
      if (key[1] < 0) sout << "c-"; else if (key[0] > 0) sout << "c#";
      if (key[0] < 0) sout << "f-"; else if (key[0] > 0) sout << "g#";
   }
   sout << "]";
   *output = sout.str();
   
   return i - index;
}



//////////////////////////////
//
// getNote --
//

int getNote( const char* incipit, NoteObject *note, MeasureObject *measure, int index ) {

   regex_t re;
   
   int i = index;
   
   note->duration = measure->durations[measure->durations_offset];
   note->dot = measure->dots[measure->durations_offset];
   if ( note->tuplet != 1.0 ) {
      note->duration /= note->tuplet;
      //cout << durations[0] << ":" << note->tuplet << endl;
   }
   note->pitch = getPitch( incipit[i], note->octave, note->accident, measure->a_key );
   
   // beaming
   // detect if it is a fermata or a tuplet
   if (note->beam > 0) {
      regcomp(&re, "^[^}/]*[ABCDEFG-].*", REG_EXTENDED);
      int is_not_last_note = regexec(&re, incipit + i + 1, 0, NULL, 0);
      regfree(&re);
      //cout << "regexp " << is_not_last_note << endl;
      if ( is_not_last_note != 0 ) {
         note->beam = -1; // close the beam
      }
   }
   
   // trills
   regcomp(&re, "^[^ABCDEFG]*t", REG_EXTENDED);
   int has_trill = regexec(&re, incipit + i + 1, 0, NULL, 0);
   regfree(&re);
   if( has_trill == 0 ) {
      note->trill = true;
   }

   // tie
   regcomp(&re, "^[^ABCDEFG]*\\+", REG_EXTENDED);
   int has_tie = regexec(&re, incipit + i + 1, 0, NULL, 0);
   regfree(&re);
   //cout << "regexp " << has_tie << endl;
   if( has_tie == 0 ) {
      note->tie = 1; // 1 for the first note, 2 for the second one
   }
   
   measure->notes.append( *note );
   
   // reset values
   // beam
   if(note->beam > 0) {
      note->beam++;
   } else if (note->beam == -1) {
      note->beam = 0;
   }
   // tie
   if(note->tie == 1) {
      note->tie++;
   } else if (note->tie == 2) {
      note->tie = 0;
   }
   // grace notes
   note->acciaccatura = false;
   if(note->appoggiatura > 0) {
      //cout << note->appoggiatura << endl; 
      note->appoggiatura--;
      note->appoggiatura_multiple = false;
   }
   // durations
   if (measure->durations.getSize()) {
      measure->durations_offset++;
      if (measure->durations_offset >= measure->durations.getSize()) {
         measure->durations_offset = 0;
      }
   }
   
   note->fermata = false; // only one note per fermata;
   note->trill = false;
   note->accident = 0;
   
   return i - index;
}



//////////////////////////////
//
// printMeasure --
//

void printMeasure(ostream& out, MeasureObject *measure ) {

   if ( measure->clef.length() ) {
      out << measure->clef << "\n";
   }
   if ( measure->s_key.length() ) {
      out << measure->s_key << "\n";
   }   
   if ( measure->s_timeinfo.length() ) {
      out << measure->s_timeinfo << "\n";
   }
   
   char buffer[1024] = {0};
   int i,j;
   
   if( measure->wholerest > 0 ) {
      out << Convert::durationToKernRhythm(buffer, measure->measure_duration);
      out << "rr\n";
      for (i=1; i<measure->wholerest; i++) {
         out << "=\n";
         out << Convert::durationToKernRhythm(buffer, measure->measure_duration);
         out << "rr\n";
      }
         
      //if (measure->notes[i].fermata) {
      //      out << ";";
      //}
   }

   for (i=0; i<measure->notes.getSize(); i++) {
      if (measure->notes[i].pitch < 0) {
         if (measure->notes[i].tie == 1) {
            out << "[";
         }
         out << Convert::durationToKernRhythm(buffer, measure->notes[i].duration);
         for(j=0; j<measure->notes[i].dot; j++) {
            out << ".";
         }
         out << "r";
         
         if (measure->notes[i].fermata) {
            out << ";";
         } 

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

         if (measure->notes[i].tie == 2) {
            out << "]";
         //} else if (measure->notes[i].tie == 3) {
         //   out << "_";
         }
      } else {
         if (measure->notes[i].tie == 1) {
            out << "[";
         }
         if (!measure->notes[i].acciaccatura) {
            out << Convert::durationToKernRhythm(buffer, measure->notes[i].duration);
            for(j=0; j<measure->notes[i].dot; j++) {
               out << ".";
            }
         }
         out << Convert::base40ToKern(buffer, measure->notes[i].pitch);
         
         if (measure->notes[i].acciaccatura) {
            out << "q";
         } else if (measure->notes[i].appoggiatura > 0) {
            out << "Q";
         }
         
         if (measure->notes[i].fermata) {
            out << ";";
         } 
         
         if (measure->notes[i].tie == 2) {
            out << "]";
         //} else if (measure->notes[i].tie == 3) {
         //   out << "_";
         }
         
         if (measure->notes[i].beam == 1) {
            out << "L";
         } else if (measure->notes[i].beam == -1) {
            out << "J";
         }
         
         if (measure->notes[i].trill == true) {
            out << "t";
         }
      }
      out << "\n";
   }
   if ( measure->barline.length() ) {
      out << measure->barline << "\n";
   }
}



//////////////////////////////
//
// getAtRecordKeyValue --
//   Formats: @key: value
//            @key:value
//            @key :value
//            @ key :value
//            @ key : value
//   only one per line
//

void getAtRecordKeyValue(Array<char>& key, Array<char>& value,
     const char* input) {

  #define SKIPSPACE while((index<length)&&isspace(input[index])){index++;}

  char MARKER    = '@';
  char SEPARATOR = ':';
  char EMPTY     = '\0';

  int length = strlen(input);


  if(length == 0) {
     key.setSize(1);
     value.setSize(1);
     key[0] = EMPTY;
     value[0] = EMPTY;
     return;
  }

  // pre-allocate storage space in character arrays to avoid
  // additional allocation will appending data:
  key.setSize(length);
  value.setSize(length);
  key[0] = EMPTY;
  value[0] = EMPTY;
  key.setSize(0);
  value.setSize(0);

  char ch;
  int index = 0;

  // find starting @ symbol (ignoring any starting space)
  SKIPSPACE
  if(input[index] != MARKER) {
     // invalid record format since it does not start with @
     return;
  }
  index++;
  SKIPSPACE

  // start storing the key value:
  while ((index < length) && (input[index] != SEPARATOR)) {
     if (isspace(input[index])) {
        continue;
        index++;
     }
     ch = input[index]; key.append(ch);
     index++;
  }
  // check to see if valid format: (:) must be the current character
  if(input[index] != SEPARATOR) {
     key[0] = EMPTY;
     key.setSize(0);
     return;
  }
  key.append(EMPTY); // terminate the string for regular C use of char*
  index++;
  SKIPSPACE
  value.setSize(length - index + 1);
  strcpy(value.getBase(), &input[index]);
  int i;
  for (i=value.getSize()-2; i>0; i--) {
     if (isspace(value[i])) {
        value[i] = EMPTY;
        value.setSize(value.getSize()-1);
        continue;
     }
     break;
  }
}



//////////////////////////////
//
// 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.define("s|stdout=b", "print to stdout"); 
   opts.define("d|outdir=s:./", "output directory");
   opts.define("e|extension=s:krn", "output file extension");
   opts.define("a|hum2abc=s", "hum2abc options");
   opts.define("q|quiet=b", "quiet mode 1");
   opts.define("Q|Quiet=b", "quiet mode 2");
   opts.process(argc, argv);
   
   // handle basic options:
   if (opts.getBoolean("author")) {
      cout << "Written by Laurent Pugin, "
           << "puginl@ccrma.stanford.edu, October 2008" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 16 October 2008" << 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");
   stdoutQ = opts.getBoolean("stdout");
   strcpy(outdir, opts.getString("outdir"));
   strcpy(extension, opts.getString("extension"));
   strcpy(hum2abc, opts.getString("hum2abc"));
   quiet2Q = opts.getBoolean("Quiet");
   quietQ  = opts.getBoolean("quiet");
   if(quiet2Q) {
      quietQ = 1;
   }
}



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

void example(void) {


}



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

void usage(const char* command) {

}




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

int getLineIndex(Array<ArrayChar>& 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 pae2kern.cpp [20050403]