Goto: [ Program Documentation ]

//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Sun Oct 22 15:33:41 PDT 2000
// Last Modified: Sun May 26 19:39:01 PDT 2002 (mostly finished)
// Last Modified: Tue Mar 16 05:53:19 PST 2010 (added *M meter description)
// Last Modified: Wed Apr 21 14:31:44 PDT 2010 (added search feature)
// Last Modified: Wed May 19 15:30:49 PDT 2010 (added tick & rational values)
// Filename:      ...sig/examples/all/beat.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/humdrum/beat.cpp
// Syntax:        C++; museinfo
//
// Description:   Generates metrical location data for **kern entries
//                Test functions for the built-in rhythmic analysis
//                in the HumdrumFile class.  Should give the same
//                output as the beat program.
//
// There are two cases when an incomplete measure needs to
//    be counted backwards.  These cases will be handled by
//    the beat program:
//    (1) an initial pickup beat
//    (2) a repeat sign breaks a true measure
//
// There is a bug that needs fixing:
//   The *M2/2 interpretations are erased (at least with the -s option)
//
// five types of outputs can be given:
//   -s   = sum the number of beats in a measure
//        = display the beat (default if no other output type given)
//   -d   = duration
//   -c   = cumulative running total beat/duration
//

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

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

#ifndef OLDCPP
   #include <sstream>
   #define SSTREAM stringstream
   #define CSTRING str().c_str()
   using namespace std;
#else
   #ifdef VISUAL
      #include <strstrea.h>     /* for windows 95 */
   #else
      #include <strstream.h>
   #endif
   #define SSTREAM strstream
   #define CSTRING str()
#endif
   

// function declarations
void      checkOptions(Options& opts, int argc, char* argv[]);
void      example(void);
void      usage(const char* command);
void      printOutput(HumdrumFile& file, 
		              Array<RationalNumber>& Bfeatures, 
                              Array<int>& Blines, 
			      Array<RationalNumber>& Dfeatures, 
			      Array<int>& Dlines, Array<int>& tickanalysis);
RationalNumber getPickupDuration(HumdrumFile& file);
void      fillSearchString(Array<double>& searcher, const char* string);
void      printSearchResults(HumdrumFile& infile, 
		              Array<RationalNumber>& Bfeatures,
                              Array<int>& Blines, 
			      Array<RationalNumber>& Dfeatures,
                              Array<int>& Dlines);
void      printSearchResultsFinal(Array<int>& linematch, HumdrumFile& infile,
                              Array<RationalNumber>& Bfeatures, 
			      Array<int>& Blines, 
                              Array<RationalNumber>& Dfeatures, 
			      Array<int>& Dloines);
void      doBeatSearch(Array<int>& results, HumdrumFile& infile, 
                              Array<double> search, 
			      Array<RationalNumber>& Bfeatures,
                              Array<int>& Blines);
void      doDurSearch(Array<int>& results, HumdrumFile& infile, 
                              Array<double> search, 
			      Array<RationalNumber>& Dfeatures,
                              Array<int>& Dlines);
void      doDurSearch(Array<int>& results, HumdrumFile& infile, 
                              Array<double> search, Array<double>& Dfeatures, 
			      Array<int>& Dlines);
void      mergeResults(Array<int>& output, Array<int>& input1, 
                              Array<int>& input2);
void      printSequence(Array<double>& pattern);
void      printSequence(Array<RationalNumber>& pattern);
void      fillMeasureInfo(HumdrumFile& infile, Array<double>& measures);
void      doComparison(Array<int>& results, Array<int>& line, 
                              Array<double>& search, Array<double>& data,
                              HumdrumFile& infile);
int       checkForWildcard(Array<double>& sequence);
void      extractBeatFeatures(HumdrumFile& infile, Array<int>& line,
                              Array<RationalNumber>& data);
void      extractDurFeatures(HumdrumFile& infile, Array<int>& line,
                              Array<RationalNumber>& data);
void      printSequence(Array<double>& features, Array<int>& lines, 
                              Array<double>& search, int startline);
void      printSequence(Array<RationalNumber>& features, 
		              Array<int>& lines, Array<RationalNumber>& search,
			      int startline);
void      printSequence(Array<RationalNumber>& features, 
		              Array<int>& lines, Array<double>& search, 
			      int startline);
void      printMatchesWithData(Array<int>& linematch, HumdrumFile& infile);
void      fillAttackArray(HumdrumFile& infile, Array<int>& attacks);
int       getCountForLine(HumdrumFile& infile, int line);
int       doTickAnalysis(Array<int>& tickanalysis, HumdrumFile& infile);
RationalNumber getDurationOfFirstMeasure(HumdrumFile& file);

