//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Jun 10 21:08:22 PDT 2002
// Last Modified: Mon Jun 10 21:08:27 PDT 2002
// Last Modified: Wed Jan  1 22:45:16 PST 2003 (not finished?)
// Filename:      ...sig/examples/all/koto2midi.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/koto2midi.cpp
// Syntax:        C++; museinfo
//
// Description:   Convert **koto representation to a MIDI performance.
// 

#include "humdrum.h"
#include "MidiFile.h"

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

/*class StringEvent {
   public:
      StringEvent(void) { state = ornament = time = 0; }
      int pitch;         // MIDI note number of natural string
      int state;         // 0 = note off, 1 = note on
      int ornament;      // enumerated ornaments here
      int time;          // time of event in MIDI ticks
};

typedef Array<StringEvent> StringEventArray;


// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   example(void);
void   usage(const char* command);
void   convertKoto(HumdrumFile& infile);
void   convertKotoToMidi(HumdrumFile& infile, MidiFile& midifile, 
                              int instrument);
void   processKotoData(HumdrumRecord& line);
void   processInterpretation(HumdrumRecord& line);
void   storeTuning(const char* tunestring);
void   printRhythm(const char* string);
void   printPitch(const char* string);

// command line options:
Options    options;          // database for command-line arguments
int        debugQ = 0;       // for debugging options --debug
int        appendQ = 0;      // for use with the -a option
int        instrument = 107; // 107 = General MIDI Koto

Array<int> tuning;	   // for koto tuning


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

*/

int main(int argc, char* argv[]) {
/*   HumdrumFile infile;           // input Humdrum Format file
   MidiFile midifile;            // output MIDI file
   tuning.setSize(13);
   tuning.allowGrowth(0);

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

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

   infile.clear();
   if (numinputs < 1) {
      infile.read(cin);
   } else {
      infile.read(options.getArg(1));
   }
   convertKotoToMidi(infile, midifile);
   cout << midifile;

*/
   return 0;
}


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


