//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Tue Jun  5 13:48:27 PDT 2001
// Last Modified: Tue Jun  5 13:48:29 PDT 2001
// Last Modified: Fri Feb 25 00:09:19 PST 2005 (balanced <measure> markers)
// Last Modified: Tue Dec 12 20:23:42 PST 2006 (handling > and < as text)
// Last Modified: Tue Nov 22 07:08:10 PST 2011 (fixed grace noteshapes)
// Last Modified: Tue Jun 26 15:13:50 PDT 2012 (added <time-modification> for tuplets)
// Last Modified: Tue Jun 26 17:06:38 PDT 2012 (some bug fixes for multi-voice treatment)
// Filename:      ...sig/examples/all/hum2xml.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/hum2xml.cpp
// Syntax:        C++; museinfo
//
// Description:   Converts a Humdrum file into MusicXML.
//

#include "humdrum.h"
#include "PerlRegularExpression.h"
#include "RationalNumber.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      convertToMusicXML(HumdrumFile& infile);
int       makePartList(HumdrumFile& infile);
void      makePart(HumdrumFile& infile, int start, int col,
                                 int count);
void      generatePartInfo(HumdrumFile& infile, int start, int col,
                                 int count);
void      convertDataToMusicXML(HumdrumFile& infile, int line, int col, 
                                 int voice);
void      convertMeasureToXML(HumdrumFile& infile, int line, int col, 
                                 int voice);
void      convertAttributeToXML(HumdrumFile& infile, int line, int col, 
                                 int voice);
void      convertNoteToXML(HumdrumFile& infile, int line, int col, 
                                 int voice);
void      pline(int level, const char* string);
void      usage(const char* command);
double    convertNoteEntryToXML(HumdrumFile& infile, int line, int col,
                                 const char* buffer, int chord, int vlevel, 
                                 int voice);
void      checkMeasure(void);
void      printDurationType(const char* durstring);
void      printDots(const char* durstring);
void      adjustKey(int keyinfo);
void      checkAccidentals(int diatonic, int alter, int chord);
void      updateAccidentals(void);
void      printGlobalComments(HumdrumFile& infile, int direction);
void      printGlobalComment(HumdrumFile& infile, int line);
void      printBibliography(HumdrumFile& infile, int line);
void      checkbackup(int currenttick, int targettick);
void      processTextUnderlay(HumdrumFile& infile, int line, int col);
void      displayUnknownTextType(HumdrumFile& infile, int line, int col, 
                                 int verse);
void      displayLyrics(HumdrumFile& infile, int line, int col, 
                                 int verse);
void      displayHTMLText(const char* buffer);
void      processBeams(HumdrumFile& infile, int line, int col, 
                                 const char* buffer, int vlevel);
void      setLineTicks(Array<int>& LineTick, HumdrumFile& infile, int divisions);
void      getBarlines(Array<int>& barlines, HumdrumFile& infile);
void      convertMeasureToMusicXML(HumdrumFile& infile, int track, 
                                 int startbar, int endbar);
int       getMaxVoice(HumdrumFile& infile, int track, 
                                 int startline, int endline);
void      convertVoice(HumdrumFile& infile, int track, int startbar, 
                                 int endbar, int voice);
void      printMode(int lev, HumdrumFile& infile, int line, 
                                 int col, int voice);
int       adjustKeyInfo(HumdrumFile& infile, int line, int col, 
                                 int voice);

// User interface variables:
Options   options;
int       debugQ = 0;          // used with the --debug option
int       cautionaryQ = 1;     // used with the --nocaution option
int       reverseQ = 0;        // reverse the ordering of the parts


int       lev = 0;             // indentation level in output text
int       minit = 0;           // at start of each part: if measure is active
double    smallestdur = 1.0;   // smallest durational component in the music
int       divisions = 1;       // number of divisions per beat
int       measurestate = 0;    // 0 = measure is not open, 1 = measure open


int v1lastmeasure[7] = {0};    // for explicit accidentals ABCDEFG
int v1states[7] = {0};         // for explicit accidentals ABCDEFG
int v1key[7] = {0};            // for explicit accidentals ABCDEFG
int v1chord[7] = {0};          // for explicit accidentals ABCDEFG
int v1prechordstates[7] = {0}; // for explicit accidentals ABCDEFG

int AbsTick = 0;                // for <backup> <forward> commands
Array<int> LineTick;            // for <backup> and <forward> commands
int beamlevel[128] = {0};       // for beaming information
int musicstart = 0;             // for suppressing bars before music starts
// int attributes = 0;             // for suppressing multiple attributes entries

Array<int> Barlines;

int  ClefOctaveTranspose = 0;   // used to fix bug in MusicXML vocal tenor clef notes

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

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

   // figure out the number of input files to process
   int numinputs = options.getArgCount();

   HumdrumFile infile;
   for (int i=0; i<numinputs || i==0; i++) {
      infile.clear();

      // if no command-line arguments read data file from standard input
      if (numinputs < 1) {
         infile.read(cin);
      } else {
         infile.read(options.getArg(i+1));
      }

      infile.analyzeRhythm("4");
      getBarlines(Barlines, infile);
      divisions = infile.getMinTimeBase();
      if (divisions % 4 == 0) {
         divisions = divisions/4;
      } else {
         // don't know what this case may be
      }
      setLineTicks(LineTick, infile, divisions);
      convertToMusicXML(infile);

   }

   return 0;
}

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


//////////////////////////////
//
// getBarlines --
//

void getBarlines(Array& barlines, HumdrumFile& infile) {
   barlines.setSize(infile.getNumLines());
   barlines.setSize(0);
   int i;
   int datafoundQ = 0;
   // int exinterpline = 0;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isData()) {
         datafoundQ = 1;
      }
      if (strncmp(infile[i][0], "**", 2) == 0) {
         // exinterpline = i;
         barlines.append(i);
      }
      if (!infile[i].isMeasure()) {
         continue;
      }
      if (!datafoundQ) {
         continue;
      }
      barlines.append(i);
   }
   i = infile.getNumLines() - 1;
   barlines.append(i);
}



//////////////////////////////
//
// setLineTicks --
//

void setLineTicks(Array& LineTick, HumdrumFile& infile, int divisions) {
   LineTick.setSize(infile.getNumLines());
   LineTick.allowGrowth(0);
   RationalNumber rat;
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      rat = infile[i].getAbsBeatR();
      rat *= divisions;
      if (rat.getDenominator() != 1) {
         cerr << "Divisions error on line " << i+1 << ": " << infile[i] << endl;
         cerr << "GetAbsBeat = " << rat << endl;
         exit(1);
      }
      LineTick[i] = rat.getNumerator();
   }
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("debug=b",  "print debug information"); 
   opts.define("nocaution=b",  "print cautionary accidentals"); 
   opts.define("r|reverse=b",  "reverse the order of the parts"); 

   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")) {
      ClefOctaveTranspose = 0;
      example();
      exit(0);
   }
   
   debugQ      = opts.getBoolean("debug");
   cautionaryQ = !opts.getBoolean("nocaution");
   reverseQ    = opts.getBoolean("reverse");
}



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

