//
// Programmer:    Craig Stuart Sapp <craig@ccrma.stanford.edu>
// Creation Date: Fri Sep 14 14:23:32 PDT 2001
// Last Modified: Tue Oct  9 16:44:19 PDT 2001 (after dots added)
// Last Modified: Thu Dec 27 19:22:48 PST 2001 (incorporated into museinfo)
// Filename:      ...sig/examples/all/bol2score.cpp
// Web Address:   http://sig.sapp.org/examples/museinfo/score/bol2score.cpp
// Syntax:        C++; museinfo
//
// Description:   Convert tabla bol notation in the humdrum format to
//                SCORE notation.
//

#include "humdrum.h"

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

// function declarations
void   checkOptions(Options& opts, int argc, char* argv[]);
void   example(void);
void   usage(const char* command);
void   generateScoreNotation(HumdrumFile& infile);
int    getStaffCount(HumdrumFile& infile, Array<int>& staffstart);
void   printLineInfo(HumdrumFile& infile, Array<int>& staffstart);
void   generateStaffInfo(HumdrumFile& infile, Array<int>&staffstart, 
                              Array<int>& staffend, Array<double>& duration,
                              Array<int>& staffpage, Array<int>& staffnumber);
void   printLineInfo(Array<int>&staffstart, Array<int>& staffend,
                              Array<double>& duration, Array<int>& staffpage, 
                              Array<int>& staffnumber);
void   printScoreStaff(HumdrumFile& infile, int start, int stop, double
                              dur, int staffno, int alignmentpoints);
char*  extractBol(char* buffer, const char* data);
double getBolWidth(const char* buffer, double staffsize, 
                              double ptsize, int line);
double getLetterWidth(char letter, double staffsize, double ptsize);
double getBolDuration(const char* bol);
int    getDotCount(const char* string);


// global variables
Options      options;            // database for command-line arguments
double       lmargin = 6.0;      // the right margin width
double       rmargin = 3.0;      // the right margin width
double       textbase = 0.0;     // vertical position of bol text
double       ptsize = 12.0;      // pointsize of text
double       staffsize = 1.0;    // size of staff (invisible)
int          staffperpage = 12;  // max number of staves per page
int          page = 1;           // page to output
int          debugQ = 0;         // for debugging lines;
int          infoQ = 0;          // for debugging lines;
int          pagecountQ = 0;     // for printing out the number of pages only
double       beambase = 10.5;    // base line for lowest beam (8th note)
double       beamsep = 0.70;     // distance between beams lines
double       beamthick = 0.35;   // thickness of beams
double       beatspace = 1.0;    // number of spaces between beats
double       spacefactor = 0.5;  // spacer shrinkage for end of beat
double       tfactor = 1.2;      // spacing of triplet mark on beam
double       rfactor = 1.0;      // sizing value of rests
double       staffspacer = -12.0; // spacing between staves
double       samspace = 0.2;     // spacing between sams


// nominal widths of characters in Helvetica 10pt
double letterwidth[28] = {
	2.11,  // a
	2.11,  // b
	1.90,  // c
	2.11,  // d
	2.11,  // e
	1.06,  // f
	2.11,  // g
	2.11,  // h
	0.84,  // i
	0.84,  // j
	1.90,  // k
	0.84,  // l
	3.16,  // m
	2.11,  // n
	2.11,  // o
	2.11,  // p
	2.11,  // q
	1.26,  // r
	1.90,  // s
	1.06,  // t
	2.11,  // u
	1.90,  // v
	2.74,  // w
	1.90,  // x
	1.90,  // y
	1.90,  // z
	1.60,  // space
	1.60   // +
};



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