// global variables
Options   options;             // database for command-line arguments
int       appendQ  = 0;        // used with -a option
int       prependQ = 0;        // used with -p option
int       durQ     = 0;        // used with -d option
int       absQ     = 0;        // used with -t option
int       beatQ    = 0;        // used with -b option
int       sumQ     = 0;        // used with -s option
int       zeroQ    = 0;        // zero offset instead of 1 for first beat
int       nullQ    = 0;        // used with -n option
Array<double> Bsearch;         // used with -B option
Array<double> Dsearch;         // used with -D option
double    Rvalue   = -1.0;     // used with -R option
double    Tolerance = 0.001;   // used for rounding
int       Attack   = 1;        // used with -A option
Array<int> Attacks;            // used with -A option
int       tickQ    = 0;        // used with -t option
int       rationalQ= 0;        // used with -r option
int       tpwQ     = 0;        // used with --tpw option
int       tpqQ     = 0;        // used with --tpq option
const char* beatbase = "4";    // used with --beatsize option
int       uQ       = 0;        // used for -f and -u interactions

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

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

   Array<RationalNumber> Bfeatures; // used to extract beat data from input
   Array<RationalNumber> Dfeatures; // used to extract duration data from input
   Array<int>    Blines;            // used to extract beat data from input
   Array<int>    Dlines;            // used to extract duration data from input

   // 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));
      }
      // analyze the input file according to command-line options
      infile.analyzeRhythm(beatbase);

      Array<int> tickanalysis;
      tickanalysis.setSize(infile.getNumLines());
      tickanalysis.setAll(0);
      int tickfactor = 1;

      if (tickQ) {
         tickfactor = doTickAnalysis(tickanalysis, infile);
      }

      if (tpwQ) {
         cout << infile.getMinTimeBase() * tickfactor << endl;
         exit(0);
      } else if (tpqQ) {
         cout << infile.getMinTimeBase() * tickfactor /4.0 << endl;
         exit(0);
      }

      fillAttackArray(infile, Attacks);
      extractBeatFeatures(infile, Blines, Bfeatures);
      extractDurFeatures(infile, Dlines, Dfeatures);

      if (Bsearch.getSize() > 0 || Dsearch.getSize() > 0) {
         printSearchResults(infile, Bfeatures, Blines, Dfeatures, Dlines);
      } else {
         printOutput(infile, Bfeatures, Blines, Dfeatures, Dlines, 
               tickanalysis);
      }
   }

   return 0;
}


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



//////////////////////////////
//
// doTickAnalysis --
//

int doTickAnalysis(Array& tickanalysis, HumdrumFile& infile) {
   int i;
   tickanalysis.setSize(infile.getNumLines());

   Array<RationalNumber> pretick(tickanalysis.getSize());

   int minrhy = infile.getMinTimeBase();
   if (minrhy <= 0.0) {
      return 1;
   }
   RationalNumber value;
   int monitor = 0;
   for (i=0; i<infile.getNumLines()-1; i++) {
      value = ((infile[i+1].getAbsBeatR()-infile[i].getAbsBeatR())/4)*minrhy;
      pretick[i] = value;
      if (value.getDenominator() != 1) {
         monitor = 1;
      }
   }

   if (monitor == 0) {
      for (i=0; i<pretick.getSize(); i++) {
         tickanalysis[i] = pretick[i].getNumerator();
      }
      return 1;
   }

   for (i=0; i<pretick.getSize(); i++) {
      // estimate a multiplication of 4 to remove fractional part.
      tickanalysis[i] = pretick[i].getNumerator() * 4;
   }

   return 4;
}



//////////////////////////////
//
// fillAttackArray --
//

void fillAttackArray(HumdrumFile& infile, Array& attacks) {
   int i;
   attacks.setSize(infile.getNumLines());
   attacks.allowGrowth(0);
   attacks.setAll(0);

   if (Attack <= 0) {
      // don't need to waste time analyzing the attack structure of the data...
      return;
   }

   int count;
   for (i=0; i<infile.getNumLines(); i++) {
      count = 0;
      if (infile[i].isData()) {
         count = getCountForLine(infile, i);
      }
      attacks[i] = count;
   }
}



//////////////////////////////
//
// getCountForLine -- return the number of note attacks on the given
//   line in the Humdrum File.  Only **kern data is examined.  Rests
//   and tied notes are ignored when counting attacks.
//

