//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Mon Jan  8 23:45:43 PST 2007
// Last Modified: Mon Jan  8 23:45:36 PST 2007
// Filename:      ...sig/examples/all/seqalign.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/seqalign.cpp
// Syntax:        C++; museinfo
//
// Description:   Align score beats to audio onset times.
//

#include "humdrum.h"
#include <math.h>

#include <string.h>

#ifndef OLDCPP
   using namespace std;
#else
#endif
   

// function declarations
void      checkOptions(Options& opts, int argc, char* argv[]);
void      example(void);
void      usage(const char* command);
void      extractSequence(Array<double>& hdata, HumdrumFile& hfile);
void      fillSequence(Array<int>& array, Array<int>& index, 
                              Array<double>& data, int totalsize);
int       findNearest(int index, Array<int>& beatarray, 
                              Array<int>& beatindex, Array<int>& onsetarray, 
                              Array<int>& onsetindex);

// global variables
Options   options;             // database for command-line arguments

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

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

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

   // figure out the number of input files to process
   int numinputs = options.getArgCount();
   if (numinputs != 2) {
      usage(options.getCommand());
      exit(1);
   }

   beats.read(options.getArg(1));
   onsets.read(options.getArg(2));

   Array<double> beatdata;
   Array<double> onsetdata;

   extractSequence(beatdata, beats);
   extractSequence(onsetdata, onsets);

// cout << "BEAT DATA contains " << beatdata.getSize() << " numbers" << endl;
// cout << "ONSET DATA contains " << onsetdata.getSize() << " numbers" << endl;

   Array<int> beatarray;
   Array<int> onsetarray;

   int sequencesize = 100000;

   Array<int> beatindex;
   Array<int> onsetindex;

   fillSequence(beatarray, beatindex, beatdata, sequencesize);
   fillSequence(onsetarray, onsetindex, onsetdata, sequencesize);

   int i;
   int newi;

   Array<int> mapping;
   mapping.setSize(2*beatindex.getSize());
   mapping.setSize(0);

   for (i=0; i<beatindex.getSize(); i++) {
      newi = findNearest(i, beatarray, beatindex, onsetarray, onsetindex);
      mapping.append(newi);
   }

   double difference;
   for (i=0; i<beatindex.getSize(); i++) {
      difference = beatdata[i] - onsetdata[mapping[i]];
      cout << i << " to " << mapping[i] << "\t" 
           << beatdata[i]  << " - " << onsetdata[mapping[i]] << " = "
           << difference << endl;
   }


   return 0;
}


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



//////////////////////////////
//
// findNearest --
//

int findNearest(int index, Array<int>& beatarray, Array<int>& beatindex, 
      Array<int>& onsetarray, Array<int>& onsetindex) {

   int correction = onsetarray[beatindex[index]];
   if (correction > 0) {
      correction = 0;
   } else if (correction > -1000) {
      correction = correction;
   } else {
      correction = -correction / 1000;
   }

   return onsetarray[correction + beatindex[index]];
}



//////////////////////////////
//
// fillSequence -- make a literal representation of the sequence
//

#define OFFSET 100000

void fillSequence(Array<int>& array, Array<int>& index, 
   Array<double>& data, int totalsize) {

   double minval = data[0];
   double maxval = data[data.getSize()-1];
   int indexmap;

   array.setSize(totalsize);
   array.allowGrowth(0);

   int i;
   for (i=0; i<array.getSize(); i++) {
      array[i] = -1;
   }

   index.setSize(data.getSize());

   for (i=0; i<data.getSize(); i++) {
      indexmap = (int)((data[i]-minval)/(maxval-minval)*totalsize + 0.5);
      if (indexmap > array.getSize()-1) { indexmap = array.getSize()-1; }
      if (indexmap < 0)                 { indexmap = 0; }
      index[i] = indexmap;
      array[indexmap] = i;
   }

   // mark the closest event in the long array
   int counter = 0;
   int j;
   int found = 0;
   for (i=0; i<array.getSize(); i++) {
      if (array[i] > 0) {
         if (found == 0) {
            counter = 0;
            for (j=i-1; j>=0; j--) {
               counter++;
               array[j] = -counter * 1000;
            }
         }
         found = 1;
         counter = 0;
         continue;
      }
      if (array[i] < 0 && !found) {
         continue;
      }

      counter++;
      array[i] = -counter;
   }

   int rfound = 0;
   counter = 0;
   int distance;
   for (i=array.getSize()-1; i>=0; i--) {
      if (array[i] > 0) {
         rfound = 1;
         counter = 0;
         continue;
      }
      if (array[i] < 0 && !rfound) {
         continue;
      }
      
      counter++;
      distance = -array[i];
      if (distance > counter) {
         array[i] = -counter * 1000;
      }

   }

}




//////////////////////////////
//
// extractSequence --
//

void extractSequence(Array& hdata, HumdrumFile& hfile) {
   int i;
   hdata.setSize(hfile.getNumLines());
   hdata.setSize(0);
   double value;
   int count;

   for (i=0; i<hfile.getNumLines(); i++) {
      if (hfile[i].getType() != E_humrec_data) {
         continue;
      }
      if (strcmp(hfile[i][0], ".") == 0) {
         continue;
      }
      count = sscanf(hfile[i][0], "%lf", &value);
      if (count == 1) {
         hdata.append(value);
      }
   }

}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   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 2000" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 28 May 2002" << 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);
   }

}
  


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

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



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

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



// md5sum: a0ebffb4f13c943a8f40f62cbac85e77 seqalign.cpp [20070512]