//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sun Jul 27 11:57:21 PDT 2003
// Last Modified: Thu Jul 31 22:00:31 PDT 2003
// Last Modified: Sun May  2 22:26:31 PDT 2010 (added **recip rhythm spec.)
// Filename:      ...sig/examples/all/harm2root.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/harm2root.cpp
// Syntax:        C++; museinfo
//
// Description:   Convert a Humdrum **harm spine into a **root spine
//

#include "humdrum.h"
#include "PerlRegularExpression.h"

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

// function declarations
void      checkOptions(Options& opts, int argc, char* argv[]);
void      example(void);
void      usage(const char* command);
void      generateAnalysis(HumdrumFile& infile, Array<double>& durs);
void      doRhythmAnalysis(HumdrumFile& infile, Array<double>& durs);
int       makeRootInterval(const char* harmdata, int keyroot, int keymode);
void      getChordPitches(Array<int>& pitches, const char* token, int root, 
                             int keyroot, int keymode);
void      printChordInfo(const char* token,  int rootinterval, 
                             int keyroot, int keymode, double duration);
int       getInversion(const char* token);
int       getScaleDegree(const char* harm);
int       adjustKeyMode(int keymode, int keyroot, const char* lastpart);
int       getRecipField(int line, HumdrumFile& infile, int recip);
double    getRecipDuration(HumdrumFile& infile, int line, int primarySpine);
int       checkForKeyDesignation(HumdrumFile& infile, int line);
int       checkForTimeSignature(HumdrumFile& infile, int line);

// global variables
Options   options;           // database for command-line arguments
int       debugQ       = 0;  // used with --debug option
int       appendQ      = 0;  // used with -a option
int       octave       = 2;  // used with -o option
int       rhythmQ      = 1;  // used with --RR option
int       rootQ        = 1;  // used with -r option 
int       recip        = -1; // used with -R option
int       recipField   = -1; // used with -R option
const char *instrument = ""; // used with -I option

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


int main(int argc, char* argv[]) {
   HumdrumFile infile;

   // process the command-line options
   checkOptions(options, argc, argv);

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

   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));
      }

      Array<double> durs;
      durs.setSize(0);
      doRhythmAnalysis(infile, durs);
      generateAnalysis(infile, durs);
   }

   return 0;
}


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


//////////////////////////////
//
// doRhythmAnalysis --
//

void doRhythmAnalysis(HumdrumFile& infile, Array& durs) {
   int i, j;
   infile.analyzeRhythm();
   durs.setSize(infile.getNumLines());
   durs.setSize(0);
   double lastbeat = 0.0;
   double beat = 0.0;
   double duration = 0.0;

   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isData()) {
         for (j=infile[i].getFieldCount()-1; j>=0; j--) {
            if (strcmp(infile[i].getExInterp(j), "**harm") == 0) {
               if (strcmp(infile[i][j], ".") != 0) {
                  lastbeat = beat; 
                  beat = infile[i].getAbsBeat();
                  duration = beat - lastbeat;
                  durs.append(duration);
               }
            }
         }
      }
   } 

   for (i=0; i<durs.getSize()-1; i++)  {
      durs[i] = durs[i+1];
   }
   if (durs.getSize() > 0) {
      duration = infile[infile.getNumLines()-1].getAbsBeat() - beat;
      durs[durs.getSize()-1] = duration;
   }
}



//////////////////////////////
//
// generateAnalysis --
//