int getCountForLine(HumdrumFile& infile, int line) {
   int j, k;
   char buffer[128] = {0};
   int output = 0;
   int tcount;
   for (j=0; j<infile[line].getFieldCount(); j++) {
      if (!infile[line].isExInterp(j, "**kern")) {
         continue;
      }
      tcount = infile[line].getTokenCount(j);
      for (k=0; k<tcount; k++) {
         infile[line].getToken(buffer, j, k);
         if (strchr(buffer, 'r') != NULL) {
            // ignore rests
            continue;
         }
         if (strcmp(buffer, ".") == 0) {
            // ignore null tokens
            continue;
         }
         if (strchr(buffer, '_') != NULL) {
            // ignore notes which are in the middle of a series of tied notes
            continue;
         }
         if (strchr(buffer, ']') != NULL) {
            // ignore notes which are at the end of a series of tied notes
            continue;
         }
         output++;
      }
   }

   return output;
}



//////////////////////////////
//
// printSearchResults -- Search for beat and/or duration patterns
//   in the composition rhythmic data 
//

void printSearchResults(HumdrumFile& infile, Array<RationalNumber>& Bfeatures,
      Array<int>& Blines, Array<RationalNumber>& Dfeatures, 
      Array<int>& Dlines) {
   Array<int> Bresults;
   Array<int> Dresults;
   Bresults.setSize(100000);
   Bresults.setGrowth(100000);
   Bresults.setSize(0);
   Dresults.setSize(100000);
   Dresults.setGrowth(100000);
   Dresults.setSize(0);

   if ((Bsearch.getSize() > 0) && (Dsearch.getSize() > 0)) {
      doBeatSearch(Bresults, infile, Bsearch, Bfeatures, Blines);
      doDurSearch(Dresults, infile, Dsearch, Dfeatures, Dlines);
      Array<int> finalresults;
      mergeResults(finalresults, Bresults, Dresults);

      cout << "!!parallel beat search: ";
      printSequence(Bsearch);
      cout << endl;
      cout << "!!parallel duration search: ";
      printSequence(Dsearch);
      cout << endl;

      printSearchResultsFinal(finalresults, infile, Bfeatures, Blines,
            Dfeatures, Dlines);
   } else if (Bsearch.getSize() > 0) {
      doBeatSearch(Bresults, infile, Bsearch, Bfeatures, Blines);
      cout << "!!beat search: ";
      printSequence(Bsearch);
      cout << endl;
      printSearchResultsFinal(Bresults, infile, Bfeatures, Blines,
            Dfeatures, Dlines);
   } else if (Dsearch.getSize() > 0) {
      doDurSearch(Dresults, infile, Dsearch, Dfeatures, Dlines);
      cout << "!!duration search: ";
      printSequence(Dsearch);
      cout << endl;
      printSearchResultsFinal(Dresults, infile, Bfeatures, Blines,
            Dfeatures, Dlines);
   } else {
      cout << "ERROR in search" << endl;
   }

}



//////////////////////////////
//
// printSequence --
//

void printSequence(Array& pattern) {
   int i;
   for (i=0; i<pattern.getSize(); i++) {
      if (pattern[i] < 0) {
         cout << "*";
      } else {
         cout << pattern[i];
      }
      if (i < pattern.getSize()-1) {
         cout << ' ';
      }
   }
}

void printSequence(Array& pattern) {
   int i;
   for (i=0; i<pattern.getSize(); i++) {
      if (pattern[i] < 0) {
         cout << "*";
      } else {
         cout << pattern[i];
      }
      if (i < pattern.getSize()-1) {
         cout << ' ';
      }
   }
}



//////////////////////////////
//
// checkForWildcard -- returns true if any of the values are negative.
//

int checkForWildcard(Array& sequence) {
   int i;
   for (i=0; i<sequence.getSize(); i++) {
      if (sequence[i] < 0.0) {
         return 1;
      }
   }
   return 0;
}



//////////////////////////////
//
// printMatchesWithData --
//

void printMatchesWithData(Array& linematch, HumdrumFile& infile) {
   int i;
   int counter = 1;


   Array<int> lookup;
   lookup.setSize(infile.getNumLines());
   lookup.allowGrowth(0);
   lookup.setAll(-1);

   for (i=0; i<linematch.getSize(); i++) {
      lookup[linematch[i]] = counter++;
   }

   for (int i=0; i<infile.getNumLines(); i++) {
      switch (infile[i].getType()) {
         /* case E_humrec_data_comment: break; */

         case E_humrec_data_kern_measure:
            if (prependQ) {
               cout << infile[i][0] << "\t";
               cout << infile[i] << "\n";
            } else if (appendQ) {
               cout << infile[i] << "\t";
               cout << infile[i][0] << "\n";
            } else {
               cout << infile[i][0] << "\n";
            }
            break;

         case E_humrec_interpretation:
            if (appendQ) {
               cout << infile[i] << "\t";
            } 

            if (strncmp(infile[i][0], "**", 2) == 0) {
               cout << "**match";
            } else if (strcmp(infile[i][0], "*-") == 0) {
               cout << "*-";
            } else {
               cout << "*";
            }

            if (prependQ) {
               cout << "\t" << infile[i];
            }
            cout << "\n";

            break;

         case E_humrec_data:
            if (appendQ) {
               cout << infile[i] << "\t";
            } 
            if (lookup[i] < 0) {
               cout << ".";
            } else {
               cout << lookup[i];
            }

            if (prependQ) {
               cout << "\t" << infile[i];
            }

            cout << "\n";

            break;
         case E_humrec_local_comment:
            if (appendQ) {
               cout << infile[i] << "\t";
            }
            cout << "!";
            if (prependQ) {
               cout << "\t" << infile[i];
            }
            cout << "\n";

            break;
         case E_humrec_none:
         case E_humrec_empty:
         case E_humrec_global_comment:
         case E_humrec_bibliography:
         default:
            cout << infile[i] << "\n";
            break;
      }
   }
}