/*
//////////////////////////////
//
// convertKotoToMidi -- 
//

void convertKotoToMidi(HumdrumFile& infile, MidiFile& midifile, int instrument) {
   midifile.clear();
   midifile.addTrack(13);  // 13 tracks for strings + track 0 for tempo
   midifile.absolute;
   int i;
   Array<unsigned char> data;
   data.setSize(2);
   data.setSize(2);
   data[1] = instrument;
   for (i=0; i<13; i++) {
      data[0] = 0xc0 + i;      
      track = i;
      midifile.addEvent(i, 0, data);
   }
   
   Array<StringEventArray> stringEvents;
   stringEvents.setSize(13);
   getStringEvents(stringEvents, infile);

   for (i=0; i<13; i++) {
      storeStringTrack(midifile, i, stringEvents[i]);
   }

   midifile.sort();
}



//////////////////////////////
//
// storeStringTrack --
//

void storeStringTrack(MidiFile& midifile, int track, 
      Array<StringEvent>& events) {
   Array<unsigned char> noteondata;
   Array<unsigned char> noteoffdata;
   noteondata.setSize(3);
   noteoffdata.setSize(3);
   if (track >= 9) {
      // avoiding channel 10
      noteondata[0]  = 0x90 + track + 1;
      noteoffdata[0] = 0x80 + track + 1;
   } else {
      noteondata[0]  = 0x90 + track;
      noteoffdata[0] = 0x90 + track;
   }
   noteondata[2]  = 64;
   noteoffdata[2] = 64;

   int i;
   for (i=0; i<events.getSize()-1; i++) {
      if (events[i].state == 1) {
         noteondata[1]  = events[i].pitch;
         noteoffdata[1] = events[i].pitch;
         midifile.addEvent(track+1, events[i].time, noteondata);
         midifile.addEvent(track+1, events[i+1].time, noteoffdata);
      }
   }
}



//////////////////////////////
//
// getStringEvents --
//

void getStringEvents(Array& stringEvents, MidiFile& infile) {
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      switch (infile[i].getType()) {
         case E_humrec_none:
         case E_humrec_empty:
         case E_humrec_global_comment:
         case E_humrec_bibliography:
            // cout << infile[i] << "\n";
            break;
         case E_humrec_data_comment:
            // if (appendQ) {
            //    cout << infile[i] << "\t!\n";
            // } else {
            //    cout << infile[i][0] << "\n";
            // }
            break;
         case E_humrec_data_kern_measure:
            // if (appendQ) {
            //    cout << infile[i] << "\t" << infile[i][0] << "\n";
            // } else {
            //    cout << infile[i][0] << "\n";
            // }
            break;
         case E_humrec_interpretation:
            processInterpretation(infile[i]);
            break;
         case E_humrec_data:
            processKotoData(infile[i], stringEvents);
            break;
         default:
            // cout << "!!" << infile[i] << "\n";
            break;
      }
   }

}



//////////////////////////////
//
// processInterpretation --
//

void processInterpretation(HumdrumRecord& line) {
   if (strncmp(line[0], "*MM", 2) == 0) {
      int testtempo;
      int count = sscanf(line[0], "*MM%d", &testtempo);
      if (count == 1) {
         tempo = testtempo;
      }
      return;
   }

   if (strncmp(line[0], "*tune[", 6) == 0) {
      storeTuning(line[0]);
      return;
   }

}



///////////////////////////////
//
// storeTuning --
//

void storeTuning(const char* tunestring) {
   int length = strlen(tunestring);
   int i;
   int stringnum = 0;
   Array<int> info(13);
   for (i=0; i<13; i++) {
      info[i] = -1;
   }
   for (i=6; i<length-1; i++) {
      if (tunestring[i] == ':') {
         stringnum++;
      } else {
         if (info[stringnum] < 0) {
            info[stringnum] = Convert::kernToBase40(&tunestring[i]);
         }
      }
   }

   for (i=0; i<13; i++) {
      if (info[i] > 0) {
         tuning[i] = info[i];
      }
   }

}



//////////////////////////////
//
// processKotoData --
//

void processKotoData(HumdrumRecord& line, 
      Array<StringEventArray>& stringEvents) {
   static char buffer[1024] = {0};

   if (strncmp(line[0], "-", 1) == 0) {
      return;
   }
   if (strcmp(line[0], ".", 1) == 0) {
      return;
   }

   int tokencount = line.getTokenCount(0);
   int i;
   for (i=0; i<tokencount; i++) {
      line.getToken(buffer, 0, i);
      processKotoDatum(buffer, stringEvents);
      if (i < tokencount - 1) {
         cout << " ";
      }
   }
   cout << "\n";
}


//////////////////////////////
//
// processKotoDatum --
//
   
void processKotoDatum(const char* buffer, 
      Array<StringEventArray>& stringEvents) {


}



//////////////////////////////
//
// printRhythm --
//

double getDuration(const char* string) {
   int pluscount = 0;
   int dotcount = 0;
   int pipecount = 0;

   int i;
   int length = strlen(string);
   double output = 1.0;

   // print a rhythm only if there is a string number
   int stringQ = 0;
   for (i=0; i<length; i++) {
      if (isdigit(string[i])) {
         stringQ = 1;
         break;
      } else if (string[i] == 'A' || string[i] == 'B' || string[i] ==
            'C' || string[i] == 'D' || string[i] == 'E') {
         stringQ = 1;
         break;
      }
   }
   
   if (stringQ == 0) {
      return;
   }


   for (i=0; i<length; i++) {
      if (string[i] == '+') {
         pluscount++;
      }
      if (string[i] == '|') {
         pipecount++;
      }
      if (string[i] == '.') {
         dotcount++;
      }
   }
   if (pluscount == 0 && pipecount == 0) {
      // cout << 4;
   } else if (pluscount > 0) {
      // cout << 4 / pow(2, pluscount);
   } else if (pipecount > 0) {
      // cout << 4 * pow(2, pipecount);
   } else {
      // cout << "RERROR";
   }

   for (i=0; i<dotcount; i++) {
      // cout << ".";
   }

   return output;
}



//////////////////////////////
//
// printPitch --
//

int getPitch(const char* string) {
   int i, j;
   int pvalue = 0;
   int length = strlen(string);
   int index = 0;
   char buffer[32] = {0};
   int sharpcount = 0;
   int sharpcount2 = 0;
   for (i=0; i<length; i++) {
      if (string[i] == '#') {
         sharpcount++;
      }
   }

   for (i=0; i<length; i++) {
      if (isxdigit(string[i])) {
         if (string[i] == 'a' || string[i] == 'b' || string[i] == 'c'
              || string[i] == 'd' || string[i] == 'e' || string[i] == 'f' ||
              string[i] == 'F') {
            continue;
         } else {
            if (isdigit(string[i])) {
               index = string[i] - '0' - 1;
            } else {
               index = string[i] - 'A' - 1 + 10;
            }
            if (index < 0) {
               cout << "r";
            } else {
               pvalue = tuning[index];
               Convert::base40ToKern(buffer, pvalue);
               length = strlen(buffer);
               for (j=0; j<length; j++) {
                  if (buffer[j] == '#') {
                     sharpcount2++;
                  }
               }
               if (sharpcount == 1 && sharpcount2 == 1) {
                  pvalue = pvalue - sharpcount2 + 6;
               } else if (sharpcount == 2 && sharpcount2 == 1) {
                  pvalue = pvalue - sharpcount2 + 7;
               } else if (sharpcount == 3 && sharpcount2 == 0) {
                  pvalue = pvalue + 11;
               } else if (sharpcount == 1 && sharpcount2 == 0) {
                  pvalue = pvalue + 1;
               } else if (sharpcount == 2 && sharpcount2 == 0) {
                  pvalue = pvalue + 6;
               } else if (sharpcount + sharpcount2 > 3) {
                  cout << "PERROR";
               }
// cout << ">" << sharpcount << "," << sharpcount2 << "<";
               Convert::base40ToKern(buffer, pvalue);
               cout << buffer;
            }
            return;
            
         }
      }
   } 

   cout << "x";
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("a|append=b", "append analysis");

   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("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, Dec 2001" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: Dec 2001" << 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);
   }

   appendQ = opts.getBoolean("append");
   debugQ = opts.getBoolean("debug");
}



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

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



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

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


*/


// md5sum: 29ef80a3264eaf45d0230ba8e9be4ff6 koto2midi.cpp [20050403]