void generateAnalysis(HumdrumFile& infile, Array& durs) {
   int i, j;
   int length;
   int keyroot = 2;
   int keymode = 0;      // 0 = major, 1 = minor
   int rootinterval = 0;
   int testpitch = 0;
   char buffer[128] = {0};
   int rindex = 0;
   double rdur;

   for (i=0; i<infile.getNumLines(); i++) {
      if (appendQ) {
         cout << infile[i];
      }
      if (infile[i].isData()) {
         for (j=0; j<infile[i].getFieldCount(); j++) {
            if (strcmp(infile[i].getExInterp(j), "**harm") == 0) {
               if (appendQ) {
                  cout << "\t";
               }
               if (strcmp(infile[i][j], ".") != 0) {
                  rootinterval = makeRootInterval(infile[i][j], keyroot, 
                        keymode);
                  if (rootQ) {
                     if (durs.getSize() > 0) {
                        Convert::durationToKernRhythm(buffer, durs[rindex++]);
                        if (rhythmQ && buffer[0] != '-') {
                           cout << buffer;
                        }
                     }
                     if (rootinterval >= 0) {
                        cout << Convert::base40ToKern(buffer, 
                              (keyroot + rootinterval) % 40 + octave * 40); 
                     } else {
                        cout << "r";
                     }
                     if (strchr(infile[i][j], ';') != NULL) {
                        cout << ';';
                     }
                  } else {
                     // print entire chord
                     if ((recipField <= 0) && (durs.getSize() > 0)) {
                        printChordInfo(infile[i][j], rootinterval, keyroot, 
                           keymode, durs[rindex++]);
                     } else if (recipField > 0) { 
                        rdur = getRecipDuration(infile, i, recipField);
                        printChordInfo(infile[i][j], rootinterval, keyroot, 
                           keymode, rdur);
                     } else {
                        printChordInfo(infile[i][j], rootinterval, keyroot, 
                           keymode, -1);
                     }
                  }
               } else {
                  cout << ".";
               }
            }
         }
      } else if(infile[i].isInterpretation()) {
         if (appendQ) {
            cout << "\t";
         }
         length = strlen(infile[i][0]);
         if (infile[i][0][length-1] == ':') {
            testpitch = Convert::kernToBase40(infile[i][0]);
            if (testpitch >= 0) {
               cout << infile[i][0];
               keyroot = testpitch % 40;
               keymode = islower(infile[i][0][1]) ? 1 : 0;
            }
         } else if (strncmp(infile[i][0], "**", 2) == 0) {
            if (rootQ) {
               cout << "**root";
            } else {
               cout << "**kern";
            }
	    if (recip >= 0) {
               recipField = getRecipField(i, infile, recip);
            }
         } else if (strncmp(infile[i][0], "*I", 2) == 0) {
            if (instrument[0] == '\0') {
               cout << "*";
            } else {
               cout << "*I" << instrument;
            }
         } else if (infile[i].equalFieldsQ()) {
            if (strcmp(infile[i][0], "*+") == 0) {
               cout << "*";
            } else if (strcmp(infile[i][0], "*^") == 0) {
               cout << "*";
            } else if (strcmp(infile[i][0], "*v") == 0) {
               cout << "*";
            } else if (strcmp(infile[i][0], "*x") == 0) {
               cout << "*";
            } else {
               cout << infile[i][0];
            }
         } else {
            int status = 0;
            status = checkForKeyDesignation(infile, i);
            if (status == 0) {
               status = checkForTimeSignature(infile, i);
            } 
            if (status == 0) {
               cout << "*";
            }
         }
      } else if (infile[i].isMeasure()) {
         if (appendQ) {
            cout << "\t";
         }
         cout << infile[i][0];
      } else if (infile[i].isLocalComment()) {
         if (appendQ) {
            cout << "\t";
         }
         cout << '!';
      } else if (infile[i].isGlobalComment()) {
         cout << infile[i];
      } else if (infile[i].isBibliographic()) {
         cout << infile[i];
      }
      if (appendQ) {
         cout << "\n";
      } else {
         cout << "\n";
      }
   }
}



//////////////////////////////
//
// checkForKeyDesignation -- look for a key designation on the current
//   line, and echo it in the output spine.
//

int checkForKeyDesignation(HumdrumFile& infile, int line) {
   if (!infile[line].isInterpretation()) {
      return 0;
   }
   PerlRegularExpression pre;
   int j;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (pre.search(infile[line][j], "^\\*[A-G]([#-]*):", "i")) {
         cout << infile[line][j];
         return 1;
      }
   }

   return 0;
}



//////////////////////////////
//
// checkForTimeSignature -- look for a time signature on the current
//   line in the input **recip data if it exists, and echo it in 
//   the output spine.
//