//////////////////////////////
//
// printSearchResultsFinal --
//

void printSearchResultsFinal(Array<int>& linematch, HumdrumFile& infile,
      Array<RationalNumber>& Bfeatures, Array<int>& Blines, 
      Array<RationalNumber>& Dfeatures, Array<int>& Dlines) {
   cout << "!!matches: " << linematch.getSize() << "\n";

   if (appendQ || prependQ) {
      printMatchesWithData(linematch, infile);
      return;
   }

   Array<double> measures;
   fillMeasureInfo(infile, measures);


   int hasBWildcard = checkForWildcard(Bsearch);
   int hasDWildcard = checkForWildcard(Dsearch);
	 
   int i;
   cout << "**line\t**bar\t**beat\t**absb";

   if (hasBWildcard) {
      cout << "\t**bseq";
   }
   if (hasDWildcard) {
      cout << "\t**dseq";
   }

   cout << "\n";
   for (i=0; i<linematch.getSize(); i++) {
      cout << linematch[i]-1;
      cout << "\t" << measures[linematch[i]];
      if (zeroQ) {
         cout << "\t" << infile[linematch[i]].getBeat()-1;
      } else {
         cout << "\t" << infile[linematch[i]].getBeat();
      }
      cout << "\t" << infile[linematch[i]].getAbsBeat();
      if (hasBWildcard) {
         cout << "\t";
         printSequence(Bfeatures, Blines, Bsearch, linematch[i]);	       
      }
      if (hasDWildcard) {
         cout << "\t";
         printSequence(Dfeatures, Dlines, Dsearch, linematch[i]);	       
      }
      cout << endl;
   }
   cout << "*-\t*-\t*-\t*-";
   if (hasBWildcard) {
      cout << "\t*-";
   }
   if (hasDWildcard) {
      cout << "\t*-";
   }
   cout << "\n";
}



///////////////////////////////
//
// printSequence --
// 

void printSequence(Array<double>& features, Array<int>& lines, 
     Array<double>& search, int startline) {

   int index = -1;
   int i;
   for (i=0; i<lines.getSize(); i++) {
      if (lines[i] == startline) {
         index = i;
         break;
      }
   }

   if (index < 0) {
      cout << ".";
      return;
   }

   int stopindex = index + search.getSize() - 1;
   for (i=index; (i<features.getSize()) && (i<=stopindex); i++) {
      cout << features[i];
      if (i < stopindex) {
         cout << " ";
      }
   }
}



void printSequence(Array<RationalNumber>& features, Array<int>& lines, 
     Array<double>& search, int startline) {

   int index = -1;
   int i;
   for (i=0; i<lines.getSize(); i++) {
      if (lines[i] == startline) {
         index = i;
         break;
      }
   }

   if (index < 0) {
      cout << ".";
      return;
   }

   int stopindex = index + search.getSize() - 1;
   for (i=index; (i<features.getSize()) && (i<=stopindex); i++) {
      cout << features[i];
      if (i < stopindex) {
         cout << " ";
      }
   }
}



/*void printSequence(Array<double>& features, Array<int>& lines, 
     Array<double>& search, int startline) {

   int index = -1;
   int i;
   for (i=0; i<lines.getSize(); i++) {
      if (lines[i] == startline) {
         index = i;
         break;
      }
   }

   if (index < 0) {
      cout << ".";
      return;
   }

   int stopindex = index + search.getSize() - 1;
   for (i=index; (i<features.getSize()) && (i<=stopindex); i++) {
      cout << features[i];
      if (i < stopindex) {
         cout << " ";
      }
   }
}

*/

/* 
void printSequence(Array<double>& features, Array<int>& lines, 
     Array<double>& search, int startline) {

   int index = -1;
   int i;
   for (i=0; i<lines.getSize(); i++) {
      if (lines[i] == startline) {
         index = i;
         break;
      }
   }

   if (index < 0) {
      cout << ".";
      return;
   }

   int stopindex = index + search.getSize() - 1;
   for (i=index; (i<features.getSize()) && (i<=stopindex); i++) {
      cout << features[i];
      if (i < stopindex) {
         cout << " ";
      }
   }
}

*/