void example(void) {


}



//////////////////////////////
//
// convertToMusicXML -- 
//
// MusicXML 0.6a header:
// <?xml version="1.0" standalone="no"?>
// <!DOCTYPE score-partwise PUBLIC "-//Recordare//DTD MusicXML 0.6a Partwise//EN" "//C:/Program Files/Finale 2003/Component Files/partwise.dtd">
//
// MusicXML 0.3 (alpha) header:
// <?xml version=\"1.0\" standalone=\"no\"?>
// <!DOCTYPE score-partwise SYSTEM \"/musicxml/partwise.dtd\">
//   <!-- DOCTYPE score-partwise PUBLIC
// \"-//Recordare//DTD MusicXML 0.3 Partwise//EN\"
// \"http://www.musicxml.org/dtds/partwise.dtd\" -->
//

void convertToMusicXML(HumdrumFile& infile) {
   pline (lev, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
   pline (lev, "<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 2.0 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">\n");

   // previous xml declaration:
   // pline(lev, "<?xml version=\"1.0\" standalone=\"no\"?>\n");
   // pline(lev, "<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 0.6a Partwise//EN\" \"//C:/Program Files/Finale 2003/Component Files/partwise.dtd\">\n");

   printGlobalComments(infile, 1);
   // pline(lev, "<score-partwise version="2.0">\n");
   pline(lev, "<score-partwise version=\"2.0\">\n");

   Array<int> kerntracks;
   infile.getTracksByExInterp(kerntracks, "**kern");

   int count = makePartList(infile);

   int start = 0;  // should be the start of exclusive interpretation line  
                   // (but setting to zero).
   int gcount = 0;
   int i;
   if (!reverseQ) {
      // print parts in reverse order because Humdrum scores are printed lowest 
      // part first going towards highest, but MusicXML is from highest to lowest.
      for (i=kerntracks.getSize()-1; i>=0 ;i--) {
         makePart(infile, start, kerntracks[i], ++gcount);
      }
   } else {
      // print reverse because Humdrum score is probably reversed 
      for (i=0; i<kerntracks.getSize(); i++) {
         makePart(infile, start, kerntracks[i], ++gcount);
      }
   }

   lev = 0;

   pline(lev, "</score-partwise>\n");
   printGlobalComments(infile, -1);
   if (count != kerntracks.getSize()) {
      cerr << "Error in generating parts: number of parts has changed: " << count << endl;
   }
}



//////////////////////////////
//
// printGlobalComments --
//

void printGlobalComments(HumdrumFile& infile, int direction) {
   int start = 0;
   int stop = infile.getNumLines();
   int i;

   if (direction == 1) {
      start = 0;
      i = 0;
      while (i < infile.getNumLines()) {
         if (strncmp(infile[i][0], "**", 2) == 0) {
            stop = i;
            break;
         }
         i++;
      }
      
   } else {
      stop = infile.getNumLines() - 1;
      i = stop;
      while (i >= 0) {
         if (strcmp(infile[i][0], "*-") == 0) {
            start = i;
            break;
         }
         i--;
      }
   }

   for (i=start; i<=stop; i++) {
      switch (infile[i].getType()) {
         case E_humrec_global_comment:
            printGlobalComment(infile, i);
            break;
         case E_humrec_bibliography:
            printBibliography(infile, i);
            break;
      }

   }

}
 

//////////////////////////////
//
// printGlobalComment --
//

void printGlobalComment(HumdrumFile& infile, int line) {
   pline(lev, "<!-- GCOMMENT value=\"");
   displayHTMLText(&(infile[line][0][2]));
   cout << "\" -->\n";
}



//////////////////////////////
//
// printBibliography --
//

void printBibliography(HumdrumFile& infile, int line) {
   char buffer[1024] = {0};
   strcpy(buffer, infile[line][0]);
   int i = 0;
   while (buffer[i] != '\0') {
      if (buffer[i] == ':') {
         buffer[i] = '\0';
         i++;
         if (buffer[i] == ' ') {
            buffer[i] = '\0';
            i++;
         }
         break;
      }
      i++;
   }
   if (i == 0) return;

   // Handle stupid XML parsers which find "--" within a comment and choke:
   Array<char> newvalue;
   newvalue.setSize(strlen(&(buffer[i]))+1);
   strcpy(newvalue.getBase(), &(buffer[i]));
   PerlRegularExpression pre;
   pre.sar(newvalue, "--", "--", "g");

   pline(lev, "<!-- INFO key=\"");
   cout << &(buffer[3]) << "\" value=\"" << newvalue << "\" -->\n";

}


//////////////////////////////
//
// makePart -- ggg
//

void makePart(HumdrumFile& infile, int start, int track, int count) {
// spine == track ?
   cout << "\n";
   pline(lev, "<part id=\"P");
   cout << count << "\">\n";
   lev++;
   minit = 0;  
   musicstart = 0;
   AbsTick = 0;
   ClefOctaveTranspose = 0;  // reset any clef transposition from previous part

   checkMeasure();
   lev++;
   pline(lev, "<attributes>\n");
   //   attributes = 1;
   lev++;
   pline(lev, "<divisions>");
   cout << divisions << "</divisions>\n";
   lev--;

   pline(lev, "</attributes>\n");  // att
   lev--;                          // att

   int i;

   for (i=0; i<Barlines.getSize()-1; i++) {
      convertMeasureToMusicXML(infile, track, Barlines[i], Barlines[i+1]);
   }

/*   int i;
   int j;
   int testtrack;
   for (i=start+1; i<infile.getNumLines(); i++) {
      if (infile[i].isComment() || infile[i].isEmpty()) {
         continue;
      }

      for (j=0; j<infile[i].getFieldCount(); j++) {
         testtrack = infile[i].getPrimaryTrack(j);
         if (testtrack != track) {
            continue;
         }
         convertDataToMusicXML(infile, i, j);
      }
   }
*/

   if (measurestate == 1) {
      pline(lev, "</measure>\n");
      measurestate = 0;
   }
   lev--;
   pline(lev, "</part>\n");
}



//////////////////////////////
//
// convertMeasureToMusicXML --
//

void convertMeasureToMusicXML(HumdrumFile& infile, int track, int startbar,
      int endbar) {

   int maxVoice = getMaxVoice(infile, track, startbar, endbar);

//cout << "<!-- Track = " << track << "\tstart = " 
//      << startbar << "\tend = " << endbar 
//      << "\tvoices = " << maxVoice
//      << " -->" << endl;

   int voice;
   for (voice=1; voice<=maxVoice; voice++) {
      convertVoice(infile, track, startbar, endbar, voice);
   }
}


//////////////////////////////
//
// convertVoice --
//

void convertVoice(HumdrumFile& infile, int track, int startbar, int endbar, 
      int voice) {

   int i, j, t, v;

   int start = startbar;
   if (voice > 1) {
      // don't print measure information for secondary voices
      start++;
   }
   
   for (i=start; i<=endbar; i++) {
      v = 0;
      for (j=0; j<infile[i].getFieldCount(); j++) {
         t = infile[i].getPrimaryTrack(j);
         if (t != track) { 
            continue;
         }
         v++;
         if (v != voice) {
            continue;
         }
         convertDataToMusicXML(infile, i, j, v);
         break;
      }
   }
}



//////////////////////////////
//
// getMaxVoice -- return the maximum voice for the particular track
//      within the given line range (excluding the endpoints).
//

int getMaxVoice(HumdrumFile& infile, int track, int startline, int endline) {
   int i, j;
   int t;
   int maxv = 0;

   int count;
   for (i=startline+1; i<endline; i++) {
      if (!infile[i].isData()) {
         continue;
      }
      count = 0;
      for (j=0; j<infile[i].getFieldCount(); j++) {
         t = infile[i].getPrimaryTrack(j);
         if (t == track) {
            count++;
         }
      }
      if (count > maxv) {
         maxv = count;
      }
   }

   return maxv;
}



//////////////////////////////
//
// convertDataToMusicXML --
//

void convertDataToMusicXML(HumdrumFile& infile, int line, int col, int voice) {
   if (debugQ) {
      cout << "<!-- PROCESSING  " << infile[line][col] << "-->\n";
   }
   switch(infile[line].getType()) {
      case E_humrec_none:
      case E_humrec_empty:
      case E_humrec_global_comment:
      case E_humrec_bibliography:
      case E_humrec_data_comment:
         break;
      case E_humrec_data_kern_measure:
         convertMeasureToXML(infile, line, col, voice);
         break;
      case E_humrec_interpretation:
         convertAttributeToXML(infile, line, col, voice);
         break;
      case E_humrec_data:
         musicstart = 1;
         convertNoteToXML(infile, line, col, voice);
         break;
      default:
         break;
   }
}

   

//////////////////////////////
//
// updateAccidentals -- restore accidentals at a measure boundary.
// 

void updateAccidentals(void) {
   // copy v1states to v1lastmeasure
   // and fill v1states with v1key
   int i;
   for (i=0; i<7; i++) {
      v1lastmeasure[i] = v1states[i];
      v1states[i] = v1key[i];
   }
}



//////////////////////////////
//
// convertMeasureToXML --
//

void convertMeasureToXML(HumdrumFile& infile, int line, int col, int voice) { 
   int measureno = -1;

   if (!musicstart) {
      return;
   }

   updateAccidentals();
   const char *ptr;

/*   int nextline = -1;
   int nextcol = -1;
   int track = infile[line].getPrimaryTrack(col);
   
   // locate the next barline
   int i, j;
   for (i=line+1; i<infile.getNumLines(); i++) {
      if (infile[i].isMeasure()) {
         nextline = i;
         break;
      }
   }
   if (nextline > 0) {
      for (j=0; j<infile[nextline].getFieldCount(); j++) {
         if (track != infile[nextline].getPrimaryTrack(j)) {
            continue;
         }
         nextcol = j;
      }
   }

   const char* nextbar = "";
   if ((nextline >= 0) && (nextcol >= 0)) {
      nextbar = infile[nextline][nextcol];
   }
*/

   if ((strncmp(infile[line][col], "==", 2) == 0) 
         || (strcmp(infile[line][col], "*-") == 0)) {
      checkbackup(AbsTick, LineTick[line]);
      lev++;
      pline(lev, "<barline location=\"right\">\n"); 
      lev++;
      pline(lev, "<bar-style>light-heavy</bar-style>\n");
      lev--;
      pline(lev, "</barline>\n"); 
      lev--;
      pline(lev, "</measure>\n");
      measurestate = 0;
   } else if ((ptr = strchr(infile[line][col], ':')) != NULL) {
      if ((ptr+1)[0] == '|' || (ptr+1)[0] == '!') {
         lev++;
         pline(lev, "<barline>\n"); 
         lev++;
         if (strstr(infile[line][col], ":|!") != NULL) {
            pline(lev, "<bar-style>light-heavy</bar-style>\n");
         } else if (strstr(infile[line][col], ":||") != NULL) {
            pline(lev, "<bar-style>light-light</bar-style>\n");
         } else if (strstr(infile[line][col], ":!") != NULL) {
            pline(lev, "<bar-style>heavy</bar-style>\n");
         } else if (strstr(infile[line][col], ":|") != NULL) {
            pline(lev, "<bar-style>light</bar-style>\n");
         }
         pline(lev, "<repeat direction=\"backward\"/>\n");
         lev--;
         pline(lev, "</barline>\n"); 
         lev--;
      }

   } else if (strstr(infile[line][col], "||") != NULL) {
      checkbackup(AbsTick, LineTick[line]);

      lev++;
      pline(lev, "<barline>\n"); 
      lev++;
      pline(lev, "<bar-style>light-light</bar-style>\n");
      lev--;
      pline(lev, "</barline>\n"); 
      lev--;

   } 


   if (strcmp(infile[line+1][0], "*-") == 0) {
      return;
   }

   checkbackup(AbsTick, LineTick[line]);

   if (minit != 0) {
      if (measurestate != 0) {
         pline(lev, "</measure>\n");
         measurestate = 0;
      }
   } 
   minit++;

   if (sscanf(infile[line][col], "=%d", &measureno)) {
      pline(lev, "<measure number=\"");
      cout << minit << "\">\n";
      measurestate = 1;
   } else if (strncmp(infile[line][col], "=", 1) == 0) {
      if (infile.getTotalDuration() > infile[line].getAbsBeat()) {
         // don't start a new measure if we are at the end of the music
         pline(lev, "<measure number=\"");
         cout << minit << "\">\n";
         measurestate = 1;
      }
   }

   if ((ptr = strchr(infile[line][col], ':')) != NULL) {
      if ((ptr-1)[0] == '|' || (ptr-1)[0] == '!') {
         lev++;
         pline(lev, "<barline>\n"); 
         lev++;
         if (strstr(infile[line][col], ":|!") != NULL) {
            pline(lev, "<bar-style>light-heavy</bar-style>\n");
         } else if (strstr(infile[line][col], ":||") != NULL) {
            pline(lev, "<bar-style>light-light</bar-style>\n");
         } else if (strstr(infile[line][col], ":!") != NULL) {
            pline(lev, "<bar-style>heavy</bar-style>\n");
         } else if (strstr(infile[line][col], ":|") != NULL) {
            pline(lev, "<bar-style>light</bar-style>\n");
         }
         pline(lev, "<repeat direction=\"forward\"/>\n");
         lev--;
         pline(lev, "</barline>\n"); 
         lev--;
      }
   }
}



//////////////////////////////
//
// checkMeasure -- 
//

void checkMeasure(void) {
   if (minit == 0) {
      minit++;
      pline(lev, "<measure number=\"");
      cout << minit << "\">\n";
      measurestate = 1;
   } 
}


//////////////////////////////
//
// adjustKey -- replace the old key signature with a new one
//

void adjustKey(int keyinfo) {
   int i;
   for (i=0; i<7; i++) {
      v1key[i] = 0;
   }

   if (keyinfo < 0) {
      v1key['B' - 'A'] = -1;
      if (keyinfo < -1) v1key['E' - 'A'] = -1;
      if (keyinfo < -2) v1key['A' - 'A'] = -1;
      if (keyinfo < -3) v1key['D' - 'A'] = -1;
      if (keyinfo < -4) v1key['G' - 'A'] = -1;
      if (keyinfo < -5) v1key['C' - 'A'] = -1;
      if (keyinfo < -6) v1key['F' - 'A'] = -1;
   }

   if (keyinfo > 0) {
      v1key['F' - 'A'] = 1;
      if (keyinfo > 1) v1key['C' - 'A'] = 1;
      if (keyinfo > 2) v1key['G' - 'A'] = 1;
      if (keyinfo > 3) v1key['D' - 'A'] = 1;
      if (keyinfo > 4) v1key['A' - 'A'] = 1;
      if (keyinfo > 5) v1key['E' - 'A'] = 1;
      if (keyinfo > 6) v1key['B' - 'A'] = 1;
   }

   // erase previous measure accidentals:
   for (i=0; i<7; i++) {
      v1lastmeasure[i] = v1key[i];
      v1states[i] = v1key[i];
   }

}





//////////////////////////////
//
// convertAttributeToXML --
//

void convertAttributeToXML(HumdrumFile& infile, int line, int col, int voice) {
   int length = strlen(infile[line][col]);

   if (strncmp(infile[line][col], "*M", 2) == 0 &&
         strchr(infile[line][col], '/') != NULL) {
      // meter marking
      int top = 0;
      int bottom = 0;

      int flag = sscanf(infile[line][col], "*M%d/%d", &top, &bottom);
      if (flag == 2) {
         checkMeasure();
         lev++;
//         if (!attributes) {
            pline(lev, "<attributes>\n");
            lev++;
//          attributes = 1;
//         }
         pline(lev, "<time>\n");
         lev++;

         pline(lev, "<beats>");
         cout << top << "</beats>\n";
         pline(lev, "<beat-type>");
         cout << bottom << "</beat-type>\n";

         lev--;
         pline(lev, "</time>\n");
         lev--;
         pline(lev, "</attributes>\n");  // att
         lev--;                          // att
      }
   } else if (strncmp(infile[line][col], "*k[", 3) == 0 &&
         infile[line][col][length-1] == ']') {
      // key signature
      int pitch = 0;
      if (length > 4) {
         pitch = Convert::kernToBase40(&infile[line][col][length-3]);
         pitch = pitch % 40;
      } else {
         pitch = E_muse_c;
      }
      int keyinfo = 0;
      switch (pitch) {
         case E_muse_c:   keyinfo = 0;   break;
         case E_muse_fs:  keyinfo = 1;   break;
         case E_muse_cs:  keyinfo = 2;   break;
         case E_muse_gs:  keyinfo = 3;   break;
         case E_muse_ds:  keyinfo = 4;   break;
         case E_muse_as:  keyinfo = 5;   break;
         case E_muse_es:  keyinfo = 6;   break;
         case E_muse_bs:  keyinfo = 7;   break;
         case E_muse_bf:  keyinfo = -1;  break;
         case E_muse_ef:  keyinfo = -2;  break;
         case E_muse_af:  keyinfo = -3;  break;
         case E_muse_df:  keyinfo = -4;  break;
         case E_muse_cf:  keyinfo = -5;  break;
         case E_muse_gf:  keyinfo = -6;  break;
         case E_muse_ff:  keyinfo = -7;  break;
      }

      adjustKey(keyinfo);

      checkMeasure();
      lev++;
//      if (!attributes) {
         pline(lev, "<attributes>\n");
         lev++;
//         attributes = 1;
//      }
      pline(lev, "<key>\n");
      lev++;
      pline(lev, "<fifths>");
      // keyinfo += adjustKeyInfo(infile, line, col, voice);
      cout << keyinfo << "</fifths>\n";
      printMode(lev, infile, line, col, voice);
      lev--;
      pline(lev, "</key>\n");
      lev--;
      pline(lev, "</attributes>\n"); // att
      lev--;                         // att

   } else if (strcmp(infile[line][col], "*clefF4") == 0) {
      checkMeasure();
      lev++;
//      if (!attributes) {
         pline(lev, "<attributes>\n");
         lev++;
//         attributes = 1;
//      }
      pline(lev, "<clef>\n");
      lev++;
      pline(lev, "<sign>F</sign>\n");
      pline(lev, "<line>4</line>\n");
      lev--;
      pline(lev, "</clef>\n");
      lev--;
      pline(lev, "</attributes>\n");  // att
      lev--;                          // att
      ClefOctaveTranspose = 0;

   } else if (strcmp(infile[line][col], "*clefG2") == 0) {
      checkMeasure();
      lev++;
//      if (!attributes) {
         pline(lev, "<attributes>\n");
         lev++;
//         attributes = 1;
//      }
      pline(lev, "<clef>\n");
      lev++;
      pline(lev, "<sign>G</sign>\n");
      pline(lev, "<line>2</line>\n");
      lev--;
      pline(lev, "</clef>\n");
      lev--;
      pline(lev, "</attributes>\n");  // att
      lev--;                          // att
      ClefOctaveTranspose = 0;

   } else if (strcmp(infile[line][col], "*clefGv2") == 0) {
      checkMeasure();
      lev++;
//      if (!attributes) {
         pline(lev, "<attributes>\n");
         lev++;
//         attributes = 1;
//      }
      pline(lev, "<clef>\n");
      lev++;
      pline(lev, "<sign>G</sign>\n");
      pline(lev, "<line>2</line>\n");
      pline(lev, "<clef-octave-change>-1</clef-octave-change>\n");
      lev--;
      pline(lev, "</clef>\n");
      lev--;
      pline(lev, "</attributes>\n");  // att
      lev--;                          // att
      ClefOctaveTranspose = -1;
   }


}



//////////////////////////////
//
// adjustKeyInfo -- account for modal keys:
//   ionian     =  0 (to get to major)
//   dorian     = -1 (to get to minor)
//   phrygian   = +1 (to get to minor)
//   lydian     = -1 (to get to major)
//   mixolydian = +1 (to get to major)
//   aeolean    =  0 (to get to minor)
//   mixolydian = +2 (to get to minor)
//

int adjustKeyInfo(HumdrumFile& infile, int line, int col, int voice) {
   int startline = line;
   int endline   = line;
   int i, j;
   int track = infile[line].getPrimaryTrack(col);
   PerlRegularExpression pre;
   for (i=line; i>=0; i--) {
      if (infile[i].isData()) {
         startline = i+1;
         break;
      } else {
         startline = i;
      }
   }
   for (i=line; i<infile.getNumLines(); i++) {
      if (infile[i].isData()) {
         endline = i-1;
         break;
      } else {
         endline = i;
      }
   }

   for (i=startline; i<=endline; i++) {
      if (!infile[i].isInterpretation()) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (track != infile[i].getPrimaryTrack(j)) {
            continue;
         }

         if (pre.search(infile[i][j], "^\\*([A-Ga-g][#-]*):")) {
            if (strstr(infile[i][j], ":dor") != NULL) {
               return -1;
            }
            if (strstr(infile[i][j], ":phr") != NULL) {
               return +1;
            }
            if (strstr(infile[i][j], ":lyd") != NULL) {
               return -1;
            }
            if (strstr(infile[i][j], ":mix") != NULL) {
               return +1;
            }
            if (strstr(infile[i][j], ":aeo") != NULL) {
               return 0;
            }
            if (strstr(infile[i][j], ":loc") != NULL) {
               return +2;
            }
            return 0;
         }
      }
   }
   return 0;
}



//////////////////////////////
//
// printMode -- print the mode of the key
//

void printMode(int lev, HumdrumFile& infile, int line, int col, int voice) {

   int startline = line;
   int endline   = line;
   int i, j;
   int track = infile[line].getPrimaryTrack(col);
   PerlRegularExpression pre;
   for (i=line; i>=0; i--) {
      if (infile[i].isData()) {
         startline = i+1;
         break;
      } else {
         startline = i;
      }
   }
   for (i=line; i<infile.getNumLines(); i++) {
      if (infile[i].isData()) {
         endline = i-1;
         break;
      } else {
         endline = i;
      }
   }

   for (i=startline; i<=endline; i++) {
      if (!infile[i].isInterpretation()) {
         continue;
      }
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (track != infile[i].getPrimaryTrack(j)) {
            continue;
         }

         if (pre.search(infile[i][j], "^\\*([A-Ga-g][#-]*):")) {
            if (strstr(infile[i][j], ":dor") != NULL) {
               pline(lev, "<mode>dorian</mode>\n");
               return;
            }
            if (strstr(infile[i][j], ":phr") != NULL) {
               pline(lev, "<mode>phrygian</mode>\n");
               return;
            }
            if (strstr(infile[i][j], ":lyd") != NULL) {
               pline(lev, "<mode>lydian</mode>\n");
               return;
            }
            if (strstr(infile[i][j], ":mix") != NULL) {
               pline(lev, "<mode>mixolydian</mode>\n");
               return;
            }
            if (strstr(infile[i][j], ":aeo") != NULL) {
               pline(lev, "<mode>aeolean</mode>\n");
               return;
            }
            if (strstr(infile[i][j], ":loc") != NULL) {
               pline(lev, "<mode>locrian</mode>\n");
               return;
            }
            if (islower(pre.getSubmatch(1)[0])) {
               pline(lev, "<mode>minor</mode>\n");
               return;
            } else {
               pline(lev, "<mode>major</mode>\n");
               return;
            }
         }
      }
   }
}