int checkForTimeSignature(HumdrumFile& infile, int line) {
   if (!infile[line].isInterpretation()) {
      return 0;
   }
   if (recipField <= 0) {
      return 0;
   }

   PerlRegularExpression pre;
   int j;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (recipField != infile[line].getPrimaryTrack(j)) {
         continue;
      }
      if (pre.search(infile[line][j], "^\\*M\\d+/\\d+$")) {
         cout << infile[line][j];
         return 1;
      }
   }

   return 0;
}



//////////////////////////////
//
// getRecipDuration -- 
//

double getRecipDuration(HumdrumFile& infile, int line, int primarySpine) {
   int ii, jj;
   int j;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (primarySpine != infile[line].getPrimaryTrack(j)) {
         continue;
      }
      if (!infile[line].isExInterp(j, "**recip")) {
         // maybe also allow **kern spines to define rhythm here someday.
         return -1;
      }
      if (strcmp(infile[line][j], ".") == 0) {
         ii = infile[line].getDotLine(j);
         jj = infile[line].getDotSpine(j);
      } else {
         ii = line;
         jj = j;
      }
      return Convert::kernToDuration(infile[ii][jj]);
   }

   return -1.0;
}



//////////////////////////////
//
// getRecipField -- get the primary spine number of the nth (offset from 1)
//     **recip spine from left to right in the file.
//

int getRecipField(int line, HumdrumFile& infile, int recip) {
   if (recip < 1) {
      return -1;
   }

   int output = -1;
   int counter = 0;
   int j;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (infile[line].isExInterp(j, "**recip")) {
         counter++;
         if (counter == recip) {
            return j+1;
         }
      }
   }
   if (counter > 0) {
      output = 1;
   }
 
   return output;
}



//////////////////////////////
//
// printChordInfo --
//

void printChordInfo(const char* token,  int root, 
      int keyroot, int keymode, double duration) {
   int i;
   char dbuffer[128] = {0};
   char pbuffer[128] = {0};
   Convert::durationToKernRhythm(dbuffer, duration);
   if (!rhythmQ) {
      dbuffer[0] = '\0';
   }

   Array<int> pitches;
   if (root >= 0) {
      getChordPitches(pitches, token, root, keyroot, keymode);
      for (i=0; i<pitches.getSize(); i++) {
         if (duration > 0) {
            cout << dbuffer;
         }
         cout << Convert::base40ToKern(pbuffer, pitches[i]);
         if (strchr(token, ';') != NULL) {
            cout << ';';
         }
         if (i<pitches.getSize()-1) {
            cout << ' ';
         }
      }
   } else {
      if (duration > 0) {
         cout << dbuffer;
      }
      cout << "r";
      if (strchr(token, ';') != NULL) {
         cout << ';';
      }
   }
}




//////////////////////////////
//
// getChordPitches --
//