int main(int argc, char* argv[]) {
   checkOptions(options, argc, argv);   // process the command-line options

   HumdrumFile infile;

   if (options.getArgCount() < 1) {
      infile.read(cin);
   } else {
      infile.read(options.getArg(1));
   }

   generateScoreNotation(infile);

   return 0;
}


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


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

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

   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.define("pagecount=b",            "print number of pages only");
   opts.define("i|info=b",               "print staff/page information only");
   opts.define("m|leftmargin=d:6.0",     "starting margin on left");
   opts.define("r|rightmargin=d:3.0",    "starting margin on right");
   opts.define("b|textbase=d:0.0",       "vertical position of bol text");
   opts.define("z|pointsize=d:14.0",     "point size of bol text");
   opts.define("s|staffsize=d:1.0",      "staff size");
   opts.define("l|staffperpage=i:12",    "number of staves per page");
   opts.define("p|page=i:1",             "page number to generate as output");
   opts.define("base|beambase=d:10.5",   "base line for lowest beam");
   opts.define("sep|beamsep=d:0.7",      "distance between beam lines");
   opts.define("thick|beamthick=d:0.35", "thickness of beams");
   opts.define("bs|beatspace=d:1.0",     "spaces between beats");
   opts.define("sf|spacefactor=d:0.5",   "spacer shrinkage for end of beat");
   opts.define("ss|staffspacer=d:-12",   "spacer shrinkage between staves");
   opts.define("sam|samspace=d:0.20",    "space between sam staves");
   opts.define("tfactor=d:1.2",          "spacing of triplet mark on beam");
   opts.define("rfactor=d:1.0",          "sizing value of rests");

   opts.process(argc, argv);
   
   // handle basic options:
   if (opts.getBoolean("author")) {
      cout << "Written by Craig Stuart Sapp, "
           << "craig@ccrma.stanford.edu, Sep 2001" << endl;
      exit(0);
   } else if (opts.getBoolean("version")) {
      cout << argv[0] << ", version: 14 Sep 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);
   }

   lmargin      = opts.getDouble("leftmargin");
   rmargin      = opts.getDouble("rightmargin");
   textbase     = opts.getDouble("textbase");
   ptsize       = opts.getDouble("pointsize");
   staffsize    = opts.getDouble("staffsize");
   staffperpage = opts.getInt("staffperpage");
   page         = opts.getInt("page");
   debugQ       = opts.getBoolean("debug");
   infoQ        = opts.getBoolean("info");
   pagecountQ   = opts.getBoolean("pagecount");
   beambase     = opts.getDouble("beambase");
   beamsep      = opts.getDouble("beamsep");
   beamthick    = opts.getDouble("beamthick");
   beatspace    = opts.getDouble("beatspace");
   spacefactor  = opts.getDouble("spacefactor");
   samspace     = opts.getDouble("samspace");
   staffspacer  = opts.getDouble("staffspacer");
   tfactor      = opts.getDouble("tfactor");
   rfactor      = opts.getDouble("rfactor");

   if (ptsize == 14.0) {
      beambase = beambase;
   } else if (ptsize == 10.0) {
      beambase = beambase * ((ptsize + 2.85) / 14.0);
   } else {
      beambase = beambase * ((ptsize + 2.6) / 14.0);
   }


}



//////////////////////////////
//
// generateScoreNotation --
//

void generateScoreNotation(HumdrumFile& infile) {
   Array<int>    staffstart;
   Array<int>    staffend;
   Array<double> duration;
   Array<int>    staffpage;
   Array<int>    staffnumber;

   infile.analyzeRhythm();
   // int staffcount = getStaffCount(infile, staffstart);
   if (pagecountQ) {
      cout << (int)((double)staffstart.getSize()/staffperpage + 0.99);
      cout << endl;
      exit(0);
   }
   generateStaffInfo(infile, staffstart, staffend, duration,
         staffpage, staffnumber);
   if (infoQ) {
      printLineInfo(staffstart, staffend, duration, staffpage, staffnumber);
      exit(1);
   }


   int i;
   int xstaffcount = 0;
   for (i=0; i<staffstart.getSize(); i++) {
      if (staffpage[i] == page) {
         xstaffcount++;
         if ((duration[i] - 8.0) < 0.01) {
            printScoreStaff(infile, staffstart[i], staffend[i],
                  duration[i], staffnumber[i], 2);
         } else {
            printScoreStaff(infile, staffstart[i], staffend[i],
                  duration[i], staffnumber[i], (int)(duration[i] + 0.001));
         }
      }
   }

   // write a horizontal spacing of 0.2 inches between staves
   // based on 10 pt size
   if (xstaffcount > 0) {
      int spacers = xstaffcount / 2;
      double hsize = 0.236 * xstaffcount + spacers * samspace;
      cout << "h\n";
      cout << hsize << "\n";
      if (xstaffcount % 2 == 0) {
         cout << "2\n";
      } else {
         cout << "1,2\n"; 
      }
   }

}



