//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Oct 25 20:55:49 PDT 2004
// Last Modified: Mon Oct 25 20:55:53 PDT 2004
// Filename:      ...sig/examples/all/ottava.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/ottava.cpp
// Syntax:        C++; museinfo
//
// Description:   Convert ottava sections between sounding and score forms.
// 
// *8va = found in sounding score which indicates that the following
//        music is printed one octave lower than it sounds when printed.
// *8va/V = found in visual score which indicates that the following
//        music is one octave lower than actual sounding pitch.
// *8ba, *8ba/V = used for ottava basso indications.
// *15ma, *15ma/V = used for quintessima (two octaves up) indications.
// *15ba, *15ba/V = used for quintessima basso (two octaves down) indications.
//
// Signifiers cancelled by adding an "X" in front of the prevailing
// ottava marker: *X8va, *X8va/V, *X8ba, *X8ba/V, *X15ma, *X15ma/V, 
// *X15ba, *X15ba/V.
//

#include "humdrum.h"

#ifndef OLDCPP
   using namespace std;
#endif

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

#define TOSOUND   1
#define TOVISUAL -1

// function declarations
void      checkOptions(Options& opts, int argc, char* argv[]);
void      example(void);
void      usage(const char* command);
void      processFile(HumdrumFile& infile);
void      checkLineForOttavas(HumdrumFile& infile, int index, 
                              Array<int>& states);
void      printDataLine(HumdrumFile& infile, int line, 
                              Array<int>& octavestate, int direction);
void      printNoteData(HumdrumRecord& dataline, int index, 
                              int transpose);
void      printTandemInterpretation(HumdrumFile& infile, int line, 
                              int direction);

// global variables
Options   options;            // database for command-line arguments
int       direction = 0;      // used with -v (+1) and -s (-1) arguments


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

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

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

   const char* filename;
   infile.clear();
   // if no command-line arguments read data file from standard input
   int numinputs = options.getArgCount();
   if (numinputs < 1) {
      infile.read(cin);
   } else {
      filename = options.getArg(1);
      infile.read(options.getArg(1));
   }
   processFile(infile);

}

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


//////////////////////////////
//
// processFile --
//

void processFile(HumdrumFile& infile) {
   Array<int> octavestate;
   int maxtracks = infile.getMaxTracks();
   octavestate.setSize(maxtracks);
   octavestate.allowGrowth(0);
   octavestate.setAll(0);

   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].getType() == E_humrec_interpretation) {
         checkLineForOttavas(infile, i, octavestate);
      } 

      if (infile[i].getType() == E_humrec_data) {
         printDataLine(infile, i, octavestate, direction);
      } else if (infile[i].getType() == E_humrec_interpretation) {
         printTandemInterpretation(infile, i, direction);
      } else {
         cout << infile[i];
      }
      cout << "\n";
   }
}



//////////////////////////////
//
// printTandemInterpretation --
//

void printTandemInterpretation(HumdrumFile& infile, int line, int direction) {
   int i;
   for (i=0; i<infile[line].getFieldCount(); i++) {
      if (strcmp(infile[line].getExInterp(i), "**kern") == 0) {
         if (direction == TOSOUND) {

            if (strcmp(infile[line][i], "*8va/V") == 0) {
               cout << "*8va";
            } else if (strcmp(infile[line][i], "*X8va/V") == 0) {
               cout << "*X8va";
            } else if (strcmp(infile[line][i], "*8ba/V") == 0) {
               cout << "*8ba";
            } else if (strcmp(infile[line][i], "*X8ba/V") == 0) {
               cout << "*X8ba";
            } else if (strcmp(infile[line][i], "*15ma/V") == 0) {
               cout << "*15ma";
            } else if (strcmp(infile[line][i], "*X15ma/V") == 0) {
               cout << "*X15ma";
            } else if (strcmp(infile[line][i], "*15ba/V") == 0) {
               cout << "*15ba";
            } else if (strcmp(infile[line][i], "*X15ba/V") == 0) {
               cout << "*X15ba";
            } else {
               cout << infile[line][i];
            }

         } else if (direction == TOVISUAL) {

            if (strcmp(infile[line][i], "*8va") == 0) {
               cout << "*8va/V";
            } else if (strcmp(infile[line][i], "*X8va") == 0) {
               cout << "*X8va/V";
            } else if (strcmp(infile[line][i], "*8ba") == 0) {
               cout << "*8ba/V";
            } else if (strcmp(infile[line][i], "*X8ba") == 0) {
               cout << "*X8ba/V";
            } else if (strcmp(infile[line][i], "*15ma") == 0) {
               cout << "*15ma/V";
            } else if (strcmp(infile[line][i], "*X15ma") == 0) {
               cout << "*X15ma/V";
            } else if (strcmp(infile[line][i], "*15ba") == 0) {
               cout << "*15ba/V";
            } else if (strcmp(infile[line][i], "*X15ba") == 0) {
               cout << "*X15ba/V";
            } else {
               cout << infile[line][i];
            }

         }
      } else {
         cout << infile[line][i];
      }

      if (i < infile[line].getFieldCount()-1) {
         cout << "\t";
      }
   }


}