//////////////////////////////
//
// convertNoteToXML --
//

void convertNoteToXML(HumdrumFile& infile, int line, int col, int voice) {
   static char buffer[128] = {0};
   int i;

   if (!infile[line].isExInterp(col, "**kern")) {
      return;
   }

   musicstart = 1;

   // copy current accidentals into chord buffer
   for (i=0; i<7; i++) {
      v1chord[i] = v1lastmeasure[i];
      v1prechordstates[i] = v1states[i];
   }

   lev++;

   // double cdur = 0;
   // int xdur = 0;

   int tokencount = infile[line].getTokenCount(col);
   for (i=0; i<tokencount; i++) {
      infile[line].getToken(buffer, col, i);
      // cdur = convertNoteEntryToXML(infile, line, col, buffer, i, 0);
      convertNoteEntryToXML(infile, line, col, buffer, i, 0, voice);
   }

/*   if ((col+1 < infile[line].getFieldCount()) && 
         (infile[line].getPrimaryTrack(col) == 
         infile[line].getPrimaryTrack(col+1))) {

      xdur = (int)(cdur * divisions);
      // AbsTick += xdur;

      tokencount = infile[line].getTokenCount(col+1);
      for (i=0; i<tokencount; i++) {
         infile[line].getToken(buffer, col+1, i);
         cdur = convertNoteEntryToXML(infile, line, col+1, buffer, i, 1);
      }

      xdur = (int)(cdur * divisions) - 
            (int)(divisions * infile[line].getDuration());
      // AbsTick += xdur;
   }
*/

   lev--;
}