//////////////////////////////
//
// printScoreStaff -- print the starting and stopping text line and
//   duration of each staff line for debugging purposes.
//

void printScoreStaff(HumdrumFile& infile, int start, int stop, double
      dur, int staffno, int alignmentpoints) {

   if (staffsize == 1.0) {
      cout << "8.0 " << staffno << ".0 0.0 "
           <<  staffno * staffspacer + staffspacer
           << " 0.0 0.0 -1.0" << "\n";
   } else {
      cout << "8.0 " << staffno << ".0 0.0 "
           << staffno * staffspacer + staffspacer
           << " " << staffsize 
           << "0.0 -1.0 0.0\n";
   }

//   int divisions = (int)dur;   // assume integer duration for now
   int divisions = alignmentpoints;
   Array<double> position;
   position.setSize(divisions);
   position.allowGrowth(0);
   int i;
   for (i=0; i<divisions; i++) {
      position[i] = (200.0 - lmargin - rmargin) * i / divisions + lmargin;
   }

   double p6size = ptsize/13.56;

   double currdur = 0.0;
   double width = 0.0;
   int beat;
   char buffer[128] = {0};

   Array<double> durations;
   durations.setSize(stop - start);
   durations.setSize(0);
   durations.allowGrowth();
   double cdur = 0.0;

   Array<int> dots;
   dots.setSize(stop-start);
   dots.setSize(0);
   int dot = 0;
   
   // fill the durations for each bol into the durations array
   double mindur = 9999;
   for (i=start; i<=stop; i++) {
      if (infile[i].getType() != E_humrec_data) {
         continue;
      }
      cdur = getBolDuration(infile[i][0]);
      durations.append(cdur);

      dot = getDotCount(infile[i][0]);
      dots.append(dot);

      if (mindur > cdur) {
         mindur = cdur;
      }
   }

   Array<double> spacers;   // number of semi-alignment spaces after each bol
   Array<double> beams;     // number of beams for each bol
   spacers.setSize(durations.getSize());
   beams.setSize(durations.getSize());
   int currbeat;
   int nnextbeat;
   double dursum = 0;
   for (i=0; i<spacers.getSize(); i++) {
      currbeat = (int)(dursum + 1.001);
      if (i < spacers.getSize() - 1) {
         nnextbeat = (int)(dursum + 1.001 + durations[i]);
      } else {
         nnextbeat = currbeat + 1;
      }
      dursum += durations[i];

      beams[i] = (int)(log10(1.0/durations[i])/log10(2.0) + 0.05);
      if (dots[i] > 0) {
         beams[i]++;
      }

      spacers[i]  = (int)(durations[i] / mindur + 0.001) - 1;
      if (spacers[i] < 0) {
         spacers[i] = 0.0;
      }
      spacers[i] *= getLetterWidth(' ', staffsize, ptsize);
   
      // minimize spacers at ends of beats
      if (nnextbeat != currbeat) {
         spacers[i] *= spacefactor;
      }
   }

   Array<double> bolstartpoints;
   bolstartpoints.setSize(spacers.getSize());
   Array<double> bolendpoints;               // including spacers after bol
   bolendpoints.setSize(spacers.getSize());

   int counter = 0;
   int region = 0;
   int lastbeat = 1;
   // int nextbeat = 2;
   beat = 1;
   double posit;
   double bbeatspace = getLetterWidth(' ', staffsize, ptsize) * beatspace;
   for (i=start; i<=stop; i++) {
      if (infile[i].getType() == E_humrec_interpretation) {
         if (strcmp(infile[i][0], "*sam") == 0) {
            posit = lmargin - getLetterWidth(' ', staffsize, ptsize) -
                  getLetterWidth('+', staffsize, ptsize);
            cout << "t " << staffno << " " << posit
                 << " 0.0 1.0 "
                 << p6size << " 0.0 0.0 0.0 0.0 0.0\n";
            cout << "_04+\n";
         }
      }

      if (infile[i].getType() != E_humrec_data) {
         continue;
      }


      extractBol(buffer, infile[i][0]);
      lastbeat = beat; 
      beat = (int)(currdur + 0.0005) + 1;
      if (lastbeat != beat) {
         position[region] += bbeatspace;
      }
      region = (int)((double)(beat - 1) / dur * divisions + 0.001);
      if (strchr(infile[i][0], '[') != NULL) {
         // display repetition marker
         width = 2.0 * getLetterWidth('w', staffsize, ptsize);
         cout << 12.0 << " " 
              << staffno << " "
              << position[region] + width/2.0 << " "
              << 1.5 / p6size << " "
              << 0.0 << " "         // 0 = rectangle shape
              << width / 2.7*rfactor << " " // 6: horizontal size
              << 0.0 << " "         // 7: vertical size
              << 0.0  << " "        // 8: thickness
              << 45.0 << " "        // 9: rotation
              << 0.0  << " "        // 10: parallelogram
              << 0.0  << " "        // 11: endpoint in arc
              << 0.0  << " "        // 12: changes circles into polygons
              << 1.0  << " "        // 13: filled in
              << "\n";
         position[region] += width; // approximate width of marker
         position[region] += bbeatspace;
      }
      if (strcmp(buffer, "r") == 0) {
         width = 2.0 * getLetterWidth('w', staffsize, ptsize);
      } else {
         width = getBolWidth(buffer, staffsize, ptsize, i+1);
      }
      bolstartpoints[counter] = position[region];
      bolendpoints[counter] = bolstartpoints[counter] + spacers[counter] +
            width;

      double restvpos = 1.5 * p6size;

      if (strcmp(buffer, "r") == 0) {
         // rests displayed as circles
         cout << 12.0 << " " 
              << staffno << " "
              << bolstartpoints[counter] + width/2.0 << " "
              << restvpos << " "
              << 1.0 << " "         // 1 = circle shape
              << width/2.7*rfactor  << " "  // size
              << 0.0 << " "  
              << 3.0  << " "        // thickness
              << endl;
      } else if (strncmp(buffer, "ry", 2) == 0) {
         // don't display editor marked rests
      } else {
         cout << "t " << staffno << " " << bolstartpoints[counter] 
              << " 0.0 1.0 "
              << p6size << " 0.0 0.0 0.0 0.0 0.0" << "\n";
         cout << "_04" << buffer << "\n";
      }
      position[region] += width;
      position[region] += spacers[counter];
      currdur += getBolDuration(infile[i][0]);
      counter++;
   }

   // write beam lines here

   int j;
   double vertpos;
   double sh = 0.0;
   int triplet = 0;
   int nexttriplet = 0;
   int tbeat = 1;
   double tsum = 0;
   int ntbeat = 1;

   int stbeat = 1;
   int sntbeat = 1;

   int striplet = 0;
   int nextstriplet = 0;

   int striplethold = 0;

   for (i=0; i<bolstartpoints.getSize(); i++) {
      tsum += durations[i];
      tbeat = ntbeat;
      stbeat = sntbeat;
      ntbeat = (int)(tsum + 0.001);
      sntbeat = (int)(2.0 * (tsum + 0.001));

      triplet = nexttriplet;

      if (i < bolstartpoints.getSize() - 1) {
         if  (fabs(durations[i+1] - 0.333333) < 0.01) {
            nexttriplet = 1;
         } else {
            nexttriplet = 0;
         }
      } else {
         nexttriplet = 0;
      }

      striplet = nextstriplet;

      if (i < bolstartpoints.getSize() - 1) {
         if  (fabs(durations[i+1] - 1.0/6.0) < 0.01) {
            nextstriplet = 1;
         } else {
            nextstriplet = 0;
         }
      } else {
         nextstriplet = 0;
      }

      striplethold += striplet;

      if (striplethold && triplet) {
         triplet = 0;
         striplet = 1;
      }

      if ((triplet == 1 && nexttriplet == 0) ||
          (triplet == 1 && (ntbeat != tbeat))) {
         sh = 0.75 * getLetterWidth('i', staffsize, ptsize);
         // display a triplet number
         cout << 10.0 << " "                         // 1: number code
              << staffno << " "                      // 2: staff number
              << bolendpoints[i] - sh*tfactor << " " // 3: horizontal position
              << beambase - 7.0 << " "               // 4: vertical position
              << 3.0   << " "                        // 5: number to display
              << p6size/1.5 << " "                   // 6: size
              << 1 << " "                            // 7: font (times italic)
              << "\n";
      } else if (striplet == 1 && (sntbeat != stbeat)) {
         sh = 0.75 * getLetterWidth('i', staffsize, ptsize);
         // display a triplet number
         cout << 10.0 << " "                         // 1: number code
              << staffno << " "                      // 2: staff number
              << bolendpoints[i] - sh*tfactor << " " // 3: horizontal position
              << beambase - 7.0 + beamsep << " "     // 4: vertical position
              << 3.0   << " "                        // 5: number to display
              << p6size/1.5 << " "                   // 6: size
              << 1 << " "                            // 7: font (times italic)
              << "\n";
      } else {
         sh = 0.0;
      }

      if (ntbeat != tbeat) {
         striplethold = 0;
      }

      if (dots[i] > 0) {
         // ignoring double dots for now
         double offst = getLetterWidth(' ', staffsize, ptsize);
         vertpos = (beambase + beams[i] * beamsep + 0.5)/2.0;
         if (ptsize == 10.0) {
            vertpos = vertpos * 0.70;
         }
         cout << "12.0 "                // 1: item number for circles
              << staffno << " "         // 2: staff number
              << bolstartpoints[i] + offst << " " // 3: horizontal position
              << vertpos << " "         // 4: vertical position
              << 1.0     << " "         // 5: shape, circle=1
              << 0.85    << " "         // 6: size
              << 0.0     << " "         // 7: 
              << 0.0     << " "         // 8: 
              << 0.0     << " "         // 9: 
              << 0.0     << " "         // 10:
              << 0.0     << " "         // 11: 
              << 0.0     << " "         // 12: 
              << 1.0                    // 13: filled = 1
              << "\n";
      }

      for (j=0; j<beams[i]; j++) {
         // draw beam from bolstartpoints[i] to bolendpoints[i]
         // starting at beambase, and going up by beamsep amount
         // beam thickness is given by beamthick.
         vertpos = beambase + j * beamsep;
         cout << "6.0 "                   // 1: item number for beams
              << staffno << " "           // 2: staff number
              << bolstartpoints[i]-sh << " " // 3: left horizontal position
              << vertpos << " "           // 4: left vertical position
              << vertpos << " "           // 5: right vertical position
              << bolendpoints[i]-sh << " "   // 6: right horizontal position
              << 21 << " "                // 7: beam orientation
              << 0.0 << " "               // 8: number for tuplets
              << 0.0 << " "               // 9: displace endpoints
              << 0.0 << " "               // 10: secondary beams
              << 0.0 << " "               // 11: secondary beams
              << 0.0 << " "               // 12: secondary beams
              << 0.0 << " "               // 13: additional secondary beams
              << 0.0 << " "               // 14: additional secondary beams
              << 0.0 << " "               // 15: additional secondary beams
              << 0.0 << " "               // 16: unused
              << beamthick                // 17: beam thickness
              << endl;
      }
   }

   cout << "\n";
}