void getChordPitches(Array<int>& pitches, const char* token, int root, 
   int keyroot, int keymode) {

   int oct = octave + 2;
   int inversion = getInversion(token);

   pitches.setSize(0);

   // store the root pitch
   int pitch = (root + keyroot) % 40 + oct * 40;
   int base = pitch;
   if (inversion == 0) {
      pitch = pitch - 40;
   }
   pitches.append(pitch);

   char firstpart[128] = {0};
   char lastpart[128] = {0};
   strcpy(firstpart, token);
   int i;
   int length = strlen(firstpart);
   for (i=0; i<length; i++) {
      if (firstpart[i] == '/') {
         firstpart[i] = '\0';
         break;
      }
   }

   int offset = 0;

   // determine the third
   if (strchr(firstpart, 'i') != NULL) {
      pitch = base + 11;
      pitches.append(pitch);
   } else if (strchr(firstpart, 'I') != NULL) {
      pitch = base + 12;
      pitches.append(pitch);
   } else if (strchr(firstpart, 'v') != NULL) {
      pitch = base + 11;
      pitches.append(pitch);
   } else if (strchr(firstpart, 'V') != NULL) {
      pitch = base + 12;
      pitches.append(pitch);
   } else if (strchr(firstpart, 'N') != NULL) {
      pitch = base + 12;
      pitches.append(pitch);
   } else if (strstr(firstpart, "Lt") != NULL) {
      pitch = base + 12;
      pitches.append(pitch);
   } else if (strstr(firstpart, "Gn") != NULL) {
      pitch = base + 12;
      pitches.append(pitch);
   } else if (strstr(firstpart, "Fr") != NULL) {
      pitch = base + 12;
      pitches.append(pitch);
   }
   if (inversion == 1) {
      pitches[1] = pitches[1] % 40 + 40 * (oct - 1);
   }
   pitches[1] = pitches[1] + offset;

   // determine the fifth
   if (strchr(firstpart, 'o') != NULL) {
      pitch = base + 22;
      pitches.append(pitch);
   } else if (strchr(firstpart, '+') != NULL) {
      pitch = base + 24;
      pitches.append(pitch);
   } else {
      pitch = base + 23;
      pitches.append(pitch);
   }
   if (inversion == 2) {
      pitches[2] = pitches[2] % 40 + 40 * (oct - 1);
   }
   pitches[2] = pitches[2] + offset;


   // determine the seventh

   if (strchr(firstpart, '7') == NULL) {
      return;
   }

   if (strchr(token, '/') != NULL) {
      strcpy(lastpart, strchr(token, '/')+1);
      offset = makeRootInterval(lastpart, keyroot, keymode);
      keymode = adjustKeyMode(keymode, keyroot, lastpart);
   } 

   int rootscaledegree = getScaleDegree(firstpart);
   int seventhdegree = (rootscaledegree + 6) % 7;

   // int pitch = (root + keyroot) % 40 + oct * 40;
   // int base = pitch;
   
   pitches.append(pitch);
   if (strstr(firstpart, "D7") != NULL) {
      pitches[3] = base + 33;
      offset = 0;
   } else if (keymode == 0) {
      // major key
      int degrees[7] = {0, 6, 12, 17, 23, 29, 35};
      pitches[3] = (keyroot + degrees[seventhdegree]) % 40 + oct * 40;
      if (pitches[3] - base < 25) {
         pitches[3] += 40;
      } 
      //if (base % 40 > 25) {
      //   pitches[3] += 40;
      //}
   } else {
      // minor key (harmonic minor used)
      int degrees[7] = {0, 6, 11, 17, 23, 28, 35};
      pitches[3] = (keyroot + degrees[seventhdegree]) % 40 + oct * 40;
      if (base % 40 > 25) {
         pitches[3] += 40;
      }
   } 

   // fix the octave placement of the seventh (to be above the fifth)
   pitches[3] = pitches[3] + offset;
   if (pitches[3] - pitches[2] < 0) {
      pitches[3] += 40;
   }

   // put 3rd inversions in the octave below middle C
   if (inversion == 3) {
      pitches[3] = pitches[3] % 40 + 40 * (oct - 1);
   }

}



//////////////////////////////
//
// adjustKeyMode --
//

int adjustKeyMode(int keymode, int keyroot, const char* lastpart) {
   char string[128] = {0};
   strcpy(string, lastpart);
   const char* ptr = lastpart;
   if (strchr(string, '/') != NULL) {
      ptr = strchr(string, '/') + 1;
   } 
   if (strchr(ptr, 'v') != NULL) {
      keymode = 1;
   } else if (strchr(ptr, 'i') != NULL) {
      keymode = 1;
   } else if (strchr(ptr, 'V') != NULL) {
      keymode = 0;
   } else if (strchr(ptr, 'I') != NULL) {
      keymode = 0;
   } else { // don't know the key so assume major
      keymode = 0;
   }

   return keymode;
}



//////////////////////////////
//
// getScaleDegree -- 
//