void printSequence(Array<RationalNumber>& features, Array<int>& lines, 
     Array<RationalNumber>& search, int startline) {

   int index = -1;
   int i;
   for (i=0; i<lines.getSize(); i++) {
      if (lines[i] == startline) {
         index = i;
         break;
      }
   }

   if (index < 0) {
      cout << ".";
      return;
   }

   int stopindex = index + search.getSize() - 1;
   for (i=index; (i<features.getSize()) && (i<=stopindex); i++) {
      cout << features[i];
      if (i < stopindex) {
         cout << " ";
      }
   }
}



//////////////////////////////
//
// fillMeasureInfo --
//

void fillMeasureInfo(HumdrumFile& infile, Array& measures) {
   int i;
   measures.setSize(infile.getNumLines());
   measures.allowGrowth(0);
   measures.setAll(0.0);

   double current = 0.0;
   for (i=0; i<infile.getNumLines(); i++) {
      if (infile[i].isMeasure()) {
         sscanf(infile[i][0], "=%lf", ¤t);
      }
      measures[i] = current;
   }
}


//////////////////////////////
//
// mergeResults -- do an intersection of two lists of sorted integers
//

void mergeResults(Array& output, Array& input1, Array& input2) {
   int i, j;
   int maxsize = input1.getSize();
   if (input2.getSize() < maxsize) {
      maxsize = input2.getSize();
   }
   output.setSize(maxsize);
   output.setSize(0);
   if (maxsize == 0) {
      return;
   }
   int similar;
   j=0;
   for (i=0; i<input1.getSize(); i++) {
      while ((j<input2.getSize()) && (input2[j] < input1[i])) {
         j++;
      }
      if (j >= input2.getSize()) {
         break;
      }
      if (input2[j] == input1[i]) {
         similar = input2[j];
         output.append(similar);
      }
   }
}



//////////////////////////////
//
// doBeatSearch -- search for specific beat pattern in data.
//

void doBeatSearch(Array<int>& results, HumdrumFile& infile, 
      Array<double> search, Array<RationalNumber>& Bfeatures, 
      Array<int>& Blines) {
   // extractBeatFeatures(infile, Blines, Bfeatures);
   Array<double> doubleBfeatures;
   doubleBfeatures.setSize(Bfeatures.getSize());
   int i;
   for (i=0; i<doubleBfeatures.getSize(); i++) {
      doubleBfeatures[i] = Bfeatures[i].getFloat();
   }
   doComparison(results, Blines, search, doubleBfeatures, infile);
}



//////////////////////////////
//
// extractBeatFeatures --
//

void extractBeatFeatures(HumdrumFile& infile, Array<int>& line,
      Array<RationalNumber>& data) {
   line.setSize(infile.getNumLines());
   line.setSize(0);
   data.setSize(infile.getNumLines());
   data.setSize(0);

   int lval;
   RationalNumber bval;
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      if (!infile[i].isData()) {
         continue;
      }
      bval = infile[i].getBeatR();
      lval = i;
      if (Attacks[i] < Attack) {
         // ignore lines which do not have enough note onsets
         continue;
      }
      line.append(lval);
      data.append(bval);
   }
}



//////////////////////////////
//
// extractDurFeatures --
//

void extractDurFeatures(HumdrumFile& infile, Array<int>& line,
      Array<RationalNumber>& data) {
   line.setSize(infile.getNumLines());
   line.setSize(0);
   data.setSize(infile.getNumLines());
   data.setSize(0);

   int lval;
   RationalNumber bval;
   int i;
   for (i=0; i<infile.getNumLines(); i++) {
      if (!infile[i].isData()) {
         continue;
      }
      bval = infile[i].getDurationR();
      if (zeroQ) {
         bval = bval - 1;
      }
      lval = i;
      if (Attacks[i] < Attack) {
         // ignore lines which do not have enough note onsets,
	 // adding duration of current line to last data line
	 // which exceeds the onset threshold test.
	 if (data.getSize() > 0) {
	    data[data.getSize()-1] += bval;
	 }
         continue;
      }
      line.append(lval);
      data.append(bval);
   }
}



//////////////////////////////
//
// doComparison --
//