//////////////////////////////
//
// getDotCount -- returns the number of '.' in the string.
//

int getDotCount(const char* string) {
   int output = 0;
   int i = 0;
   while (string[i] != '\0' && i < 10000) {
      if (string[i] == '.') {
         output++;
      }
      i++;
   }
   return output;
}



//////////////////////////////
//
// getBolDuration -- returns the rhythmic duration of the bol
//

double getBolDuration(const char* bol) {
   return Convert::kernToDuration(bol);
}



//////////////////////////////
//
// getBolWidth -- return the size of the bol 
//

double getBolWidth(const char* buffer, double staffsize, double ptsize, 
      int line) {
   double width = 0.0;
   int i = 0;
   while (buffer[i] != '\0') {
      if (isalpha(buffer[i])) {
         width += getLetterWidth(buffer[i], staffsize, ptsize);
      } else {
         cout << "Error with bol on line " << line << ": " << buffer << endl;
         cout << "Funny character in bol: " << buffer[i] << endl;
         exit(1);
      }
      i++;
   }

   return width;
}



//////////////////////////////
//
// getLetterWidth -- returns the size of the letter
//

double getLetterWidth(char letter, double staffsize, double ptsize) {
   double nominalsize = 0.0;
   if (isalpha(letter)) {
      nominalsize = letterwidth[tolower(letter) - 'a'];
   } else if (letter == ' ') {
      nominalsize = letterwidth[26];
   } else if (letter == '+') {
      nominalsize = letterwidth[27];
   }
   return nominalsize * ptsize/10.0 * staffsize;
}