//////////////////////////////
//
// checkbackup -- 
//

void checkbackup(int currenttick, int targettick) {

   int direction = currenttick - targettick;

   if (direction > 0) {
      // backwards
      pline(lev, "<backup>\n");
      lev++;
      pline(lev, "<duration>");
      cout << direction << "</duration>\n";
      lev--;
      pline(lev, "</backup>\n");
   } else if (direction < 0) {
      // forwards
      pline(lev, "<forward>\n");
      lev++;
      pline(lev, "<duration>");
      cout << -direction << "</duration>\n";
      lev--;
      pline(lev, "</forward>\n");
   }

   AbsTick = targettick;
}



//////////////////////////////
//
// convertNoteEntryToXML --
//

double convertNoteEntryToXML(HumdrumFile& infile, int line, int col,
      const char* buffer, int chord, int vlevel, int voice) {

   RationalNumber durationR = Convert::kernToDurationR(buffer);

   if (strcmp(buffer, ".") == 0) {
      // nothing to do
      return 0.0;
   }

   if ((strstr(buffer, "yy") != NULL) && (strchr(buffer, 'r') != NULL)) {
      if (!chord) {
         checkbackup(AbsTick, LineTick[line]);
      }
      // invisible rest: generate a <forward> direction
      RationalNumber rduration = durationR * divisions;
      int tickdur = (int)rduration.getFloat();
      AbsTick += tickdur;
      pline(lev, "<forward>\n");
      pline(lev+1, "<duration>");
      cout << tickdur  << "</duration>\n";
      pline(lev, "</forward>\n");
      return durationR.getFloat();
   }
 
   double output = 0;

   // int explicitz = 0;
   int altered = 0;
   int pitch = Convert::kernToBase40(buffer);
   // Sibelius interprets vocal tenor clef improperly, uncomment to fix:
   // pitch = pitch - 40 * ClefOctaveTranspose;
   char buff2[64] = {0};
   if (pitch > 0) {
      Convert::base40ToKern(buff2, pitch);
   } else {
      strcpy(buff2, "r");
   }

   if (strstr(buff2, "--") != NULL) {
   } else if (strstr(buff2, "--") != NULL) {
      altered = -2;
   } else if (strstr(buff2, "##") != NULL) {
      altered = 2;
   } else if (strstr(buff2, "-") != NULL) {
      altered = -1;
   } else if (strstr(buff2, "#") != NULL) {
      altered = 1;
   } else if (strstr(buffer, "n") != NULL) {
      altered = 0;
      // explicitz = 1;
   } else {
      altered = 0;
      // explicitz = 0;
   }
   char diapitch = toupper(buff2[0]);
   int octave = 0;
   if (pitch > 0) {
      octave = pitch / 40;
   }

   if (debugQ) {
      cout << "+++ processing note token: " << buffer << endl;
   }

//   if (attributes) {
//      lev--;
//      pline(lev, "</attributes>\n");
//      lev--;   // att
//      attributes = 0;
//   }

   if (!chord) {
      checkbackup(AbsTick, LineTick[line]);
   }

   pline(lev, "<note>\n");
   lev++;

   int grace = 0;
   if (strchr(buffer, 'q') != NULL || strchr(buffer, 'Q') != NULL) {
      grace = 1;
   }
   if (grace) {
      pline(lev, "<grace/>\n");
   }

   if (chord) {
      pline(lev, "<chord/>\n");
   }
   if (toupper(diapitch) != 'R') {
      pline(lev, "<pitch>\n");
      lev++;
      pline(lev, "<step>");
      cout << diapitch << "</step>\n";
      if (altered != 0) {
         pline(lev, "<alter>");
         cout << altered << "</alter>\n";
      }
      pline(lev, "<octave>");
      cout << octave << "</octave>\n";
      lev--;
      pline(lev, "</pitch>\n");
   } else {
      pline(lev, "<rest/>\n");
   }
   if (!grace) {
      pline(lev, "<duration>");
      RationalNumber ratnum;
      ratnum = durationR * divisions;
      int value = (int)ratnum.getFloat();
      cout << value << "</duration>\n";
      if (!chord) {
         // don't keep track of chord notes, and presume first note of 
         // chord has correct duration for entire chord.
         AbsTick += value;
      }
      output = durationR.getFloat();
   } else {
      output = 0.0;
   }

   int tietype = 0;
   if (strchr(buffer, '[') != NULL) {
      tietype = 1;
   } else if (strchr(buffer, '_') != NULL) {
      tietype = 2;
   } else if (strchr(buffer, ']') != NULL) {
      tietype = 3;
   }
   switch (tietype) {
      case 1:
         pline(lev, "<tie type=\"start\"/>\n");
         break;
      case 2:
         pline(lev, "<tie type=\"stop\"/>\n");
         pline(lev, "<tie type=\"start\"/>\n");
         break;
      case 3:
         pline(lev, "<tie type=\"stop\"/>\n");
         break;
   }

   pline(lev, "<voice>");
   cout << voice << "</voice>\n";
   int vcase = voice;

/*   int vcase = 0;
   if (strchr(infile[line].getSpineInfo(col), '(') == NULL) {
      vcase = 0;
      pline(lev, "<voice>1</voice>\n");
      pline(lev, "<type>");
   } else if (strchr(infile[line].getSpineInfo(col), 'a') != NULL) {
      vcase = 1;
      pline(lev, "<voice>1</voice>\n");
      pline(lev, "<type>");
   } else if (strchr(infile[line].getSpineInfo(col), 'b') != NULL) {
      vcase = 2;
      pline(lev, "<voice>2</voice>\n");
      pline(lev, "<type>");
   } else {
      vcase = 3;
      pline(lev, "<voice>1</voice>\n");
      pline(lev, "<type>");
   }
*/

   pline(lev, "<type>");

   char durstring[32] = {0};
   PerlRegularExpression pre;
   Array<char> newbuffer;
   newbuffer.setSize(strlen(buffer)+1);
   strcpy(newbuffer.getBase(), buffer);

   Array<char> nodots;
   nodots.setSize(strlen(newbuffer.getBase()) + 1);
   strcpy(nodots.getBase(), newbuffer.getBase());
   PerlRegularExpression removedots;
   removedots.sar(nodots, "\\.", "", "g");

   pre.sar(newbuffer, "q", "", "i");
   if (grace) {
      // if a grace note and no rhythm, set the visual duration to 8th
      if (!pre.search(newbuffer, "\\d")) {
         newbuffer.setSize(2);
         strcpy(newbuffer.getBase(), "8");
      }
   }

   // have to ignore augmentation dots on rhythm?
   RationalNumber rat = Convert::kernToDurationR(nodots.getBase());
   double newduration = rat.getFloat();
   RationalNumber ratout = 1;

   // newduration should be in terms of quarter note durations
   // whole = 4.0; half - 0.5, etc.
   double tempval = 0.0;
   if (newduration > 0.0) {
      double base2val =  log10(newduration)/log10(2);
      tempval = pow(2.0, floor(base2val + 0.99));
   }

   Convert::durationToKernRhythm(durstring, tempval);
   ratout = Convert::kernToDurationR(durstring);

   printDurationType(durstring);
   // printDurationType(nodots.getBase());

   cout << "</type>\n";

   // print number of augmentation dots in duration ///////////////////
   // printDots(durstring);
   printDots(newbuffer.getBase());


   /// WRITTEN ACCIDENTALS ////////////////////////////////////////////

   if (toupper(buff2[0]) != 'R') {
      if (strchr(buffer, 'n') != NULL) {
         pline(lev, "<accidental>natural</accidental>\n");
         v1states[toupper(buff2[0]) - 'A'] = 0;
         v1lastmeasure[toupper(buff2[0]) - 'A'] = 0;
      } else {
         checkAccidentals(toupper(buff2[0]), altered, chord);
      }
   }

   // Time modification (tuplet indications) //////////////////////////
   if (!grace) {  // don't do <time-modification> if a grace note
      RationalNumber timemod = rat / ratout;
      if (timemod.getDenominator() != 1) {
         pline(lev, "<time-modification>\n");
         pline(lev+1, "<actual-notes>");
         cout << timemod.getDenominator() << "</actual-notes>\n";
         pline(lev+1, "<normal-notes>");
         cout << timemod.getNumerator() << "</normal-notes>\n";
         pline(lev, "</time-modification>\n");
      }
   }
    
   // Stem direction
   if (strchr(buffer, '/') != NULL) {
      pline(lev, "<stem>up</stem>\n");
   } else if (strchr(buffer, '\\') != NULL) {
      pline(lev, "<stem>down</stem>\n");
   } else if (vcase == 1) {
      pline(lev, "<stem>down</stem>\n");
   } else if (vcase == 2) {
      if (strchr(buffer, '/') == NULL && strchr(buffer, '\\') == NULL) {
         pline(lev, "<stem>up</stem>\n");
      }
   }

   if (durationR.getFloat() >= 1.0) {
      // kill rogue beam marks.
      beamlevel[vlevel] = 0;
   }
   if (!chord) {
      // processBeams(infile, line, col, buffer, vlevel);
      processBeams(infile, line, col, infile[line][col], vlevel);
   }

   /// WRITTEN TIES ///////////////////////////////////////////////////

   if (tietype) {
      pline(lev, "<notations>\n");
      lev++;
      switch (tietype) {
         case 1:
            pline(lev, "<tied type=\"start\"/>\n");
            break;
         case 2:
            pline(lev, "<tied type=\"stop\"/>\n");
            pline(lev, "<tied type=\"start\"/>\n");
            break;
         case 3:
            pline(lev, "<tied type=\"stop\"/>\n");
            break;
      }

      lev--;
      pline(lev, "</notations>\n");
   }

   /// FERMATAS ///////////////////////////////////////////////////////
 
   if (strchr(buffer, ';') != NULL) {
      pline(lev, "<notations>\n");
      lev++;
      pline(lev, "<fermata type=\"upright\"/>\n"); 
      lev--;
      pline(lev, "</notations>\n");
   }

   /// ARTICULATIONS ////////////////////////////////////////////
 
   if (strchr(buffer, '\'') != NULL) {
      pline(lev, "<notations>\n");
      lev++;
      pline(lev, "<articulations>\n");
      lev++;
      pline(lev, "<staccato placement=\"above\"/>\n"); 
      lev--;
      pline(lev, "</articulations>\n");
      lev--;
      pline(lev, "</notations>\n");
   }
 
   if (strchr(buffer, '~') != NULL) {
      pline(lev, "<notations>\n");
      lev++;
      pline(lev, "<articulations>\n");
      lev++;
      pline(lev, "<tenuto placement=\"above\"/>\n"); 
      lev--;
      pline(lev, "</articulations>\n");
      lev--;
      pline(lev, "</notations>\n");
   }
 
   if (strchr(buffer, '^') != NULL) {
      pline(lev, "<notations>\n");
      lev++;
      pline(lev, "<articulations>\n");
      lev++;
      pline(lev, "<accent placement=\"above\"/>\n"); 
      lev--;
      pline(lev, "</articulations>\n");
      lev--;
      pline(lev, "</notations>\n");
   }


   /// LYRICS /////////////////////////////////////////////////////////

   if (!chord) {
      processTextUnderlay(infile, line, col);
   }

   lev--;
   pline(lev, "</note>\n");
   return output;
}



