//
// 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]