//////////////////////////////
//
// printDataLine --
//

void printDataLine(HumdrumFile& infile, int line, Array<int>& octavestate,
      int direction) {
   int i;
   int ptrack = 0;
   int transpose = 0;
   for (i=0; i<infile[line].getFieldCount(); i++) {
      if (strcmp(infile[line][i], ".") == 0) {
         cout << ".";
      } else if (strcmp(infile[line].getExInterp(i), "**kern") == 0) {
         ptrack = infile[line].getPrimaryTrack(i) - 1;
         transpose = octavestate[ptrack] * direction;
         printNoteData(infile[line], i, transpose);
      } else {
         cout << infile[line][i];
      }

      if (i < infile[line].getFieldCount() - 1) {
         cout << "\t";
      }

   }

}



//////////////////////////////
//
// printNoteData --
//

void printNoteData(HumdrumRecord& dataline, int index, int transpose) {
   if (transpose == 0) {
      cout << dataline[index];
      return;
   }
 
   int tokencount = dataline.getTokenCount(index);
   int i;
   int j;
   int pitch = 0;
   int printQ = 0;
   int slen;
   char buffer[256] = {0};
   char newpitch[32] = {0};
   for (i=0; i<tokencount; i++) {
      dataline.getToken(buffer, index, i);
      if (strchr(buffer, 'r') != NULL) {
         cout << buffer;
      } else {
         pitch = Convert::kernToBase40(buffer);
         pitch = pitch + transpose * 40;
         slen = strlen(buffer);
         printQ = 0;
         for (j=0; j<slen; j++) {
            if (toupper(buffer[j]) == 'A' || 
                toupper(buffer[j]) == 'B' || 
                toupper(buffer[j]) == 'C' || 
                toupper(buffer[j]) == 'D' || 
                toupper(buffer[j]) == 'E' || 
                toupper(buffer[j]) == 'F' || 
                toupper(buffer[j]) == 'G' || 
                buffer[j] == '#' || 
                buffer[j] == '-'  ) {
                if (printQ == 0) {
                   Convert::base40ToKern(newpitch, pitch);
                   cout << newpitch;
                   printQ = 1;
                }
             } else {
                cout << buffer[j];
             }
         }
      }

      if (i < tokencount-1)  {
         cout << ' ';
      }
   }
}



//////////////////////////////
//
// checkLineForOttavas --
//  *8va = up one octave
//  *8ba = down one octave
//  *15ma, *15va = up two octaves
//  *15ba = down two octaves
//

void checkLineForOttavas(HumdrumFile& infile, int index, Array& states) {
   int j;
   for (j=0; j<infile[index].getFieldCount(); j++) {
      if (direction == TOSOUND) {

         if (strcmp(infile[index][j], "*8va/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = +1;
         } else if (strcmp(infile[index][j], "*X8va/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*8ba/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = -1;
         } else if (strcmp(infile[index][j], "*X8ba/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*15ma/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = +2;
         } else if (strcmp(infile[index][j], "*X15ma/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*15va/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = +2;
         } else if (strcmp(infile[index][j], "*X15va/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*15ba/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = -2;
         } else if (strcmp(infile[index][j], "*X15ba/V") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         }

      } else if (direction == TOVISUAL) {

         if (strcmp(infile[index][j], "*8va") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = +1;
         } else if (strcmp(infile[index][j], "*X8va") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*8ba") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = -1;
         } else if (strcmp(infile[index][j], "*X8ba") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*15ma") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = +2;
         } else if (strcmp(infile[index][j], "*X15ma") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*15va") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = +2;
         } else if (strcmp(infile[index][j], "*X15va") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         } else if (strcmp(infile[index][j], "*15ba") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = -2;
         } else if (strcmp(infile[index][j], "*X15ba") == 0) {
            states[infile[index].getPrimaryTrack(j)-1] = 0;
         }

      }
   }


}




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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("p|print|v|visual=b", "convert to printed visual score format");
   opts.define("s|sound=b", "convert to sounding score format");
   opts.define("debug=b");           // determine bad input line num
   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, Oct 2004" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 25 Oct 2004" << endl;
      cout << "compiled: " << __DATE__ << endl;
      cout << MUSEINFO_VERSION << endl;
      exit(0);
   } else if (opts.getBoolean("help")) {
      usage(opts.getCommand());
      exit(0);
   } else if (opts.getBoolean("example")) {
      example();
      exit(0);
   }

   if (opts.getBoolean("sound")) {
      direction = +1;
   } else if (opts.getBoolean("visual")) {
      direction = -1;
   }

   if (direction == 0) {
      cout << "Error: specify -v to convert to visual score or -s ";
      cout << "for sounding score\n";
      exit(1);
   }
}



//////////////////////////////
//
// 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: c4481c6932f208dde76d06b4c1d8ae0e ottava.cpp [20110215]