//////////////////////////////
//
// processBeams --
//

void processBeams(HumdrumFile& infile, int line, int col, const char* buffer,
      int vlevel) {

   int backhook = 0;
   int forehook = 0;
   int openbeam = 0;
   int closebeam = 0;

   int i=0; 
   int sum = 0;
   int length = strlen(buffer);
   for (i=0; i<length; i++) {
      switch (buffer[i]) {
         case 'k': backhook++;  sum++; break;
         case 'K': forehook++;  sum++; break;
         case 'L': openbeam++;  sum++; break;
         case 'J': closebeam++; sum++; break;
      }
   }
   if (closebeam && openbeam) {
      openbeam = 0;
   }

   if (forehook && backhook) {
      if (openbeam) {
         backhook = 0;
      } else {
         forehook = 0;
      }
   }

   if (sum == 0) {
      // there is nothing new to do
      for (i=0; i<beamlevel[vlevel]; i++) {
         pline(lev, "<beam number=\"");
         cout << i+1 << "\">continue</beam>\n";
      }
      return;
   }

   // int totalcount = beamlevel[vlevel] + openbeam - closebeam + 
   //       backhook + forehook;
   int rcount = 0;

   if (openbeam != 0) {
      // add any new beams which are starting
 
      // continue any old beams
      for (i=0; i<beamlevel[vlevel]; i++) {
         pline(lev, "<beam number=\"");
         cout << i+1 << "\">continue</beam>\n";
         rcount++;
      }
   
      // add new beams
      for (i=0; i<openbeam; i++) {
         pline(lev, "<beam number=\"");
         cout << beamlevel[vlevel] + i + 1 << "\">begin</beam>\n";
         rcount++;
      }
      beamlevel[vlevel] += openbeam;

      // add any opening beam hook
      for (i=0; i<forehook; i++) {
         pline(lev, "<beam number=\"");
         cout << beamlevel[vlevel] + i + 1 << "\">forward hook</beam>\n";
         rcount++;
      }

   }

   if (closebeam != 0) {
      // close any old beams which are ending

      // continue any old beams
      for (i=0; i<beamlevel[vlevel] - closebeam - backhook; i++) {
         pline(lev, "<beam number=\"");
         cout << i+1 << "\">continue</beam>\n";
         rcount++;
      }
   
      // close old beams
      for (i=0; i<closebeam; i++) {
         // some strange problem required this following if statement to preven end=0
         if ((beamlevel[vlevel] - closebeam + i + 1) > 0) {
            pline(lev, "<beam number=\"");
            cout << beamlevel[vlevel] - closebeam + i + 1 << "\">end</beam>\n";
            rcount++;
         }
      }
      beamlevel[vlevel] -= closebeam;

      // add any ending beam hook
      for (i=0; i<backhook; i++) {
         pline(lev, "<beam number=\"");
         cout << beamlevel[vlevel] + closebeam + i + 1 
              << "\">backward hook</beam>\n";
         rcount++;
      }
   
   }

   if (beamlevel[vlevel] < 0) {
      beamlevel[vlevel] = 0;
   }

}