int getScaleDegree(const char* harm) {
   if (strstr(harm, "VII") != NULL) { return 6; } 
   if (strstr(harm, "vii") != NULL) { return 6; } 
   if (strstr(harm, "VI")  != NULL) { return 5; } 
   if (strstr(harm, "vi")  != NULL) { return 5; } 
   if (strstr(harm, "IV")  != NULL) { return 3; } 
   if (strstr(harm, "iv")  != NULL) { return 3; } 
   if (strstr(harm, "V")   != NULL) { return 4; } 
   if (strstr(harm, "v")   != NULL) { return 4; } 
   if (strstr(harm, "III") != NULL) { return 2; } 
   if (strstr(harm, "iii") != NULL) { return 2; } 
   if (strstr(harm, "II")  != NULL) { return 1; } 
   if (strstr(harm, "ii")  != NULL) { return 1; } 
   if (strstr(harm, "I")   != NULL) { return 0; } 
   if (strstr(harm, "i")   != NULL) { return 0; } 
   if (strstr(harm, "Fr")  != NULL) { return 5; }
   if (strstr(harm, "Gn")  != NULL) { return 5; }
   if (strstr(harm, "Lt")  != NULL) { return 5; }
   if (strstr(harm, "N")   != NULL) { return 1; }
   return 0;
}



//////////////////////////////
//
// getInversion --
//

int getInversion(const char* token) {
   if (strchr(token, 'b') != NULL) { return 1; }
   if (strchr(token, 'c') != NULL) { return 2; }
   if (strchr(token, 'd') != NULL) { return 3; }
   return 0;
}



//////////////////////////////
//
// makeRootInterval --
//

int makeRootInterval(const char* harmdata, int keyroot, int keymode) {
   int slashcount = 0;
   int i;
   int output = 0;
   int length = strlen(harmdata);
   char abuffer[128] = {0};
   strcpy(abuffer, harmdata);

   for (i=0; i<length; i++) {
      if (abuffer[i] == '/') {
         slashcount++;
         abuffer[i] = '\0';
      }
   }
   int offset = 0;
   
   if (slashcount >= 1) {
      const char* ptr = strchr(harmdata, '/') + 1;
      offset = makeRootInterval(ptr, keyroot, keymode);
   } 

   if (keymode) {
      // minor mode (harmonic minor)
      if      (strstr(abuffer, "-vii") != 0) { output = E_base40_min7; }
      else if (strstr(abuffer, "vii")  != 0) { output = E_base40_maj7; }
      else if (strstr(abuffer, "-VII") != 0) { output = E_base40_min7; }
      else if (strstr(abuffer, "VII")  != 0) { output = E_base40_maj7; }
      else if (strstr(abuffer, "#vi")  != 0) { output = E_base40_maj6; }
      else if (strstr(abuffer, "vi")   != 0) { output = E_base40_min6; }
      else if (strstr(abuffer, "#VI")  != 0) { output = E_base40_maj6; }
      else if (strstr(abuffer, "VI")   != 0) { output = E_base40_min6; }
      else if (strstr(abuffer, "iv")   != 0) { output = E_base40_per4; }
      else if (strstr(abuffer, "IV")   != 0) { output = E_base40_per4; }
      else if (strstr(abuffer, "#iii") != 0) { output = E_base40_maj3; }
      else if (strstr(abuffer, "#III") != 0) { output = E_base40_maj3; }
      else if (strstr(abuffer, "iii")  != 0) { output = E_base40_min3; }
      else if (strstr(abuffer, "III")  != 0) { output = E_base40_min3; }
      else if (strstr(abuffer, "-ii")  != 0) { output = E_base40_min2; }
      else if (strstr(abuffer, "-II")  != 0) { output = E_base40_min2; }
      else if (strstr(abuffer, "ii")   != 0) { output = E_base40_maj2; }
      else if (strstr(abuffer, "II")   != 0) { output = E_base40_maj2; }
      else if (strstr(abuffer, "N")    != 0) { output = E_base40_min2; }
      else if (strstr(abuffer, "v")    != 0) { output = E_base40_per5; }
      else if (strstr(abuffer, "V")    != 0) { output = E_base40_per5; }
      else if (strstr(abuffer, "i")    != 0) { output = E_base40_per1; }
      else if (strstr(abuffer, "I")    != 0) { output = E_base40_per1; }
      else if (strstr(abuffer, "Lt")   != 0) { output = E_base40_aug4; }
      else if (strstr(abuffer, "Gn")   != 0) { output = E_base40_aug4; }
      else if (strstr(abuffer, "Fr")   != 0) { output = E_base40_aug4; }
      else { output = E_base40_rest; }
   } else {
      // major mode
      if      (strstr(abuffer, "-vii") != 0) { output = E_base40_min7; }
      else if (strstr(abuffer, "vii")  != 0) { output = E_base40_maj7; }
      else if (strstr(abuffer, "-VII") != 0) { output = E_base40_min7; }
      else if (strstr(abuffer, "VII")  != 0) { output = E_base40_maj7; }
      else if (strstr(abuffer, "-vi")  != 0) { output = E_base40_min6; }
      else if (strstr(abuffer, "vi")   != 0) { output = E_base40_maj6; }
      else if (strstr(abuffer, "-VI")  != 0) { output = E_base40_min6; }
      else if (strstr(abuffer, "VI")   != 0) { output = E_base40_maj6; }
      else if (strstr(abuffer, "iv")   != 0) { output = E_base40_per4; }
      else if (strstr(abuffer, "IV")   != 0) { output = E_base40_per4; }
      else if (strstr(abuffer, "-iii") != 0) { output = E_base40_min3; }
      else if (strstr(abuffer, "-III") != 0) { output = E_base40_min3; }
      else if (strstr(abuffer, "iii")  != 0) { output = E_base40_maj3; }
      else if (strstr(abuffer, "III")  != 0) { output = E_base40_maj3; }
      else if (strstr(abuffer, "-ii")  != 0) { output = E_base40_min2; }
      else if (strstr(abuffer, "-II")  != 0) { output = E_base40_min2; }
      else if (strstr(abuffer, "ii")   != 0) { output = E_base40_maj2; }
      else if (strstr(abuffer, "II")   != 0) { output = E_base40_maj2; }
      else if (strstr(abuffer, "N")    != 0) { output = E_base40_min2; }
      else if (strstr(abuffer, "v")    != 0) { output = E_base40_per5; }
      else if (strstr(abuffer, "V")    != 0) { output = E_base40_per5; }
      else if (strstr(abuffer, "i")    != 0) { output = E_base40_per1; }
      else if (strstr(abuffer, "I")    != 0) { output = E_base40_per1; }
      else if (strstr(abuffer, "Lt")   != 0) { output = E_base40_aug4; }
      else if (strstr(abuffer, "Gn")   != 0) { output = E_base40_aug4; }
      else if (strstr(abuffer, "Fr")   != 0) { output = E_base40_aug4; }
      else { output = E_base40_rest; }
   }

   if (output < 0) {
      return output;
   } else {
      return (output + offset) % 40;
   }
}