void doComparison(Array<int>& results, Array<int>& line, Array<double>& search,
     Array<double>& data, HumdrumFile& infile) {
   results.setSize(data.getSize() - search.getSize() + 1);
   results.setSize(0);

   double startdur;
   double stopdur;

   int match;
   int i, j;
   for (i=0; i<data.getSize() - search.getSize() + 1; i++) {
      match = 1;
      for (j=0; j<search.getSize(); j++) {
         if (search[j] < 0) {
            // for wildcard match (*)
            continue;
         }
         if (fabs(search[j] - data[i+j]) > Tolerance) {
            match = 0;
            break;
         }
      }
      if (match) {
         if (Rvalue > 0) {
            startdur = infile[line[i]].getAbsBeat();
            stopdur  = infile[line[i+search.getSize()-1]].getAbsBeat();
            if (fabs(Rvalue - (stopdur-startdur)) < Tolerance) {
               results.append(line[i]);
            }
         } else {
            results.append(line[i]);
         }
      }
   }
}



//////////////////////////////
//
// doDurSearch -- search for specific beat pattern in data.
//

void doDurSearch(Array<int>& results, HumdrumFile& infile, 
      Array<double> search, Array<double>& Dfeatures, Array<int>& Dlines) {
   // extractDurFeatures(infile, Dlines, Dfeatures);
   doComparison(results, Dlines, search, Dfeatures, infile);
}

void doDurSearch(Array<int>& results, HumdrumFile& infile, 
      Array<double> search, Array<RationalNumber>& Dfeatures, 
      Array<int>& Dlines) {
   int i;
   Array<double> doubleDfeatures(Dfeatures.getSize());
   for (i=0; i<doubleDfeatures.getSize(); i++) {
      doubleDfeatures[i] = Dfeatures[i].getFloat();
   }

   doComparison(results, Dlines, search, doubleDfeatures, infile);
}



//////////////////////////////
//
// getPickupDuration --
//

RationalNumber getPickupDuration(HumdrumFile& file) {
   int i;
   for (i=0; i<file.getNumLines(); i++) {
      if (!file[i].isMeasure()) {
         continue;
      }
      return file[i].getAbsBeatR();
   }

   RationalNumber nothing(-1,1);
   return nothing;
}



//////////////////////////////
//
// printOutput --
//