//////////////////////////////
//
// processTextUnderlay --
//

void processTextUnderlay(HumdrumFile& infile, int line, int col) {
   int fields = infile[line].getFieldCount();
   if (col >= fields-1) {
      return;
   }

   int tcol = col+1;
   int verse = 1;
   while (tcol < fields && 
         strcmp(infile[line].getExInterp(tcol), "**kern") != 0) {
      if (strcmp(infile[line][tcol], ".") == 0) {
         // ignore null tokens
      } else if (strcmp(infile[line].getExInterp(tcol), "**text") == 0) {
         displayLyrics(infile, line, tcol, verse);
      } else {
         displayUnknownTextType(infile, line, tcol, verse);
      }

      tcol++;
      verse++;
   }

}



//////////////////////////////
//
// displayLyrics --
//

void displayLyrics(HumdrumFile& infile, int line, int col, int verse) {
   displayUnknownTextType(infile, line, col, verse);
}



//////////////////////////////
//
// displayUnknownTextType --
//

void displayUnknownTextType(HumdrumFile& infile, int line, int col, 
      int verse) {

   pline(lev, "<lyric number=\"");
   cout << verse << "\" name=\"verse\">\n";
   lev++;
   pline(lev, "<syllabic>single</syllabic>\n");
   pline(lev, "<text>");

   displayHTMLText(infile[line][col]);

   cout << "</text>\n";
   lev--;
   pline(lev, "</lyric>\n");
}