//////////////////////////////
//
// extractBol -- get the bol syllable from the humdrum data
//

char * extractBol(char* buffer, const char* data) {
   int len = strlen(data);
   int i;
   int outindex = 0;
   int found = 0;
   for (i=0; i<len; i++) {
      if (isalpha(data[i])) {
         buffer[outindex++] = tolower(data[i]);
         found = 1;
      } else if (found == 1) {
         break;
      }
   }
   buffer[outindex] = '\0';

   return buffer;
}



//////////////////////////////
//
// printLineInfo -- print the starting and stopping text line and
//   duration of each staff line for debugging purposes.
//

void printLineInfo(Array<int>&staffstart, Array<int>& staffend,
      Array<double>& duration, Array<int>& staffpage, Array<int>& staffnumber) {
   int startpage = 0;
   int i;
   for (i=0; i<staffstart.getSize(); i++) {
      if (startpage != staffpage[i]) {
         startpage = staffpage[i];
         cout << "Page " << startpage << ":" << endl;
      }
      cout << "\tStaff=" << staffnumber[i] << "   ";
      cout << "\tStart=" << staffstart[i] << "   ";
      cout << "\tEnd=" << staffend[i] << "     ";
      cout << "\tDur=" << duration[i];
      cout << endl;
   }
}