//////////////////////////////
//
// checkOptions -- validate and process command-line options.
//

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("a|append=b",         "append analysis data to input");
   opts.define("o|octave=i:2",       "octave to output root pitch");
   opts.define("RR|no-rhythm=b",     "don't try to do any rhythm analysis");
   opts.define("R|recip=i:-1",       "use **recip column for rhythm");
   opts.define("r|root=b",           "extract only root information");
   opts.define("b|bass|bass-line=b", "extract only bass-line information");
   opts.define("I|instrument=s",     "instrument to play music on");

   opts.define("debug=b",          "trace input parsing");   
   opts.define("author=b",         "author of the program");   
   opts.define("version=b",        "compilation information"); 
   opts.define("example=b",        "example usage"); 
   opts.define("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, July 2003" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: July 2003" << 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");
   appendQ    =  opts.getBoolean("append");
   rhythmQ    = !opts.getBoolean("no-rhythm");
   octave     =  opts.getInteger("octave");
   rootQ      =  opts.getBoolean("root");
   instrument =  opts.getString("instrument");
   recip      =  opts.getInteger("recip");
}



//////////////////////////////
//
// example -- example usage of the program
//

void example(void) {
   cout <<
   "                                                                        \n"
   << endl;
}



//////////////////////////////
//
// usage -- gives the usage statement for the program
//

void usage(const char* command) {
   cout <<
   "                                                                        \n"
   << endl;
}


// md5sum: 89e0dd0ee8fac22256dac44d0f7d9bdb harm2kern.cpp [20100505]