//////////////////////////////
//
// displayHTMLText --
//

void displayHTMLText(const char* buffer) {
   PerlRegularExpression pre;
   Array<char> tbuf;
   tbuf = buffer;
   pre.sar(tbuf, "&", "&", "g");
   pre.sar(tbuf, "--", "--", "g");

   int length = strlen(tbuf.getBase());
   for (int i=0; i<length; i++) {
      switch (tbuf[i]) {
         case 'ä':  cout << "ä"; break;
         case 'ë':  cout << "ë"; break;
         case 'ï':  cout << "ï"; break;
         case 'ö':  cout << "ö"; break;
         case 'ü':  cout << "ü"; break;
         case '<':  cout << "<"; break;
         case '>':  cout << ">"; break;
         case '"':  cout << """; break;
         default:   cout << tbuf[i];
      }
   }
}



//////////////////////////////
//
// checkAccidentals --
//

void checkAccidentals(int diatonic, int alter, int chord) {
   // if a note is different than the current state
   // in the measure, then add an accidental to make
   // sure that the note is played right

   if (toupper(diatonic) == 'R') {
      return;
   }

   if (!chord) {

      diatonic = toupper(diatonic);

      if (v1states[diatonic - 'A'] != alter) {
   
         pline(lev, "<accidental>");
         switch (alter) {
            case 2:  cout << "double-sharp"; break;
            case 1:  cout << "sharp"; break;
            case 0:  cout << "natural"; break;
            case -1:  cout << "flat"; break;
            case -2:  cout << "double-flat"; break;
         }
         cout << "</accidental>\n";
         v1states[diatonic - 'A'] = alter;
         v1lastmeasure[diatonic - 'A'] = alter;
   
      } else if (cautionaryQ && v1lastmeasure[diatonic - 'A'] != alter) {
         // if a cautionary accidental accross measure, then adjust here
   
         pline(lev, "<accidental>");
         switch (alter) {
            case 2:  cout << "double-sharp"; break;
            case 1:  cout << "sharp"; break;
            case 0:  cout << "natural"; break;
            case -1:  cout << "flat"; break;
            case -2:  cout << "double-flat"; break;
         }
         cout << "</accidental>\n";
         v1states[diatonic - 'A'] = alter;
         v1lastmeasure[diatonic - 'A'] = alter;
      }

   } else {
      // note in a chord

      diatonic = toupper(diatonic);
      if (v1prechordstates[diatonic - 'A'] != alter) {
   
         pline(lev, "<accidental>");
         switch (alter) {
            case 2:  cout << "double-sharp"; break;
            case 1:  cout << "sharp"; break;
            case 0:  cout << "natural"; break;
            case -1:  cout << "flat"; break;
            case -2:  cout << "double-flat"; break;
         }
         cout << "</accidental>\n";
         v1states[diatonic - 'A'] = alter;
         v1lastmeasure[diatonic - 'A'] = alter;
   
      } else if (cautionaryQ && v1chord[diatonic - 'A'] != alter) {
         // if a cautionary accidental accross measure, then adjust here
   
         pline(lev, "<accidental>");
         switch (alter) {
            case 2:  cout << "double-sharp"; break;
            case 1:  cout << "sharp"; break;
            case 0:  cout << "natural"; break;
            case -1:  cout << "flat"; break;
            case -2:  cout << "double-flat"; break;
         }
         cout << "</accidental>\n";
         v1states[diatonic - 'A'] = alter;
         v1lastmeasure[diatonic - 'A'] = alter;
      }

   }


}



//////////////////////////////
//
// printDots --
//

void printDots(const char* durstring) {
   int length = strlen(durstring);
   int i;
   for (i=0; i<length; i++) {
      if (durstring[i] == '.') {
         pline(lev, "<dot/>\n");
      }
   }
}



//////////////////////////////
//
// printDurationType --
//

void printDurationType(const char* durstring) {
   char buffer[32] = {0};
   int length = strlen(durstring);
   strcpy(buffer, durstring);
   int counter = 0;
   for (int i=0; i<length; i++) {
      if (buffer[i] == '0') {
         counter++;
      } else if (isdigit(buffer[i])) {
         counter = -100;
      }
      if (buffer[i] == '.') {
         buffer[i] = '\0';
      }
   }

   int number = atoi(buffer);
   if (counter == 2) {
      cout << "long";
   } else if (number == 0) {
      cout << "breve";
   } else if (number <= 1) {
      cout << "whole";
   } else if (number <= 2) {
      cout << "half";
   } else if (number <= 4) {
      cout << "quarter";
   } else if (number <= 8) {
      cout << "eighth";
   } else if (number <= 16) {
      cout << "16th";
   } else if (number <= 32) {
      cout << "32nd";
   } else if (number <= 64) {
      cout << "64th";
   } else if (number <= 128) {
      cout << "128th";
   } else if (number <= 256) {
      cout << "256th";
   } else if (number <= 512) {
      cout << "512th";
   } else if (number <= 1024) {
      cout << "1024th";
   } else if (number <= 2048) {
      cout << "2048th";
   } else if (number <= 4096) {
      cout << "4096th";
   } else {
      cout << "UNKNOWN";
   }
}



//////////////////////////////
//
// makePartList -- generate a list of the parts in the score
//

int makePartList(HumdrumFile& infile) {
   lev++;
   pline(lev, "<part-list>\n");
   lev++;
   
   // find the start of the spine data and output a part for each
   // spine
   int i = 0;
   int count = 0;   // the number of **kern spines
   int j = 0;
   while (i<infile.getNumLines() && infile[i].getType() != 
         E_humrec_interpretation) {
      i++;
   }
   if (infile[i].getType() == E_humrec_interpretation) {
      if (strncmp("**", infile[i][0], 2) != 0) {
         cout << "Error on line " << i + 1 << " of file: No start of data" 
              << endl;
      }
   } else {
      cout << "Error: no data in file" << endl;
      exit(1);
   }

   if (reverseQ) {
      for (j=0; j<infile[i].getFieldCount(); j++) {
         if (strcmp(infile[i].getExInterp(j), "**kern") != 0) {
            continue;
         }
         count++;
         generatePartInfo(infile, i, j, count);
      }
   } else {
      // doing parts in reverse order
      for (j=infile[i].getFieldCount()-1; j>=0; j--) {
         if (strcmp(infile[i].getExInterp(j), "**kern") != 0) {
            continue;
         }
         count++;
         generatePartInfo(infile, i, j, count);
      }
   }

   lev--;
   pline(lev, "</part-list>\n");

   return count;
}



//////////////////////////////
//
// generatePart --
//

void generatePartInfo(HumdrumFile& infile, int start, int col, int count) {
   int i = start + 1;
   int j = 0;
   pline(lev, "<score-part id=\"P");
   cout << count << "\">\n";
   lev++;

   int done = 0;
   while (!done && i < infile.getNumLines()) {
      if (infile[i].getType() == E_humrec_interpretation) {
         for (j=0; j<infile[i].getFieldCount(); j++) {
            if (infile[i].getPrimaryTrack(j) == col + 1) {
               if (strncmp(infile[i][j], "*I", 2) == 0 &&
                   strncmp(infile[i][j], "*IC", 3) != 0) {
                  done = 1;
                  break;
               }
            } else {
            }
         }
      }
      if (done) break;
      i++;
   }

   if (done == 1) {
      pline(lev, "<part-name>");
      cout << &(infile[i][j][2]) << "</part-name>\n";
   } else {
      // pline(lev, "<part-name>XPart ");
      // cout << col << "</part-name>\n";
      pline(lev, "<part-name></part-name>\n");
   }
 
   lev--;
   pline(lev, "</score-part>\n");
}



//////////////////////////////
//
// pline -- print a line of data
//

void pline(int level, const char* string) {
   for (int i=0; i<level; i++) {
      cout << '\t';
   }
   cout << string;
} 



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

void usage(const char* command) {

}




// md5sum: 911cb3259ae038c19f9cff00704c9be5 hum2xml.cpp [20130504]
//