void printOutput(HumdrumFile& file, Array<RationalNumber>& Bfeatures, 
      Array<int>& Blines, Array<RationalNumber>& Dfeatures, Array<int>& Dlines,
      Array<int>& tickanalysis) {
   int lastmeasureline = -1;
   int pickupstate = 0;
   int suppressreturn = 0;
   int i;

   Array<unsigned long> abstick;
   if (tickQ) {
      unsigned long csum = 0;
      abstick.setSize(tickanalysis.getSize());
      abstick.setAll(0);
      for (i=0; i<tickanalysis.getSize(); i++) {
         abstick[i] = csum;
         csum += tickanalysis[i];
      }
   }

   RationalNumber minrhy(file.getMinTimeBase(), 4);
   RationalNumber rat;
   Array<RationalNumber> Binfo;
   Array<RationalNumber> Dinfo;
   Binfo.setSize(file.getNumLines());
   Binfo.allowGrowth(0);
   Binfo.setAll(-1);
   Dinfo.setSize(file.getNumLines());
   Dinfo.allowGrowth(0);
   Dinfo.setAll(-1);
   int measurecount = 0;

   for (i=0; i<Blines.getSize(); i++) {
      Binfo[Blines[i]] = Bfeatures[i];
      if (Binfo[Blines[i]] == file[Blines[i]].getAbsBeatR()) { 
         Binfo[Blines[i]]++;
         Binfo[Blines[i]] -= file.getPickupDurationR();
      }
      if (zeroQ) {
         Binfo[Blines[i]]--;
      }
   }
   for (i=0; i<Dlines.getSize(); i++) {
      Dinfo[Dlines[i]] = Dfeatures[i];
   }

   for (i=0; i<file.getNumLines(); i++) {
      switch (file[i].getType()) {
         /*case E_humrec_data_comment:            if (appendQ) {
               cout << file[i] << "\t" << "!" << "\n";
            }  else if (prependQ) {
               cout << "!\t" << file[i] << "\n";
            } else {
               cout << file[i] << "\n";
            }
            break;
	    */

         case E_humrec_data_kern_measure:
            if (prependQ) {
               cout << file[i][0] << "\t";
               cout << file[i] << "\n";
            } else if (appendQ) {
               cout << file[i] << "\t";
               cout << file[i][0] << "\n";
            } else {
               cout << file[i][0] << "\n";
            }
            lastmeasureline = i;
	    measurecount++;
            break;

         case E_humrec_interpretation:
            if (appendQ) {
               cout << file[i] << "\t";
            } 

            if (strncmp(file[i][0], "**", 2) == 0) {
               if (absQ && !tickQ) {
                  cout << "**absb";
	       } else if (absQ && tickQ && !rationalQ) {
                  cout << "**atick";
	       } else if (absQ && tickQ && rationalQ) {
                  cout << "**adur";
               } else if (tickQ && durQ && !rationalQ) {
                  cout << "**dtick";
               } else if (tickQ && durQ && rationalQ) {
                  cout << "**dur";
               } else if (durQ && !tickQ) {
                  cout << "**dur";
               } else {
                  cout << "**beat";
               }
            } else if (strcmp(file[i][0], "*-") == 0) {
               cout << "*-";
            } else {
               if ((strncmp(file[i][0], "*M", 2) == 0) && 
                  (strchr(file[i][0], '/') != NULL)) {
	          cout << file[i][0];	     
               } else if (strncmp(file[i][0], "*MM", 3) == 0) {
	          cout << file[i][0];	     
               } else if (appendQ || prependQ) {
                  cout << "*";
               } else {
                  cout << "*";
               }
            }

            if (prependQ) {
               cout << "\t" << file[i];
            }
            cout << "\n";

            break;

         case E_humrec_data:
            if (appendQ) {
               cout << file[i] << "\t";
            } 
            if (file[i][0][0] == '=') {
               pickupstate++;
            }

            if (durQ) {
               // cout << file[i].getDuration();
               if (Dinfo[i] >= 0) {
                  if (tickQ && !rationalQ) {
                     cout << tickanalysis[i];
                  } else if (tickQ && rationalQ) {
                     rat.setValue(tickanalysis[i], file.getMinTimeBase());
		     if (uQ) {
                        rat *= 4;
                     }
                     cout << rat << " = " 
			     << tickanalysis[i] << " x "
			     << file.getMinTimeBase();
                  } else {
                     cout << Dinfo[i].getFloat();
                  }
               } else {
                  if (nullQ || appendQ || prependQ) {
                     cout << ".";
                  } else {
                     suppressreturn = 1;
                  }
               }
            } else if (absQ) {
               if (tickQ && !rationalQ) {
                  cout << abstick[i];
	       } else if (tickQ && rationalQ) {
                  RationalNumber anumber(abstick[i], file.getMinTimeBase());
		  if (uQ) {
                     anumber *= 4;
                  }
                  anumber.printTwoPart(cout);
               } else {
                  cout << file[i].getAbsBeat();
               }
            } else if (sumQ) {
               if (lastmeasureline > 0) {
                  cout << fabs(file[lastmeasureline].getBeat());
                  pickupstate++;
                  lastmeasureline = -1;
               } else if (pickupstate < 1) {
                  if (!file.getPickupDurationR().isNegative()) {
                     if (measurecount == 0) {
                        cout << getDurationOfFirstMeasure(file).getFloat();
                     } else {
                        cout << file.getPickupDuration();
                     }
                  } else if (file.getPickupDurationR().isZero()) {
                     cout << file.getTotalDurationR().getFloat();
                  } else {
                     cout << file.getTotalDurationR().getFloat();
                  }
                  pickupstate++;
                  lastmeasureline = -1;
               } else {
                  if (appendQ || prependQ) {
                     cout << ".";
                  } else {
                     if (nullQ) {
                        cout << ".";
                     } else {
                        suppressreturn = 1;
                     }
                  }
               }
            } else if (beatQ) {
               if (Binfo[i] >= 0) {
                  if (!tickQ && !rationalQ) {
                     cout << Binfo[i].getFloat();
		  } else if (tickQ && !rationalQ) {
                     cout << (Binfo[i] * minrhy);
                  } else {
                     Binfo[i].printTwoPart(cout); 
                  }
               } else {
                  if (nullQ || appendQ || prependQ) {
                     cout << ".";
                  } else {
                     suppressreturn = 1;
                  }
               }
            }
            
            if (prependQ) {
               cout << "\t" << file[i];
            }

            if (suppressreturn) {
               suppressreturn = 0;
            } else {
               cout << "\n";
            }

            break;
         case E_humrec_local_comment:
            if (appendQ) {
               cout << file[i] << "\t";
            }
            cout << "!";
            if (prependQ) {
               cout << "\t" << file[i];
            }
            cout << "\n";

            break;
         case E_humrec_none:
         case E_humrec_empty:
         case E_humrec_global_comment:
         case E_humrec_bibliography:
         default:
            cout << file[i] << "\n";
            break;
      }
   }

}



//////////////////////////////
//
// getDurationOfFirstMeasure --
//


RationalNumber getDurationOfFirstMeasure(HumdrumFile& file) {
   int i;
   RationalNumber output(0,1);
   for (i=0; i<file.getNumLines(); i++) {
      if (file[i].getType() == E_humrec_data_measure) {
         break;
      }
      if (file[i].getType() != E_humrec_data) {
         continue;
      }
      output += file[i].getDurationR();
   }
   return output;
}



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