//////////////////////////////
//
// generateStaffInfo -- caucluate the starting and stopping text line and
//   duration of each staff line for generating pages.
//

void generateStaffInfo(HumdrumFile& infile, Array<int>&staffstart, 
      Array<int>& staffend, Array<double>& duration, Array<int>& staffpage, 
      Array<int>& staffnumber) {

   staffend.setSize(staffstart.getSize());
   duration.setSize(staffstart.getSize());
   staffpage.setSize(staffstart.getSize());
   staffnumber.setSize(staffstart.getSize());

   staffend.allowGrowth(0);
   duration.allowGrowth(0);
   staffpage.allowGrowth(0);
   staffnumber.allowGrowth(0);

   int i;
   double dur;
   int e;
   int staff;
   int page = 0;
   // int pagecount = (int)((double)staffstart.getSize()/staffperpage + 0.99);
   int lastpagestaff = staffstart.getSize() % staffperpage;
   if (lastpagestaff == 0) {
      lastpagestaff = staffperpage;
   }
   int lines = staffstart.getSize();
   for (i=0; i<lines; i++) {
      if (i % staffperpage == 0) {
         page++;
      }
      staffpage[i] = page;
      staff = lines - i;
      if (staff > lastpagestaff) {
         staff = (staff - lastpagestaff - 1) % staffperpage + 1;
      }
      staffnumber[i] = staff;
      if (i<lines-1) {
         e = staffstart[i+1];
      } else {
         e = infile.getNumLines() - 1;
      }
      staffend[i] = e;
      dur = infile[e].getAbsBeat() - infile[staffstart[i]].getAbsBeat();
      dur = ((int)(dur * 10000.0 + 0.5))/10000.0;
      duration[i] = dur;
   }
}



//////////////////////////////
//
// getStaffCount --
//

int getStaffCount(HumdrumFile& infile, Array& staffstart) {
   staffstart.setSize(128);
   staffstart.setSize(0);
   staffstart.allowGrowth(1);

   int i;
   double linedur = 0.0;
   for (i=0; i<infile.getNumLines(); i++) {
      if (strncmp(infile[i][0], "**", 2) == 0) {
         staffstart.append(i);
      } else if (strcmp(infile[i][0], "*break") == 0) {
         linedur = infile[i].getAbsBeat() -
               infile[staffstart.last()].getAbsBeat();
         if (fabs(linedur) > 0.001) {
            staffstart.append(i);
         } else {
            staffstart.last() = i;
         }
      }
   }

   if (fabs(infile.getTotalDuration() -
         infile[staffstart.last()].getAbsBeat()) < 0.001) {
      staffstart.setSize(staffstart.getSize()-1);
   }

   staffstart.allowGrowth(0);
   return staffstart.getSize();
}



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

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



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

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


// md5sum: 1a590f43c64970f25515ebb7c19aac12 bol2score.cpp [20050403]