void checkOptions(Options& opts, int argc, char* argv[]) {
   opts.define("a|append=b");           // append analysis to input
   opts.define("p|prepend=b");          // prepend analysis to input
   opts.define("base|timebase=s:");     // rhythmic unit of one beat
   opts.define("b|beat=b");             // display beat of note in measure
   opts.define("c|abs|total-beat=b");   // cumulative, absolute beat location
   opts.define("d|dur|duration=b");     // display rhymic duration of records
   opts.define("s|sum=b");              // sum the duration of each measure
   opts.define("z|zero|zero-offset=b"); // first beat is represented by a 0
   opts.define("n|nulls|keep-nulls=b"); // doesn't remove nulls with -s option
   opts.define("B=s");                  // Do a composite beat search
   opts.define("D=s");                  // Do a composite duration search
   opts.define("R=d:-1.0");             // Limit total duration range of search
   opts.define("u|beatsize=s:4");       // beat unit
   opts.define("A|attacks|attack=i:1"); // Minimum num of note onsets for event
   opts.define("t|tick=b", "display durations as tick values");  
   opts.define("f|rational=b", "display durations as rational values");  
   opts.define("tpw=b", "display only ticks per whole note");  
   opts.define("tpq=b", "display only ticks per quarter note");  

   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: 21 April 2010" << 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 (options.getBoolean("zero")) {
      zeroQ = 1;
   }
   appendQ   = opts.getBoolean("append");
   prependQ  = opts.getBoolean("prepend");
   durQ      = opts.getBoolean("duration");
   absQ      = opts.getBoolean("total-beat");
   beatQ     = opts.getBoolean("beat");
   sumQ      = opts.getBoolean("sum");
   nullQ     = opts.getBoolean("keep-nulls");
   tickQ     = opts.getBoolean("tick");
   rationalQ = opts.getBoolean("rational");
   tpwQ      = opts.getBoolean("tpw");
   tpqQ      = opts.getBoolean("tpq");
   Rvalue    = opts.getDouble("R");
   beatbase  = opts.getString("beatsize");
   uQ        = opts.getBoolean("beatsize");

   if (rationalQ) {
      tickQ = 1;
   }
 
   Attack = opts.getInteger("attack");
   if (Attack < 0) {
      Attack = 0;
   }

   Bsearch.setSize(0);
   Dsearch.setSize(0);

   if (opts.getBoolean("B")) {
      fillSearchString(Bsearch, opts.getString("B"));
   } 
   if (opts.getBoolean("D")) {
      fillSearchString(Dsearch, opts.getString("D"));
   }

   if (prependQ && appendQ) {
      prependQ = 1;
      appendQ = 0;
   }

   if (prependQ || appendQ) {
      nullQ = 1;
   }

   // display beat information if no other output option is given.
   if (!(absQ || durQ)) {
      beatQ = 1;
   }
}



//////////////////////////////
//
// fillSearchString --
//

void fillSearchString(Array& searcher, const char* string) {
   int len = strlen(string);
   char* tempstr;
   tempstr = new char[len+1];
   strcpy(tempstr, string);
   char* ptr;
   ptr = strtok(tempstr, " \t\n:;,");
   double value;

   searcher.setSize(1000);
   searcher.setGrowth(1000);
   searcher.setSize(0);
   
   while(ptr != NULL) {
      if (strcmp(ptr, "*") == 0) {
         value = -1;
      } else {
         value = atof(ptr);
      }
      searcher.append(value);
      ptr = strtok(NULL, " \t\n:;,");
   }

   delete [] tempstr;
}


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

void example(void) {
   cout <<
   "                                                                         \n"
   "# example usage of the meter program.                                    \n"
   "# analyze a Bach chorale for meter position:                             \n"
   "     meter chor217.krn                                                   \n"
   "                                                                         \n"
   "# display the metrical location spine with original data:                \n"
   "     meter -a chor217.krn                                                \n"
   "                                                                         \n"
   << endl;
}



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

void usage(const char* command) {
   cout <<
   "                                                                         \n"
   "Analyzes **kern data and generates a rhythmic analysis which gives       \n"
   "the beat location of **kern data records in the measure.  Currently,     \n"
   "input spines cannot split or join.                                       \n"
   "                                                                         \n"
   "Usage: " << command << " [-a][-b base-rhythm][-s|-d][input1 [input2 ...]]\n"
   "                                                                         \n"
   "Options:                                                                 \n"
   "   -a = assemble the analysis spine with the input data.                 \n"
   "   -b = set the base rhythm for analysis to specified kern rhythm value. \n"
   "   -d = gives the duration of each kern record in beat measurements.     \n"
   "   -s = sum the beat count in each measure.                              \n"
   "   --options = list of all options, aliases and default values           \n"
   "                                                                         \n"
   << endl;
}



// md5sum: 73039771851e574d20683314636364f6 beat.cpp [20100602]